How to (correctly) update the M in MVVM of WPF application?
Asked Answered
B

4

3

Having passed a series of Edward Tanguay's questions refractoring the usage of MVVM for WPF app which can be found in Linked sidebar of his Fat Models, skinny ViewModels and dumb Views, the best MVVM approach?, I am a little confused by his final WPF application in Big smart ViewModels, dumb Views, and any model, the best MVVM approach?

Its M (Model) is Customer class:

//model
public class Customer
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime TimeOfMostRecentActivity { get; set; }

    public static Customer GetCurrentCustomer()
    {
        return new Customer 
                   { FirstName = "Jim"
                     , LastName = "Smith"
                     , TimeOfMostRecentActivity = DateTime.Now 
                   };
    }

}

which returns current user. Kind of, beause it returns duplicates of newly created "current" user...

But where is the M's data stored and updated in case of need?

Suppose, I want to change the model's current user's FirstName to "Gennady"?

I added a button for updating the model with this button click event handler:

private void button1_Click(object sender, RoutedEventArgs e)
{


} 

aiming to change the model's data from it which will be reflected in GUI.

How can I do this, by clicking this button... sorry, by placing the code into this button1_Click()?

Or it is something wrong with my wish to do it?
Then. how to correctly update/change M in MVVM ?

Update:
All answers seem refer that I should not make changes in M but on VM.
Though I've specifically asked about referenced M-V-VM implementation with:

public CustomerViewModel()
{
    _timer = new Timer(CheckForChangesInModel, null, 0, 1000);
} 
private void CheckForChangesInModel(object state)
{
    Customer currentCustomer = CustomerViewModel.GetCurrentCustomer();
    MapFieldsFromModeltoViewModel(currentCustomer, this);
} 
public static void MapFieldsFromModeltoViewModel
     (Customer model, CustomerViewModel viewModel) 
{
    viewModel.FirstName = model.FirstName;
    viewModel.LastName = model.LastName;
    viewModel.TimeOfMostRecentActivity = model.TimeOfMostRecentActivity;
}

So, for example, upon implementing the code from Adolfo Perez's answer changes, the TextBox's content is changed from "Jim" to "Gennady" only for a period of interval set in _timer = new Timer(CheckForChangesInModel, null, 0, 1000);.

All logic of referenced by me M-V-VM in WPF approach is such that it is "M" should be updated, in order VM has caught up those changes, but not the "VM".

Even more, I cannot understand, if to make changes in VM how can they be reflected in M if the VM knows about M but - not vice versa - Model does not know about ViewModel).

Boser answered 19/4, 2013 at 9:20 Comment(1)
Marked the question as answered since the title question: "How to (correctly) update the M in MVVM of WPF application?" was answered though I should have formulated the question differently. Like, "How to update M in this specific flavor of M-V-VM?"Gilroy
R
11

In MVVM you should avoid code-behind. The reason is that you want to end up with testable classes, in this case your VM's that are completely independent from your V. You could run a set of unit tests on your VM without involving the V. You could also hook different types of Views without affecting your business logic.

Your button will bind its Command property to an ICommand property exposed in your VM. This Command in your VM will handle your click event in the method you specify.

In your View:

<Button Content="Change FirstName" 
        Command="{Binding Path=ChangeFirstNameCommand"}/>

In your ViewModel:

//Define your command
public ICommand ChangeFirstNameCommand {get;set;}

//Initialize your command in Constructor perhaps
ChangeFirstNameCommand = new RelayCommand(OnChangeFirstName,CanChangeFirstName);

private void OnChangeFirstName()
{ 
    //Your FirstName TextBox in your V will be updated after you click the Button
    this.FirstName = "Gennady";
}

private bool CanChangeFirstName()
{
  //Add any validation to set whether your button is enabled or not. 
  // WPF internals take care of this.
  return true;
}

It is very important to keep in mind that in this pattern your V knows about your VM and your VM knows about your M but not the other way around.

In your example if you want to change your Model FirstName property you woud have to do the following:

  • Create a VM which implements INotifyPropertyChanged
  • Expose your M FirstName property in your VM notifying changes
  • Create a TextBox in your XAML View and bind its Text property to your VM.FirstName setting Binding Mode=TwoWay.

    <TextBox Text=
         "{Binding Path=FirstName,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"/>
    

As you type in the TextBox your FirstName will be directly populated in the VM-M. Also, thanks to the Two way binding, if you modify your FirstName property in your VM, that change will be reflected automatically in your V

  • Set your View.DataContext to your VM. This is what sets the Context for all your data bindings, unless you specify a different binding source.
  • If you want to persist changes in a DB then inject a service class in your VM which will take care of CRUD operations

Take a look at this simple example:

http://www.codeproject.com/Articles/126249/MVVM-Pattern-in-WPF-A-Simple-Tutorial-for-Absolute

Rhetoric answered 19/4, 2013 at 9:54 Comment(9)
Thanks. Could you change the phrase "In MVVM no code-behind"? I am not sure I grasp it correctlyGilroy
Sure, just rephrased that part. Let me know if you have more questionsRhetoric
@ГеннадийВанинНовосибирск To clarify, when Adolfo says "No code-behind" in MVVM, I think he really means "no application/business logic in code behind". It's perfectly fine to have UI-specific code in the code-behind, such as setting the default focus or resizing an element in the layout.Swithbart
Thanks for the clarification @Rachel, realistically it is very hard to get rid of the code-behind completely, especially when you are dealing with Grids, or TreeViews and more complex UI controls.Rhetoric
@AdolfoPerez Yes, I would consider modifying a Grid or a TreeView to be UI-specific code behind, which is fine with me since you're modifying the View layer, not the application/data layer. That said, there have been times when I've been forced to modify my data layer from the view's code-behind because of some limitation of the View, but that's really not ideal and you should strive to avoid that whenever possible :)Swithbart
@AdolfoPerez, please see "Update:" in my question. Also RelayCommand() in your reference has only one parameter, so I had todropped your CanChangeFirstName() . Though, implementation of your code, updating VM, updates TextBlock's content only for a period of interval set in _timer, updated after that the previous content from M is restored since referenced in my question MVVM is essentially based on updating M (then VM from M). What was my question all avoutGilroy
I think i know now what you are talking about. So you want your VM to be aware of changes in your M. Now my question is: who is updating your model? One possible solution would be implementing INPC in your model and just exposing your whole model Customer instance in your VM. The normal scenario in MVVM is manipulating the M through the V-VMRhetoric
Yes, the aim of question was to change M from V (clicking button). Though, after after your discussion with @Swithbart I'd like to understand: Where do I put complex (and intensive) calculations in WPF app with "No code-behind in MVVM" approach?Gilroy
These "complex/intensive" calculations sound like business logic to me and belong to the business logic layer. That BLL class or classes in charge of your calculations can be injected into your VM to manipulate the model or user input. If you give an example on what you are trying to achieve i may have a better answer for you.Rhetoric
S
3

Your model is your domain (business) objects. There are number of ways you can get them. For example you may have a repository class that gives you your data when you request it and handles the persistance when you wish to store it.

Your view-model is a class that handles UI logic, like updating fields, reacting on user actions, etc. In your case, you may pass an instance of CustomerRepository class to your view model. Then in view-model code you get the instance of Customer from the repository and fill your view-model properties on wich your UI elements are bound.

Your view is just a set of rules of how you wish to show the information to a user. It must be as declarative and logic free as possible.

Having a code like this:

private void button1_Click(object sender, RoutedEventArgs e)
{


} 

in your view (or even worse - in your view-model) is a huge mistake wich breaks the pattern and may (and surely will) lead to troubles. You should bind ICommand fields of ViewModel to the buttons. You should not try to write a WPF application in a WinForm event-driven style.

That's how mvvm works in general and it's main purpose is to support multi-tier architecture in your application.

Sachs answered 19/4, 2013 at 9:33 Comment(2)
Thanks, what is repository class in terms of M-V-VM?Gilroy
The repository is a service class that belongs to a domain layer of your application. Within MVVM terms it can be treated as a part of a model. But don't get it wrong: your actual model is a Customer class (let's suppose you have an edit form for it). Your view-model updates the Customer's fields according to user's input. The repository only handles retrivial of a model object (for example from a database) and storing it when user finishes input.Sachs
V
1

First off, you need to work on your V and VM.

If you are using a Click event for a button, you definatly aren't following this architecture.

You need to use WPF and XAML in your view to bind to your ViewModel, your ViewModel should be a subset of a particular or potentially many models and present the properties to the View which allows for binding.

I would also consider researching:

  • RelayCommand and ICommand for binding your buttons.
  • Repository pattern for interchanging your models and creating a way of CRUD

The tutorial which you have followed doesn't seem to be very good in that the concepts haven't really been put across properly or you haven't understood them.

Vidette answered 19/4, 2013 at 9:28 Comment(6)
Could you advise the simplest C# code sample illustrating the concepts of MVVM?Gilroy
@ГеннадийВанинНовосибирск there are many code examples for MVVM, however it is not the code which matters the most when it comes to architectures/patterns and practices, it is the understanding of the concepts. Start with MSDN msdn.microsoft.com/en-us/library/gg405484(v=pandp.40).aspxVidette
@ LukeHennerley, thanks but the sample from your link requires from me to install Windows7, then VS2012 + Prism Framework. I just wanted something pretty simple and fast to grasp concepts. I am on Windows XP SP3, VS2010, .NET 4.0Gilroy
@ГеннадийВанинНовосибирск follow the details and concepts not code examples and solutions. You can apply the principles on any format, code examples are on there so just copy and paste them into VS2010.Vidette
@ГеннадийВанинНовосибирск If you're looking for a simple MVVM example, I have one posted on my blog which requires no external libraries. There's a link at the bottom to download the sample project, and I think it will help you get a better understanding of what should be in the different layers and how they interact with each other.Swithbart
@Rachel, really I've already read your blog articles on MVVM-WPF and tried to post comments but they all disappeared without any reaction. Not much surprised, see this ref, wrote many times to WordPress and Akismet support about this feature of Akismet/Wordpress blogs, sometimes recovered ability to post but after short period of time it is (blocking me) is repeating itself againGilroy
P
0

If you have a pure WPF application it could be interesting to investigate the MVVM 'reverse' pattern, ViewModel-First.

For webdevelopment it is common to use MVVM because webpages get loaded through a browser and the View is constructed wich creates the ViewModel.

WPF users do not browse to pages (unless you use Page navigation) so it gets more interesting to follow VM-V-VM-M :

interface IMyView
  Show();

//view implementations in different assemblies:

//silverlight
class SilverlightMyView:IMyView
  Show();

//wpf
class WpfMyView:IMyView
  Show();

class MyViewModel

   IMyView _view;
   MyModel _model;

  //ex. view resolved by DI (Unity, ..)
  MyViewModel(IMyView view)
   _view = view

  Show(model as MyModel)
      _model = model;
      _view.DataContext = this;
      _view.Show();
Palingenesis answered 26/4, 2013 at 19:52 Comment(1)
did you mean "VM-V-VM-M" instead of "MV-V-MV-M"?Gilroy

© 2022 - 2024 — McMap. All rights reserved.