2012-06-04 2 views
3

Я использую свойство зависимостей GroupDescription для группировки элементов в представлении списка WPF в соответствии с свойством моего списка, рассматривающим источник элементов.ListView Grouping не обновляется при изменении связанного свойства

Моя проблема заключается в том, что группировка обновляется только в том случае, если значение GroupDescription изменено, и не сразу изменилось связанное свойство элемента в источнике списка.

В этом примере ниже GroupDescription установлен в город, в результате которого приводится описание группы «Город: Гамбург». Но когда изменяется свойство города объектов, группировка в виде списка не обновляется, а это означает, что в группе «Город: Гамбург» будет элемент с городом «Берлин».

Группирование обновляется только после обновления GroupDescription. Я попытался найти работу с использованием метода PersonPropertyChanged, который меняет GroupDescription на PersonId и сразу возвращается в City. Однако эта работа вокруг результатов в представлении списка всегда возвращается к вершине в случае, если позиция прокрутки была, например, посередине или в конце списка. Это может быть очень раздражающим при работе со списком с hundres записей с изменяющимися свойствами.

Есть ли способ «обновить» группировку после изменения свойства элементов без просмотра списка, переходящего назад?

Заранее благодарю за помощь! Томас

GroupingListView.cs

using System.Windows.Controls; 
using System.Windows; 
using System.Windows.Data; 

namespace WpfApplication 
{ 
    /// <summary> 
    /// Enhanced list view based on WPF ListView with dependency properties for GroupDescriptions 
    /// </summary> 
    public class GroupingListView : ListView 
    { 
     /// <summary> 
     /// Dependency property for group descriptions 
     /// </summary> 
     public string GroupDescription 
     { 
      get { return (string)GetValue(GroupDescriptionProperty); } 
      set { SetValue(GroupDescriptionProperty, value); } 
     } 

     /// <summary> 
     /// Using a DependencyProperty as the backing store for GroupDescription. This enables animation, styling, binding, etc... 
     /// </summary> 
     public static readonly DependencyProperty GroupDescriptionProperty = 
      DependencyProperty.Register("GroupDescription", 
             typeof(string), 
             typeof(GroupingListView), 
             new UIPropertyMetadata(string.Empty, GroupDescriptionChanged)); 

     private static void GroupDescriptionChanged(DependencyObject source, DependencyPropertyChangedEventArgs args) 
     { 
      var control = source as GroupingListView; 
      // Stop if source is not of type DetailedListView 
      if (control == null) return; 

      // Stop if myView is not available, myView can not group, groupdescription missing\ 
      // or the argument is empty 
      var myView = (CollectionView)CollectionViewSource.GetDefaultView(control.ItemsSource); 
      if (myView == null || !myView.CanGroup || (string) args.NewValue == string.Empty || 
       myView.GroupDescriptions == null) 
      { 
       return; 
      } 
      myView.GroupDescriptions.Clear(); 
      // If a group description already 
      if(myView.GroupDescriptions.Count > 0) 
      { 
       var prop = myView.GroupDescriptions[0] as PropertyGroupDescription; 
       if(prop != null) 
       { 
        if(!prop.PropertyName.Equals((string)args.NewValue)) 
        { 
         myView.GroupDescriptions.Clear(); 
        } 
       } 
      } 

      // Stop if at this point a group description still exists. This means the newValue is 
      // equal to the old value and nothing needs to be changed 
      if (myView.GroupDescriptions.Count != 0) return; 

      // If this code is reached newValue is different than the current groupDescription value 
      // therefore the newValue has to be added as PropertyGroupDescription 
      var groupDescription = new PropertyGroupDescription((string)args.NewValue); 
      // Clear and add the description only if it's not already existing 
      myView.GroupDescriptions.Add(groupDescription); 
     } 
    } 
} 

MainWindow.xaml

<Window x:Class="WpfApplication.MainWindow" 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:WpfApplication="clr-namespace:WpfApplication" 
     Title="MainWindow" Height="300" Width="300"> 
    <StackPanel> 
     <Button Content="Change" Click="btnChangeCity" Height="22"/><Button Content="Change back" Click="btnChangeCityBack" Height="22"/> 
     <WpfApplication:GroupingListView ItemsSource="{Binding Persons}" Height="200" 
             GroupDescription="{Binding GroupDescription}" x:Name="GroupingListView"> 
      <ListView.GroupStyle> 
       <GroupStyle> 
        <GroupStyle.HeaderTemplate> 
         <DataTemplate> 
          <Grid HorizontalAlignment="Stretch"> 
           <Border HorizontalAlignment="Stretch" BorderBrush="Transparent" BorderThickness="1" CornerRadius="3"> 
            <Border HorizontalAlignment="Stretch" BorderBrush="LightGray" BorderThickness="0,0,0,1" CornerRadius="0"> 
             <StackPanel Orientation="Horizontal"> 
              <TextBlock Foreground="LightGray" Text="{Binding GroupDescription, ElementName=GroupingListView}"/> 
              <TextBlock Foreground="LightGray" Text=" : "/> 
              <TextBlock Foreground="LightGray" Text="{Binding Name}" HorizontalAlignment="Stretch"/> 
             </StackPanel> 
            </Border> 
           </Border> 
          </Grid> 
         </DataTemplate> 
        </GroupStyle.HeaderTemplate> 
       </GroupStyle> 
      </ListView.GroupStyle> 
      <ListView.View> 
       <GridView> 
        <GridViewColumn Header="PersonId" Width="100"> 
         <GridViewColumn.CellTemplate> 
          <DataTemplate> 
           <TextBlock TextTrimming="CharacterEllipsis" Text="{Binding PersonId, Mode=Default}"/> 
          </DataTemplate> 
         </GridViewColumn.CellTemplate> 
        </GridViewColumn> 
        <GridViewColumn Header="City" Width="100"> 
         <GridViewColumn.CellTemplate> 
          <DataTemplate> 
           <TextBlock TextTrimming="CharacterEllipsis" Text="{Binding City, Mode=Default}"/> 
          </DataTemplate> 
         </GridViewColumn.CellTemplate> 
        </GridViewColumn> 
       </GridView> 
      </ListView.View> 
     </WpfApplication:GroupingListView> 
    </StackPanel> 
</Window> 

MainWindow.xaml.cs

using System.Collections.ObjectModel; 
using System.Collections.Specialized; 
using System.ComponentModel; 

namespace WpfApplication 
{ 
    /// <summary> 
    /// Interaction logic for MainWindow.xaml 
    /// </summary> 
    public partial class MainWindow : INotifyPropertyChanged 
    { 
     public class Person : INotifyPropertyChanged 
     { 


      public Person(string personId, string city) 
      { 
       PersonId = personId; 
       City = city; 
      } 

      private string _personId; 
      private string _city; 

      public event PropertyChangedEventHandler PropertyChanged; 
      protected void OnPropertyChanged(string name) 
      { 
       PropertyChangedEventHandler pc = PropertyChanged; 
       if (pc != null) 
        pc(this, new PropertyChangedEventArgs(name)); 
      } 

      public string PersonId 
      { 
       get { return _personId; } 
       set { _personId = value; OnPropertyChanged("PersonId"); } 
      } 

      public string City 
      { 
       get { return _city; } 
       set { _city = value; OnPropertyChanged("City"); } 
      } 


     } 

     public ObservableCollection<Person> Persons { get; set; } 

     public string GroupDescription 
     { 
      get { return _groupDescription; } 
      set { _groupDescription = value; OnPropertyChanged("GroupDescription"); } 
     } 

     private string _groupDescription; 

     public MainWindow() 
     { 
      InitializeComponent(); 
      DataContext = this; 
      GroupDescription = "City"; 
      Persons = new ObservableCollection<Person>(); 
      Persons.CollectionChanged += PersonsCollectionChanged; 
      Persons.Add(new Person("1", "Hamburg")); 
      Persons.Add(new Person("2", "Hamburg")); 
      Persons.Add(new Person("3", "Hamburg")); 
      Persons.Add(new Person("4", "Hamburg")); 
      Persons.Add(new Person("5", "Hamburg")); 
      Persons.Add(new Person("6", "Hamburg")); 
      Persons.Add(new Person("7", "Hamburg")); 
      Persons.Add(new Person("8", "Hamburg")); 
      Persons.Add(new Person("9", "Berlin")); 
      Persons.Add(new Person("10", "Hamburg")); 
      Persons.Add(new Person("11", "Hamburg")); 
      Persons.Add(new Person("12", "Munich")); 
      Persons.Add(new Person("13", "Munich")); 
      OnPropertyChanged("Persons"); 
     } 

     public void PersonsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
     { 
      if (e.Action == NotifyCollectionChangedAction.Remove) 
      { 
       foreach(Person item in e.OldItems) 
       { 
        //Removed items 
        item.PropertyChanged -= PersonPropertyChanged; 
       } 
      } 
      else if (e.Action == NotifyCollectionChangedAction.Add) 
      { 
       foreach(Person item in e.NewItems) 
       { 
        //Added items 
        item.PropertyChanged += PersonPropertyChanged; 
       }  
      }  
     } 

    public void PersonPropertyChanged(object sender, PropertyChangedEventArgs e) 
    { 
     //GroupDescription = "PersonId"; 
     //GroupDescription = "City"; 
    } 


     public event PropertyChangedEventHandler PropertyChanged; 
     protected void OnPropertyChanged(string name) 
     { 
      PropertyChangedEventHandler pc = PropertyChanged; 
      if (pc != null) 
       pc(this, new PropertyChangedEventArgs(name)); 
     } 

     private void btnChangeCity(object sender, System.Windows.RoutedEventArgs e) 
     { 
      Persons[0].City = "Berlin"; 
     } 

     private void btnChangeCityBack(object sender, System.Windows.RoutedEventArgs e) 
     { 
      Persons[0].City = "Hamburg"; 
     } 

    } 
} 

ответ

8

Я сам нашел рабочее решение.

В методе PersonPropertyChanged я называю

ICollectionView view = CollectionViewSource.GetDefaultView(GroupingListView.ItemsSource); 
view.Refresh(); 

Я не уверен, если это идеальное решение, но это работает для моей проблемы.

11

Я понимаю, что это довольно поздно, но если вы используете .NET4.5 или выше, вы можете использовать функцию Live Grouping, которая, я думаю, будет делать именно то, что вы хотите.

Например, вместо связывания ListViewItemsSource непосредственно Persons, вы бы вместо того, чтобы связываться с CollectionViewSource который сам связывается с Persons:

<Window.Resources> 
    <CollectionViewSource x:Key="PersonsViewSource" Source="{Binding Persons}" IsLiveGroupingRequested="True"> 
     <CollectionViewSource.GroupDescriptions> 
       <PropertyGroupDescription PropertyName="GroupName" /> 
     </CollectionViewSource.GroupDescriptions>    
    </CollectionViewSource> 
</Window.Resources> 

Как показано выше, вы бы просто добавить свойство IsLiveGroupingRequested="True" и добавить имя свойства, которое вы хотите перегруппировать.

При изменении свойства GroupName (при использовании INotifyPropertyChanged) соответствующий элемент переместится в нужную группу в ListView, не изменив ничего.

+0

Прекрасно работает с шаблоном MVVM. –

+0

Возьми мой верхний! Должен быть ответ. –

Смежные вопросы