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>
Resources