DynamicData:每次更改时重新加载绑定列表的排序
DynamicData: Sorting of bound list reloads on every change
我正在使用出色的 dynamicdata 将我的数据层桥接到 UI(使用 RxUI)。动态数据部分归结为以下典型代码。有一个 ItemViewModels ("_sourceCache") 的 SourceCache,它绑定到一个 ItemViewModels ("Models") 的 ReactiveList,后者又(在视图中)绑定为 ListView.ItemsSource:
var propertyChanges = _sourceCache.Connect().WhenPropertyChanged(p => p.LastChange)
.Throttle(TimeSpan.FromMilliseconds(250))
.Select(_ => Unit.Default);
var comparer = SortExpressionComparer<ItemViewModel>.Ascending(l => l.LastChange);
_sourceCache.Connect()
.Sort(comparer, propertyChanges)
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(Models)
.Subscribe();
(可以找到完整的工作示例 here)
当 运行 示例时,每次排序被 属性 更改触发时整个列表都会重新加载可见(观看 LastChange 属性)。如果选择一个 ListViewItem,通常会丢失选择,而且整个列表的可见重新加载相当分散注意力。
我现在的问题是:有没有什么方法可以在我的 DynamicData 链中进行排序而不出现上述问题?
理想情况下,只有更改的项目会改变它们在列表中的位置,而其余项目将保持可见。
更新
感谢 Roland 的提示,但观察到的行为仍然是相同的,无论使用
Sort(comparer, propertyChanges)
或
AutoRefresh(m => m.LastChange)
.Sort(comparer)
整个列表 UI 看起来仍然像重新加载一样(所有项目在每次更改时消失并重新出现)。从我链接的 github 存储库中可以看出,我随机选择了一个绑定的 ItemViewModel 并更改了 "LastChanged" 属性。也许这是 UWP ListView 的行为?
更新 2
如果我只是每 1 秒随机 ReactiveList.Move 一个项目,可以看到相同的行为,仍然整个列表看起来好像每次都重新加载。所以我猜测是UWP ListView本身...
您正在使用的排序过载将强制对每个 属性 更改和/或添加或更新新项目时进行完整的处理。该重载旨在在比较器更改时使用,而不是在项目更改时使用。
解决方案是利用 AutoRefresh
指示 dd 在属性更改时重新评估单个项目。
.AutoRefresh(p=>p.LastChange).Sort(comparer)
指定调节器和调度程序时出现过载。
更新
为 DynamicData 实施了一个新操作,将 "move" 更改转换为 "remove on old index" 和 "add on new index",称为 TreatMovesAsRemoveAdd,使用方式如下:
_sourceCache.Connect()
.Sort(comparer, propertyChanges)
.TreatMovesAsRemoveAdd()
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(Models)
.Subscribe();
旧答案:
ListView 似乎无法处理(语义上)四处移动的项目。只需添加+删除。所以我听从了Roland的建议,修改了SortedReactiveListAdaptor(复制过来修改了下面的方法:
private void DoUpdate(IChangeSet<TObject, TKey> changes)
{
foreach (var change in changes)
{
switch (change.Reason)
{
case ChangeReason.Add:
_target.Insert(change.CurrentIndex, change.Current);
break;
case ChangeReason.Remove:
_target.RemoveAt(change.CurrentIndex);
break;
case ChangeReason.Moved:
// ************************************** change from original ************************************************
//_target.Move(change.PreviousIndex, change.CurrentIndex);
//break;
// ************************************** ******************** ************************************************
case ChangeReason.Update:
{
_target.RemoveAt(change.PreviousIndex);
_target.Insert(change.CurrentIndex, change.Current);
}
break;
}
}
}
因此所有移动都将被视为更新(RemoteAt + Insert)
上述 SortedReactiveListAdaptor 的应用将是:
.Bind(new MovesToUpdatesReactiveListAdaptor<IItemViewModel, string>(Items))
而不是通常的
.Bind(Items)
我正在使用出色的 dynamicdata 将我的数据层桥接到 UI(使用 RxUI)。动态数据部分归结为以下典型代码。有一个 ItemViewModels ("_sourceCache") 的 SourceCache,它绑定到一个 ItemViewModels ("Models") 的 ReactiveList,后者又(在视图中)绑定为 ListView.ItemsSource:
var propertyChanges = _sourceCache.Connect().WhenPropertyChanged(p => p.LastChange)
.Throttle(TimeSpan.FromMilliseconds(250))
.Select(_ => Unit.Default);
var comparer = SortExpressionComparer<ItemViewModel>.Ascending(l => l.LastChange);
_sourceCache.Connect()
.Sort(comparer, propertyChanges)
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(Models)
.Subscribe();
(可以找到完整的工作示例 here)
当 运行 示例时,每次排序被 属性 更改触发时整个列表都会重新加载可见(观看 LastChange 属性)。如果选择一个 ListViewItem,通常会丢失选择,而且整个列表的可见重新加载相当分散注意力。
我现在的问题是:有没有什么方法可以在我的 DynamicData 链中进行排序而不出现上述问题?
理想情况下,只有更改的项目会改变它们在列表中的位置,而其余项目将保持可见。
更新
感谢 Roland 的提示,但观察到的行为仍然是相同的,无论使用
Sort(comparer, propertyChanges)
或
AutoRefresh(m => m.LastChange)
.Sort(comparer)
整个列表 UI 看起来仍然像重新加载一样(所有项目在每次更改时消失并重新出现)。从我链接的 github 存储库中可以看出,我随机选择了一个绑定的 ItemViewModel 并更改了 "LastChanged" 属性。也许这是 UWP ListView 的行为?
更新 2
如果我只是每 1 秒随机 ReactiveList.Move 一个项目,可以看到相同的行为,仍然整个列表看起来好像每次都重新加载。所以我猜测是UWP ListView本身...
您正在使用的排序过载将强制对每个 属性 更改和/或添加或更新新项目时进行完整的处理。该重载旨在在比较器更改时使用,而不是在项目更改时使用。
解决方案是利用 AutoRefresh
指示 dd 在属性更改时重新评估单个项目。
.AutoRefresh(p=>p.LastChange).Sort(comparer)
指定调节器和调度程序时出现过载。
更新
为 DynamicData 实施了一个新操作,将 "move" 更改转换为 "remove on old index" 和 "add on new index",称为 TreatMovesAsRemoveAdd,使用方式如下:
_sourceCache.Connect()
.Sort(comparer, propertyChanges)
.TreatMovesAsRemoveAdd()
.ObserveOn(RxApp.MainThreadScheduler)
.Bind(Models)
.Subscribe();
旧答案:
ListView 似乎无法处理(语义上)四处移动的项目。只需添加+删除。所以我听从了Roland的建议,修改了SortedReactiveListAdaptor(复制过来修改了下面的方法:
private void DoUpdate(IChangeSet<TObject, TKey> changes)
{
foreach (var change in changes)
{
switch (change.Reason)
{
case ChangeReason.Add:
_target.Insert(change.CurrentIndex, change.Current);
break;
case ChangeReason.Remove:
_target.RemoveAt(change.CurrentIndex);
break;
case ChangeReason.Moved:
// ************************************** change from original ************************************************
//_target.Move(change.PreviousIndex, change.CurrentIndex);
//break;
// ************************************** ******************** ************************************************
case ChangeReason.Update:
{
_target.RemoveAt(change.PreviousIndex);
_target.Insert(change.CurrentIndex, change.Current);
}
break;
}
}
}
因此所有移动都将被视为更新(RemoteAt + Insert) 上述 SortedReactiveListAdaptor 的应用将是:
.Bind(new MovesToUpdatesReactiveListAdaptor<IItemViewModel, string>(Items))
而不是通常的
.Bind(Items)