DynamicData:对每个更改进行绑定列表重新加载的排序

时间:2018-04-30 12:02:24

标签: c# uwp dynamic-data reactiveui

我使用优秀的dynamicdata将我的数据层桥接到我的UI(使用RxUI)。动态数据部分归结为以下典型代码。有一个ItemViewModels的SourceCache(" _sourceCache"),它被绑定到ItemViewModels的ReactiveList(" Models"),后者(在视图中)被绑定为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

运行示例时,每次排序由Property Change触发时都会重新加载整个列表(观察LastChange属性)。如果选择ListViewItem,通常选择会丢失,加上整个列表的可见重新加载会让人分心。

我现在的问题是:我有什么方法可以在我的DynamicData链中进行排序而没有上述问题吗?

理想情况下,只有更改的项目才会更改其在列表中的位置,其余项目将保持可见。

更新

感谢Roland提示,但无论使用

,观察到的行为仍然相同
Sort(comparer, propertyChanges)

AutoRefresh(m => m.LastChange)
.Sort(comparer)

整个列表UI仍然看起来像重新加载(所有项目都会消失并在每次更改时重新出现)。从我的链接github仓库中可以看出,我随机选择了一个绑定的ItemViewModel并更改了&#34; LastChanged&#34;属性。也许这是UWP ListView的行为?

更新2

如果我只是ReactiveList.Move每隔1秒随机移动一个项目,可以看到相同的行为,仍然整个列表看起来好像每次都重新加载。所以我猜测它是UWP ListView本身...

2 个答案:

答案 0 :(得分:2)

您正在使用的排序重载会强制完全依赖每个属性更改和/或添加或更新新项目。该重载旨在在比较器更改时使用,而不是在项目更改时使用。

解决方案是使用AutoRefresh指示dd在属性更改时重新评估单个项目。

.AutoRefresh(p=>p.LastChange).Sort(comparer)

指定节流和调度程序时会出现重载。

答案 1 :(得分:1)

<强>更新

实现了一个新的DynamicData操作,它将“移动”更改转换为“删除旧索引”和“添加新索引”,名为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)