View Models as a WPF Dependency Property

Posted in software by Christopher R. Wirz on Wed Feb 10 2016



Windows Presentation Foundation uses xaml-based user controls that natively lend themselves to the Model View ViewModel paradigm. The View is the XAML and its code-behind, the Model is the parameters that the view is setting and getting through the ViewModel, and the ViewModel is providing access to these properties. The ViewModel may expose the model directly, or properties related to the model, for data-binding. The ViewModel can contain interfaces to services, configuration data, etc in order to fetch and manipulate the properties it exposes to the view.

First, let's start with a simple view model...


public abstract class AbstractViewModelBase : DependencyObject, INotifyPropertyChanged
{

    public static string ViewModelKey = "ViewModelKey";

    /// <summary>
    ///     Occurs when a property value changes.
    /// </summary>
    public event PropertyChangedEventHandler PropertyChanged;


    /// <summary>
    ///     Called when [property changed].
    /// </summary>
    /// <param name="propertyName">Name of the property.</param>
    protected virtual void OnPropertyChanged(string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }

    /// <summary>
    ///     Sets the value of a field, raising property changed if the value changed.
    /// </summary>
    /// <typeparam name="T">The generic type</typeparam>
    /// <param name="field">The field.</param>
    /// <param name="value">The value.</param>
    /// <param name="propertyName">Name of the property.</param>
    /// <returns>True if the field has changed value</returns>
    protected virtual bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null)
    {
        if (!EqualityComparer<T>.Default.Equals(field, value))
        {
            field = value;
            OnPropertyChanged(propertyName);
            return true;
        }
        return false;
    }
}
public class RunningControlViewModel : AbstractViewModelBase
{
    private bool _running = false;
    /// <summary>
    ///     True if the process is running
    /// </summary>
    public bool Running {
        get {return _running;}
        set {SetValue(ref _running, value, nameof(Running));}
    }
}

This is nothing new, nor is the following way of adding a ViewModel to a UserControl.


/// <summary>
///     Interaction logic for RunningControl.xaml
/// </summary>
public partial class RunningControl : UserControl
{
    public RunningControl()
    {
        InitializeComponent();
        this.ViewModel = new RunningControlViewModel();
    }

    public RunningControlViewModel ViewModel
    {
        get { return (RunningControlViewModel)this.Resources[RunningControlViewModel.ViewModelKey]; }
        set {
            if (value == null) { return; }
            this.Resources[RunningControlViewModel.ViewModelKey] = value;
            var fe = this.Content as FrameworkElement;
            if (fe == null) { return; }
            fe.DataContext = value;
        }
    }
}

This way allows you to set and get the view model, but not from the XAML. Well, that feels like we're just missing out. We are. When the ViewModel is basically a data object, and that data object comes from a collection, it makes a lot of sense to bind the ViewModel as a dependency property.


/// <summary>
///     Interaction logic for RunningControl.xaml
/// </summary>
public partial class RunningControl : UserControl
{
    public RunningControl()
    {
        InitializeComponent();
        this.ViewModel = new RunningControlViewModel();
    }

    public static readonly DependencyProperty ViewModelProperty =
        DependencyProperty.Register(
        nameof(ViewModel),
        typeof(RunningControlViewModel),
        typeof(RunningControl),
        new PropertyMetadata((s, a) => {
            var c = s as RunningControl;
            if (c == null) { return; }
            var fe = pc.Content as FrameworkElement;
            if (fe == null) { return; }
            c.Resources[RunningControlViewModel.ViewModelKey] = a.NewValue;
            fe.DataContext = a.NewValue;
        })
    );

    public RunningControlViewModel ViewModel
    {
        get { return (RunningControlViewModel)GetValue(ViewModelProperty); }
        set { SetValue(ViewModelProperty, value); }
    }
}

Ah, that is better. But what if you want to propagate this going forward? A change to the ViewModel should


/// <summary>
///     Interaction logic for RunningControl.xaml
/// </summary>
public partial class RunningControl : UserControl
{
    public RunningControl()
    {
        InitializeComponent();
        this.ViewModel = new RunningControlViewModel();
        this.ViewModel.PropertyChanged += (s, a) =>{
            if (a.PropertyName == nameof(RunningControlViewModel.Running)){
                if (ViewModel.Running != Running){
                    Running = ViewModel.Running;
                }
            }
        };
    }

    public static readonly DependencyProperty ViewModelProperty =
        DependencyProperty.Register(
        nameof(ViewModel),
        typeof(RunningControlViewModel),
        typeof(RunningControl),
        new PropertyMetadata((s, a) => {
            var c = s as RunningControl;
            if (c == null) { return; }
            var fe = c.Content as FrameworkElement;
            if (fe == null) { return; }
            c.Resources[RunningControlViewModel.ViewModelKey] = a.NewValue;
            fe.DataContext = a.NewValue;
        })
    );

    public RunningControlViewModel ViewModel
    {
        get { return (RunningControlViewModel)GetValue(ViewModelProperty); }
        set { SetValue(ViewModelProperty, value); }
    }

    public static readonly DependencyProperty RunningProperty =
        DependencyProperty.Register(
        nameof(Running),
        typeof(bool),
        typeof(RunningControl),
        new PropertyMetadata((s, a) => {
            var c = s as RunningControl;
            if (c == null) { return; }
            var v = c.ViewModel as RunningControlViewModel;
            if (v == null) { return; }
            try {v.Running = a.NewValue as bool ?? false; } catch {}
        })
    );

    public bool Running
    {
        get { return (bool)GetValue(RunningProperty); }
        set { SetValue(RunningProperty, value); }
    }
}

So now what? Well, let's put it all together in XAML assuming the parent control has a ViewModel with properties of RcViewModel and RcIsRunning.


<Grid>
    <local:RunningControl
        ViewModel="{Binding RcViewModel, Mode=OneWayToSource, UpdateSourceTrigger=PropertyChanged}"
        Running="{Binding RcIsRunning, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
    />
<Grid>

And that's it! You can call Running from both the XAML and the code-behind!

Looking for a job?