在ViewModel中使用CollectionViewSource的正确方法

时间:2014-01-02 17:45:22

标签: c# wpf mvvm datagrid collectionviewsource

我使用Drag and Drop将数据源对象(数据库模型)绑定到DataGrid(基本上遵循Entity Framework Databinding with WPF中的此示例。

这个实现一切正常。

XAML

<Window.Resources>    
<CollectionViewSource x:Key="categoryViewSource"  
    d:DesignSource="{d:DesignInstance {x:Type local:Category}, CreateList=True}"/>
</Window.Resources>
<Grid DataContext="{StaticResource categoryViewSource}">
..

代码背后

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中的{1}}。

我真的很感激任何帮助,指出用x:Key实施CollectionViewSourceDataBinding的正确方法。

3 个答案:

答案 0 :(得分:52)

有两个选项可以正确使用CollectionViewSource MVVM -

  1. 通过ObservableCollection公开Categories个项目({1}})并在XAML中创建ViewModel -

    CollectionViewSource

    scm:<CollectionViewSource Source="{Binding Path=Categories}"> <CollectionViewSource.SortDescriptions> <scm:SortDescription PropertyName="CategoryName" /> </CollectionViewSource.SortDescriptions> </CollectionViewSource>

    见到这一点 - Filtering collections from XAML using CollectionViewSource

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

    创建并公开ICollectionView

    见到这一点 - How to Navigate, Group, Sort and Filter Data in WPF

  3. 以下示例显示了如何创建集合视图和 将其绑定到ViewModel

    查看XAML:

    ListBox

    查看Codebehind:

    <Window 
        x:Class="CustomerView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
        <ListBox ItemsSource={Binding Customers} />
    </Window>
    

    视图模型:

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

    <强>更新

    Q值。如果没有可以排序的财产?例如如果有public class CustomerViewModel { private ICollectionView _customerView; public ICollectionView Customers { get { return _customerView; } } public CustomerViewModel() { IList<Customer> customers = GetCustomers(); _customerView = CollectionViewSource.GetDefaultView(customers); } } 的字符串或int?

    一个。在这种情况下,您只需使用作为属性名称:

    ObservableCollection

答案 1 :(得分:8)

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

像这样:

ViewModel:

    public DesignTimeVM()  //I'm using this as a Design Time VM 
    {
        Items = new List<Foo>();
        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:

<ListBox ItemsSource="{Binding FooViewSource.View}" SelectedItem="{Binding SelectedFoo}"/>

这意味着我可以根据需要在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,在你问之前,这里是你如何使用设计时虚拟机:WPF Design Time View Model

答案 2 :(得分:1)

仅供参考,另一种方法是在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中的用法:

<CollectionViewSource
        x:Key="yourKey"
        Source="{Binding YourCollection}"
        classes:CollectionViewSourceFilter.FilterObject="{Binding}" />

以及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<YourItemType> _YourCollection = new ObservableCollection<YourItemType>();
    public ObservableCollection<YourItemType> 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;
    }
}