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