如何在ViewModel属性中包装Model属性

本文关键字:属性 Model 包装 ViewModel | 更新日期: 2023-09-27 18:29:33

我不想将整个模型直接暴露给视图,而是希望拥有ViewModel属性,这些属性只是每个Model

private Product _product;
public string ProductName
{
     get { return _product.ProductName; }
     set
     {
          SetProperty(ref _product.ProductName, value);
     }
}

但是上面的例子导致了CCD_ 1的错误。

我应该如何解决这个问题?

p.S.我的模型不是由INPC接口实现的。它们只是简单的POCO类

如何在ViewModel属性中包装Model属性

您想要的是一个外观或装饰器对象,它将在VM中充当您的模型,而不是用ViewModel属性包装每个模型属性。这不仅允许您重用模型(立面/装饰器),而且还可以将关注点保留在它们所属的位置。您可以像提供的chipples一样定义属性,但在setter中调用OnPropertyChanged()。包装其他属性时不能使用SetProperty方法。

类似的东西:

class Person
{
    public string Name { get; set; }
}
class PersonFacade : BindableBase
{
    Person _person;
    public string Name
    {
        get { return _person.Name; }
        set
        {
            _person.Name = value;
            OnPropertyChanged();
        }
    }
}
class ViewModel : BindableBase
{
    private PersonFacade _person;
    public PersonFacade Person
    {
        get { return _person; }
        set { SetProperty(ref _person, value); }
    }
}

也许这是一个旧线程,但C#MVVM属性让我倍感压力。想象一下,你每天需要写200个属性。

我有另一种创建BaseClass的方法。

public abstract class NotifyPropertiesBase : INotifyPropertyChanged, INotifyPropertyChanging
{
    public event PropertyChangingEventHandler PropertyChanging;
    public event PropertyChangedEventHandler PropertyChanged;
    readonly Dictionary<string, object> _propertyStore = new Dictionary<string, object>();
    public PropertyChangingEventArgs NotifyChanging([CallerMemberName] string propertyName = null)
    {
        var arg = new PropertyChangingEventArgs(propertyName);
        PropertyChanging?.Invoke(this, arg);
        return arg;
    }
    public PropertyChangedEventArgs NotifyChanged([CallerMemberName] string propertyName = null)
    {
        var arg = new PropertyChangedEventArgs(propertyName);
        PropertyChanged?.Invoke(this, arg);
        return arg;
    }
    public void SetPropValue(object newValue, [CallerMemberName] string propertyName = null)
    {
        if (GetType().GetMember(propertyName).Count() != 1)
            throw new NotSupportedException($"'"{propertyName}'" Not Supported or maybe its not a Property");
        var member = GetType().GetMember(propertyName).FirstOrDefault();
        if (member.MemberType != System.Reflection.MemberTypes.Property)
            throw new NotSupportedException($"Not Support Member Type {member.MemberType}");
        var pInfo = member.DeclaringType.GetProperties().First();
        NotifyChanging(propertyName);
        if (!_propertyStore.ContainsKey(propertyName))
            _propertyStore.Add(propertyName, newValue);
        else
            _propertyStore[propertyName] = newValue;
        NotifyChanged(propertyName);
    }
    public T GetPropertyValue<T>([CallerMemberName] string propertyName = null)
    {
        return (T)GetPropertyValue(propertyName);
    }
    public object GetPropertyValue([CallerMemberName] string propertyName = null)
    {
        if (GetType().GetMember(propertyName).Count() != 1)
            throw new NotSupportedException($"'"{propertyName}'" Not Supported or maybe its not a Property");
        var member = GetType().GetMember(propertyName).FirstOrDefault();
        if (member.MemberType != System.Reflection.MemberTypes.Property)
            throw new NotSupportedException($"Not Support Member Type {member.MemberType}");
        var pInfo = member.DeclaringType.GetProperties().First();
        if (!_propertyStore.ContainsKey(propertyName))
        {
            _propertyStore.Add(propertyName, GetDefault(pInfo.PropertyType));
        }
        return _propertyStore[propertyName];
    }
    object GetDefault(Type t)
    {
        if (t.IsValueType)
        {
            return Activator.CreateInstance(t);
        }
        return null;
    }
}

类别用法:

class Program
{
    static void Main(string[] args)
    {
        var t = new Test();
        t.PropertyChanged += T_PropertyChanged;
        t.ValueTest = "Hello World!";
        var data = t.GetPropertyValue(nameof(t.ValueTest));
        Console.Write(data);
    }
    private static void T_PropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        Console.WriteLine(e.PropertyName);
    }
}
public class Test : NotifyPropertiesBase
{
    public string ValueTest
    {
        get => GetPropertyValue<string>();
        set => SetPropValue(value);
    }
}

使用ViewModelBase这样的辅助类可以简单地生成,它简化了引发PropertyChanged事件:

public class ViewModelBase : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged = delegate { };
    protected void OnPropertyChanged<T>(Expression<Func<T>> propertyExpression)
    {
        PropertyChanged(this, new PropertyChangedEventArgs(ExtractPropertyName(propertyExpression)));
    }
    private static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
    {
        if (propertyExpression == null)
            throw new ArgumentNullException("propertyExpression");
        var memberExpression = propertyExpression.Body as MemberExpression;
        if (memberExpression == null)
            throw new ArgumentException("memberExpression");
        var property = memberExpression.Member as PropertyInfo;
        if (property == null)
            throw new ArgumentException("property");
        var getMethod = property.GetGetMethod(true);
        if (getMethod.IsStatic)
            throw new ArgumentException("static method");
        return memberExpression.Member.Name;
    }
}

然后,简单的POCO类人员:

class Person
{
    public string Name { get; set; }
    public double Age { get; set; }
}

我们可以像一样包装到ViewModel

public class PersonViewModel : ViewModelBase
{
    private readonly Person person = new Person();
    public string Name
    {
        get { return person.Name; }
        set
        {
            person.Name = value;
            OnPropertyChanged(() => Name);
        }
    }
    public double Age
    {
        get { return person.Age; }
        set
        {
            person.Age = value;
            OnPropertyChanged(() => Age);
        }
    }
}

这里不需要SetProperty,您可以这样做:

private Product _product;
public string ProductName
{
     get { return _product.ProductName; }
     set
     {
          _product.ProductName = value;
     }
}