This is an old revision of the document!
Implementing MVVM
Setup the following:
- Model: Data models or data structures implemented as classes.
- ViewModel: Models of the data as “viewed” or required by the Views. A ViewModel encapsulates a Model and exposes it in a format that the View can use.
- View: Data binding to ViewModel, with little “code-behind” to implement. It displays a limited window into the Model (data).
Model
View Model
//======================================================================== // Class TAppViewModel //======================================================================== public class TAppViewModel : INotifyPropertyChanged { #region Fields, Events, and Properties //------------------------------------------------------------------------ // Fields //------------------------------------------------------------------------ private string m_ApplicationName = "MyApp"; private string m_ApplicationVersion = "5.0"; private string m_ApplicationBuildNumber = "20110310"; private string m_ApplicationDevStage = "Dev"; private int m_Tag = 0; //------------------------------ // Events //------------------------------ public event PropertyChangedEventHandler PropertyChanged; //------------------------------------------------------------------------ // Properties //------------------------------------------------------------------------ public string ApplicationName { get { return m_ApplicationName; } set { m_ApplicationName = value; } } public string ApplicationVersion { get { return m_ApplicationVersion; } set { m_ApplicationVersion = value; } } public string ApplicationBuildNumber { get { return m_ApplicationBuildNumber; } set { m_ApplicationBuildNumber = value; } } ... public TAppViewModel() { } public void SomeWorkToDo() { // Trigger Change if (PropertyChanged != null) { TAppLog.LogMessage("TFittingVM.SetBusyFlag", "PropertyChanged event triggered for: IsDataLoading, IsControlAvailable."); PropertyChanged(this, new PropertyChangedEventArgs("IsDataLoading")); PropertyChanged(this, new PropertyChangedEventArgs("IsControlAvailable")); } } }
Main View
The Main View
should implement an instance of ViewModel as a ObjectDataProvider (in XAML), that we call AppViewModel
.
<UserControl x:Class="Acme.App.ucMainView" ... xmlns:viewmodel="Acme.App.ViewModel"> <UserControl.Resources> <!-- Instantiate the main AppViewModel object --> <viewmodel:TAppViewModel x:Key="AppViewModelDataSource" /> </<UserControl.Resources> ... </UserControl>
Alternatively, it can be spelled out as:
<UserControl.Resources> <!-- Instantiate the main AppViewModel object --> <ObjectDataProvider x:Key="AppViewModelDataSource" ObjectType="{x:Type viewmodel:TAppViewModel }" /> </<UserControl.Resources>
User Controls (View)
User Controls in Main View
should implement an AppViewModel
dependency property.
For example, in the Main View
, user controls instances should be called as:
<TabControl> <TabItem> <Grid> <ucUserControlTriangle AppViewModel="{Binding AppViewModelDataSource}"/> </Grid> </TabItem> <TabItem> <Grid> <ucUserControlSquare AppViewModel="{Binding AppViewModelDataSource}"/> </Grid> </TabItem> <TabItem> <Grid> <ucUserControlCircle AppViewModel="{Binding AppViewModelDataSource}"/> </Grid> </TabItem> </TabControl>
The user control dependency property AppViewModel
would be implemented like this (eg, in user control ucUserControlTriangle
):
public class ucUserControlTriangle : UserControl { #region AppViewModel dependency property implementation ///------------------------------- /// App View Model ///------------------------------- // Dependency Property public static readonly DependencyProperty AppViewModelProperty = DependencyProperty.Register( "AppViewModel", typeof(TAppViewModel), typeof(ucUserControlTriangle), new FrameworkPropertyMetadata(new TAppViewModel(), OnAppViewModelPropertyChanged, OnCoerceAppViewModelProperty ), OnValidateAppViewModelProperty ); // .NET Property Wrapper public TAppViewModel AppViewModel { // Important: Do not add any logic to these properties, because they are only // called when you set the property from code. If you set the property from XAML // the SetValue() method is called directly. get { return (TAppViewModel)GetValue(AppViewModelProperty); } set { SetValue(AppViewModelProperty, value); } } ///---------------------------------------------------------------------------------------- /// <summary> /// OnPropertyChanged event handler for app view model. /// </summary> /// <param name="source">Source control</param> /// <param name="e">Event Arguments</param> ///---------------------------------------------------------------------------------------- private static void OnAppViewModelPropertyChanged( DependencyObject source, DependencyPropertyChangedEventArgs e) { ucWorkshopRegistration control = source as ucWorkshopRegistration; //DateTime time = (DateTime)e.NewValue; // Put some update logic here... control.DataContext = (TAppViewModel)e.NewValue; } ///---------------------------------------------------------------------------------------- /// <summary> /// /// </summary> /// <param name="sender"></param> /// <param name="data"></param> /// <returns></returns> ///---------------------------------------------------------------------------------------- private static object OnCoerceAppViewModelProperty(DependencyObject sender, object data) { //if ((DateTime)data > DateTime.Now) //{ // data = DateTime.Now; //} return data; } ///---------------------------------------------------------------------------------------- /// <summary> /// /// </summary> /// <param name="data"></param> /// <returns></returns> ///---------------------------------------------------------------------------------------- private static bool OnValidateAppViewModelProperty(object data) { return data is TAppViewModel; } #endregion ... ///------------------------------- /// Constructor ///------------------------------- public ucUserControlTriangle() { InitializeComponents(); // Set DataContext this.DataContext = AppViewModel; } ... }
Set that user control's DataContext
property to AppViewModel
dependency property.
///------------------------------- /// Constructor ///------------------------------- public ucUserControlTriangle() { InitializeComponents(); // Set DataContext to dependency property AppViewModel this.DataContext = AppViewModel; }
Add Data Binding to controls inside that user control, taking advantage of DataContext
set to the AppViewModel
. For example, a DataGrid
would look like this:
<UserControl x:Class="Acme.App.ucUserControlTriangle" ... xmlns:viewmodel="Acme.App.ViewModel"> ... <!-- <DataGrid ItemsSource="{Binding Path=TSubCategoryHere}" Margin="5" AutoGenerateColumns="False"> --> <DataGrid ItemsSource="{Binding}" Margin="5" AutoGenerateColumns="False"> <DataGrid.Columns> <DataGridTextColumn Binding="{Binding Path=Type}" Header="Type" /> <DataGridTextColumn Binding="{Binding Path=Name}" Header="Name" /> <DataGridTextColumn Binding="{Binding Path=Color}" Header="Color" /> </DataGrid.Columns> </DataGrid> </UserControl>