UWP ObservableCollection排序和分组 [英] UWP ObservableCollection sorting and grouping

查看:70
本文介绍了UWP ObservableCollection排序和分组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在UWP应用中,如何对ObservableCollection进行分组和排序,并保持所有实时通知有效?

In UWP apps, how can you group and sort an ObservableCollection and keep all the live notification goodness?

在我所看到的大多数简单的UWP示例中,通常都有一个ViewModel,它公开了一个ObservableCollection,然后将其绑定到View中的ListView. 在ObservableCollection中添加或删除项目时,ListView通过对INotifyCollectionChanged通知作出反应来自动反映更改.在未排序或未分组的ObservableCollection的情况下,这一切都很好,但是如果需要对集合进行排序或分组,似乎没有明显的方法可以保留更新通知.而且,即时更改排序或组顺序似乎会引发重大的实现问题.

In most simple UWP examples I've seen, there is generally a ViewModel that exposes an ObservableCollection which is then bound to a ListView in the View. When items are added or removed from the ObservableCollection, the ListView automatically reflects the changes by reacting to the INotifyCollectionChanged notifications. This all works fine in the case of an unsorted or ungrouped ObservableCollection, but if the collection needs to be sorted or grouped, there seems to be no readily apparent way to preserve the update notifications. What's more, changing the sort or group order on the fly seems to throw up significant implementation issues.

++

假设您有一个现有的数据缓存后端,该后端公开了非常简单的Contact类的ObservableCollection.

Take a scenario where you have an existing datacache backend that exposes an ObservableCollection of very simple class Contact.

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

此ObservableCollection随时间变化,我们想在更新的视图中显示实时分组和排序的列表 响应数据缓存中的更改.我们还希望为用户提供动态切换姓氏和州之间分组的选项.

This ObservableCollection changes over time, and we want to present a realtime grouped and sorted list in the view that updates in response to changes in the datacache. We also want to give the user the option to switch the grouping between LastName and State on the fly.

++

在WPF世界中,这是相对琐碎的. 我们可以创建一个引用数据缓存的简单ViewModel,按原样呈现缓存的Contacts集合.

In a WPF world, this is relatively trivial. We can create a simple ViewModel referencing the datacache that presents the cache's Contacts collection as-is.

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

    Cache _cache;

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

然后,我们可以将其绑定到一个视图,在该视图中,我们将CollectionViewSource以及Sort和Group定义实现为XAML资源.

Then we can bind this to a view where we implement a CollectionViewSource and Sort and Group definitions as XAML resources.

<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>

然后,当用户单击窗口底部的GroupBy按钮时,我们可以在后台代码中即时对分组和排序.

Then when the user clicks on the GroupBy buttons at the bottom of the window we can we can group and sort on the fly in code-behind.

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);
     }
}

这一切都很好,并且随着数据缓存集合的更改,这些项目会自动更新. Listview的分组和选择不会受到集合更改的影响,并且新的联系人项已正确分组.可以在运行时由用户初始在State和LastName之间交换分组.

This all works fine, and the items are updated automatically as the data cache collection changes. The Listview grouping and selection remains unaffected by collection changes, and the new contact items are correctly grouped.The grouping can be swapped between State and LastName initial by user at runtime.

++

在UWP世界中,CollectionViewSource不再具有GroupDescriptions和SortDescriptions集合,并且需要在ViewModel级别上进行排序/分组.我找到的最可行解决方案的最接近方法是按照Microsoft的示例程序包

In the UWP world, the CollectionViewSource no longer has the GroupDescriptions and SortDescriptions collections, and sorting/grouping need to be carried out at the ViewModel level. The closest approach to a workable solution I've found is along the lines of Microsoft's sample package at

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

和这篇文章

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

其中ViewModel使用Linq对ObservableCollection进行分组,并将其作为已分组项的ObservableCollection呈现给视图

where the ViewModel groups the ObservableCollection using Linq and presents it to the view as an ObservableCollection of grouped items

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定义为

where GroupInfoList is defined as

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

这至少会使我们在View中显示一个分组的集合,但是对数据缓存集合的更新不再实时反映.我们可以捕获数据缓存的CollectionChanged事件,并在视图模型中使用它来刷新GroupedContacts集合,但这将为数据缓存中的每个更改创建一个新集合,从而导致ListView闪烁并重置选择,这显然不是最佳选择.

This does at least get us a grouped collection displayed in the View, but updates to the datacache collection are no longer reflected in real time. We could capture the datacache's CollectionChanged event and use it in the viewmodel to refresh the GroupedContacts collection, but this creates a new collection for every change in the datacache, causing the ListView to flicker and reset selection etc which is clearly suboptimal.

即时交换分组似乎还需要针对每个分组方案以及ListView的ItemSource绑定在运行时交换完全独立的分组项目的ObservableCollection.

Also swapping the grouping on the fly would seem to require a completely seperate ObservableCollection of grouped items for each grouping scenario, and for the ListView's ItemSource binding to be swapped at runtime.

我在UWP环境中看到的其余部分似乎非常有用,所以我很惊讶地发现像对列表进行分组和排序一样重要的东西……

The rest of what I've seen of the UWP environment seems extremely useful, so I'm surprised to find something as vital as grouping and sorting lists throwing up obstacles...

有人知道如何正确执行此操作吗?

Anyone know how to do this properly?

推荐答案

我已经开始整理一个名为 GroupedObservableCollection的库为我的一个应用执行了类似的操作.

I've started putting together a library called GroupedObservableCollection that does something along these lines for one of my apps.

我需要解决的关键问题之一是刷新用于创建组的原始列表,即我不希望用户使用稍有不同的条件进行搜索来刷新整个列表,只是差异.

One of the key problems that I needed to solve was the refreshing of the original list that was used to create the group, i.e. I didn't want a user searching with slightly different criteria to cause the whole list to be refreshed, just the differences.

以目前的形式,它可能不会立即回答您所有的排序问题,但对于其他人来说可能是一个很好的起点.

In its current form it probably won't answer all your sorting questions right now, but it might be a good starting point for others.

这篇关于UWP ObservableCollection排序和分组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆