实施ICollectionView实时排序

15

ICollectionViewLiveShaping在过滤方面是如何实现的?它是类似于:

public ICollectionView WorkersEmployed { get; set; }

WorkersEmployed = new CollectionViewSource { Source = GameContainer.Game.Workers }.View;

我没有使用 GetDefaultView,因为我需要在此集合上使用多个过滤器实例。如果有关系的话,GameContainer.Game.Workers 是一个 ObservableCollection

ApplyFilter(WorkersEmployed);

private void ApplyFilter(ICollectionView collectionView)
{
    collectionView.Filter = IsWorkerEmployed;
}

public bool IsWorkerEmployed(object item)
{
    Worker w = item as Worker;
    return w.EmployerID == this.ID;
}

这一切都可以正常工作,但当然需要手动刷新,这就是为什么我正在尝试使用 ICollectionViewLiveShaping。实时过滤是如何工作的?

更新:看起来添加一个属性到ICollectionViewLiveShapingLiveFilteringProperties集合的唯一方法是通过一个字符串。鉴于这种限制,甚至可能无法按另一个类(在本例中是工人的雇主ID)的属性进行过滤?

在这种情况下,我尝试做的事情是否可行?

3个回答

14

你只需要在LiveFilteringProperties中添加一个属性,这样过滤器在属性更改时会调用它,并将IsLiveFiltering设置为true,以启用实时过滤你的集合。

确保在EmployerID属性更改时引发PropertyChanged事件,即你的Worker类应实现INotifyPropertyChangedEvent接口。

然后这个就可以工作了 -

public ICollectionViewLiveShaping WorkersEmployed { get; set; }

ICollectionView workersCV = new CollectionViewSource
                         { Source = GameContainer.Game.Workers }.View;

ApplyFilter(workersCV);

WorkersEmployed = workersCV as ICollectionViewLiveShaping;
if (WorkersEmployed.CanChangeLiveFiltering)
{
    WorkersEmployed.LiveFilteringProperties.Add("EmployerID");
    WorkersEmployed.IsLiveFiltering = true;
}

INotifyPropertyChanged是唯一需要监听的吗?也就是说,重新过滤是否会被INotifyCollectionChanged触发? - MikeT
@MikeT - 是的,因为源集合本身是ObservableCollection,所以在集合更改事件上,筛选器也会再次触发。 - Rohit Vats
只是想确保我们都在同一个页面上,如果你有 OC<A> 并且 A 作为属性有 OC<B> Bs,如果针对 OC<A> 的 ICollectionViewLiveShaping 监控了 Bs 字段,当你想要过滤 Bs 计数大于0的数据时,它会自动重新筛选。 - MikeT

2
我们正在使用WPF + MVVM + Visual Studio 2017。
我们想要添加实时过滤功能来进行转换:最初的回答
public ObservableCollection<RowViewModel> Rows { get; set; }

以下方法有两个关键优点:
  • 它是为了与WPF运行时高效地配合工作,以最小化屏幕渲染而设计的,使用批量更新。
  • 所以它很快。
  • 由于下面列出了样板代码,相比您在网上找到的任何其他文档,更容易理解。

如果这对您有用,请告诉我,如果有任何问题,我会更新说明以使其更容易。

以下是步骤:

步骤1:不通知集合包装器

创建一个特殊的ObservableCollection,它不会触发更新事件。这是一次性的。我们想要自己触发批量更新事件,这样更快。

public class NonNotifyingObservableCollection<T> : ObservableCollection<T>
{
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { /* Do nothing */ }
}

第二步:转换为非通知ObservableCollection

将其转换为使用这个新集合的私有变量。

private NonNotifyingObservableCollection<RowViewModel> rows;
// ... and in constructor
rows = new NonNotifyingObservableCollection<RowViewModel>();

第三步:添加包装器

添加以下变量:

private ICollectionView rowsView;
public ICollectionViewLiveShaping RowsLiveView { get; set; }

在ViewModel构建完成后的Initialize()调用中(或者在构造函数中):
最初的回答:
// Call on the dispatcher.
dispatcher.InvokeAsync(() =>
{
    this.rowsView = CollectionViewSource.GetDefaultView(this.rows);
    this.rowsView.Filter = o =>
        {
            // This condition must be true for the row to be visible on the grid.
            return ((RowViewModel)o).IsVisible == true;
        };
    this.RowsLiveView = (ICollectionViewLiveShaping)this.rowsView;
    this.RowsLiveView.IsLiveFiltering = true;
    // For completeness. Changing these properties fires a change notification (although
    // we bypass this and manually call a bulk update using Refresh() for speed).
    this.RowsLiveView.LiveFilteringProperties.Add("IsVisible");
});

步骤四:添加项目

现在我们将项目添加到后备集合中,然后调用.Refresh()来刷新视图:

this.rowsView.Add(new RowViewModel( /* Set properties here. */ ));

我们现在将网格绑定到RowsLiveView(而不是原始代码中的Rows)。
第五步:更新实时过滤
现在我们可以更新IsVisible属性,然后调用.Refresh()重新绘制网格。
rows[0].IsVisible=false;
this.rowsView.Refresh(); // Hides the first row.

更新

更新:这个答案可以简化。整个ICollectionViewLiveShaping的重点是自动刷新,无需调用.Refresh()。鉴于我们有一个NonNotifyingObservableCollection并且我们正在手动控制一切与.Refresh(),可以删除public ICollectionViewLiveShaping RowsLiveView { get; set; },直接使用RowsView(将其作为属性{ get; set; },并使用普通的ObservableCollection<>)。换句话说 - 对于少量行(例如<100),ICollectionViewLiveShaping非常好,但对于任何更多的行,从速度角度考虑,结合批量更新和手动Refresh()ICollectionView更好。


当使用 ICollectionViewLiveShaping 时,您是说在超过100个项目时性能会大幅下降吗? - Konrad
我目前正在使用带有ICollectionViewLiveShapingObservableCollection。希望我不会遇到任何性能问题。 - Konrad

0

我进行了实验,看起来它并不适合你(和我)想要的:当您更改过滤条件时自动过滤。当DataGrid的项目源的某些属性更改时,它会自动过滤,但是当过滤条件更改时不会自动过滤 - 您必须手动调用ICollectionViewSource.Refresh。


如果你有很多过滤器,这可能有点烦人,但并不是最糟糕的。 - Julien

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接