WPF User Control hell with MVVM and Dependency Properties

Jon Mitchell picture Jon Mitchell · Apr 14, 2010 · Viewed 26.5k times · Source

This is what I'm trying to do:

  • I'm writing a UserControl that I want to be consumed by other developers.
  • I want end users to be able to use my control using Dependency Properties.

    <lib:ControlView ControlsText={Binding Path=UsersOwnViewModelText} />
    
  • I'm using the MVVM pattern.

  • I'm binding my ViewModels to their View's using <DataTemplates>

    <DataTemplate DataType="{x:Type local:ControlViewModel}">  
        <local:ControlView />  
    </DataTemplate>
    

So I have two questions:

  1. Am I right in thinking that if a UserControl is being consumed in XAML then the UserControl must set the ViewModel as its DataContext when the control's Loaded event fires instead of using the <DataTemplate> method?

  2. How do I allow users to data bind to my control's dependency properties while still being data bound to my ViewModel?

Answer

Kent Boogaart picture Kent Boogaart · Apr 14, 2010

You should separate the two use cases:

  1. The (user) control that will be consumed by other developers.
  2. The user control that will be consumed by your application.

Importantly, the latter depends on the former - not vice versa.

Use case 1 would use dependency properties, template bindings, all the things that go into making a regular WPF control:

MyControl.cs:

public class MyControl : Control
{
    // dependency properties and other logic
}

Generic.xaml:

<ControlTemplate Type="local:MyControl">
    <!-- define the default look in here, using template bindings to bind to your d-props -->
</ControlTemplate>

You would then define use case 2 as:

MyViewModel.cs:

public class MyViewModel : ViewModel
{
    // properties and business logic
}

MyView.xaml:

<UserControl ...>
    <local:MyControl SomeProperty="{Binding SomePropertyOnViewModel}" .../>
</UserControl>

Best of both worlds with a clean separation. Other developers depend only on the control, which could (and probably should) be in a completely different assembly than your view model and view.