Thursday, December 07, 2017

MVVM - Manage View and ViewModel in C#/WPF/xaml

1. howto and benefits

This article talk about how to manage View and associated View model in C# in a WPF/xaml application focusing on the MVVM pattern.

A good way is to control explicitly the creation of view models and let the application link automaticaly the associated view defined for the view model. 

The benefit is to create the view model only when its needed. Another interest is to provide parameters to the view model through the constructor rather than create a global variable.

The application automaticaly creates the view matching the view model, the only work is to declare the association view-view model in the xaml resource file of the application. A very interesting  advantage is to use inheritance with view model classes to build rich and adaptative UI.

2. Example of an implementation

2.1. The functionnality

The example here describe displays a different panel with some properties to edit, depending on a component type: label, combobox,....
If the user choose a label component then the corresponding panel is displayed. If the user choose a combobox component, then the matching panel is displayed, etc...
(the source code of this mechanism is not present in the article to keep it lightweight and clear).

In the image below, a label is selected in the components list, so the corresponding panel is displayed (inside the red rectangle) containing just a label and a textbox.


2.2. The ViewModels hierarchy and attached Views

Of course a MVVM framework is needed, even a basic one,  to implement the solution. There are differents views and ViewModels to implement the solution. For each xaml source code, a matching  viewModel C# class is defined.

            View                    ViewModel
  • MainView.xaml,    MainVM.cs
  • PanelLabel.xaml,   PanelLabelVM.cs
  • PanelComboBox.xaml,   PanelComboBoxVM.cs
  • etc...

All the ViewModel classes inherit from a base class named PanelComponentVM which is declared in the view.

ViewModels classes:
PanelComponentVM  
                     <- PanelLabelVM
                     <- PanelComboBoxVM

2.3. The application xaml resource file

In the resource file attached to the application, we have to define all the links between the views and the view models:

1
2
3
4
5
6
7
8
<DataTemplate DataType="{x:Type vm:PanelLabelVM}">
    <v:PanelLabel />
</DataTemplate>

<DataTemplate DataType="{x:Type vm:PanelComboBoxVM}">
    <v:PanelComboBox />
</DataTemplate>
...

2.4. The main view

The main view contains the declaration of the area where to display the specific details panel depending on the component. This is done by using the ContentPresenter xaml tag.

1
2
3
4
5
6
7
<!--selected component -->
<DockPanel>
  ...common fields for all components: type, name,...

  <!--component specific view-->
  <ContentControl Content="{Binding PanelComponentVM}" />
</DockPanel>

The content presenter is bind to the ViewModel PanelEditComponentBaseVM,  it's the ViewModel base class for all ViewModel class childs. The right view will be automatically created and displayed by the program, depending on the view model instantiated in the ViewModel attached to the main view.

2.5. The views sub part

Each view component is defined in a xaml source code: 
  • PanelLabel.xaml
  • PanelComboBox.xaml
  •  ...

Each view displays a list of properties depending on the component to edit. For example the label view PanelLabel.xaml contains a textbox to edit the text of the label. The combobox view PanelComboBox.xaml contains fields to select a data table and column to display.

2.6. The main ViewModel

The main VM xaml  contains a property which is the view sub part to display, must be the same as the binding value in the view: PanelComponentVM.

1
2
3
4
5
6
7
8
9
public PanelComponentBaseVM PanelComponentVM
{
 get { return _panelComponentVM; }
 set
 {
  _panelComponentVM = value;
  RaisePropertyChanged("PanelComponentVM");
 }
}


The ViewModel is instantiated explicitly depending on the type.

1
2
3
4
5
6
7
8
9
// set the panel with dedicated fields of the component
UILabel uiLabel = componentInListVM.UIComponent as UILabel;
if(uiLabel != null)
  PanelComponentVM= new PanelLabelVM(uiLabel);

UICombobox uiComboBox = componentInListVM.UIComponent as UICombobox ;
if(uiComboBox != null)
  PanelComponentVM= new PanelComboBoxVM(uiComboBox);
...


Automatically, the matching sub part view is created and displayed. An object is passed by the constructor to the viewModel instantiated, here its an model object corresponding to the component displayed.

No comments:

Post a Comment