2016-01-21 36 views
11

UWPアプリケーションでは、ObservableCollectionをグループ化してソートし、すべてのライブ通知の良し悪しをどのように保つことができますか?UWP ObservableCollectionのソートとグループ化

私が見てきたほとんどの単純なUWPの例では、ObservableCollectionを公開するViewModelが一般にあり、ViewのListViewにバインドされています。 アイテムがObservableCollectionに追加または削除されると、ListViewはINotifyCollectionChanged通知に反応して変更を自動的に反映します。これはソートされていない、またはグループ化されていないObservableCollectionの場合はすべて正常に動作しますが、コレクションをソートまたはグループ化する必要がある場合、更新通知を保持するための容易な方法はないようです。さらに、並べ替えやグループの順序を即座に変更することは、重要な実装の問題を投げ捨てるようです。

++

あなたは非常に単純なクラスの接触ののObservableCollectionを公開して、既存のデータキャッシュバックエンドを持ってシナリオを取ります。

public class Contact 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string State { get; set; } 
} 

これのObservableCollectionの経時変化、そして我々はデータキャッシュの変化に応じて を更新ビューでグループ化されたリアルタイムおよびソートされたリストを提示したいです。私たちはまた、ユーザーに、姓と状態との間のグループ化をオンザフライで切り替えるオプションを提供したいと考えています。

++

これは比較的簡単です。 キャッシュの連絡先コレクションをそのまま表示するデータキャッシュを参照する、単純なViewModelを作成できます。

public class WpfViewModel 
{ 
    public WpfViewModel() 
    { 
     _cache = GetCache(); 
    } 

    Cache _cache; 

    public ObservableCollection<Contact> Contacts 
    { 
     get { return _cache.Contacts; } 
    } 
} 

次に、これを、XAMLリソースとしてCollectionViewSource、Sort、Groupの定義を実装するビューにバインドすることができます。ユーザーがウィンドウの下部にGROUPBYボタンをクリックしたとき

<Window ..... 
    xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"> 

    <Window.DataContext> 
     <local:WpfViewModel /> 
    </Window.DataContext> 

    <Window.Resources> 
     <CollectionViewSource x:Key="cvs" Source="{Binding Contacts}" /> 
     <PropertyGroupDescription x:Key="stategroup" PropertyName="State" /> 
     <PropertyGroupDescription x:Key="initialgroup" PropertyName="LastName[0]" /> 
     <scm:SortDescription x:Key="statesort" PropertyName="State" Direction="Ascending" /> 
     <scm:SortDescription x:Key="lastsort" PropertyName="LastName" Direction="Ascending" /> 
     <scm:SortDescription x:Key="firstsort" PropertyName="FirstName" Direction="Ascending" /> 
    </Window.Resources> 

    <Grid> 
     <Grid.RowDefinitions> 
      <RowDefinition Height="*" /> 
      <RowDefinition Height="Auto" /> 
     </Grid.RowDefinitions> 

     <ListView ItemsSource="{Binding Source={StaticResource cvs}}"> 
      <ListView.ItemTemplate> 
       <DataTemplate> 
        <Grid> 
         <Grid.ColumnDefinitions> 
          <ColumnDefinition Width="100" /> 
          <ColumnDefinition Width="100" /> 
          <ColumnDefinition Width="*" /> 
         </Grid.ColumnDefinitions> 
         <TextBlock Text="{Binding LastName}" /> 
         <TextBlock Text="{Binding FirstName}" Grid.Column="1" /> 
         <TextBlock Text="{Binding State}" Grid.Column="2" /> 
        </Grid> 
       </DataTemplate> 
      </ListView.ItemTemplate> 
      <ListView.GroupStyle> 
       <GroupStyle> 
        <GroupStyle.HeaderTemplate> 
         <DataTemplate> 
          <Grid Background="Gainsboro"> 
           <TextBlock FontWeight="Bold" 
              FontSize="14" 
              Margin="10,2" 
              Text="{Binding Name}"/> 
          </Grid> 
         </DataTemplate> 
        </GroupStyle.HeaderTemplate> 
       </GroupStyle> 
      </ListView.GroupStyle> 
     </ListView> 

     <StackPanel Orientation="Horizontal" Grid.Row="1"> 
      <Button Content="Group By Initial" Click="InitialGroupClick" /> 
      <Button Content="Group By State" Click="StateGroupClick" /> 
     </StackPanel> 

    </Grid> 
</Window> 

その後、我々は、我々は、コードビハインドでその場でグループと並べ替えることができます。

private void InitialGroupClick(object sender, RoutedEventArgs e) 
{ 
    var cvs = FindResource("cvs") as CollectionViewSource; 
    var initialGroup = (PropertyGroupDescription)FindResource("initialgroup"); 
    var firstSort = (SortDescription)FindResource("firstsort"); 
    var lastSort = (SortDescription)FindResource("lastsort"); 

    using (cvs.DeferRefresh()) 
    { 
     cvs.GroupDescriptions.Clear(); 
     cvs.SortDescriptions.Clear(); 
     cvs.GroupDescriptions.Add(initialGroup); 
     cvs.SortDescriptions.Add(lastSort); 
     cvs.SortDescriptions.Add(firstSort); 
    } 
} 

private void StateGroupClick(object sender, RoutedEventArgs e) 
{ 
    var cvs = FindResource("cvs") as CollectionViewSource; 
    var stateGroup = (PropertyGroupDescription)FindResource("stategroup"); 
    var stateSort = (SortDescription)FindResource("statesort"); 
    var lastSort = (SortDescription)FindResource("lastsort"); 
    var firstSort = (SortDescription)FindResource("firstsort"); 

    using (cvs.DeferRefresh()) 
    { 
     cvs.GroupDescriptions.Clear(); 
     cvs.SortDescriptions.Clear(); 
     cvs.GroupDescriptions.Add(stateGroup); 
     cvs.SortDescriptions.Add(stateSort); 
     cvs.SortDescriptions.Add(lastSort); 
     cvs.SortDescriptions.Add(firstSort); 
    } 
} 

これはすべて正常に動作し、アイテムはデータキャッシュコレクションの変更に応じて自動的に更新されます。リストビューのグループ化と選択はコレクションの変更の影響を受けず、新しい連絡先アイテムは正しくグループ化されます。グループ化は、実行時にユーザーによってStateとLastNameの間でスワップできます。 UWP世界で++

は、CollectionViewSourceはもはやGroupDescriptionsとSortDescriptionsのコレクションを持っていない、とソート/グループ化のViewModelレベルで実行する必要があります。私が見つけた実行可能な解決策に最も近いアプローチは

https://github.com/Microsoft/Windows-universal-samples/tree/master/Samples/XamlListView

この記事

http://motzcod.es/post/94643411707/enhancing-xamarinforms-listview-with-grouping

のLINQを使用してのViewModelグループのObservableCollectionで、Microsoftのサンプルパッケージのラインに沿っているとグループ化されたアイテムのObservableCollectionとしてビューに表示します。

public ObservableCollection<GroupInfoList> GroupedContacts 
{ 
    ObservableCollection<GroupInfoList> groups = new ObservableCollection<GroupInfoList>(); 

    var query = from item in _cache.Contacts 
       group item by item.LastName[0] into g 
       orderby g.Key 
       select new { GroupName = g.Key, Items = g }; 

    foreach (var g in query) 
    { 
     GroupInfoList info = new GroupInfoList(); 
     info.Key = g.GroupName; 
     foreach (var item in g.Items) 
     { 
      info.Add(item); 
     } 
     groups.Add(info); 
    } 

    return groups; 
} 
GroupInfoListが

public class GroupInfoList : List<object> 
{ 
    public object Key { get; set; } 
} 

これは、少なくとも私たちのビューに表示されたグループ化されたコレクションを取得していないんが、データキャッシュのコレクションへの更新がもはやリアルタイムに反映されるように定義されて

。データキャッシュのCollectionChangedイベントをキャプチャしてviewmodelで使用してGroupedContactsコレクションを更新することができますが、これはデータキャッシュ内のすべての変更に対して新しいコレクションを作成し、リストビューをちらつきさせたり選択をリセットしたりします。

また、グループ化シナリオごとにグループ化されたアイテムのObservableCollectionを完全に分離し、実行時にListViewのItemSourceバインディングをスワップする必要があるようです。

私はUWP環境で見たものの残りの部分は非常に有用と思われるので、私は障害物を投げリストをグループ化と並べ替えのように重要な何かを見つけるために驚いた...行う方法

誰もが知っていますこれは適切ですか?

+1

基になるコレクションのイベントをリッスンし、適切にグループ化されたコレクションを更新しないのはなぜですか?正確に配置する場所を再構築するよりも少し労力が必要になるかもしれませんが、CollectionViewSourceが何をしたのだろうと想像します –

+0

ありがとう。はい、あなたが正しい。私は基本的に、自分自身ですべてを行う必要があるのか​​、既存のObservableCollectionクラスの力を活用するより良い代替方法があるのか​​を尋ねています。そんな基本的な必要条件のために、毎回配管コードを書いてみると、車輪を再発明する必要があり、プラットフォームには適切なアプローチが組み込まれている必要があります。 – Nick

答えて

1

ベストエフォートは、これまでのところ(自身がのObservableCollectionを公開)一般的なグループ化クラスが動作デモを行うには、この記事

http://motzcod.es/post/94643411707/enhancing-xamarinforms-listview-with-grouping

から来て、次のヘルパークラスObservableGroupingCollection

public class ObservableGroupingCollection<K, T> where K : IComparable 
{ 
    public ObservableGroupingCollection(ObservableCollection<T> collection) 
    { 
     _rootCollection = collection; 
     _rootCollection.CollectionChanged += _rootCollection_CollectionChanged; 
    } 

    ObservableCollection<T> _rootCollection; 
    private void _rootCollection_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) 
    { 
     HandleCollectionChanged(e); 
    } 

    ObservableCollection<Grouping<K, T>> _items; 
    public ObservableCollection<Grouping<K, T>> Items 
    { 
     get { return _items; } 
    } 

    IComparer<T> _sortOrder; 
    Func<T, K> _groupFunction; 

    public void ArrangeItems(IComparer<T> sortorder, Func<T, K> group) 
    { 
     _sortOrder = sortorder; 
     _groupFunction = group; 

     var temp = _rootCollection 
      .OrderBy(i => i, _sortOrder) 
      .GroupBy(_groupFunction) 
      .ToList() 
      .Select(g => new Grouping<K, T>(g.Key, g)); 

     _items = new ObservableCollection<Grouping<K, T>>(temp); 

    } 

    private void HandleCollectionChanged(NotifyCollectionChangedEventArgs e) 
    { 
     if (e.Action == NotifyCollectionChangedAction.Add) 
     { 
      var item = (T)(e.NewItems[0]); 
      var value = _groupFunction.Invoke(item); 

      // find matching group if exists 
      var existingGroup = _items.FirstOrDefault(g => g.Key.Equals(value)); 

      if (existingGroup == null) 
      { 
       var newlist = new List<T>(); 
       newlist.Add(item); 

       // find first group where Key is greater than this key 
       var insertBefore = _items.FirstOrDefault(g => ((g.Key).CompareTo(value)) > 0); 
       if (insertBefore == null) 
       { 
        // not found - add new group to end of list 
        _items.Add(new Grouping<K, T>(value, newlist)); 
       } 
       else 
       { 
        // insert new group at this index 
        _items.Insert(_items.IndexOf(insertBefore), new Grouping<K, T>(value, newlist)); 
       } 
      } 
      else 
      { 
       // find index to insert new item in existing group 
       int index = existingGroup.ToList().BinarySearch(item, _sortOrder); 
       if (index < 0) 
       { 
        existingGroup.Insert(~index, item); 
       } 
      } 
     } 
     else if (e.Action == NotifyCollectionChangedAction.Remove) 
     { 
      var item = (T)(e.OldItems[0]); 
      var value = _groupFunction.Invoke(item); 

      var existingGroup = _items.FirstOrDefault(g => g.Key.Equals(value)); 

      if (existingGroup != null) 
      { 
       // find existing item and remove 
       var targetIndex = existingGroup.IndexOf(item); 
       existingGroup.RemoveAt(targetIndex); 

       // remove group if zero items 
       if (existingGroup.Count == 0) 
       { 
        _items.Remove(existingGroup); 
       } 
      } 
     } 

    } 
} 

を使用しています。 -

新しいUWPブランクアプリケーションから、上記ObservableGroupingCollectionクラス。そして、同じ名前空間内の別のクラスファイルを追加し、以下のすべてのクラスに追加

// Data models 

public class Contact 
{ 
    public string FirstName { get; set; } 
    public string LastName { get; set; } 
    public string State { get; set; } 
} 

public class DataPool 
{ 
    public static string GenerateFirstName(Random random) 
    { 
     List<string> names = new List<string>() { "Lilly", "Mukhtar", "Sophie", "Femke", "Abdul-Rafi", "Mariana", "Aarif", "Sara", "Ibadah", "Fakhr", "Ilene", "Sardar", "Hanna", "Julie", "Iain", "Natalia", "Henrik", "Rasa", "Quentin", "Gadi", "Pernille", "Ishtar", "Jimmy", "Justine", "Lale", "Elize", "Randy", "Roshanara", "Rajab", "Marcus", "Mark", "Alima", "Francisco", "Thaqib", "Andreas", "Marianna", "Amalie", "Rodney", "Dena", "Amar", "Anna", "Nasreen", "Reema", "Tomas", "Filipa", "Frank", "Bari'ah", "Parvaiz", "Jibran", "Tomas", "Elli", "Carlos", "Diego", "Henrik", "Aruna", "Vahid", "Eliana", "Roxanne", "Amanda", "Ingrid", "Wesley", "Malika", "Basim", "Eisa", "Alina", "Andreas", "Deeba", "Diya", "Parveen", "Bakr", "Celine", "Daniel", "Mattheus", "Edmee", "Hedda", "Maria", "Maja", "Alhasan", "Alina", "Hedda", "Vanja", "Robin", "Victor", "Aaftab", "Guilherme", "Maria", "Kai", "Sabien", "Abdel", "Jason", "Bahaar", "Vasco", "Jibran", "Parsa", "Catalina", "Fouad", "Colette", "John", "Fred", "James", "Harry", "Ben", "Steven", "Philip", "Dougal", "Jasper", "Elliott", "Charles", "Gerty", "Sarah", "Sonya", "Svetlana", "Dita", "Karen", "Christine", "Angela", "Heather", "Spence", "Graham", "David", "Bernie", "Darren", "Lester", "Vince", "Colin", "Bernhard", "Dieter", "Norman", "William", "Nigel", "Nick", "Nikki", "Trent", "Devon", "Steven", "Eric", "Derek", "Raymond", "Craig" }; 
     return names[random.Next(0, names.Count)]; 
    } 
    public static string GenerateLastName(Random random) 
    { 
     List<string> lastnames = new List<string>() { "Carlson", "Attia", "Quincey", "Hollenberg", "Khoury", "Araujo", "Hakimi", "Seegers", "Abadi", "Krommenhoek", "Siavashi", "Kvistad", "Vanderslik", "Fernandes", "Dehmas", "Sheibani", "Laamers", "Batlouni", "Lyngvær", "Oveisi", "Veenhuizen", "Gardenier", "Siavashi", "Mutlu", "Karzai", "Mousavi", "Natsheh", "Nevland", "Lægreid", "Bishara", "Cunha", "Hotaki", "Kyvik", "Cardoso", "Pilskog", "Pennekamp", "Nuijten", "Bettar", "Borsboom", "Skistad", "Asef", "Sayegh", "Sousa", "Miyamoto", "Medeiros", "Kregel", "Shamoun", "Behzadi", "Kuzbari", "Ferreira", "Barros", "Fernandes", "Xuan", "Formosa", "Nolette", "Shahrestaani", "Correla", "Amiri", "Sousa", "Fretheim", "Van", "Hamade", "Baba", "Mustafa", "Bishara", "Formo", "Hemmati", "Nader", "Hatami", "Natsheh", "Langen", "Maloof", "Patel", "Berger", "Ostrem", "Bardsen", "Kramer", "Bekken", "Salcedo", "Holter", "Nader", "Bettar", "Georgsen", "Cuninho", "Zardooz", "Araujo", "Batalha", "Antunes", "Vanderhoorn", "Srivastava", "Trotter", "Siavashi", "Montes", "Sherzai", "Vanderschans", "Neves", "Sarraf", "Kuiters", "Hestoe", "Cornwall", "Paisley", "Cooper", "Jakoby", "Smith", "Davies", "Jonas", "Bowers", "Fernandez", "Perez", "Black", "White", "Keller", "Hernandes", "Clinton", "Merryweather", "Freeman", "Anguillar", "Goodman", "Hardcastle", "Emmott", "Kirkby", "Thatcher", "Jamieson", "Spender", "Harte", "Pinkman", "Winterman", "Knight", "Taylor", "Wentworth", "Manners", "Walker", "McPherson", "Elder", "McDonald", "Macintosh", "Decker", "Takahashi", "Wagoner" }; 
     return lastnames[random.Next(0, lastnames.Count)]; 
    } 
    public static string GenerateState(Random random) 
    { 
     List<string> states = new List<string>() { "Alabama", "Alaska", "Arizona", "Arkansas", "California", "Colorado", "Connecticut", "Delaware", "District Of Columbia", "Florida", "Georgia", "Hawaii", "Idaho", "Illinois", "Indiana", "Iowa", "Kansas", "Kentucky", "Louisiana", "Maine", "Maryland", "Massachusetts", "Michigan", "Minnesota", "Mississippi", "Missouri", "Montana", "Nebraska", "Nevada", "New Hampshire", "New Jersey", "New Mexico", "New York", "North Carolina", "North Dakota", "Ohio", "Oklahoma", "Oregon", "Pennsylvania", "Rhode Island", "South Carolina", "South Dakota", "Tennessee", "Texas", "Utah", "Vermont", "Virginia", "Washington", "West Virginia", "Wisconsin", "Wyoming" }; 
     return states[random.Next(0, states.Count)]; 
    } 
} 

public class Cache 
{ 
    public Cache() 
    { 
     InitializeCacheData(); 
     SimulateLiveChanges(new TimeSpan(0, 0, 1)); 
    } 

    public ObservableCollection<Contact> Contacts { get; set; } 

    private static Random rnd = new Random(); 

    private void InitializeCacheData() 
    { 
     Contacts = new ObservableCollection<Contact>(); 

     var i = 0; 
     while (i < 5) 
     { 
      Contacts.Add(new Contact() 
      { 
       FirstName = DataPool.GenerateFirstName(rnd), 
       LastName = DataPool.GenerateLastName(rnd), 
       State = DataPool.GenerateState(rnd) 
      }); 

      i++; 
     } 
    } 

    private async void SimulateLiveChanges(TimeSpan MyInterval) 
    { 
     double MyIntervalSeconds = MyInterval.TotalSeconds; 
     while (true) 
     { 
      await Task.Delay(MyInterval); 

      //int addOrRemove = rnd.Next(1, 10); 
      //if (addOrRemove > 3) 
      //{ 
      // add item 
      Contacts.Add(new Contact() 
      { 
       FirstName = DataPool.GenerateFirstName(rnd), 
       LastName = DataPool.GenerateLastName(rnd), 
       State = DataPool.GenerateState(rnd) 
      }); 
      //} 
      //else 
      //{ 
      // // remove random item 
      // if (Contacts.Count > 0) 
      // { 
      //  Contacts.RemoveAt(rnd.Next(0, Contacts.Count - 1)); 
      // } 
      //} 
     } 
    } 

} 

// ViewModel 

public class ViewModel : BaseViewModel 
{  
    public ViewModel() 
    { 
     _groupingCollection = new ObservableGroupingCollection<string, Contact>(new Cache().Contacts); 
     _groupingCollection.ArrangeItems(new StateSorter(), (x => x.State)); 
     NotifyPropertyChanged("GroupedContacts"); 

    } 

    ObservableGroupingCollection<string, Contact> _groupingCollection; 
    public ObservableCollection<Grouping<string, Contact>> GroupedContacts 
    { 
     get 
     { 
      return _groupingCollection.Items; 
     } 
    } 

    // swap grouping commands 

    private ICommand _groupByStateCommand; 
    public ICommand GroupByStateCommand 
    { 
     get 
     { 
      if (_groupByStateCommand == null) 
      { 
       _groupByStateCommand = new RelayCommand(
        param => GroupByState(), 
        param => true); 
      } 
      return _groupByStateCommand; 
     } 
    } 
    private void GroupByState() 
    { 
     _groupingCollection.ArrangeItems(new StateSorter(), (x => x.State)); 
     NotifyPropertyChanged("GroupedContacts"); 
    } 

    private ICommand _groupByNameCommand; 
    public ICommand GroupByNameCommand 
    { 
     get 
     { 
      if (_groupByNameCommand == null) 
      { 
       _groupByNameCommand = new RelayCommand(
        param => GroupByName(), 
        param => true); 
      } 
      return _groupByNameCommand; 
     } 
    } 
    private void GroupByName() 
    { 
     _groupingCollection.ArrangeItems(new NameSorter(), (x => x.LastName.First().ToString())); 
     NotifyPropertyChanged("GroupedContacts"); 
    } 

} 

// View Model helpers 

public class BaseViewModel : INotifyPropertyChanged 
{ 
    public event PropertyChangedEventHandler PropertyChanged; 

    protected virtual void NotifyPropertyChanged([CallerMemberName] string propertyName = null) 
    { 
     if (PropertyChanged != null) 
     { 
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
     } 
    } 
} 

public class RelayCommand : ICommand 
{ 
    readonly Action<object> _execute; 
    readonly Predicate<object> _canExecute; 

    public RelayCommand(Action<object> execute) 
     : this(execute, null) 
    { 

    } 

    public RelayCommand(Action<object> execute, Predicate<object> canExecute) 
    { 
     if (execute == null) 
      throw new ArgumentNullException("execute"); 

     _execute = execute; 
     _canExecute = canExecute; 

    } 

    public bool CanExecute(object parameter) 
    { 
     return _canExecute == null ? true : _canExecute(parameter); 
    } 

    public event EventHandler CanExecuteChanged 
    { 
     add { } 
     remove { } 
    } 

    public void Execute(object parameter) 
    { 
     _execute(parameter); 
    } 

} 

// Sorter classes 

public class NameSorter : Comparer<Contact> 
{ 
    public override int Compare(Contact x, Contact y) 
    { 
     int result = x.LastName.First().CompareTo(y.LastName.First()); 

     if (result != 0) 
     { 
      return result; 
     } 
     else 
     { 
      result = x.LastName.CompareTo(y.LastName); 

      if (result != 0) 
      { 
       return result; 
      } 
      else 
      { 
       return x.FirstName.CompareTo(y.FirstName); 
      } 
     } 
    } 
} 

public class StateSorter : Comparer<Contact> 
{ 
    public override int Compare(Contact x, Contact y) 
    { 
     int result = x.State.CompareTo(y.State); 

     if (result != 0) 
     { 
      return result; 
     } 
     else 
     { 
      result = x.LastName.CompareTo(y.LastName); 

      if (result != 0) 
      { 
       return result; 
      } 
      else 
      { 
       return x.FirstName.CompareTo(y.FirstName); 
      } 
     } 
    } 
} 

// Grouping class 
// credit 
// http://motzcod.es/post/94643411707/enhancing-xamarinforms-listview-with-grouping 

public class Grouping<K, T> : ObservableCollection<T> 
{ 
    public K Key { get; private set; } 

    public Grouping(K key, IEnumerable<T> items) 
    { 
     Key = key; 
     foreach (var item in items) 
     { 
      this.Items.Add(item); 
     } 
    } 
} 

は最後に、編集メインページは次のように

<Page.DataContext> 
     <local:ViewModel /> 
    </Page.DataContext> 

    <Page.Resources> 
     <CollectionViewSource 
      x:Key="cvs" 
      Source="{Binding GroupedContacts}" 
      IsSourceGrouped="True" /> 
    </Page.Resources> 

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> 

     <Grid.RowDefinitions> 
      <RowDefinition Height="*" /> 
      <RowDefinition Height="Auto" /> 
     </Grid.RowDefinitions> 

     <ListView ItemsSource="{Binding Source={StaticResource cvs}}" 
        x:Name="targetListBox"> 
      <ListView.ItemTemplate> 
       <DataTemplate> 
        <Grid> 
         <Grid.ColumnDefinitions> 
          <ColumnDefinition Width="100" /> 
          <ColumnDefinition Width="100" /> 
          <ColumnDefinition Width="*" /> 
         </Grid.ColumnDefinitions> 

         <TextBlock Text="{Binding LastName}" /> 
         <TextBlock Text="{Binding FirstName}" Grid.Column="1" /> 
         <TextBlock Text="{Binding State}" Grid.Column="2" HorizontalAlignment="Right" /> 
        </Grid> 
       </DataTemplate> 
      </ListView.ItemTemplate> 
      <ListView.GroupStyle> 
       <GroupStyle> 
        <GroupStyle.HeaderTemplate> 
         <DataTemplate> 
          <Grid Background="Gainsboro"> 
           <TextBlock FontWeight="Bold" 
              FontSize="14" 
              Margin="10,2" 
              Text="{Binding Key}"/> 
          </Grid> 
         </DataTemplate> 
        </GroupStyle.HeaderTemplate> 
       </GroupStyle> 
      </ListView.GroupStyle> 
     </ListView> 

     <StackPanel Orientation="Horizontal" Grid.Row="1"> 
      <Button Content="Group By Initial" Command="{Binding GroupByNameCommand}" /> 
      <Button Content="Group By State" Command="{Binding GroupByStateCommand}" /> 
     </StackPanel> 
    </Grid> 

HandleCollectionChangedメソッドのみ扱い、これまでに追加/削除とNotifyCollectionChangedEventArgsパラメータが含まれている場合にブレークダウンします複数のアイテム(既存のObservableCollectionクラスは一度に1つの変更のみを通知します)

これは問題なく動作します。

改善の提案は大歓迎です。

5

GroupedObservableCollectionという名前のライブラリを作成しました。このライブラリは、自分のアプリケーションの1つでこれらの行に沿って何かを行います。

私が解決する必要があった重要な問題の1つは、グループを作成するために使用された元のリストをリフレッシュすることでした。つまり、リスト全体をリフレッシュさせるために、ちょうど違い。

現在のフォームでは、今すぐあなたのソートの質問にすべて答えることはできませんが、他の人にとっては良い出発点かもしれません。

関連する問題