Nested view model in WPF - c#

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.

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.

Related

How do I don't validate a nested class in FluentValidation MVC

I have a Viewmodel that have another Viewmodel inside, take a look
[Validator(typeof(ParentValidatorVM))]
public class ParentVM {
public ChildVM { get; set; }
public ParentVM () {
this.ChildVM = new ChildVM ();
}
}
//rulles for validation
internal sealed class ParentValidatorVM : AbstractValidator<ParentVM> {
public ParentValidatorVM() {
//set rules
this.RuleFor(x => x.ChildVM).NotValidate();
//the row above is just an example the method 'NotValidate', I invented
}
}
The ChildVM has the structure with properties
[Validator(typeof(ChildValidatorVM))]
public class ChildVM {
//My properties
}
//rulles for validation
internal sealed class ChildValidatorVM : AbstractValidator<ChildVM> {
public ChildValidatorVM() {
//set rules
}
}
My problem is, when I call ModelState.isValid the rules from the child comes together the rules from the Parent, it is not to happen.
Someone know a way to avoid it?

Multiple viewmodels sharing a service with notification

Question: I have two viewmodels that share a service with a list. My question is how to setup the notification so that both viewmodels know when this list is changed. Description below and code of where i'm at.
I found this example HERE that looks right for what I'm trying to do, but I have a question regarding how to be notified in my viewmodels of a change in my service. I'll add some code I've mocked up to see if I'm on the right track. I'm using WPF/MVVM Light.
First part is a service with a interface that will have a list of data, in this example I'm using a string list. I want a property in both viewmodels to have access to this list of data, and be notified when it's changed. I think what's throwing me is the interface IOC into my viewmodels. I'm understanding more and more why this is good, but I'm still wrapping my mind around it and I'm not sure how to setup the notification when the list is changed in the service. If my service was not injected I might have setup a event or property that my viewmodel property would access get/set but injecting my service does not expose my public fields, just methods. This is new to me so it's very likely i'm not understanding this correctly or missing something.
I used a List in my service instead of a ObservableCollection based on some reading I've done warning against using the ObservableCollection here. Thanks you for any help.
public class MyService : IMyService
{
private List<string> myList = new List<string>();
public List<string> getMyList()
{
return this.myList;
}
public void setMyList(List<string> value)
{
this.myList = value;
}
public void addValue(string value)
{
this.myList.Add(value);
}
public void insertValue(int index, string value)
{
this.myList.Insert(index, value);
}
}
public class MyViewModelOne : ViewModelBase
{
private readonly IMyService myService;
public MyViewModelOne(IMyService myService)
{
this.myService = myService;
}
public List<string> MyProperty // control item source will bind to this
{
get
{
return this.myService.getSource();
}
}
public void setSomeValue(value)
{
this.myService.addValue(value);
}
}
public class MyViewModelTwo : ViewModelBase
{
private readonly IMyService myService;
public MyViewModelTwo(IMyService myService)
{
this.myService = myService;
}
public List<string> MyProperty // control item source will bind to this
{
get
{
return this.myService.getSource();
}
}
public void setSomeValue(value)
{
this.myService.addValue(value);
}
}
From what I understood about your problem, what you need essentially is that your INotifyPropertyChanged implementation at your service level and the list my list to be an ObservableCollection being injected from the service.
Now if there is a notification change it would be directly on the service and hence no explicit need of handling.
Your binding could look like "{Binding MyService.MyProperty}"
I got this working two different ways, I went with the first example because I think it's easier to follow in the code.
This came up because I had a control in my mainview with related code that was growing and I realized I wanted the same control/behavior in a separate view that would use the same data/control for a different purpose.
I did not want to duplicate this control/template/code in two places so I made it into a User Control. I then nest the user control in my views. The user control has it's own VM. The main view updates the service with new data, and the nested control listens on a event to know when there is new data.
Still very new to MVVM thinking so please feel free to point out in issues with either of these examples.
Example using a service with eventhandler.
public interface IMyInterface
{
event EventHandler OnSomeEvent;
void addSomeData(string value);
void getSomeData();
}
public class MyInterface: IMyInterface
{
public event EventHandler OnSomeEvent = delegate { };
public void addSomeData(string value)
{
// do stuff
OnSomeEvent();
}
public string getSomeData()
{
return "some data";
}
}
// Main ViewModel
public class ViewModelOne : ViewModelBase
{
IMyInterface myInterface;
public NotifyViewModel(IMyInterface myInterface)
{
this.myInterface = myInterface;
this.myInterface.OnItemSourceChanged += myInterface_OnSomeEvent;
}
void testEvent()
{
this.myInterface.addSomeData("test data");
}
}
// My nested user control
public class ViewModelTwo : ViewModelBase
{
IMyInterface myInterface;
public NotifyViewModel(IMyInterface myInterface)
{
this.myInterface = myInterface;
this.myInterface.OnItemSourceChanged += myInterface_OnSomeEvent;
}
void myInterface_OnSomeEvent(object sender, System.EventArgs e)
{
// do stuff
}
}
Example using MVVM Light Messaging
public class EventDataSource
{
public string MyItemSource { get; set; }
public EventDataSource()
{
MyItemSource = string.Empty;
}
}
// Message class
public class MyDataSourceMessage : MessageBase
{
public EventDataSource MyItemSource { get; set; }
public MyDataSourceMessage(EventDataSource myItemSource)
{
MyItemSource = myItemSource;
}
}
// Main ViewModel
public class ViewModelOne : ViewModelBase
{
public NotifyViewModel() {}
void testMessage()
{
EventDataSource msg = new EventDataSource() { MyItemSource = "magic message!"};
Messenger.Default.Send(new MyDataSourceMessage(msg as EventDataSource));
}
}
// My nested user control
public class ViewModelTwo : ViewModelBase
{
public NotifyViewModel()
{
Messenger.Default.Register<MyDataSourceMessage>(this, (action) => ReceiveMessage(action));
}
private ObservableCollection<string> myProperty = new ObservableCollection<string>();
public ObservableCollection<string> MyProperty
{
get { return myProperty; }
set
{
myProperty: = value;
RaisePropertyChanged(() => MyProperty);
}
}
void ReceiveMessage(MyDataSourceMessage action)
{
// do something with the data
MyProperty.Add(action.DGItemSource.ItemSource);
}
}
Ok let me try to shed some light on this. First of all, change notification is not meant to pass information between view models, it is meant to notify the view itself that the a property of the view model has changed.
There are a few methods for view models to issue change notifications to views:
INotifyPropertyChanged interface
INotifyCollectionChanged interface
A custom event with the name of the property suffixed with Changed (e.g. an event called MyPropChanged for a property called MyProp)
Having said all that, it is still possible for one view model to subscribe to the events generated by the above methods, and if you really need to, you may of course.
EDIT:
Check this link for a description on item number 3 above.

Implementing NotifyPropertyChange on EF Generated Models to achieve TwoWay binding

My goal is to do TwoWay binding off a generated EntityFramework model.
What is the best way to implement NotifyPropertyChanged on properties in a generated entity model?
For example, suppose I have this entity from a database:
public partial class Survey
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public bool Answer { get; set; }
}
I then create a ViewModel...
public class SurveyViewModel : ViewModelBase
{
private Survey _survey = new Survey();
public Survey
{
get { return _survey; }
set
{
_survey = value;
}
}
}
How could I achieve 2 way binding other than writing dependency properties for every single property in the entity model, like so...
//below the declaration of the Survey entity in the viewmodel
public string FirstName
{
get { return Survey.FirstName; }
set
{
Survey.FirstName = value;
NotifyPropertyChanged("FirstName");
}
}
//This works but is very time consuming for large models
Let me know if I'm attempting this wrong...
PropertyChanged.Fody may be what you are looking for:
// Non-auto generated partial class declaration
[ImplementPropertyChanged]
public partial class Survey
{
}
As commented by TyCobb, this question has been asked repeatedly and the result remains the same... here is a summary.
While there are ways pollute your data models with UI accommodating
features such as INotifyPropertyChanged, the MVVM mantra teaches us
that it is the View-Model's job to interact with the UI and the
Data-Model should remain as pure as possible (POCO).
So what? How do we keep to MVVM but avoid the boiler-plate codes of exposing individual properties on the View-Model?
From experience, calling a RaisePropertyChanged is not reserved only for property setters but could be used to manually raise a property changed for a model that has had its own properties modified, thus, cause the UI to update.
Here is a code example...
public class SurveyViewModel : INotifyPropertyChanged
{
private Survey _survey;
public Survey Survey
{
get { return _survey; }
set
{
_survey = value;
RaisePropertyChanged(() => Survey);
}
}
public void ModifySurvey()
{
// Modify a property of the model.
Survey.FirstName = "Modified";
// Make other modifications here...
// Notify property changed
RaisePropertyChanged(() => Survey);
}
}

Tree structure with a nested observablecollection of children

I have a class that looks like the following
class ModelCollection : ObservableCollection<MyModel>
{
public ModelCollection ()
{
this.CollectionChanged += (s, e) =>
{
if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
{
//do something
}
if(e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
{
//do something
}
};
}
//etc..
}
and the class MyModel
public class MyModel
{
public int id {get;set;}
public string Name {get;set;}
public MyModel Parent {get;set;}
public ModelCollection Descendants {get;set;}
public ModelCollection Children {get;set;}
//etc..
}
and in general i have a collection that have the root MyModel which has Children of type MyModel and those children have children of that type and it can reach up to 20 levels of nesting, so its basicly a tree structure.
as you can see in the ModelCollection i have an event listener, my question is is it ok to have this amount of event listeners considering that i have thousands of MyModel items and multiple nested levles per item, if not is there a better suggestion?
You can use so match event listeners, but then you have to maintain it. In addition it can cause memory leaks.
Another option is to use INotifyPropertyChanged on all of your model objects, for example:
public class MyModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public int id { get {...} set { OnPropertyChanged("id"); } }
public string Name { get {...} set { OnPropertyChanged("Name"); } }
public MyModel Parent { get {...} set { OnPropertyChanged("Parent"); } }
public ModelCollection Descendants { get {...} set { OnPropertyChanged("Descendants"); } }
public ModelCollection Children { get {...} set { OnPropertyChanged("Children"); } }
private void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = this.PropertyChanged;
if (handler != null)
{
handler(this, new PropertyChangedEventArgs(propertyName));
}
}
}
Then you can use hierarchical data context and similar WPF's features which can utilize the INotifyPropertyChanged implementation without any work from your side.
Another option is to inherit DependencyObject and use DependencyProperties instead of simple properties, which WPF can utilize in similar ways. However I think DependencyObject may be an overkill in cases of non-UI elements, and INotifyPropertyChanged is preferred in such cases.
Update: I noticed you didn't mention that you need the ObservalbleCollection for UI reasons, so my comment about WPF may be redundant. Anyway I think that using INotifyPropertyChanged is anyway a good way to manage such things, and I think it is also the standard one.

Model And ViewModel Relations and Inheritance - how should be implemented?

Sorry for the confusing title of the question. I am uncertain about how should I implement ViewModels and Models which derive from a base class.
My ViewModel classes are based on a ViewModelBase, the ViewModelBase holds a ModelBase which serves as a base class for all other models.
Each ViewModel holds a "Model" Property, however, because a Model property was defined by the base ViewModel class as the ModelBase class, I always have to create another property which casts the Model - from ModelBase to the relevant Model class.
My question is - is there not a simpler solution? Is there a design pattern which is relevant for these issues?
Here's a code sample:
public abstract class BasicViewModel : ViewModelBase
{
public BasicViewModel()
{
}
public ModelBase Model { get; set; }
}
public class ModelBase
{
}
public class ContainableViewModel : BasicViewModel
{
public ContainableViewModel(ContainableModel model)
{
this.Model = model;
}
public ContainableModel MyModel { get { return (ContainableModel)Model; } }
public int Children { get { return MyModel.Children; } set { MyModel.Children = value; } }
}
public class ContainableModel : ModelBase
{
public ContainableModel()
{
Children = 2;
}
public int Children { get; set; }
}
As you can see, the "MyModel" property is the one which bugs me.
Thank you very much for your help and time!
If each of your view model contains a Model property, you could use generics:
public abstract class BasicViewModel<TModelType> : ViewModelBase
where TModelType : ModelBase
{
public BasicViewModel(TModelType model)
{
Model = model;
}
public TModelType Model { get; set; }
}
public class ModelBase
{
}
Your containable view model is now defined as follows:
public class ContainableViewModel : BasicViewModel<ContainableModel>
{
public ContainableViewModel(ContainableModel model)
: base(model)
{
}
// you can now omit this method, it is defined on the abstract superclass
//public ContainableModel Model { get { return ()Model; } }
public int Children { get { return MyModel.Children; } set { MyModel.Children = value; } }
}
public class ContainableModel : ModelBase
{
public ContainableModel()
{
Children = 2;
}
public int Children { get; set; }
}
I generally don't use a base class for the different Model types, as there is generally not a one to one correspondance between ViewModel and Model objects. Further, the Model objects generally don't have much in common. This is different from the ViewModel objects, which all tie in via data binding and therefore could benefit from various helper methods that standardize the way you bind to your ViewModel instances.
The 'Model' in Model-View-ViewModel is a layer which provides your business logic and data interaction (everything not related to visualization and interaction with the user.) Your CustomerViewModel for your CustomerWindow might make use of a Customer object from your Model layer, but it very likely has references to other objects from your model, like some sort of Repository. Other Views, like say your MainWindow, might have a MainViewModel that doesn't directly correspond to any Model object, but probably has at least a few dependencies on your Model.

Categories