Im trying to learn MVVM, but I'm having some trouble. Im new to xaml and c#.
What I have so far:
A person class, that define a person object: name, age and other info
A model class people, owns a private linkedlist (list of person), which also contains methods such as get, remove, add and do some calculations
a viewmodel class, doing casting/parsing/transforming between xaml behind code and the model.
A xaml behind code file mainWindow.xaml.cs, that listen to button click and such, and invoke methods from the viewModel class, and do some simple binding such as total.Content = objModelView.getTotal().
I didnt use INotifyPropertyChanged ObservableCollection, still trying to wrap my head around it. While my program does what I want I'm not sure how to structure it better.
Basically I have 2 main questions:
I see examples online where people store/initiate the list of items in viewmodel, shouldn't I keep the list in model instead, that should be where all the data be stored right?
Let's say I'm suppose to display all the items (in the list of the model class) onto a dataGrid. Right now in my program: mainWindow.xaml.cs will detect the button click, then it ask viewModel to store it in model, if no error then xaml behind code will do
people_dataGrid.Items.Add(new person { name = newName, age = newAge, address = newAdd }); Is this bad practice? Dont get how to use ObservableCollection here, can it somehow detect a change in the list of my model class, and then remove and add the rows to the datagrid?
I've been reading for the whole day but I'm struck here, hope I can get some direction
The model stores data, the view display it, the viewmodel is the bridge between the two.
That doesn't mean that the view have a direct access to model data because you don't always need to display all data in model layer. So we have a viewmodel layer that makes only useful information accessible.
The viewmodel is very useful when you want to display the same data multiple times but displayed differently: you don't have to replicate data, you only need to define twice what information you need from those data and how to display them.
What you're doing in your second question is using model in view : This is not MVVM. What you want to do is bind the ItemsSource DP of Datagrid to a list of PersonVM which will fetch information from Person.
You code could be structured like that:
public class Person {
public String Name {get; set;}
public int Age {get; set;}
}
public class PersonVM {
public PersonVM(Person model) {
_model = model;
}
private readonly Person _model;
internal Person Model {get {return _model;}}
public String Name {
get { return _model.Name; }
set { _model.Name = value; }
}
public int Age {
get {return _model.Age;}
set { _model.Name = value; }
}
}
//PersonV.xaml
<StackPanel>
<TextBlock Text="{Binding Name}"/>
<TextBlock Text="{Binding Age}"/>
</StackPanel>
public class People : ObservableCollection<Person> {
}
public class PeopleVM : ObservableCollection<PersonVM> {
public PeopleVM(People model) {
_model = model;
foreach(Person p in _model) {
Add(new PersonVM(p));
}
_model.CollectionChanged += CollectionChangedHandler;
}
private void CollectionChangedHandler(Object sender, NotifyCollectionChangedEventArgs args) {
switch (notifyCollectionChangedEventArgs.Action) {
case NotifyCollectionChangedAction.Add:
foreach(People p in args.NewItems) {
if(!this.Any(pvm => pvm.Model == p)) {
this.Add(new PersonVM(p));
}
}
break;
case NotifyCollectionChangedAction.Remove:
foreach(People p in args.OldItems) {
PersonVM pvm = this.FirstOrDefault(pe => pe.Model == p);
if(pvm != null) this.Remove(pvm);
}
break;
case NotifyCollectionChangedAction.Reset:
Clear();
break;
default:
break;
}
}
private readonly People _model;
}
//PeopleV.xaml
<ItemsControl ItemsSource={Binding}>
<ItemsControl.ItemTemplate>
<DataTemplate DataType="{x:Type PersonVM}">
<PersonV/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
public class AppVM {
public AppVM() {
People p = ServiceLayer.LoadPeople(); //load people
People = new PeopleVM(p);
}
public PeopleVM People {get; set;};
}
//MainWindow.xaml
<Window ...
>
<Window.DataContext>
<local:AppVM/>
</Window.DataContext>
<PeopleV/>
</Window>
Answer to your post can be as long as one wishes to explain, perhaps a whole lengthy blog itself. I will try to just answer 2 of your specific questions here. I am not going to show the code for each sub-answer, you have to take it as home work. :)
I didnt use INotifyPropertyChanged ObservableCollection, still trying
to wrap my head around it. While my program does what I want I'm not
sure how to structure it better.
Why? If you don't use these magic sticks, it's better you write a WinForms app and not a WPF one. Forget everything and dive into these two. You must (no escape) understand and use them in MVVM/WPF. You can even defer reading my further answer for that matter.
I see examples online where people store/initiate the list of items in
viewmodel, shouldn't I keep the list in model instead, that should be
where all the data be stored right?
They are not wrong. Person class in model layer represents a real world entity and is must, however, I would not bother about having People class in model. It's just a collection that could easily be accommodated by ViewModel. I personally would prefer that way always.
Let's say I'm suppose to display all the items (in the list of the
model class) onto a dataGrid. Right now in my program:
mainWindow.xaml.cs will detect the button click, then it ask viewModel
to store it in model, if no error then xaml behind code will do
people_dataGrid.Items.Add(new person { name = newName, age = newAge,
address = newAdd }); Is this bad practice? Dont get how to use
ObservableCollection here, can it somehow detect a change in the list
of my model class, and then remove and add the rows to the datagrid?
That's not MVVM, trust me. At the maximum what you should be required to write in view code behind, is initializing view model and setting it as view's data context.
To handle view events (Button.Click for ex) you should use ICommand implementation that will be bound to Button.Command property in XAML. This way you decouple control's event handler from the code behind.
You need to have a ObservableCollection<Person> in your viewmodel which will be bound the DataGrid in view. So when click a button to add person, button's command object will update this collection and view will be refreshed automatically without having you to add it manually to data grid.
You aren't using MVVM at all. It sounds like you are using MVP, which is a completely different pattern.
Before you continue, you need to understand what MVVM was designed for, because its a highly complicated (seemlying over engineered pattern) with a huge number of abstractions just to write the ubiquitous To-Do list.
But you must do all of it, otherwise its not MVVM.
The Zen of MVVM
MVVM grew out of the realisation that writing good, bug-free, safe UI code is hard. Testing UI code is harder, and involves hiring human testers, that are slow and can get it wrong.
So the solution that they came up with was simple.
DON'T WRITE ANY CODE IN YOUR UI
Done.
Except, not. Now, your UI doesn't do anything, it just looks pretty. So they added an extra layer between the UI and the Program/Business Logic/Model, and they called it the ViewModel.
The job of the ViewModel was to tell the UI what to do. But the trick was to get it to tell the UI what to do, without the ViewModel knowing about the UI at all.
(MVP has a similar concept, but the Presenter DOES know about the UI)
By having the ViewModel not know about the UI at all, we can write clean code that can easily be debugged and tested using our usual bag of tricks. Such as unit testing, refactoring, static code analysis, dependency injection etc etc...
Times are good!
Except the View Model still doesn't know about the UI. So we know what the UI should look like, but the UI doesn't know, because no one is telling it...
So they added the Binding class. The binding class's job is to watch the ViewModel, and then update the UI whenever something changes in the ViewModel.
In the world of MVVM there have been two different approaches to how the Binding class works.
You have the WPF way of doing things, which is to implement an event that tells the Binding class that the ViewModel has been updated. This is fast and flexible, but really annoying to write.
And you have the AngularJS way of doing things, which is to poll the ViewModel for updates. This is ridiculously slow and buggy.
If you have been following me thus far, you will note that MVVM defines a flow of data from your Model to your View. A break in any part of this chain will make it "not work".
It all so COMPLICATED, why bother?
The only reason I've found that justifies MVVM's excessive complexity is that you can write a GUI which you can have 90% test coverage, as the view only covers a tiny part of your program.
If you think automated testing is overrated, then you shouldn't use MVVM.
I am also pretty new to WPF, C# and MVVM. I have read quite a fair bit for these two to three months, so maybe I'll share what I understood.
You seem to have the same question that I had a week or two ago. Data should not be stored in the model. Models are just data structures. Databases (or simulated alternatives like Lists) are the actual storages that store these data. Some people would simply keep them in ViewModel, while some would move them to something outside of MVVM (e.g. a "Repository" class).
You are doing it all wrong. In MVVM, Views do not interact with ViewModels in this manner - they interact via Commands and Bindings. The fact that your View is directly manipulating the list means that it's definitely wrong.
Example:
View (Window):
<Window.Resources>
<vm:MyViewModel x:Key="MyVM" />
</Window.Resources>
<Window.DataContext>
<StaticResourceExtension ResourceKey="MyVM" />
</Window.DataContext>
<DataGrid ItemsSource="{Binding PeopleList}" ..... >
<Button Command="{Binding StoreCommand}" .... >
ViewModel:
public static readonly DependencyProperty PeopleListProperty =
DependencyProperty.Register("PeopleList",
typeof(ObservableCollection<Person>),
typeof(ViewModel));
public ObservableCollection<Person> PeopleList
{
get
{
return GetValue(PeopleListProperty) as ObservableCollection<EmissionEntry>;
}
set
{
SetValue(PeopleListProperty, value);
}
}
private ICommand _storeCommand;
public ICommand StoreCommand
{
get
{
if (_storeCommand == null)
_storeCommand = new MyCommandImplementation();
return _storeCommand;
}
}
Person is your model class with name/age etc. The list is kept in ViewModel, unless you want to have a repository somewhere.
You probably haven't really read anything about ICommand, so I suggest reading it up first. It is too long to give a tutorial here, but you can ask questions after you have read up some.
Related
I am new to both WPF and MVVM and a came across an issue when attempting to set the DataContext to the same instance of my ViewModel in two separate views.
This was because:
<Window.DataContext>
<local:ViewModel/>
</Window.DataContext>
would create a new instance of the view model for each view.
To get around this I decided to create a class that stored static instances of each ViewModel I used. Then in the cs file of each view I would then set the DataContext to the appropriate ViewModel from this static class.
This works but doesn't seem the best idea for larger programs where the multiple instances of the ViewModel may be needed simultaneously.
What are better approaches to this problem - Are there sound ways to have multiple Views using the same instance of a ViewModel?
Or is this approach bad practice - Should I be designing a program with one View for every ViewModel?
You can instantiate that view model in App.xaml so that it is accessible to the whole application.
<Application.Resources>
<local:ViewModel x:Key="sharedViewModel" />
</Application.Resources>
Then in your views when you want to use that datacontext, you do the following...
DataContext="{StaticResource sharedViewModel}"
I had this same question and I couldn't find a good answer. After thinking about it for a while I came to the conclusion that in most cases it's best to create a one to one mapping between view model and view. So in this situation I would create two separate view models that inherit from a base view model. That way you can put whatever is common in the base view model and add any fields or methods that might be different to the more specific view model. If the view models truly are equivalent then you might want to ask yourself why you have two separate views in the first place. You may consider merging them into one view. It's possible that having two separate views is what you want, but it's just something to consider.
Simple and easy as well as one of the recommended approach is implementing ViewModelLocator.
Idea is having defined all the ViewModels in ViewModelLocator class and access the ViewModel wherever needed. Using Same ViewModel in different View will not be a problem here.
public class ViewModelLocator
{
private MainWindowViewModel mainWindowViewModel;
public MainWindowViewModel MainWindowViewModel
{
get
{
if (mainWindowViewModel == null)
mainWindowViewModel = new MainWindowViewModel();
return mainWindowViewModel;
}
}
private DataFactoryViewModel dataFactoryViewModel;
public DataFactoryViewModel DataFactoryViewModel
{
get
{
if (dataFactoryViewModel == null)
dataFactoryViewModel = new DataFactoryViewModel();
return dataFactoryViewModel;
}
}
}
App.xaml
xmlns:core="clr-namespace:MyViewModelLocatorNamespace"
<Application.Resources>
<core:ViewModelLocator x:Key="ViewModelLocator" />
</Application.Resources>
Usage
<Window ...
DataContext="{Binding Path=MainWindowViewModel, Source={StaticResource ViewModelLocator}}">
refer : So Question codes copied from there.. as i cannot rip the codes from my project..
I am using the prism framework and was also looking for a solution of using one viewmodel for many (child) views. In prism there are two possible solutions:
Define the viewmodel as singleton (code smell) or
Using the RegionContext (for full description see the prism doc)
According to the prism doc my solution looks like this.
In the prism bootstrapper:
Container.RegisterTypeForNavigation<MyView>("MyView");
In the viewmodel:
private void DisplayView(string viewName)
{
RegionManager.Regions["ContentRegion"].Context = this; // set viewmodel
RegionManager.RequestNavigate("ContentRegion", viewName);
}
In the code behind of each view:
public MyView()
{
InitializeComponent();
ObservableObject<object> viewRegionContext = RegionContext.GetObservableContext(this);
viewRegionContext.PropertyChanged += this.ViewRegionContext_OnPropertyChangedEvent;
}
private void ViewRegionContext_OnPropertyChangedEvent(object sender, PropertyChangedEventArgs args)
{
if (args.PropertyName == "Value")
{
var context = (ObservableObject<object>)sender;
DataContext = context.Value; // get viewmodel as DataContext
}
}
The staticresource/singleton approach (is this what it's called? I'm bad at programming terminology) by #Fabulous is great because it's simple code that's easy to implement, but it also means that your code can execute even when you didn't explicitly want it to and that can cause some headaches.
In my case, it would execute e.g. when rebuilding (not too bad, but not great), when re-opening the solution (ugh), and when editing parts of app.xaml (which would for some reason softlock VS2019 i.e. very bad). As I understand it, people who Know What They're Doing probably already knew this would happen, but it was a pain in the ass as a novice programmer to figure out what was going on haha.
One way to keep the simplicity benefits without ruining your day with phantom-running code is to implement a simple check on whatever your main code is to see if you're running in designtime or not (basically a simplified version of #WPFUser's link).
Example:
using System.ComponentModel;
using System.Windows;
public MainCode()
{
if (IsInDesignMode() == false)
{
//execute code normally
}
else
{
//do nothing (or maybe display an info message or something)
}
}
private DependencyObject dummy = new DependencyObject();
private bool IsInDesignMode()
{
return DesignerProperties.GetIsInDesignMode(dummy);
}
(plus still use all the stuff noted in Fabulous' answer)
I haven't tested this extensively yet so be aware that it may not be 100% foolproof, but so far for me it's worked great in the ways I've tested it.
I was having the same case, just I was using the both user control one below another in stack panel. so use the common DataContext which I set in StackPanel worked for me for both User controls, as below
<StackPanel>
<StackPanel.DataContext>
<vm:RegisterViewModel/>
</StackPanel.DataContext>
<local:Register />
<local:Users />
</StackPanel>
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
I have some kind of experience with WinForms coding but I am trying to do my new stuff with WPF (and the mvvm pattern).
One thing I am going round in circles for days searching the internet is to get the following right:
What do I have to implement in a model and what in the viewmodel when it goes down to "manage the repository (model?) data".
(I think) I do understand the basic concept of mvvm and decopeling all the data but I struggle to figure out the right implementation.
Let's assume I have an existing service (application without any UI) which is able to read values from a (ini) configuration file, do some stuff what its intended for, and write configurations to the local registry. Perhaps in future I want change the configuration to be stored in xml or json instead of ini. From my point of view this is all well placed in the existing code - let's say a configuration.cs file with a class configurations - which currently has absolutely nothing to do with my new wpf console application I want to implement next.
namespace ExistingConfigurationCode
{
public enum Setting
{
some,
values
}
public class ConfigurationItemProperties
{
public property1
public property 2
}
public class ApplicationConfiguration
{
List<ConfigurationItemProperties> config;
public void LoadConfig()
public void SaveConfig()
}
}
As just said, now I do want to implement a UI to enable a user to set up those configurations.
For what I have understand until now is:
the view is only the view and contains no logic at all
the viewmodel is responsible to "deliver" all needed informations to the view and receive commands from the view
the model is a model of the data we are dealing with
My problem in understanding (and knowledge how to proceed) is:
is this existing class already my model? But it does not make sense to add all the RaisePropertyChange stuff in this class because it is also used in the service. Also I have seen a lot of tutorials where I am told to not put any code in the model
if the existing class should be in my view model, what do I need the model for than?
Should the model have only a reference to my already existing class and control all read and write stuff to the registry/xml/ini through some kind of a wrapper?
Where do I have to add the "RaisePropertyChange" getter and setter? Because they are "controlling" the UI I would guess they should be placed in the view model, but I have seen a lot of examples where they are also in the model?
I got some kind of stuck here.
I thought the binding should be between the view and the view model, so the RaisePropertyChange should be in the view model, but in this case I would have to recreate everything a second time in the view model
Additional informations in response to comments
I understood now, that I can use my existing code as the model.
Besides the already existing "execution engine" which is utilizing the existing namespace/class, I have to create two applications with UI. An "admin console" and an end user console.
(I think) I am fine with the admin console, because it has a view for all the properties of ConfigurationItemProperties, so I can following Fabios first explanations.
But the second UI for the end user should have a stripped down UI, probably a listview with some checkboxes per list view item. And here is my problem.
For the listview I need the ObservableCollection, but for the items in the list view I need the "facade" fabio mentioned. Am I right so far?
If so, the ObservableCollection AND "ConfigurationItemPropertiesFacade" are working with the "model" which does not have any implementation of INotifyPropertyChanged or INotifyCollectionChanges stuff. But I think the "collections" must refer to the "single items" from the facade because the collection (from the view model) has no reference to the facade in the view model.
Try to think about MVVM like about three layers(not files) of your application.
View - namespace/project which contains only view logic(XAML and code
behind)
Model - namespace/project which contains all "business logic"
ViewModel - namespace/project which contains logic to link View and
Model without knowing about View's namespace. Another words ViewModel layer's responsibility to be a bridge between Model and View - Call Model's method, raise PropertyChanged event for when some value changed to inform View about changes.
So keep all your logic of configuration in the Model namespace.
In case when Model class need to be represent in the View with editing possibilities, create Facade class in the ViewModel layer
public class ConfigurationItemPropertiesFacade : INotifyPropertyChanged
{
private ConfigurationItemProperties _Model;
public string Property1
{
get { return _Model.Property1; }
set
{
if(Equals(_Model.Property1, value)) return;
_Model.Property1 = value;
RaisePropertyChanged();
}
}
public ConfigurationItemPropertiesFacade(ConfigurationItemProperties model)
{
_Model = model;
}
}
Use ConfigurationItemPropertiesFacade class as ViewModel in the View.
With this approach your Model layer stays clean and can be used anywhere.
In response on comment:
So tutorials telling it is not allowed to put "code" in the model are wrong, right?
Again try thinking about Model in MVVM not like a one class/file, but layer/namespace/project which can contains more then one class.
There is no right or wrong - you can implement your logic as you fill better suite your specifications. But if you will respect Single responsibility principle (https://softwareengineering.stackexchange.com/a/17170/102569), then you will separate responsibilities of ApplicationConfiguration to (I didn't know inner logic of methods Load and Save)
"Model" class which represents only data, class which has no
functionality just properties for keeping data.
You have this already ConfigurationItemProperties
"Service" classes which contains functionality of saving and loading
configurations. Your class can be spitted in two classes.
// Have one responsibility take configurations as parameter and save them somewhere
public class SaveService
{
public void Save(List<ConfigurationItemProperties> items) {}
}
// Have one responsibility load configurations and return them to the caller
public class LoadService
{
public List<ConfigurationItemProperties> Load() {}
}
So then your ViewModel will use those classes to represent and modify configurations in UI.
public class ViewModel
{
private readonly LoadService _LoadService;
private readonly SaveService _SaveService;
//Here you can use your"Facade" implementation
private ObservableCollection<ConfigurationItemPropertiesFacade> _Items
public ObservableCollection<ConfigurationItemPropertiesFacade> Items
{
get { return _Items; }
set
{
_Items = value;
RaisePropertyChanged(nameOf(Items));
}
}
public ICommand Save { get; set; }
public ICommand Load { get; set; }
public ViewModel(LoadService loadService, SaveService saveService)
{
_LoadService = loadService;
_SaveService = saveService;
// Create command instance for Save
// Create command instance for Load
var itemsList = _LoadService.Load();
var facadeItems = itemsList.Select(item => new ConfigurationItemPropertiesFacade(item));
Items = new ObservableCollection(facadeItems);
}
}
Let's think about these definitions:
Model - Data access and business Logic.
ViewModel - it's responsibility is to provide model's data and logic to view in such way that is easily consumable from view.
I believe that these definition gives you clear answer:
ExistingConfigurationCode class should be considered as part of your model.
Few more thoughts however:
INotifyPropertyChanged and INotifyCollection changed is not limited to DataBinding or WPF. These interfaces are much more general.
There nothing wrong about implementing INotifyPropertyChanged in a Model if you want your model to notify other layers or compoments about changes
It's common practice that you bind your Views directly to Model entities. ViewModel often just expose entity from Model as a property.
Wrapping model properties in ViewModel is one way of doing it. Another way is having separate properties in ViewModel with their own backing fields. You set the ViewModel properties to initial values when user visits the page and you save the edited values back to Model when user clicks save.
Hello good people of stackoverflow.
EDIT: Sourcecode is avaliable on:
https://www.dropbox.com/sh/yq4qbznl4b6gm4h/AADdjd_hb-OQXV5KL8OU5cbqa?dl=0
More specefic on I4PRJ4 --> Backend --> Backend.sln.
I'm currently making a productmanagementsystem that has a GUI. We've decided to use MVVM, and we're still learning it.
Though I have a problem. On the main screen, a list of categories is shown, and the products in the selected category is also shown.
As for now, we've binded the data to an observeable collection. But the problem arises when we need to add another product, using a differen view and viewmodel. In that case we need to have Categories with data. We open the add-product-view through a command in the mainwindow, so to get the data to the viewmodel we have to pass the object from MainWindowViewModel to AddProductView and then to AddProductViewModel - and that's not the coupling I want.
So I tried using the singletonpattern, and binding to the observable collections as:
xmlns:models="clr-namespace:Backend.Models"
..
..
<ListBox Margin="0, 30, 0, 0" ItemsSource="{Binding Source={x:Static models:GlobalCategories.CategoryList}, Path=Name}" DisplayMemberPath="Name"/>
Where GlobalCategories is as follows:
[Models: GlobalCategories]
public class GlobalCategories
{
private static BackendProductCategoryList _list;
public static BackendProductCategoryList CategoryList
{
get
{
if (_list == null)
{
MessageBox.Show("Made new list");
return new BackendProductCategoryList();
}
MessageBox.Show("Returned old list");
return _list;
}
set { _list = value; }
}
}
As you can see in the code, a messagesbox appears, telling me what was returned. But with the above XAML-code, it actually creates that object, which was my understanding it wouldn't do that and you therefore would have to initialize it yourself. It actually creates the object, and the msgbox will say that a new list has been made.
Though if I then do the following in MainWindowViewModel
public class MainWindowViewModel
{
public MainWindowViewModel()
{
MessageBox.Show("" + CategoryList.Count);
}
Then it creates ANOTHER list, but if I then do another operation, I get a "old list" message. What is going on - why is this happening?
What am I doing wrong? Oh - and the binding doesn't work, nothing is shown when I do this. And it's driving me nuts. I'm comming from a background in C and C++, and been working with c# and xaml for a couple of months - I NEED CONTROL. AND POINTERS :-D
I really hope you guys can help me out here, giving me an understanding what is going on, and how I solve it.
Or even better - is there a better way of sharing data between viewmodels? Because to be honest, then I'm not the biggest fan of singleton, and would really appericiate another soloution to share data between viewmodels.
Thank you so much for your help!
Best regards,
Benjamin
I think there may be some confusion on your end as to how property accessors work. Set and get simply allow you to declare members that look like regular properties to everything referencing them but are implemented with code. When something needs to access the list it calls your getter and is expecting that function to return a value (or null). Your current implementation is creating a new list and returning it:
return new BackendProductCategoryList();
But you're not setting the value in _list, so the next time the getter is called the value of _list is still null and you create and return the list again. And again, and so on. All you need to do is store it so that the list only gets created once:
public static BackendProductCategoryList CategoryList
{
get
{
if (_list == null)
{
MessageBox.Show("Made new list");
_list = new BackendProductCategoryList();
}
else
MessageBox.Show("Returned old list");
return _list;
}
set { _list = value; }
}
One additional tip: don't call MessageBox.Show in accessors, you should be doing as little work in accessors as possible. Using statics like this really isn't a good idea either for a host of reasons, but I'll leave that for another question (look up "dependency injection").
I haven't found an exact answer to this. Some people have mentioned that binding directly to the model as inappropriate for MVVM, however I'm trying to decide if the following pattern still follows MVVM:
View:
<TextBox Text="{Binding InfoForProcessing.BatchId}"/>
<TextBox Text="{Binding InfoForProcessing.BatchStatus}"/>
ViewModel:
private ProcessingTaskModel _infoForProcessing;
public ProcessingTaskModel InfoForProcessing
{
get
{
return _infoForProcessing;
}
private set
{
_infoForProcessing = value;
RaisePropertyChanged();
}
}
....
//doing some processing...then tell Model to populate the data:
InfoForProcessing.ResolveBatchInfo(...);
Model: (Implements INotifyPropertyChanged)
private string _batchId;
public string BatchId //VIEWABLE
{
get
{
return _batchId;
}
private set
{
_batchId = value;
RaisePropertyChanged();
}
}
private string _batchStatus;
public string BatchStatus //VIEWABLE
{
get
{
return _batchStatus;
}
private set
{
_batchStatus = value;
RaisePropertyChanged();
}
}
public bool ResolveBatchInfo(...){
//Get data from Database...
...
//Now Populate Properties with retrieved data (or return false on error)
BatchId = dr[0].ToString();
BatchStatus = dr[1].ToString();
return true;
}
I see another way to do this. I can copy those properties to the ViewModel, bind to them, and have the ViewModel populate them when returned by the Model.
For my current project here, some of the data in the Model is a dependency of queries that will be performed later
I've also made sure to encapsulate as much as possible, and only expose the properties that the View and ViewModel needs to see, while restricting setters as well.
One common stream of thinking is that - for some reason - you cannot bind directly to Model properties in your Views.
However, given that the objective of MVVM is to abstract the UI/View away from the logic, this will not be broken by bindings of this form:
{Binding ViewModel.Model.Property}
This also means you are not copying properties from Model to ViewModel all the time.
Your design, as is, is non-conforming in my opinion, as your data object contains model logic (the ResolveBatchInfo method). Here are two potential solutions:
Have seperate View Model and Model objects. In this scenario, each has their own methods. INPC for the View Model classes and database methods in the Model classes. (as shown here)
Have your POCOs "float" between layers. The important part here is that besides INPC, there is no logic in the objects, they are just data. Under this design, you would have a separate model class that encapsulates data operations and would have a method that returned populated BatchInfo objects.
I usually do (2) in my own projects, as it provides for better seperation of concerns and limits code/data replication.
Of course, with any of the three approaches (my two plus yours) the most important factor is writing code that makes sense not that it is "pure" MVVM. So go with the design that will make your life the easiest down the road.
Personally I don't mind either way, but many people (including the company who I currently work for) believe that wrapping the properties of the model, in the viewmodel is a more 'pure' approach. For me personally though, I think that it's important to think of MVVM as a guide of good practice, rather than a set of rules to be followed absolutely. I've come across instances before where I've been told to forget wrapping the properties of the model, simply because it needed to get done quickly.
Best to list this as bullets
Model DTOs can adhere to INotifyPropertyChange and be binding targets.
The VM should handle any database calls which are initiated from the View and populate stored data on the VM.
I prefer to keep my Models as POCO type entities and not the operation to get the models.
What you describe as your Model is really attributes/properties of a database operation; its status.
Short Answer
All of those items on your model should be moved and done on your VM and bound accordingly.
I've spent quite some time to try and find an elegant solution for the following challenge. I've been unable to find a solution that's more than a hack around the problem.
I've got a simple setup of a View, ViewModel and a Model. I will keep it very simple for the sake of explanation.
The Model has a single property called Title of type String.
The Model is the DataContext for the View.
The View has a TextBlock thats databound to Title on the Model.
The ViewModel has a method called Save() that will save the Model to a Server
The Server can push changes made to the Model
So far so good. Now there are two adjustments I need to make in order to keep the Model in sync with a Server. The type of server is not important. Just know that I need to call Save() in order to push the Model to the Server.
Adjustment 1:
The Model.Title property will need to call RaisePropertyChanged() in order to translate changes made to the Model by the Server to the View. This works nicely since the Model is the DataContext for the View
Not too bad.
Adjustment 2:
Next step is to call Save() to save changes made from the View to the Model on the Server. This is where I get stuck. I can handle the Model.PropertyChanged event on the ViewModel that calls Save() when the Model gets changed but this makes it echo changes made by the Server.
I'm looking for an elegant and logical solution and am willing to change my architecture if it makes sense.
In the past I 've written an application that supports "live" editing of data objects from multiple locations: many instances of the app can edit the same object at the same time, and when someone pushes changes to the server everyone else gets notified and (in the simplest scenario) sees those changes immediately. Here's a summary of how it was designed.
Setup
Views always bind to ViewModels. I know it's a lot of boilerplate, but binding directly to Models is not acceptable in any but the simplest scenarios; it's also not in the spirit of MVVM.
ViewModels have sole responsibility for pushing changes. This obviously includes pushing changes to the server, but it could also include pushing changes to other components of the application.
To do this, ViewModels might want to clone the Models they wrap so that they can provide transaction semantics to the rest of the app as they provide to the server (i.e. you can choose when to push changes to the rest of the app, which you cannot do if everyone directly binds to the same Model instance). Isolating changes like this requires still more work, but it also opens up powerful possibilities (e.g. undoing changes is trivial: just don't push them).
ViewModels have a dependency on some kind of Data Service. The Data Service is an application component that sits between the data store and the consumers and handles all communication between them. Whenever a ViewModel clones its Model it also subscribes to appropriate "data store changed" events that the Data Service exposes.
This allows ViewModels to be notified of changes to "their" model that other ViewModels have pushed to the data store and react appropriately. With proper abstraction, the data store can also be anything at all (e.g. a WCF service in that specific application).
Workflow
A ViewModel is created and assigned ownership of a Model. It immediately clones the Model and exposes this clone to the View. Having a dependency on the Data Service, it tells the DS that it wants to subscribe to notifications for updates this specific Model. The ViewModel does not know what it is that identifies its Model (the "primary key"), but it doesn't need to because that's a responsibility of the DS.
When the user finishes editing they interact with the View which invokes a Command on the VM. The VM then calls into the DS, pushing the changes made to its cloned Model.
The DS persists the changes and additionally raises an event that notifies all other interested VMs that changes to Model X have been made; the new version of the Model is supplied as part of the event arguments.
Other VMs that have been assigned ownership of the same Model now know that external changes have arrived. They can now decide how to update the View having all pieces of the puzzle at hand (the "previous" version of the Model, which was cloned; the "dirty" version, which is the clone; and the "current" version, which was pushed as part of the event arguments).
Notes
The Model's INotifyPropertyChanged is used only by the View; if the ViewModel wants to know whether the Model is "dirty", it can always compare the clone to the original version (if it has been kept around, which I recommend if possible).
The ViewModel pushes changes to the Server atomically, which is good because it ensures that the data store is always in a consistent state. This is a design choice, and if you want to do things differently another design would be more appropriate.
The Server can opt to not raise the "Model changed" event for the ViewModel that was responsible for this change if the ViewModel passes this as a parameter to the "push changes" call. Even if it does not, the ViewModel can choose to do nothing if it sees that the "current" version of the Model is identical to its own clone.
With enough abstraction, changes can be pushed to other processes running on other machines as easily as they can be pushed to other Views in your shell.
Hope this helps; I can offer more clarification if required.
I would suggest adding Controllers to the MVVM mix (MVCVM?) to simplify the update pattern.
The controller listens for changes at a higher level and propagates changes between the Model and ViewModel.
The basic rules to keep things clean are:
ViewModels are just dumb containers that hold a certain shape of data. They do not know where the data comes from or where it is displayed.
Views display a certain shape of data (via bindings to a view model).
They do not know where the data comes from, only how to display it.
Models supply real data. They do not know where it is consumed.
Controllers implement logic. Things like supplying the code for ICommands in VMs, listening for changes to data etc. They populate VMs from Models. It makes sense to have them listen for VM changes and update the Model.
As mentioned in another answer your DataContext should be the VM (or property of it), not the model. Pointing at a DataModel makes it hard to separate concerns (e.g. for Test Driven Development).
Most other solutions put logic in ViewModels which is "not right", but I see the benefits of controllers overlooked all the time. Darn that MVVM acronym! :)
binding model to view directly only works if the model implement INotifyPropertyChanged interface. (eg. your Model generated by Entity Framework)
Model implement INotifyPropertyChanged
you can do this.
public interface IModel : INotifyPropertyChanged //just sample model
{
public string Title { get; set; }
}
public class ViewModel : NotificationObject //prism's ViewModel
{
private IModel model;
//construct
public ViewModel(IModel model)
{
this.model = model;
this.model.PropertyChanged += new PropertyChangedEventHandler(model_PropertyChanged);
}
private void model_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Title")
{
//Do something if model has changed by external service.
RaisePropertyChanged(e.PropertyName);
}
}
//....more properties
}
ViewModel as DTO
if Model implements INotifyPropertyChanged(it depends) you may use it as DataContext in most cases. but in DDD most MVVM model will be considered as EntityObject not a real Domain's Model.
more efficient way is to use ViewModel as DTO
//Option 1.ViewModel act as DTO / expose some Model's property and responsible for UI logic.
public string Title
{
get
{
// some getter logic
return string.Format("{0}", this.model.Title);
}
set
{
// if(Validate(value)) add some setter logic
this.model.Title = value;
RaisePropertyChanged(() => Title);
}
}
//Option 2.expose the Model (have self validation and implement INotifyPropertyChanged).
public IModel Model
{
get { return this.model; }
set
{
this.model = value;
RaisePropertyChanged(() => Model);
}
}
both of ViewModel's properties above can be used for Binding while not breaking MVVM pattern (pattern != rule) it really depends.
One more thing..
ViewModel has dependency on Model. if Model can be changed by external service/environment. it's "global state" that make things complicate.
If your only problem is that changes from the server get immediately re-saved, why not do something like the following:
//WARNING: typed in SO window
public class ViewModel
{
private string _title;
public string Title
{
get { return _title; }
set
{
if (value != _title)
{
_title = value;
this.OnPropertyChanged("Title");
this.BeginSaveToServer();
}
}
}
public void UpdateTitleFromServer(string newTitle)
{
_title = newTitle;
this.OnPropertyChanged("Title"); //alert the view of the change
}
}
This code manually alerts the view of the property change from the server without going through the property setter and therefore without invoking the "save to server" code.
The reason you have this problem is because your model doesn't know whether it is dirty or not.
string Title {
set {
this._title = value;
this._isDirty = true; // ??!!
}
}}
The solution is to copy the server changes via a separate method:
public void CopyFromServer(Model serverCopy)
{
this._title = serverCopy.Title;
}