如何摆脱WPF MVVM视图模型中的重复属性

unc*_*all 8 c# wpf prism mvvm

我正在设置一个ViewModel具有很多属性的WPF应用程序.这些都是非常重复的,我想知道是否有办法摆脱这个.这是一个属性的样子,我有大约8-10个属性.

public string Name
{
    get
    {
        return this.name;
    }

    set
    {
        if (this.name != value)
        {
            this.name = value;
            this.RaisePropertyChanged("Name");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Dan*_*zey 5

我的建议是,如果您的要求很简单,那就去第三方。这是一个解决的问题,多亏了一些聪明的人......

编写代码的最简单的方法是INotifyPropertyChanged完全删除实现,并以最小的方式编写属性,如下所示:

public string Name { get; set; }
Run Code Online (Sandbox Code Playgroud)

然后将Fody.PropertyChanged添加到您的项目(位于NuGet 上)并使用该[ImplementPropertyChanged]属性标记您的类。

Fody 会在编译过程中使用一些聪明的 IL 魔法来神奇地实现接口和所有样板代码 - 这意味着您编写的代码尽可能简单,最终结果正是您想要的。

请注意,如果您依赖INotifyPropertyChanged代码中其他地方的接口(即,如果您手动附加到代码或类似的事件),您可能希望以不同的方式使用 Fody,因为 IDE 不会意识到您已经实现了接口. 幸运的是,Fody 也会在其他场景中自动实现(例如:INotifyPropertyChanged在类中实现,Fody 也会默认在您的属性中实现事件引发)。


unc*_*all 2

提到的线程确实包含答案,但您需要进行一些挖掘。我将展示我在那里找到的两个最佳答案。

第一个解决方案是实现一个 ViewModelBase 类,将 set 方法封装到模板方法中,并使用 lamda 表达式来检索属性名称,这样重构就不会破坏属性名称字符串。

public class ViewModelBase: INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }

    protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
    {
        if (selectorExpression == null)
            throw new ArgumentNullException("selectorExpression");
        var body = selectorExpression.Body as MemberExpression;
        if (body == null)
            throw new ArgumentException("The body must be a member expression");
        OnPropertyChanged(body.Member.Name);
    }

    protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
    {
        if (EqualityComparer<T>.Default.Equals(field, value)) return false;
        field = value;
        OnPropertyChanged(selectorExpression);
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

用法:

class ViewModel : DataBase
{
    private String _prop1;
    public String Prop1
    {
        get { return _prop1; }
        set
        {
            SetField(ref _prop1, value, () => Prop1);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

第二种解决方案使用字典来存储基类中的属性。这样我们就不需要传入旧值,因为它保存在基类中,并且我们不需要创建成员字段来保存属性的值。我最喜欢这个解决方案:

public abstract class ViewModelBase : INotifyPropertyChanged
{
    private readonly Dictionary<string, object> _propertyValueStorage;

    #region Constructor

    protected ViewModelBase()
    {
        this._propertyValueStorage = new Dictionary<string, object>();
    }

    #endregion

    protected void SetValue<T>(Expression<Func<T>> property, T value)
    {
        var lambdaExpression = property as LambdaExpression;

        if (lambdaExpression == null)
        {
            throw new ArgumentException("Invalid lambda expression", "Lambda expression return value can't be null");
        }

        var propertyName = this.getPropertyName(lambdaExpression);
        var storedValue = this.getValue<T>(propertyName);

        if (object.Equals(storedValue, value)) return;

        this._propertyValueStorage[propertyName] = value;
        this.OnPropertyChanged(propertyName);
    }

    protected T GetValue<T>(Expression<Func<T>> property)
    {
        var lambdaExpression = property as LambdaExpression;

        if (lambdaExpression == null)
        {
            throw new ArgumentException("Invalid lambda expression", "Lambda expression return value can't be null");
        }

        var propertyName = this.getPropertyName(lambdaExpression);
        return getValue<T>(propertyName);
    }

    private T getValue<T>(string propertyName)
    {
        object value;
        if (_propertyValueStorage.TryGetValue(propertyName, out value))
        {
            return (T)value;
        }
        return default(T);

    }

    private string getPropertyName(LambdaExpression lambdaExpression)
    {
        MemberExpression memberExpression;

        if (lambdaExpression.Body is UnaryExpression)
        {
            var unaryExpression = lambdaExpression.Body as UnaryExpression;
            memberExpression = unaryExpression.Operand as MemberExpression;
        }
        else
        {
            memberExpression = lambdaExpression.Body as MemberExpression;
        }

        return memberExpression.Member.Name;
    }

    #region < INotifyPropertyChanged > Members

    public event PropertyChangedEventHandler PropertyChanged;

    protected void OnPropertyChanged(string propertyName)
    {
        if (this.PropertyChanged != null)
        {
            this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    #endregion

}
Run Code Online (Sandbox Code Playgroud)

用法是:

public class ViewModel : ViewModelBase
{
    public String Prop1
    {
        get { return GetValue(() => Prop1); }
        set { SetValue(() => Prop1, value); }
    }
    public bool Bool1
    {
        get { return GetValue(() => Bool1); }
        set { SetValue(() => Bool1, value); }
    }
Run Code Online (Sandbox Code Playgroud)

解决方案1基于/sf/answers/92159651//sf/answers/92159651/

解决方案2基于http://dotnet-forum.de/blogs/thearchitect/archive/2012/11/01/die-optimale-implementierung-des-inotifypropertychanged-interfaces.aspx