ドラッグアンドドロップを使用して、データソースオブジェクト(DBモデル)を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
は使用できません)。さらに、これが正しいアプローチだとは思いません(つまり、x:Key
in MVVM)。
CollectionViewSource
とDataBinding
を使用してDataGrid
を実装する正しい方法を教えてください。
MVVMでCollectionViewSource
を適切に使用するには2つのオプションがあります-
ObservableCollection
を介してアイテムのCategories
(あなたの場合はViewModel
)を公開し、XAMLで次のようにCollectionViewSource
を作成します-
<CollectionViewSource Source="{Binding Path=Categories}">
<CollectionViewSource.SortDescriptions>
<scm:SortDescription PropertyName="CategoryName" />
</CollectionViewSource.SortDescriptions>
</CollectionViewSource>
scm:xmlns:scm="clr-namespace:System.ComponentModel;Assembly=WindowsBase"
これを参照してください- Filtering
CollectionViewSourceを使用したXAMLからのコレクション
ICollectionView
から直接ViewModel
を作成して公開する
これを参照してください- WPFでデータをナビゲート、グループ化、並べ替え、フィルターする方法
次の例は、コレクションビューを作成し、ListBox
にバインドする方法を示しています
XAMLを表示:
<Window
xmlns="http://schemas.Microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.Microsoft.com/winfx/2006/xaml"
xmlsns:scm="clr-namespace:System.ComponentModel;Assembly=WindowsBase"
x:Class="CustomerView">
<ListBox ItemsSource={Binding Customers} />
</Window>
分離コードを表示:
public class CustomerView : Window
{
public CustomerView()
{
DataContext = new CustomerViewModel();
}
}
ViewModel:
public class CustomerViewModel
{
private readonly ICollectionView customerView;
public ICollectionView Customers
{
get { return customerView; }
}
public CustomerViewModel()
{
IList<Customer> customers = GetCustomers();
customerView = CollectionViewSource.GetDefaultView( customers );
}
}
更新:
Q.並べ替えるプロパティがない場合例えばstringまたはintのObservableCollection
がある場合
A.その場合、プロパティ名として。を単に使用できます:
<scm:SortDescription PropertyName="." />
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
オブジェクトも使用するとこれが可能になると思いますが、ブログリンクのデモコードは、コードボックスの背後にあるように見え、リストボックスを直接参照するため、回避しようとしています。
ところで、質問する前に、デザインタイムVMを使用する方法を次に示します。 WPFデザインタイムビューモデル
参考のために、別の方法は、CollectionViewSourceで添付プロパティを使用して、関数をViewModelにパイプすることです(インターフェイスの実装)。
これはフィルタリングのための非常に基本的なデモンストレーションであり、例えば、 VMの2番目のコレクションですが、一般的なテクニックを示すのに十分だと思います。
これが他の方法が議論されているよりも良いか悪い場合、私はちょうどこれを行う別の方法があることを指摘したかったです
添付プロパティの定義:
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;
}
}