I have the following model:
public class Person
{
public string LastName{get;set;}
public City City {get;set;}
}
public class City
{
public string Name {get;set;}
}
I have two Views:
One for display all Persons with LastName and the Name of the city in
a DataGrid(AllPersonsViewModel)
One for adding a new Person (PersonViewModel)
My AllPersonsViewModel:
public class AllPersonViewModel : ViewModel
{
public ObservableCollection<PersonViewModel> PersonViewModels {get;set;}
}
I started with the following PersonViewModel:
public class PersonViewModel : ViewModel
{
private Person _person;
public string Name
{
get { return _person.Name;}
set { _person.Name = value; RaisePropertyChange("Name");}
}
public string CityName
{
get { return _person.City.Name;}
}
}
Then I added the properties for adding a new Person. In the View I need a Textbox for the PersonName and a Combobox for selection of a City:
public class PersonViewModel : ViewModel
{
private Person _person;
public string Name
{
get { return _person.Name;}
set { _person.Name = value; RaisePropertyChange("Name");}
}
public string CityName
{
get { return _person.City.Name;}
}
public City SelectedCity
{
get { return _person.City;}
set { _person.City = value; RaisePropertyChange("SelectedCity");}
}
public ObservableCollection<City> Cities {get;set;}
}
Is this the right approach? It seems a little bit redundant to me. In the Grid of AllPersonsView I could also bind directly to the "SelectedCity.Name" instead of the extra property CityName. The grid is also readonly.
you have multiple problems;
1 - you do not need to declare an observable collection of viewmodels in AllPersonViewModel. Just declare an ObservableCollection of Person.
2 - do not add the CityName property; not needed as you have stated.
3- do not add the Name property. Bind the textbox to Name property of the Person.
Your question really boils down "is it OK to expose my model directly to the view?" Some purist will say no while other will say that having a view model that wraps a model without adding any new functionality is redundant.
In my opinion it depends on the task at hand but "skiping" a view model may come back and bite you later when you need to add additional state that doesn't belong in the model. If in doubt use a view model but for instance when exposing simple model objects in a list you often don't need the extra layer the view model provides.
In your case you have opted for the "purist" solution and because your model object doesn't support INotifyPropertyChanged you can't get rid of the view model if a model property is changed by multiple sources. But instead of providing a CityName property you could bind to SelectedCity.Name. WPF supports property navigation in data binding expressions.
For more insight into this topic you can google mvvm expose model.
Related
I have a simple Person model:
public class Person
{
public string FirstName { get; set; }
public DateTime LastUpdated { get; set; }
}
Lets say that I have a View that has a TextBox, which is binded to LastUpdated field:
<TextBox Grid.Column="1" Margin="5" Text="{Binding Person.FirstName}"/>
Now I need to implement PropertyChanged somehow. Let's use Prism Snippet.
What I need is to perform SetProperty on a Person class:
private Person person;
public Person Person
{
get { return person; }
set { SetProperty(ref person, value); }
}
and NOT on the field LastUpdated in Person class:
private DateTime? lastUpdated;
public DateTime? LastUpdated
{
get { return lastUpdated; }
set { SetProperty(ref lastUpdated, value); }
}
This is not the matter of dependencies in the model. I got the model through DataContract ( WCF service ) so I cannot changed it. So is there a way to observe a class for changes and bind class field to some UI control.
So is there a way to observe a class for changes and bind class field to some UI control.
No. You need to raise the PropertyChanged event for the object of the property that you are actually binding to.
If you get the Person object from some third-party service for which you cannot modify the code to raise the PropertyChanged event in the setter of the FirstName property, you should not bind to these objects.
Instead you should create your own view model class and bind to this one. The view model can simply wrap the WCF class, e.g.:
public class PersonViewModel
{
private readonly Person _person;
public PersonViewModel(Person person)
{
_person = person;
}
public string FirstName
{
get { return _person.FirstName; }
set { _person.FirstName = value; RaisePropertyChanged(); }
}
}
If you're using Prism, then you likely are using the MVVM pattern. If so, then the one approach is using the view model for binding. Instead of exposing Person as a property, expose the individual properties you want to bind against - FirstName and LastUpdated:
Create a property on the view model that forwards calls to your model.
Bind your view to the view model property.
You can freely implement your change notifications in the view model.
I want to make a transition to a reactive view model / model.
I've used 3 scenarios so far:
"ValueA": The model value is only accessed from one view model at a time and the value is only changed through the view model
=> simple property in model, forwarding property with PropertyChanged in view model
"ValueB": The model value is accessed from several view models and/or changes from other sources
=> property with event in model, forwarding property and translation from changed event to PropertyChanged in view model
"ValueC": A value only used in the view model
=> no property in model, property backed by own field with PropertyChanged in view model
This is my "current" approach:
class Model
{
public string ValueA {get;set;}
private string valueB;
public event ValueBChangedEvent ValueBChanged;
public string ValueB
{
get
{
return valueB;
}
set
{
valueB = value;
ValueBChanged();
}
}
}
class ViewModel : INotifyPropertyChanged
{
private Model model;
public string ValueA
{
get
{
return model.ValueA;
}
set
{
model.ValueA = value;
OnPropertyChanged();
}
}
ViewModel()
{
model.ValueBChanged += model_ValueBChanged;
}
private void model_ValueBChanged()
{
OnPropertyChanged("ValueB");
}
public string ValueB
{
get
{
return model.ValueB;
}
set
{
model.ValueB = value;
// no change notification since done via model
}
}
private string valueC;
public string ValueC
{
get
{
return valueC;
}
set
{
valueC = value;
OnPropertyChanged();
}
}
}
This is how I intend to model them using reactive extensions:
class ReactiveModel
{
public string ValueA {get;set;}
private ISubject<string> valueB = new Subject<string>();
public ISubject<string> ValueB
{
get
{
return valueB;
}
}
}
class ReactiveViewModel : INotifyPropertyChanged
{
private ReactiveModel model;
public string ValueA
{
get
{
return ???;
}
set
{
???
}
}
private ReactiveProperty<string> valueB = model.valueB.ToReactiveProperty();
public Reactive<string> ValueB
{
get
{
return valueB;
}
// no setter since access via ValueB.Value which is read-write
}
private ISubject<string> _valueC = new Subject<string>();
private ReactiveProperty<string> valueC = _valueC.ToReactiveProperty();
public ReactiveProperty<string> ValueC
{
get
{
return valueC;
}
// no setter since access via ValueC.Value which is read-write
}
}
Summary:
"ValueA": I have no clue for this case
"ValueB": works at first glance but does neither propagate changes from view model to model nor the other way.
"ValueC": this works as intended
I'd be happy if I had a solution for ValueA and ValueB.
ValueB: View model is responsible for updating model. ReactivePropertyuses only IObservable interface from your model properties and reads values from ValueB(does not write anything).
ReactiveProperty is changed by view through Value property.
ReactiveProperty implements IObservable and you should subscribe to changes to get new values.
ValueA: We can make a ReactiveProperty on the view model side an subscribe to propagate the changed value to the model.
Here is the code for the solution:
class ReactiveModel
{
public string ValueA {get;set;}
private readonly Subject<string> valueB = new Subject<string>();
public IObservable<string> ValueB
{
get
{
return valueB;
}
}
public void UpdateB(string newValue)
{
valueB.OnNext(newValue);
}
}
class ReactiveViewModel
{
private readonly ReactiveModel model;
private readonly ReactiveProperty<string> valueA;
private readonly ReactiveProperty<string> valueB;
public ReactiveViewModel(ReactiveModel model)
{
this.model = model;
valueA = new ReactiveProperty<string>(model.ValueA);
valueA.Subscribe(x => model.ValueA = x);
valueB = model.ValueB.ToReactiveProperty();
valueB.Subscribe(model.UpdateB);
}
public IObservable<string> ValueA
{
get
{
return valueA;
}
}
public ReactiveProperty<string> ValueB
{
get
{
return valueB;
}
}
}
XAML would be in both cases:
<TextBox Text="{Binding ValueA.Value, UpdateSourceTrigger=PropertyChanged}"/>
This is a bit of a contentious topic but I personally don't see property change notification as being specific to the view model and view, I therefore use B but I add INPC to the models as well in my data layer. This can be done in a post-processing build step using Fody or by wrapping the models in a proxy using something like Castle Dynamic Proxy. I personally use the latter, although it requires integration with your ORM so as to not hammer performance i.e. you don't want your database code loading a model object and then thinking that object has changed because you've tried to update it use the proxy wrapper (this is especially true when you turn IList<> into an ObservableCollection).
Your current approach doesn't seem to make a lot of sense. You are implementing events to signal when the Model changes so the View Model can take action. However only the View Model should change the Model, therefore events are completely unnecessary.
The View Model is responsible for making changes to the Model, therefore it should know when a change has been performed, as it is the source of said change.
A pure MVVM approach would be something like this:
public class MyModel
{
public string MyValue { get; set; }
...
}
public class MyViewModel
{
private MyModel _Model;
public string MyModelValue
{
get { return _Model.MyValue; }
set
{
_Model.MyValue = value;
//Notify property changed.
}
}
...
}
It is not the responsibility of the Model to notify the View of changes, instead it is the responsibility of the ViewModel to signal these changes. The Model should not be exposed to the View, but instead the properties of the Model that the View requires should be exposed.
Think of it this way.
The user changes the MyModelValue property in a TextBox on the View.
The View notifies the ViewModel of the change.
The ViewModel changes the Model.
The only purpose of INotifyPropertyChanged is when the above process is reversed, where the ViewModel needs to tell the View that a property has changed:
A method in the ViewModel is called that updates MyModelValue.
The ViewModel notifies the View of the change.
The View updates the TextBox.
The pattern of exposing only properties of the Model that the view requires is not always followed, instead you may see the entire Model being exposed to the View, but as I have said many times before, MVVM is a pattern, not the law. Implementing INotifyPropertyChanged in the Model is perfectly acceptable.
I have a WPF application with MVVM.As I understood, the main goal of MVVM is to separate between logic layer and UI layer.
I have this Model class :
public class User
{
public string Login{get;set;}
public string Pwd{get;set;}
public List<User> GetUsers()
{
//
}
}
in my ViewModel, I instanciate a User object and an ObservableCollection of User
public class UserVM
{
public User _User{get;set;}
public ObservableCollection<User> liste{get; private set;}
public UserVM()
{
_User = new User("TODO","PWD2");
liste = new ObservableCollection(_User.GetUsers);
}
}
I feel that I bind directly a UI properties to a model object,So I need To know :
When I bind UI properties to the object _User properties, did I respect the MVVM architecture?
When I bind a listview datasource to liste, did I respect the MVVM architecture?
For the first question, if it is not suitable for MVVM, is it better to expose the model's properties instead of declaring the class?
For the second question, if it is not suitable for MVVM, How can I fix it ?
Thanks,
It looks like your User class has a tree-like structure in that it contains a List of User objects which themselves may contain a List of User objects...
The problem here is that your view model class contains User objects. Only the UserVM model would contain an ObservableCollection for example.
A simple fix would be: EDIT user.GetUsers() doesn't return a List<UserVM>
public class UserVM
{
public string Login { get; set; }
public string Pwd { get; set; }
public ObservableCollection<UserVM> Users { get; private set; }
public UserVM(User user)
{
Login = user.Login;
Pwd = user.Pwd;
Users = new ObservableCollection<UserViewModel>(
user.GetUsers().Select(subUser => new UserViewModel(subUser)));
}
}
You may also want to implement INotifyPropertyChanged so that the view gets notifications that the view model has changed.
So lets say I have a SQLite database of Person's with property Name
Public Class Person
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
And now I have a view with a ListBox Displaying those names
<ListBox ItemSource={Binding People}>
<ListBox.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
and my Views DataContext is PeopleViewVM
Public Class PeopleViewVM
{
Public PeopleViewVM
{
//Do my SQLite stuff,
// Get IEnumerable Person's
// Create Observable Collection of People
}
private ObservableCollection<Person> _people;
public ObservableCollection<Person> People
{
get { return _people; }
set
{
_people = value;
RaisePropertyChanged();
}
}
}
Now I understand this is a simple example. But I am unsure whether this is the correct implementation of the MVVM design pattern.
If Person my model this means that the view is binding directly to the model when it is binding to the property name. If I change the name of a person in code behind this won't be reflected in the view. What is the correct way to do this example using the MVVM design pattern?
That can be the "correct" implementation, based on your requirements. I wouldn't say that there's a "correct", and "incorrect" for this issue. More like: would it be better for my scenario, or not?
People choose to bind models against view directly based on their requirements, and how they feel. Sometimes I like to simplify my models, and wrap them into "PersonViewModel", in order to expose more relevant properties, and not pollute the Model.
If that doesn't suit you, you can download ReSharper(takes care of "trying" to keep the View & viewmodel synchronized), or alternatively you can encapsulate your model further, and create a "proxy" object, as such:
Public Class PersonViewModel
{
readonly Person _person;
Public PersonViewModel(Person person)
{
_person = person;
}
public string Name
{
get { return _person.Name; }
set
{
_person.Name = value;
RaisePropertyChanged();
}
}
which seems to be pointless, but helps to keep the view and model even more separate, in case of model entities that change often. ReSharper does take care of most cases, in my experience.
Say I have this domain entity:
public class Foo
{
public string Name { get; set; }
public IEnumerable<Bar> Bars { get; set; }
}
Now let's say I need to bind the Bars property to a datagrid in a WPF/MVVM app. What is the appropriate way to notify the view that the Bars property changed? I see a few options:
Change Bars to be an ObservableCollection
Create a new property, on the view model, that is an ObservableCollection that is a copy of the real Bars.
Refresh the entire view
Something else/better?
I could do #1, but I don't like the needs of the view to cause a domain entity to change.
Number 2 seems ok, but a bit hackish.
Number 3 seems inefficient.
What's the best way?
EDIT
For completeness, based on Simon's answer, I did this:
public Foo SelectedFoo
{
get { return _selectedFoo; }
set
{
_selectedFoo = value;
this.NotifyPropertyChanged(() => this.Foo);
_bars = new ObservableCollection<Bar>();
if (_selectedFoo.Bars != null) { _bars.AddRange(_selectedFoo.Bars); }
this.NotifyPropertyChanged(() => this.Bars);
}
}
private ObservableCollection<Bar> _bars;
public ObservableCollection<Bar> Bars
{
get { return _bars; }
}
In a best practice kind-of-way, your view should not directly bind to your model: that's what your view model is for. Ideally, you want #2 for maximum separation and a logic to synchronize the change back to the model when it's appropriate.