我使用Drag and Drop将数据源对象(数据库模型)绑定到DataGrid
(基本上遵循Entity Framework Databinding with WPF中的此示例。
这个实现一切正常。
<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
实施CollectionViewSource
和DataBinding
的正确方法。
答案 0 :(得分:52)
有两个选项可以正确使用CollectionViewSource
MVVM -
通过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
直接从xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
ICollectionView
以下示例显示了如何创建集合视图和
将其绑定到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;
}
}