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.

Tuesday, November 21, 2017

TabControl with closeable TabItems in WPF/xaml

1. Introduction

The MoellonToolKit project  provide convenient dialog boxes and one useful component: a TabControl with closeable TabItems.
This project can be found on github here and packaged on nuget here.

2. Description, example

The MoellonToolKit project provides a TabControl with closable TabItem Headers. It is possible to use this TabControl with mixing closeable TabItems and standard (unclosable) TabItems.

The example following implements a tabControl with 3 standard tabItems and 2 closeable TabItems:

The third and the fourth TabItems are closeable.

The image below shows the third closeable TabItem having the mouse over focus:

The cross of the TabItem is highlighted.

Now, the image below shows the third tabItem removed because the user closed it:




For more details, see the application sample named DevApp provided in the solution in the Dev folder in the github repository, the source code is available.



3. How ot use it in your application

-First step  is to download the nuget package in your solution and set it to your project.

-Declare the library containing the TabControl in the view. It can be a Window or a UserControl. The component is defined in the library: MoellonToolkit.CommonDlgs.Impl.


1
2
3
4
<Window x:Class="DevApp.Views.MainWindow"
    ...
      xmlns:tc="clr-namespace:MoellonToolkit.CommonDlgs.Impl;assembly=MoellonToolkit.CommonDlgs.Impl"
    ...

-Declare the TabControl in the View, Add standard TabItems and closeable TabItems as you want.

1
2
3
4
5
6
7
<tc:CloseableTabControl>
    <!-- standart tabItem, uncloseable -->
    <TabItem Header="Dialogs">
    ....

    <!-- Closeable tabItem-->
    <tc:CloseableTabItem Header="TabItem 3" /> 

-The last action is to add some code into the behind class of the view:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public MainWindow()
{
    InitializeComponent();

 // for closing the Closeable tabItems
 this.AddHandler(CloseableTabItem.CloseTabEvent, new RoutedEventHandler(this.CloseTab));
}

/// <summary>
/// Close the tabItem, by clic on the X.
/// </summary>
private void CloseTab(object source, RoutedEventArgs args)
{
 TabItem tabItem = args.Source as TabItem;
 if (tabItem != null)
 {
  TabControl tabControl = tabItem.Parent as TabControl;
  if (tabControl != null)
   tabControl.Items.Remove(tabItem);
 }
}