3

我正在编写一个具有大规模数据模型的应用程序 - 作为其中的一部分,我第一次使用 MVVM 模式。有很多屏幕用于管理各种实体,因此有很多视图模型。我发现每个视图模型都将我正在使用的 POCO 实体的每个属性包装在一个依赖属性中,这样我就可以将它绑定到一个编辑器字段,然后如果用户提交他们的更改,则将其写回实体。这对我来说感觉像是一项额外的腿部工作,我不禁想知道我是否错过了重点,或者是否有更简单的方法可以实现我的目标。例如,我有一个地址视图模型:

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;
    }
}

这是一个简单的五属性实体的包装。

将此与 MVC Web 应用程序的腿部工作进行比较,在该应用程序中,我只是为模型生成一个编辑器,我们的开销存在相当严重的差异,因此我可以通过两种方式绑定一堆文本框。我非常愿意有人告诉我我错过了重点,并且这样做完全错了,但据我所知,我的实体是我的模型,视图模型包装了它,视图绑定到了视图模型。我看过 MVVM 框架,但它们似乎更倾向于帮助控制程序流程和管理视图和视图模型的集合,而不是减少创建视图模型的工作量。

4

3 回答 3

9

您根本不需要这些 DependencyProperties。我建议您将它们实现为标准属性。

如果您的视图模型在显示 UI 时可能会更改,并且您希望该更改反映在 UI 中,则需要实现该INotifyPropertyChanged接口。您可以使用Fody PropertyChanged库(可通过 Nuget 获得)最轻松地完成此操作,如果您只需将属性添加到您的类,它将为您完成所有的工作。

唯一真正需要依赖属性的情况是,如果该属性的值是在 Xaml 中设置的,并且您想使用 a 设置该值MarkupExtension(例如,使用 aBindingx:Static)。在您的情况下,您将绑定应用于控件上的属性(我猜Textbox.Text),所以它TextBox.Text是一个DependencyProperty.

于 2013-09-16T13:30:48.370 回答
5

尽管我并不完全清楚您的代码到底要实现什么,但我相信在这种情况下您不需要依赖属性,而只需要普通属性(可能还有INotifyPropertyChanged接口)。当您制作控件时,将使用依赖属性,该控件的属性将绑定到视图模型中的属性。如果您只是尝试将视图模型绑定到现有控件,则只需简单的属性即可。

于 2013-09-16T13:29:48.947 回答
1

有多种选项可以减少代码膨胀:

  1. 实现INotifyPropertyChanged接口。对于 C#,它的语法更简洁、更“自然”。

  2. 使用代码生成。如果我需要使用依赖属性,我通常依赖 T4。

    例如,这里是附加依赖属性的 T4 代码生成器,它使语法像这样简单:

    "WINDOWPLACEMENT Placement": { for: "Window",
        default: "WINDOWPLACEMENT.Invalid", changed: 1,
        flags: "BindsTwoWayByDefault" }
    
于 2013-09-16T13:32:11.357 回答