Through generous help on this question, I put together the following MVVM structure which displays the changes of a model in real time in XAML (current date/time), very nice.
A cool advantage of this set up is that when you look at your view in design mode of Visual Studio or Blend, you see the time ticking by, which means that at design time you have access to live data from your model.
In the process of getting this to work, I was surprised to see most of the bulk move from my ViewModel into my Model, including implementation of INotifyPropertyChange. Another change is that I no longer bind to properties on the ViewModel but to methods.
So currently this is my favorite flavor of MVVM:
View is dumb:
ViewModel is skinny:
Model is fat:
Questions:
The following code will work if you just copy the XAML and code behind into a new WPF project.
XAML:
<Window x:Class="TestBinding99382.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestBinding99382"
Title="Window1" Height="300" Width="300">
<Window.Resources>
<ObjectDataProvider
x:Key="DataSourceCustomer"
ObjectType="{x:Type local:ShowCustomerViewModel}"
MethodName="GetCurrentCustomer"/>
</Window.Resources>
<DockPanel DataContext="{StaticResource DataSourceCustomer}">
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<TextBlock Text="{Binding Path=FirstName}"/>
<TextBlock Text=" "/>
<TextBlock Text="{Binding Path=LastName}"/>
</StackPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal">
<TextBlock Text="{Binding Path=TimeOfMostRecentActivity}"/>
</StackPanel>
</DockPanel>
</Window>
Code Behind:
using System.Windows;
using System.ComponentModel;
using System;
using System.Threading;
namespace TestBinding99382
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
}
//view model
public class ShowCustomerViewModel
{
public Customer GetCurrentCustomer() {
return Customer.GetCurrentCustomer();
}
}
//model
public class Customer : INotifyPropertyChanged
{
private string _firstName;
private string _lastName;
private DateTime _timeOfMostRecentActivity;
private static Customer _currentCustomer;
private Timer _timer;
public string FirstName
{
get
{
return _firstName;
}
set
{
_firstName = value;
this.RaisePropertyChanged("FirstName");
}
}
public string LastName
{
get
{
return _lastName;
}
set
{
_lastName = value;
this.RaisePropertyChanged("LastName");
}
}
public DateTime TimeOfMostRecentActivity
{
get
{
return _timeOfMostRecentActivity;
}
set
{
_timeOfMostRecentActivity = value;
this.RaisePropertyChanged("TimeOfMostRecentActivity");
}
}
public Customer()
{
_timer = new Timer(UpdateDateTime, null, 0, 1000);
}
private void UpdateDateTime(object state)
{
TimeOfMostRecentActivity = DateTime.Now;
}
public static Customer GetCurrentCustomer()
{
if (_currentCustomer == null)
{
_currentCustomer = new Customer
{ FirstName = "Jim"
, LastName = "Smith"
, TimeOfMostRecentActivity = DateTime.Now
};
}
return _currentCustomer;
}
//INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void RaisePropertyChanged(string property)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(property));
}
}
}
}
Here's my opinion, for what it's worth :
I don't really agree with the approach you suggest (except for the dumb view). In real life, you will often have to use an existing model : it could be legacy code that you don't have the time (or will) to change, or even a library for which you don't have the code. In my opinion, the model should be completely unaware of the way it will be displayed, and should be easily usable in a non-WPF application. So it doesn't have to implement any specific interface like INotifyPropertyChanged
of INotifyCollectionChanged
to make it usable in MVVM. I think that all the logic related to UI should reside in the ViewModel.
Regarding RoutedEvents
and RoutedCommands
, they are not really suitable for use with the MVVM pattern. I usually try to use as little RoutedEvents
as possible, and no RoutedCommands
at all. Instead, my ViewModels expose RelayCommand
properties that I bind to the UI in XAML (see this article by Josh Smith for details on RelayCommand
). When I really need to handle events for some control, I use attached behaviors to map the events to ViewModel commands (have a look at Marlon Grech's implementation)
So, in summary :
Of course it's just my approach, and it may not be the best, but I feel quite comfortable with it ;)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With