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環境で見たものの残りの部分は非常に有用と思われるので、私は障害物を投げリストをグループ化と並べ替えのように重要な何かを見つけるために驚いた...行う方法
誰もが知っていますこれは適切ですか?
基になるコレクションのイベントをリッスンし、適切にグループ化されたコレクションを更新しないのはなぜですか?正確に配置する場所を再構築するよりも少し労力が必要になるかもしれませんが、CollectionViewSourceが何をしたのだろうと想像します –
ありがとう。はい、あなたが正しい。私は基本的に、自分自身ですべてを行う必要があるのか、既存のObservableCollectionクラスの力を活用するより良い代替方法があるのかを尋ねています。そんな基本的な必要条件のために、毎回配管コードを書いてみると、車輪を再発明する必要があり、プラットフォームには適切なアプローチが組み込まれている必要があります。 – Nick