Reputation: 67195
Still trying to learn MVVM and WPF here.
I'm trying to create a complex view model EditArticleViewModel
. It has some code that is repeated for similar controls and so I've moved the repeating code into another class. Then, I've added several instances of that other class into EditArticleViewModel
.
I will set an instance of EditArticleViewModel
as my window's DataContext
. And I will bind to things like Categories.Items
and Subcategories.SelectedItem
.
public class CategoryView
{
public ObservableCollection<object> Items { /* */ }
public object SelectedItem { /* ... */ }
}
public class SubcategoryView
{
public ObservableCollection<object> Items { /* */ }
public object SelectedItem { /* ... */ }
}
public class EditArticleViewModel : INotifyPropertyChanged
{
public CategoryView Categories { get; private set; }
public SubcategoryView Subcategories { get; private set; }
public EditArticleViewModel()
{
Categories = new CategoryView();
SubcategoryView Subcategories new SubcategoryView();
}
// Additional properties and methods here
}
As you can see, my EditArticleViewModel
class implements INotifyPropertyChanged
so that I can notify the visual elements when something has changed.
My question is about how I notify visual elements about changes within CategoryView
and SubcategoryView
. Is there a way to notify the window about changes within these classes directly? Or must I raise an event from each class and have EditArticleViewModel
handle that event in order to send the appropriate notification?
Any tips appreciated.
Upvotes: 1
Views: 2905
Reputation: 1543
There should only be one ViewModel per View, with an extend that primary ViewModel can contain other "ViewModels".
So when you set DataContext
to your primary ViewModel all the content of it will be have a subscription to NotifyPropertyChanged
event, thus implementing INotifyPropertyChanged
interface in other derived ViewModel will be notified.
I would suggest implementing a base class with INotifyPropertyChanged
interface
which you could derive from in your other ViewModels.
By having this alteration you should solve the problem you are having:
public class ObservableViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName]string propName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
}
}
public class CategoryView : ObservableViewModelBase
{
public ObservableCollection<object> Items { /* */ }
public object SelectedItem { /* ... */ }
}
public class SubcategoryView : ObservableViewModelBase
{
public ObservableCollection<object> Items { /* */ }
public object SelectedItem { /* ... */ }
}
public class EditArticleView : ObservableViewModelBase
{
public CategoryView Categories { get; set; } = new CategoryView();
public SubcategoryView Subcategories { get; set; } = new SubcategoryView();
}
Regarding ObservableCollection
. It will notify view to change only when you add/remove items but it does not notify when content is changed. To update view on item content change you should have something like that:
public class GridRowItemViewModel : ObservableViewModelBase // From previous example.
{
private string _sampleProp;
public string SampleProp
{
get
{
return _sampleProp;
}
set
{
_sampleProp = value;
OnPropertyChanged();
}
}
}
And thus your Main ViewModel should look something like this:
public class MainViewModel : ObservableViewModelBase // This is your DataContext.
{
public ObservableCollection<GridRowItemViewModel> GridCollection { get; set; }
}
EDIT: You cannot bind to fields, WPF does not resolve fields. It can only handle properties. So by creating plain fields of child ViewModels you are getting no where. Change these into properties and you will be able to access its content in the View by the property name.
Upvotes: 3