Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/330.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在ViewModel属性中包装模型属性_C#_Wpf_Mvvm_Prism - Fatal编程技术网

C# 如何在ViewModel属性中包装模型属性

C# 如何在ViewModel属性中包装模型属性,c#,wpf,mvvm,prism,C#,Wpf,Mvvm,Prism,与直接将整个模型暴露于视图不同,我希望拥有视图模型属性,这些属性只是每个模型属性的代理。比如, private Product _product; public string ProductName { get { return _product.ProductName; } set { SetProperty(ref _product.ProductName, value); } } 但是上面的示例导致属性、索引器或动态成员访问不

与直接将整个模型暴露于视图不同,我希望拥有视图模型属性,这些属性只是每个模型属性的代理。比如,

private Product _product;

public string ProductName
{
     get { return _product.ProductName; }
     set
     {
          SetProperty(ref _product.ProductName, value);
     }
}
但是上面的示例导致属性、索引器或动态成员访问不能作为out或ref参数传递的错误

我应该如何解决这个问题


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

此处不需要SetProperty,您可以执行以下操作:

private Product _product;

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

您需要的是一个façade或decorator对象,它将在VM中充当您的模型,而不是用ViewModel属性包装每个模型属性。这不仅允许您重用模型(立面/装饰器),还可以将关注点保留在它们所属的位置。您可以像提供的Chippels一样定义属性,但在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); }
    }
}

它是一个简单的使用帮助器类(如ViewModelBase)的make,它简化了raise 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;
    }
}
我们可以像这样包装到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);
        }
    }
}

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

我有另一种创建基类的方法

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;
    }
}
公共抽象类NotifyPropertiesBase:INotifyPropertyChanged,INotifyPropertyChanged
{
公共事件属性更改EventHandler属性更改;
公共事件属性更改事件处理程序属性更改;
只读字典_propertyStore=新字典();
公共属性ChangingEventArgs NotifyChangeing([CallerMemberName]字符串propertyName=null)
{
var arg=新的PropertyChangingEventArgs(propertyName);
PropertyChange?调用(this,arg);
返回arg;
}
public PropertyChangedEventArgs NotifyChanged([CallerMemberName]字符串propertyName=null)
{
var arg=新的PropertyChangedEventArgs(propertyName);
PropertyChanged?调用(this,arg);
返回arg;
}
public void SetPropValue(对象newValue,[CallerMemberName]字符串propertyName=null)
{
if(GetType().GetMember(propertyName).Count()!=1)
抛出新的NotSupportedException($“\”{propertyName}\“不受支持,或者可能不是属性”);
var member=GetType().GetMember(propertyName).FirstOrDefault();
if(member.MemberType!=System.Reflection.MemberTypes.Property)
抛出新的NotSupportedException($“不支持成员类型{Member.MemberType}”);
var pInfo=member.DeclaringType.GetProperties().First();
通知更改(propertyName);
if(!\u propertyStore.ContainsKey(propertyName))
_添加(propertyName,newValue);
其他的
_propertyStore[propertyName]=新值;
NotifyChanged(propertyName);
}
公共T GetPropertyValue([CallerMemberName]字符串propertyName=null)
{
返回(T)GetPropertyValue(propertyName);
}
公共对象GetPropertyValue([CallerMemberName]字符串propertyName=null)
{
if(GetType().GetMember(propertyName).Count()!=1)
抛出新的NotSupportedException($“\”{propertyName}\“不受支持,或者可能不是属性”);
var member=GetType().GetMember(propertyName).FirstOrDefault();
if(member.MemberType!=System.Reflection.MemberTypes.Property)
抛出新的NotSupportedException($“不支持成员类型{Member.MemberType}”);
var pInfo=member.DeclaringType.GetProperties().First();
if(!\u propertyStore.ContainsKey(propertyName))
{
_Add(propertyName,GetDefault(pInfo.PropertyType));
}
返回_propertyStore[propertyName];
}
对象GetDefault(类型t)
{
if(t.IsValueType)
{
返回Activator.CreateInstance(t);
}
返回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);
    }
}
类程序
{
静态void Main(字符串[]参数)
{
var t=新测试();
t、 PropertyChanged+=t_PropertyChanged;
t、 ValueTest=“你好,世界!”;
var数据=t.GetPropertyValue(名称(t.ValueTest));
控制台。写入(数据);
}
私有静态void T_PropertyChanged(对象发送方,PropertyChangedEventArgs e)
{
Console.WriteLine(e.PropertyName);
}
}
公共类测试:NotifyPropertiesBase
{
公共字符串值测试
{
get=>GetPropertyValue();
set=>SetPropValue(值);
}
}

那么,谁来检查属性值是否真的改变了?还有,谁会为PropertyChangedEventHandler触发OnPropertyChanged()方法?嗯,我刚刚注意到这是标记为mvvm和wpf的,我认为这是一个asp.net mvc问题。我必须承认我不完全理解你的问题,所以如果有人回答可能会更好!你能给我一个你在回答中描述的方法的简单代码示例吗。我知道大概的意思,但我看不到方法的全貌。谢谢。我添加了一个非常简单的示例,说明您可能如何处理此问题。通过Façade/Decorator/Adapter类公开您想要的内容,然后将其用作VM的模型。我在我的应用程序中尝试过这种方法,效果很好。通过这种方法,我还可以更方便地在此Facade/Decorator/Adapter类上实现INotifyDataErrorInfo。您还可以添加模型上不存在的新属性,例如FullName和其他聚合/计算值。您还可以将多个模型组合到一个模型中,而视图根本不知道底层系统。您不能使用
CallerMemberNameAttribute
来执行此操作,还是我遗漏了什么?
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);
    }
}