Using MVVM in a Reusable UserControl

In my Pluralsight Course WPF MVVM In Depth I cover all the fundamentals of the MVVM pattern and show how to handle a wide range of scenarios, including hierarchical MVVM – when you have a nested hierarchy of View/ViewModel pairs containing child Views/ViewModels to form a composite view for the user. In those cases, there is usually explicit knowledge in the parent ViewModel of the child ViewModel(s) so that the parent VM can have a supervisory relationship with its children. I cover that setup in my course and show how the child Views can be rendered based on child ViewModels exposed as properties on the parent ViewModel and the use of DataTemplates.

But one scenario that I did not get into in the course that I want to cover here as supplementary material is when you want to use MVVM inside of a UserControl but the ViewModel should be an implementation detail of the control that is hidden from the user of the control (as in programmer user, not end user – the end user shouldn’t have any idea how the code is constructed of course). You just want to be able to reuse that user control in a number of places in your app, you want to expose some properties on the control to drive its functionality, and you want the code “inside” that control to be well factored, easier to maintain, and possibly unit testable.

If you are trying to encapsulate some layout and presentation of controls and functionality in WPF for small scale reuse instead of large scale general purpose reuse (as in “I’m going to need this in a number of places in this app”, as opposed to “I am writing a control for a component library that will be used in all kinds of scenarios outside my development organization”), a UserControl is a great way to go. Of course if you are trying to address the wide scale general purpose reuse, you should probably consider implementing a true custom control – a class with an associated ControlTemplate that derives directly or indirectly from Control. Doing a true custom control opens the door for the user of that control to provide their own control template, or derive their own control class from yours, two things you can’t do with UserControls.

Justifying Using MVVM Inside the User Control

When doing MVVM in WPF, most views are UserControls, and they have a ViewModel class associated with them. Those are typically just “chunks of screen” – a decomposition of the overall screen full of presentation you want to show the user. But if you want to be able to reuse that chunk of screen in a number of places and be able to pass data into it and possibly get data, events, or commands out of it as a reusable chunk, then you want to think in terms of implementing your view as a UserControl first, and having MVVM just be an implementation choice for how you built that thing.

Doing this requires a good understanding of how data binding and DataContexts work, because if you are going to use that UserControl inside of other MVVM views, the way you are going to feed data, events, and commands in and out of the control is going to be through data binding, probably to properties exposed from the ViewModel of the containing view. But then the DataContext of the UserControl content needs to be its ViewModel to follow the MVVM panel internally. But you don’t want those two different DataContexts to conflict when setting and getting properties on the control through data binding.

To illustrate how I would go about this, I’m going to implement a scenario where I want a control that can show icons that each represent something that a user can click on to take some action in the app. Maybe the action will be to navigate to a detail view for that thing in one view, maybe it will be to show a popup to the user in another view, maybe it will be to let them re-order or delete an item in a data collection in another view. But I want the presentation of those collections of icons in my app to be consistent and I’d like the implementation to be reusable. And I need the containing view to be able to supply the data that causes the icons to be presented and the actions (commands) to be taken when an icon is clicked on.

Now with those requirements as stated I could just satisfy them without necessarily wanting or needing to implement MVVM inside of the control – the UserControl would not necessarily need a ViewModel. But if there is also some interactions that are going to be common across all the presentations of the icon view, there might be cause to put a ViewModel behind that UserControl and implement the data management and interaction handling for the view in the ViewModel following the MVVM pattern.

So in addition to the requirements described above, lets say there is also some functionality I always want present when I am showing one of those icon views – for my example I’ll limit that functionality to being able to sort the icons in ascending or descending order. For some real world usage, you might also have a filter input where you could only show icons that meet some filter criteria. Or maybe it switches between small and large icon views and a details table view like Windows Explorer does. So bottom line there will be some interactions that the user can do inside that UserControl that are common, and the logic that supports those interactions needs to manipulate the data that is driving the presentation of the control.

Here is what the app is going to look like at completion:

MVVMUC_UI

The Load button is in the containing View. The blue border is just there to make it very clear where the boundary of the UserControl is. Everything inside the blue border is implemented by the UserControl and its supporting ViewModel. The small up/down icons are controls that are part of the UserControl view itself and support commands to sort the icons ascending or descending. The rest of the icons are the data being presented by this control. You can see in the middle it is also set up so that each icon can present a tooltip that tells the user a little more about what that icon represents.

Defining the Data Model

For my example, I want to be able to provide info to my icon view about what icons to show, and want to be able to leave it to the View/ViewModel that are containing the icon view to supply the info through data binding, as well as letting that code handle when a user clicks on a given icon. I might also want to support some sort of text to go with the icons.

So I need a simple model type that will be the objects I am binding as a collection. For my example, that model type looks like this:

1:publicclass IconInfo

2: {

3:publicstring Label { get; set; }

4:publicstring ResourcePath { get; set; }

5:public ICommand Command { get; set; }

6: }

#### 

Defining the UserControl Markup Structure

Depending on which order you want to work in, you can either first define the markup and structure of the UI elements of your UserControl or you could start by defining the properties the underlying ViewModel needs to support. I’m going to start with the markup and work my way down to the ViewModel.

I’ll add a new UserControl, and call it IconView. Pretty standard stuff, hopefully you don’t need a screen shot for that step. Next I’m going to choose to use a WrapPanel as the way I want to layout my icons within the view, but I know I want to drive what items are presented through data binding. Best way to do that is to use an ItemsControl and set its ItemsPanel property to the layout panel that you want as shown below. The code below also contains the StackPanel with the two sort buttons at the top.

1:<Gridx:Name="rootGrid">

2:<Grid.DataContext>

3:<local:IconViewModel/>

4:Grid.DataContext>

5:<Grid.RowDefinitions>

6:<RowDefinitionHeight="Auto"/>

7:<RowDefinitionHeight="*"/>

8:Grid.RowDefinitions>

9:<StackPanelOrientation="Horizontal">

10:<ButtonCommand="{Binding AscSortCommand}"

11:Width="16"

12:Margin="3">

13:<ImageSource="/MVVMUserControls;component/Images/DownArrow.png"/>

14:Button>

15:<ButtonCommand="{Binding DescSortCommand}"

16:Width="16"

17:Margin="3">

18:<ImageSource="/MVVMUserControls;component/Images/UpArrow.png"/>

19:Button>

20:StackPanel>

21:<ItemsControlItemsSource="{Binding IconInfos}"

22:ItemTemplate="{StaticResource IconTemplate}"

23:Grid.Row="1">

24:<ItemsControl.ItemsPanel>

25:<ItemsPanelTemplate>

26:<WrapPanel/>

27:ItemsPanelTemplate>

28:ItemsControl.ItemsPanel>

29:ItemsControl>

30:Grid>



Note that I am setting the DataContext to the ViewModel not on the root UserControl element, but on the first child – the root grid element that contains all the markup. That is so the DataContext on the root element of the UserControl can still be whatever DataContext flowed down to it from the containing View. Otherwise, when you try to set properties on the instance of your UserControl in the containing view like shown below, the DataContext on the UserControl element will be the one that was set by its own implementation, rather than the one of the containing view.

1:<Windowx:Class="MVVMUserControls.MainWindow"...>

2:<Window.DataContext>

3:<local:MainWindowViewModel/>

4:Window.DataContext>

5:<Grid>

6: ...

7:<local:IconViewIconInfos="{Binding SourceIconInfos}"

8:IconWidth="32"

9:IconMargin="3"

10:Grid.Row="1"

11:Margin="10"

12:/>

13: ...

14:Grid>

15:Window>



If the IconViewModel is set as the DataContext on the root UserControl element in IconView’s markup, you end up with this error:

System.Windows.Data Error: 40 : BindingExpression path error: ‘SourceIconInfos’ property not found on ‘object’ ”IconViewModel’ (HashCode=48519443)’. BindingExpression:Path=SourceIconInfos; DataItem=’IconViewModel’ (HashCode=48519443); target element is ‘IconView’ (Name=”); target property is ‘IconInfos’ (type ‘ObservableCollection`1’)

Instead of looking for SourceIconInfos on the MainWindowViewModel (the containing View is MainWindow), it ends up looking for it on IconViewModel because that is the current DataContext for that element after construction because it gets set by the UserControl during the parsing process. But by setting the DataContext to the IconViewModel on the root Grid element inside the UserControl, it fixes that problem.

Binding to Properties Exposed By The UserControl

You might want to have some properties exposed by the UserControl that can be set in its use, such as the IconWidth and IconMargin properties you can see in the previous snippet. To use the values of those in the XAML of the UserControl, you will need to use a RelativeBinding to get to them on the root element.

The presentation of each icon in my sample is driven by a DataTemplate in the UserControl markup (you could potentially even expose another property for setting by the User for IconTemplate or something like that). That template looks like this.



1:<DataTemplatex:Key="IconTemplate">

2:<ButtonCommand="{Binding Command}"

3:CommandParameter="{Binding Label}"

4:Width="{Binding RelativeSource={RelativeSource AncestorType=local:IconView},

5: Path=IconWidth}"

6:Margin="{Binding RelativeSource={RelativeSource AncestorType=local:IconView},

7: Path=IconMargin}">

8:<Button.ToolTip>

9:<TextBlockText="{Binding Label}"/>

10:Button.ToolTip>

11:<ImageSource="{Binding ResourcePath}"/>

12:Button>

13:DataTemplate>



If you remember from the markup of the UserControl structure itself, we have an ItemsControl that is bound to its ViewModel’s IconInfos collection. In the template above that renders each item, you can see that the button command, tooltip, and image source are just bound to the properties of the current IconInfo that the template is being rendered out for. But to get to those properties that were exposed by the control itself for setting by the user, you need to use a RelativeSource binding to “escape” the current DataContext and get to the properties that are defined on the control instance itself.

Defining Custom Bindable Properties on the UserControl

To define those properties (IconInfos at the UserControl level, IconWidth, and IconMargin), you should define DependencyProperties in the code behind of the UserControl.

1:publicpartialclass IconView : UserControl

2: {

3: IconViewModel _vm;

4:public IconView()

5: {

6: InitializeComponent();

7: _vm = (IconViewModel)rootGrid.DataContext;

8: }

9:

10:public ObservableCollection IconInfos

11: {

12: get { return (ObservableCollection)GetValue(IconInfosProperty); }

13: set { SetValue(IconInfosProperty, value); }

14: }

15:

16:publicstaticreadonly DependencyProperty IconInfosProperty =

17: DependencyProperty.Register("IconInfos", typeof(ObservableCollection),

18:typeof(IconView), new PropertyMetadata(null, OnIconInfosSet));

19:

20:privatestaticvoid OnIconInfosSet(DependencyObject d,

21: DependencyPropertyChangedEventArgs e)

22: {

23: ((IconView)d)._vm.IconInfos = e.NewValue as ObservableCollection;

24: }

25:

26:publicdouble IconWidth

27: {

28: get { return (double)GetValue(IconWidthProperty); }

29: set { SetValue(IconWidthProperty, value); }

30: }

31:

32:publicstaticreadonly DependencyProperty IconWidthProperty =

33: DependencyProperty.Register("IconWidth", typeof(double),

34:typeof(IconView), new PropertyMetadata(-1.0));

35:

36:public Thickness IconMargin

37: {

38: get { return (Thickness)GetValue(IconMarginProperty); }

39: set { SetValue(IconMarginProperty, value); }

40: }

41:

42:publicstaticreadonly DependencyProperty IconMarginProperty =

43: DependencyProperty.Register("IconMargin", typeof(Thickness),

44:typeof(IconView), new PropertyMetadata(new Thickness(0)));

45:

46: }



Note that I have a PropertyChanged handler for the IconInfos property so I can push the value set on the control down into the ViewModel for it to manage and interact with that data. The other two properties (IconWidth and IconMargin) are just consumed by the bindings in the markup.

Finally, the implementation of the ViewModel is straightforward for this simple example:

1:internalclass IconViewModel : BindableBase

2: {

3:private ObservableCollection _IconInfos;

4:

5:public IconViewModel()

6: {

7: AscSortCommand = new RelayCommand(OnSortAsc);

8: DescSortCommand = new RelayCommand(OnSortDesc);

9: }

10:

11:public RelayCommand AscSortCommand { get; set; }

12:public RelayCommand DescSortCommand { get; set; }

13:

14:public ObservableCollection IconInfos

15: {

16: get { return _IconInfos; }

17: set { SetProperty(ref _IconInfos, value); }

18: }

19:

20:privatevoid OnSortAsc()

21: {

22:if (IconInfos != null)

23: IconInfos = new ObservableCollection(

24: IconInfos.OrderBy(i => i.Label));

25: }

26:privatevoid OnSortDesc()

27: {

28:if (IconInfos != null)

29: IconInfos = new ObservableCollection(

30: IconInfos.OrderByDescending(i=>i.Label));

31: }

32: }



Hope that makes it clear how you can manage having the implementation of a UserControl use MVVM itself, but still act like a fully encapsulated reusable control that exposes bindable properties that can be set in the containing view.

You can find the full source code for the sample here.