【问题标题】:MVVM Light ViewModelBase vs EncapsulationMVVM Light ViewModelBase 与封装
【发布时间】:2016-03-09 16:40:18
【问题描述】:

我使用 MVVM Light 已经有一段时间了 - 它非常有用,几乎总是我添加到新项目中的第一个库!

我想知道开发一个实现 INotifyPropertyChanged 以封装可绑定属性的类会有什么影响(下面的示例)。

public class BindableProperty<T> : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;


    private T mValue;
    public T Value
    {
        get { return mValue; }
        set
        {
            if (!EqualityComparer<T>.Default.Equals(mValue, value))
            {
                mValue = value;
                if (PropertyChanged != null)
                {
                    PropertyChanged(this, new PropertyChangedEventArgs("Value"));
                }
            }
        }
    }


    public BindableProperty(T default_value)
    {
        mValue = default_value;
    }
}

使用这个类,我必须更改我的 Xaml,但我相信我的 ViewModel 可能更具可读性(如下) - 特别是当属性数量增加时。

<TextBox Text="{Binding FirstName.Value, UpdateSourceTrigger=PropertyChanged}"/>

public class MainVM
{
    public BindableProperty<string> FirstName { get; private set; }
    public BindableProperty<string> LastName { get; private set; }

    public MainVM()
    {
        FirstName = new BindableProperty<string>("");
        LastName = new BindableProperty<string>("");
    }
}

我知道 MVVM Light 的设计非常灵活、轻巧,并提供完全控制(它做得很好)。我当然可以组合实现,并在更复杂的情况下将上面的 BindableProperty 类用于某些属性和更明确的 ViewModelBase 代码用于其他属性。

我是否遗漏了一些明显的东西?这种设计有哪些我可能不知道的权衡取舍(例如,更改 xaml 绑定、数据验证的影响......)?

【问题讨论】:

  • 我可能错过了您想要表达的内容,但我认为您应该做的是实现dependency property
  • 看看PRISM框架中BindableBase类的实现,它是usage。一种冒险是,您绑定到属性本身,而不是property.Value
  • @Batuu - BindableBase 看起来与 mvvm-light ViewModelBase 非常相似。也许这不值得付出努力,但是使用这个样板代码通常会在我的 ViewModel 中为每个属性放置 5 行以使其看起来干净,而不是使用上面的类(在构造函数中授予 1 行)。
  • @HughJones:依赖属性是一个视图关注点和模式,它们不属于 ViewModel
  • @tseng - 好点。不过,我仍然不清楚 OP 想要实现什么。

标签: c# wpf mvvm mvvm-light


【解决方案1】:

封装属性没有额外的价值。这可能适用于非常简单的场景,但一旦您的 ViewModel 变得更加复杂,您最终会以奇怪的方式连接封装的类。

如果您有相互依赖的属性,即FirstNameLastNameFullName,其中FullName 只是一个public string FullName { get { return FirstName+" "+LastName; } },这种方法也不能很好地用于验证。

同样适用于验证(IDataErrorInfo)。使用基类,您的代码看起来像

public string FirstName 
{
    get { return firstName; }
    set
    {
        if(string.IsNullOrEmpty(value)) 
        {
             // where errors is a Dictionary<string, string>
             errors.Add(nameof(FirstName), "First name can't be empty.");
             return;
        }

        if(value.Length <2) 
        {
             errors.Add(nameof(FirstName), "First name must be at least 2 characters long.");
             return
        }

        Set(ref firstName, value);
        errors.Remove(nameof(FirstName));
    }
}

这将是一个痛苦的封装属性实现

【讨论】:

  • 我同意对于更复杂的场景,您需要灵活地编写上面显示的代码。但是对于非常简单的情况,我觉得这段代码让我的虚拟机很难扫描和理解它们的目的。对我来说,最简单的属性最终是 7 行(支持变量:1 行,属性:4 行,间距:2 行)。我认为这使得难以消化和快速理解 VM 的功能。如果我使用类似于原始问题中的类的东西 - 我的虚拟机顶部的每个属性都有 1 行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-12-07
  • 1970-01-01
  • 2011-06-19
  • 2021-04-19
  • 1970-01-01
  • 2016-07-09
  • 2014-08-29
相关资源
最近更新 更多