AdamCrawford
AdamCrawford

Reputation: 5108

In WPF MVVM, is it necessary for me to wrap all properties to be bound from the model as dependency properties?

I'm writing an application with a large scale data model - as part of this I'm working with the MVVM pattern for the first time. There are a lot of screens for managing the various entities and so there are a lot of view models. I find each view model is wrapping every property of the POCO entity I'm working with in a dependency property just so that I can bind it to an editor field and then write it back out to the entity if the user commits their change. This feels like a huge amount of extra leg work to me and I can't help wonder if I've missed the point or whether there's a simpler way of achieving my goal. As an example, I've got an Address View Model:

public class AddressViewModel : EntityViewModel<Address>
{
    #region Properties

    public string AddressLine1
    {
        get { return (string) GetValue(AddressLine1Property); }
        set { SetValue(AddressLine1Property, value); }
    }

    // Using a DependencyProperty as the backing store for AddressLine1.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty AddressLine1Property =
        DependencyProperty.Register("AddressLine1", typeof (string), typeof (AddressViewModel), new PropertyMetadata(string.Empty, HandleAddressChange));

    private static void HandleAddressChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var vm = d as AddressViewModel;
        if (vm != null)
        {
            vm.OnPropertyChanged(AddressAsSingleLineStringPropertyName);
        }
    }

    public string AddressLine2
    {
        get { return (string) GetValue(AddressLine2Property); }
        set { SetValue(AddressLine2Property, value); }
    }

    // Using a DependencyProperty as the backing store for AddressLine2.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty AddressLine2Property =
        DependencyProperty.Register("AddressLine2", typeof (string), typeof (AddressViewModel), new PropertyMetadata(string.Empty));

    public string AddressLine3
    {
        get { return (string) GetValue(AddressLine3Property); }
        set { SetValue(AddressLine3Property, value); }
    }

    // Using a DependencyProperty as the backing store for AddressLine2.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty AddressLine3Property =
        DependencyProperty.Register("AddressLine3", typeof (string), typeof (AddressViewModel), new PropertyMetadata(string.Empty));

    public string AddressLine4
    {
        get { return (string) GetValue(AddressLine4Property); }
        set { SetValue(AddressLine4Property, value); }
    }

    // Using a DependencyProperty as the backing store for AddressLine2.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty AddressLine4Property =
        DependencyProperty.Register("AddressLine4", typeof (string), typeof (AddressViewModel), new PropertyMetadata(string.Empty));

    public string AddressLine5
    {
        get { return (string) GetValue(AddressLine5Property); }
        set { SetValue(AddressLine5Property, value); }
    }

    // Using a DependencyProperty as the backing store for AddressLine2.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty AddressLine5Property =
        DependencyProperty.Register("AddressLine5", typeof (string), typeof (AddressViewModel), new PropertyMetadata(string.Empty));

    public string PostCode
    {
        get { return (string) GetValue(PostCodeProperty); }
        set { SetValue(PostCodeProperty, value); }
    }

    // Using a DependencyProperty as the backing store for PostCode.  This enables animation, styling, binding, etc...
    public static readonly DependencyProperty PostCodeProperty =
        DependencyProperty.Register("PostCode", typeof (string), typeof (AddressViewModel), new PropertyMetadata(string.Empty, HandleAddressChange));

    /// <summary>
    ///   Gets a value indicating whether this instance is valid for save.
    /// </summary>
    /// <value> <c>true</c> if this instance is valid for save; otherwise, <c>false</c> . </value>
    /// <exception cref="System.NotImplementedException"></exception>
    public override bool IsValidForSave
    {
        get { return !string.IsNullOrWhiteSpace(AddressLine1); }
    }


    /// <summary>
    ///   Gets a value indicating whether this instance is valid for edit.
    /// </summary>
    /// <value> <c>true</c> if this instance is valid for edit; otherwise, <c>false</c> . </value>
    public override bool IsValidForEdit
    {
        get { return true; }
    }



    #endregion

    #region Constructor

    /// <summary>
    /// Initializes a new instance of the <see cref="AddressViewModel" /> class.
    /// </summary>
    public AddressViewModel(Address address) : base(address)
    {
    }

    #endregion

    #region Private Methods

    /// <summary>
    ///   Sets the properties from entity.
    /// </summary>
    public override void SetPropertiesFromEntity()
    {
        AddressLine1 = Entity.AddressLine1;
        AddressLine2 = Entity.AddressLine2;
        AddressLine3 = Entity.AddressLine3;
        AddressLine4 = Entity.AddressLine4;
        AddressLine5 = Entity.AddressLine5;
        PostCode = Entity.PostCode;
    }

    /// <summary>
    ///   Sets the entity from properties.
    /// </summary>
    public override void SetEntityFromProperties()
    {
        Entity.AddressLine1 = AddressLine1;
        Entity.AddressLine2 = AddressLine2;
        Entity.AddressLine3 = AddressLine3;
        Entity.AddressLine4 = AddressLine4;
        Entity.AddressLine5 = AddressLine5;
        Entity.PostCode = PostCode;
    }
}

That's the wrapping for a simple five property entity.

Compare this to the leg work for an MVC web app in which I simply generate an editor for the model, and we have a pretty serious discrepancy in overhead just so I can two way bind a bunch of text boxes. I'm more than willing for someone to tell me I've missed the point and am doing this all completely wrong but so far as I can see it my entity is my model, the viewmodel wraps that and the view binds to the viewmodel. I've looked at the MVVM frameworks, but they seem more directed at helping control program flow and managing the collection of views and view models than reducing the effort of creating a viewmodel.

Upvotes: 3

Views: 998

Answers (3)

Kevin DiTraglia
Kevin DiTraglia

Reputation: 26078

Though I am not fully clear on exactly what your code is trying to achieve, I believe that you do not need dependency properties in this case, but just normal properties (and possibly INotifyPropertyChanged interface). Dependency properties are used when you are making a control where properties of that control are to be bound to properties within your view-model. If you are simply trying to bind a view-model to already existing controls, simple properties are all that is necessary.

Upvotes: 5

Athari
Athari

Reputation: 34293

There're multiple options to reduce code bloat:

  1. Implement INotifyPropertyChanged interface. Its syntax is more concise and more "natural" for C#.

  2. Use code generation. If I need to use dependency properties, I usually rely on T4.

    For examples, here's T4 code generator for attached dependency properties which makes syntax as simple as this:

    "WINDOWPLACEMENT Placement": { for: "Window",
        default: "WINDOWPLACEMENT.Invalid", changed: 1,
        flags: "BindsTwoWayByDefault" }
    

Upvotes: 1

Dan Puzey
Dan Puzey

Reputation: 34218

You don't need these DependencyProperties at all. I'd recommend you implement them as standard properties.

If your viewmodel is likely to change while the UI is being displayed, and you want that change to be reflected in the UI, you will need to implement the INotifyPropertyChanged interface. You can do this most easily using the Fody PropertyChanged libraries (available through Nuget), which will do all the legwork for you if you just add an attribute to your class.

The only time you really require a dependency property is if the value for that property is being set in Xaml, and you want to set the value using a MarkupExtension (e.g. with a Binding or x:Static). In your case, you're applying the binding to a property on a control (I'm guessing Textbox.Text) and so it's TextBox.Text that is a DependencyProperty.

Upvotes: 9

Related Questions