在ViewModel中使用CollectionViewSource的正确方法

我使用Drag和Drop将Data Source对象(DB模型)绑定到DataGrid (基本上遵循WPFentity framework数据绑定中的这个示例)。

这个实现一切正常。

XAML

     .. 

代码背后

 private void Window_Loaded(object sender, RoutedEventArgs e) { System.Windows.Data.CollectionViewSource categoryViewSource = ((System.Windows.Data.CollectionViewSource)(this.FindResource("categoryViewSource"))); _context.Categories.Load(); categoryViewSource.Source = _context.Categories.Local; } 

视图模型

 public MainWindow() { InitializeComponent(); this.DataContext = new MyViewModel(); } 

但是,当我尝试在ViewModel中使用相同的代码时,它不起作用( FindResource不可用),此外,我不认为这是正确的方法(即在MVVM中使用x:Key )。

我非常感谢任何帮助,指出使用DataGrid实现CollectionViewSourceDataBinding的正确方法。

您有两个选项可以在MVVM中正确使用CollectionViewSource

  1. 通过ViewModel公开ObserVableCollection项(在您的情况下为Categories )并在xaml中创建CollectionViewSource ,如下所示 –

          

    scm: xmlns:scm="clr-namespace:System.ComponentModel;assembly=Wind‌​owsBase"

    看到这个 – 使用CollectionViewSource从XAML Filtering集合

  2. 直接从ViewModel创建和公开ICollectionView

    请参阅此 – 如何在WPF中导航,分组,排序和筛选数据

以下示例显示如何创建集合视图并将其绑定到ListBox

XAML:

    

查看Codebehind:

 public class CustomerView { public CustomerView() { DataContext = new CustomerViewModel(); } } 

视图模型:

 public class CustomerViewModel { private ICollectionView _customerView; public ICollectionView Customers { get { return _customerView; } } public CustomerViewModel() { IList customers = GetCustomers(); _customerView = CollectionViewSource.GetDefaultView(customers); } } 

我发现在我的ViewModel中有一个CollectionViewSource并将ListBox(在我的情况下)绑定到CollectionViewSource.View,同时将CollectionViewSource.Source设置为我想要使用的列表是很方便的。

像这样:

视图模型:

  public DesignTimeVM() //I'm using this as a Design Time VM { Items = new List(); Items.Add(new Foo() { FooProp= "1", FooPrep= 20.0 }); Items.Add(new Foo() { FooProp= "2", FooPrep= 30.0 }); FooViewSource = new CollectionViewSource(); FooViewSource.Source = Items; SelectedFoo = Items.First(); //More code as needed } 

XAML:

  

这意味着我可以根据需要在VM中做好事(来自https://blogs.msdn.microsoft.com/matt/2008/08/28/collectionview-deferrefresh-my-new-best-friend/ ):

  using (FooViewSource.DeferRefresh()) { //Remove an old Item //add New Item //sort list anew, etc. } 

我想在使用ICollectionView对象时也可以这样做,但博客链接中的演示代码似乎是一些代码隐藏的东西,直接引用列表框,我试图避免。

BTW在您提出之前,这里是您使用设计时VM的方式: WPF设计时间视图模型

仅供参考,另一种方法是在CollectionViewSource上使用附加属性,然后将函数传递给ViewModel(实现接口)。

这是一个非常基本的演示,仅用于过滤,它需要一些工作,例如VM上的第二个集合,但我认为它足以显示一般技术。

如果这比其他方法更好或更差,我只想指出,还有另一种方法可以做到这一点

附属物的定义:

 public static class CollectionViewSourceFilter { public static IFilterCollectionViewSource GetFilterObject(CollectionViewSource obj) { return (IFilterCollectionViewSource)obj.GetValue(FilterObjectProperty); } public static void SetFilterObject(CollectionViewSource obj, IFilterCollectionViewSource value) { obj.SetValue(FilterObjectProperty, value); } public static void FilterObjectChanged(object sender, DependencyPropertyChangedEventArgs e) { if (e.OldValue is IFilterCollectionViewSource oldFilterObject && sender is CollectionViewSource oldCvs) { oldCvs.Filter -= oldFilterObject.Filter; oldFilterObject.FilterRefresh -= (s, e2) => oldCvs.View.Refresh(); } if (e.NewValue is IFilterCollectionViewSource filterObject && sender is CollectionViewSource cvs) { cvs.Filter += filterObject.Filter; filterObject.FilterRefresh += (s,e2) => cvs.View.Refresh(); } } public static readonly DependencyProperty FilterObjectProperty = DependencyProperty.RegisterAttached( "FilterObject", typeof(Interfaces.IFilterCollectionViewSource), typeof(CollectionViewSourceFilter), new PropertyMetadata(null,FilterObjectChanged) ); } 

接口:

 public interface IFilterCollectionViewSource { void Filter(object sender, FilterEventArgs e); event EventHandler FilterRefresh; } 

在xaml中的用法:

  

和ViewModel中的用法:

 class YourViewModel : IFilterCollectionViewSource { public event EventHandler FilterRefresh; private string _SearchTerm = string.Empty; public string SearchTerm { get { return _SearchTerm; } set { SetProperty(ref _SearchTerm, value); FilterRefresh?.Invoke(this, null); } } private ObservableCollection _YourCollection = new ObservableCollection(); public ObservableCollection YourCollection { get { return _YourCollection; } set { SetProperty(ref _YourCollection, value); } } public void Filter(object sender, FilterEventArgs e) { e.Accepted = (e.Item as YourItemType)?.YourProperty?.ToLower().Contains(SearchTerm.ToLower()) ?? true; } }