Am folosit Drag and Drop pentru a lega obiectul Data Source (un model DB) de DataGrid
(practic, urmând acest exemplu din Entity Framework Databinding cu WPF.
Totul funcționează bine cu această implementare.
XAML
<Window.Resources>
<CollectionViewSource x_Key="categoryViewSource"
d_DesignSource="{d:DesignInstance {x:Type local:Category}, CreateList=True}"/>
</Window.Resources>
<Grid DataContext="{StaticResource categoryViewSource}">
..
Codul din spate
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;
}
ViewModel
public MainWindow()
{
InitializeComponent();
this.DataContext = new MyViewModel();
}
Cu toate acestea, atunci când încerc să folosesc același cod din cadrul ViewModel, nu funcționează (FindResource
nu este disponibil), în plus, nu cred că aceasta este abordarea corectă (adică să folosesc x:Key
în MVVM).
Aș aprecia foarte mult orice ajutor pentru a-mi indica care este modalitatea corectă de a implementa CollectionViewSource
și DataBinding
cu DataGrid
.
Aveți două opțiuni pentru a utiliza CollectionViewSource
în mod corespunzător cu MVVM –
-
Expuneți un
ObservableCollection
de elemente (Categories
în cazul tău) prin intermediulViewModel
și creațiCollectionViewSource
în XAML astfel –<CollectionViewSource Source="{Binding Path=Categories}"> <CollectionViewSource.SortDescriptions> <scm:SortDescription PropertyName="CategoryName" /> </CollectionViewSource.SortDescriptions> </CollectionViewSource>
scm:
xmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
a se vedea acest lucru –
Filtering
colecții din XAML utilizând CollectionViewSource -
Creați și expuneți un
ICollectionView
direct din colecția dvs.ViewModel
a se vedea acest lucru – Cum să navigați, să grupați, să sortați și să filtrați date în WPF
Exemplul următor arată cum să creați o vizualizare de colecție și să o legați de un ListBox
Vizualizare XAML:
<Window
xmlns_x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns_scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
x_Class="CustomerView">
<ListBox ItemsSource={Binding Customers} />
</Window>
View Codebehind:
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 );
}
}
Update:
Q. Dacă nu există o proprietate pe care să se facă sortarea? de exemplu, dacă există o proprietate ObservableCollection
de șir sau int?
A. În acest caz, puteți utiliza pur și simplu . ca nume de proprietate:
<scm:SortDescription PropertyName="." />
Am constatat că este la îndemână să ai un tip de CollectionViewSource
în ViewModel-ul meu și să leagă ListBox
(în cazul meu) la CollectionViewSource.View
în timp ce setează CollectionViewSource.Source
să fie lista pe care vreau să o folosesc.
Astfel:
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}"/>
Acest lucru înseamnă că pot să fac lucruri îngrijite în VM, după cum este necesar (din 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.
}
Presupun că acest lucru este posibil atunci când se utilizează ICollectionView
dar codul demonstrativ din linkul de pe blog pare a fi o chestie de codebehind, care se referă direct la listbox, ceea ce încerc să evit.
BTW înainte de a întreba, iată cum se utilizează un Design Time VM: Modelul de vizualizare WPF Design Time View Model
Doar ca referință, o altă modalitate este de a utiliza o proprietate atașată pe CollectionViewSource
care apoi direcționează funcțiile către ViewModel
(Implementarea unei interfețe).
Aceasta este o demonstrație foarte elementară doar pentru filtrare, ar mai fi nevoie de ceva muncă pentru, de exemplu, o a doua colecție pe VM, dar cred că este suficientă pentru a arăta tehnica generală.
Dacă această metodă este mai bună sau mai rea decât celelalte metode este subiect de discuție, am vrut doar să subliniez că există o altă modalitate de a face acest lucru.
Definiția proprietății atașate:
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)
);
}
Interfață:
public interface IFilterCollectionViewSource
{
void Filter(object sender, FilterEventArgs e);
event EventHandler FilterRefresh;
}
utilizare în xaml:
<CollectionViewSource
x_Key="yourKey"
Source="{Binding YourCollection}"
classes:CollectionViewSourceFilter.FilterObject="{Binding}" />
și utilizarea în 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;
}
}
scm:
este aceastaxmlns:scm="clr-namespace:System.ComponentModel;assembly=WindowsBase"
– > Por user2023861.