Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should a user control have its own view model?

Absolutely, positively

NO

Your UserControls should NOT have ViewModels designed specifically for them. This is, in fact, a code smell. It doesn't break your application immediately, but it will cause you pain as you work with it.

A UserControl is simply an easy way to create a Control using composition. UserControls are still Controls, and therefore should solely be concerned with matters of UI.

When you create a ViewModel for your UserControl, you are either placing business or UI logic there. It is incorrect to use ViewModels to contain UI logic, so if that is your goal, ditch your VM and place the code in that control's codebehind. If you're placing business logic in the UserControl, most likely you are using it to segregate parts of your application rather than to simplify control creation. Controls should be simple and have a single purpose for which they are designed.

When you create a ViewModel for your UserControl, you also break the natural flow of data via the DataContext. This is where you will experience the most pain. To demonstrate, consider this simple example.

We have a ViewModel that contains People, each being an instance of the Person type.

public class ViewModel
{
    public IEnumerable<Person> People { get; private set; }
    public ViewModel()
    {
        People = PeopleService.StaticDependenciesSuckToo.GetPeople();
    }
}

public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

To show a list of people in our window is trivial.

<Window x:Class="YoureDoingItWrong.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:l="clr-namespace:YoureDoingItWrong"
        Title="Derp">
    <Window.DataContext>
        <l:ViewModel />
    </Window.DataContext>
    <Window.Resources>
        <DataTemplate DataType="{x:Type l:Person}">
            <l:PersonView />
        </DataTemplate>
    </Window.Resources>
    <ListView ItemsSource="{Binding People}" />
</Window>

The list automatically picks up the correct item template for the Person and uses the PersonView to display the person's information to the user.

What is PersonView? It is a UserControl that is designed to display the person's information. It's a display control for a person, similarly to how the TextBlock is a display control for text. It is designed to bind against a Person, and as such works smoothly. Note in the window above how the ListView transfers each Person instance to a PersonView where it becomes the DataContext for this subtree of the visual.

<UserControl x:Class="YoureDoingItWrong.PersonView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <StackPanel>
        <Label>Name</Label>
        <TextBlock Text="{Binding Name}" />
        <Label>Age</Label>
        <TextBlock Text="{Binding Age}" />
    </StackPanel>
</UserControl>

For this to work smoothly, the ViewModel of the UserControl must be an instance of the Type it is designed for. When you break this by doing stupid stuff like

public PersonView()
{
    InitializeComponent();
    this.DataContext = this; // omfg
}

or

public PersonView()
{
    InitializeComponent();
    this.DataContext = new PersonViewViewModel();
}

you've broken the simplicity of the model. Usually in these instances you end up with abhorrent workarounds, the most common of which is creating a pseudo-DataContext property for what your DataContext should actually be. And now you can't bind one to the other, so you end up with awful hacks like

public partial class PersonView : UserControl
{        
    public PersonView()
    {
        InitializeComponent();
        var vm = PersonViewViewModel();
        // JUST KILL ME NOW, GET IT OVER WITH 
        vm.PropertyChanged = (o, e) =>
        {
            if(e.Name == "Age" && MyRealDataContext != null)
                MyRealDataContext.Age = vm.PersonAge;
        };
        this.DataContext = vm; 
    }
    public static readonly DependencyProperty MyRealDataContextProperty =
        DependencyProperty.Register(
            "MyRealDataContext",
            typeof(Person),
            typeof(PersonView),
            new UIPropertyMetadata());
    public Person MyRealDataContext
    {
        get { return (Person)GetValue(MyRealDataContextProperty); }
        set { SetValue(MyRealDataContextProperty, value); }
    }
}

You should think of a UserControl as nothing more than a more complex control. Does the TextBox have its own ViewModel? No. You bind your VM's property to the Text property of the control, and the control shows your text in its UI.

MVVM doesn't stand for "No Codebehind". Put your UI logic for your user control in the codebehind. If it is so complex that you need business logic inside the user control, that suggests it is too encompassing. Simplify!

Think of UserControls in MVVM like this--For each model, you have a UserControl, and it is designed to present the data in that model to the user. You can use it anywhere you want to show the user that model. Does it need a button? Expose an ICommand property on your UserControl and let your business logic bind to it. Does your business logic need to know something going on inside? Add a routed event.

Normally, in WPF, if you find yourself asking why it hurts to do something, it's because you shouldn't do it.


This is not a yes or no question. It depends on whether having extra view models affords you better maintainability or testability. There's no point adding view models if it doesn't gain you anything. You'll need to gauge whether the overhead is worth it to your particular use case.


I would say that each user control should have its own ViewModel, because that would allow you to reuse the ViewModel/UserControl pair in new constellations in the future.

As I understand it, your window is a Composite of user controls, so you can always create a ViewModel that composes all the separate ViewModels for each of the user controls. This will give you the best of both worlds.


[should] each user control have its own ViewModel or should the window as a whole have only one ViewModel?

Unfortunately, the highest-voted answer to this question is misleading, and based on comments I've exchanged in other questions, providing poor guidance to people trying to learn WPF. That answer replies:

Your UserControls should NOT have ViewModels designed specifically for them.

The problem is, that's not the question that was asked.

I would agree with the general sentiment that when you write a UserControl, the public API of the control should not involve creating also a view model type that is specifically designed to be used for that control. The client code must be able to use whatever view model it wants.

But, that does not preclude the idea that "each user control [might] have its own ViewMomdel". There are at least two obvious scenarios I can think of where the answer to that would be "yes, a view model for each user control":

  1. The user control is part of a data template in an items presenter (e.g. ItemsControl). In this case, the view model will correspond to each individual data element, and there will be a one-to-one correspondence between the view model object and the user control that presents that view model object.

    In this scenario, the view model object is not "designed specifically for them" (so no contradiction with the questionable answer), but it certainly is the case that each user control has its own view model (making the answer to the actual question "yes, each user control may have its own view model").

  2. The user control's implementation benefits from, or even requires, a view model data structure specifically designed for the user control. This view model data structure would not be exposed to the client code; it's an implementation detail, and as such would be hidden from the client code using the user control. But, that certainly still would be a view model data structure "designed specifically for" that user control.

    This scenario is clearly not problematic at all, which directly contradicts the claim that "Your UserControls should NOT have ViewModels designed specifically for them."

Now, I don't believe it was ever the intent of the author of that answer to rule out either of these scenarios. But the problem is, people who are trying to learn WPF may not have enough context to recognize the difference, and thus may incorrectly generalize with respect to user controls and view models, based on this emphatic, highly-upvoted, and misleading answer.

It is my hope that by presenting this alternative view point as a point of clarification, and answering the original question in a less narrow-viewed way, those who found this question while learning more about WPF will have better context, and a better idea and when a view model might be implemented specific to a user control and when it should not be.