我正在为我的WPF viewModel基类编写一个小扩展,它允许我以比在getter中引发多个PropertyChanged
事件的标准方法更清晰的方式定义属性之间的依赖关系:
所以而不是:
public int ThisProperty
{
get
{
thisProperty = value;
RaisePropertyChangedEvent("ThisProperty");
RaisePropertyChangedEvent("FirstDependentProperty");
RaisePropertyChangedEvent("SecondDependentProperty");
}
}
我希望能够在我的ViewModels构造函数中执行类似的操作:
RegisterDependencies("This Property",
"FirstDependentProperty", "SecondDependentProperty");
我在viewModel中定义了以下方法(删除了错误检查以减少代码量):
public void RegisterDependencies(string property, params string[] dependencies)
{
foreach (string item in dependencies)
{
IList<string> deps;
if (dependenciesList.TryGetValue(item, out deps))
{
if (!deps.Contains(property))
{
deps.Add(property);
}
}
else
{
deps = new List<string>();
deps.Add(property);
dependenciesList[item] = deps;
}
}
}
我的viewmodel使用以下方法订阅PropertyChanged
事件:
void ViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
IList<string> dependencies;
if (dependenciesList.TryGetValue(e.PropertyName, out dependencies))
{
foreach (string item in dependencies)
{
RaisePropertyChangedEvent(item);
}
}
}
这只是检查属性是否具有任何依赖关系,如果是,则也会为这些事件引发PropertyChanged
事件。这一切都很美妙,到目前为止一直很好。
我真正想要的是使用lambda表达式而不是字符串,以便我获得自动完成和更好的重新分解支持。我所追求的是这样的:
RegisterDependencies<ViewModelType>(p => p.Property,
p => p.FirstDependency,
p => p.SecondDependency); //and so on...
我重新定义了方法签名,如下所示:
public void RegisterDependencies<T>(Expression<Func<T, object>> property,
prams Expression<Func<T, object>>[] dependencies)
{
}
目前,该方法只是尝试将表达式转换为字符串,这就是我要解开的地方。我不知道如何从表达式中获取属性的名称并将其转换为字符串。
Josh Smith的MVVM Foundation包含一个名为PropertyChangedObserver
的类,它有一段代码可以实现这一点,我试图适应我的例子:
private static string GetPropertyName<T>(Expression<Func<T, object>> expression)
{
var lambda = expression as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
return memberExpression != null ? ((PropertyInfo)memberExpression.Member).Name : null;
}
这是在我更新的RegisterDependencies
方法中调用的(这实际上是该方法的整个代码):
string propertyName = GetPropertyName(property);
foreach (Expression<Func<T, object>> expr in dependencies)
{
string dependencyName = GetPropertyName(expr);
}
运行此操作时会产生XamlParseException
。这很难调试,因为它没有抛出标准异常窗口。 Visual Studio的输出窗口提供了更多的信息,似乎最初的异常是InvalidCastException
,虽然我不确定为什么两个表达式的类型相同。
任何人都可以解决这个问题吗?
答案 0 :(得分:1)
跟进Matti Virkkunen的评论,Member属性返回一个可以表示属性或字段的MemberInfo对象。如果这是你的问题,不用担心; Name属性实际上是MemberInfo类的成员,因此无需强制转换为PropertyInfo。
如果还有其他事情发生,我们需要更多信息来提供帮助;你能用你正在传递的实际lambda表达式发布呼叫网站吗?
答案 1 :(得分:1)
似乎问题在于将表达式传递给RegisterDependencies
。
以下(稍加适应的代码)成功运行。
public static void Test()
{
var deps = RegisterDependencies((KeyValuePair<string,string> p) => p.Key, (KeyValuePair<string,string> p) => p.Value);
foreach(var d in deps)
{
Console.WriteLine(d); // Prints Key, Then Value
}
}
public static IEnumerable<string> RegisterDependencies<T>(Expression<Func<T, object>> property, params Expression<Func<T, object>>[] dependencies)
{
var deps = new List<string>();
deps.Add(GetPropertyName(property));
foreach (var d in dependencies)
{
deps.Add(GetPropertyName(d));
}
return deps;
}
public static string GetPropertyName<T>(Expression<Func<T, object>> expression)
{
var lambda = expression as LambdaExpression;
MemberExpression memberExpression;
if (lambda.Body is UnaryExpression)
{
var unaryExpression = lambda.Body as UnaryExpression;
memberExpression = unaryExpression.Operand as MemberExpression;
}
else
{
memberExpression = lambda.Body as MemberExpression;
}
return memberExpression != null ? ((PropertyInfo)memberExpression.Member).Name : null;
}
答案 2 :(得分:0)
事实证明,不知何故,我设法输入错误的属性名称,例如我想要的地方:
p => p.Quantity
我实际输入了:
p => p.quantity
注意小写q 。由于我在构造函数中调用它,我也可以访问所有私有成员(因此我的类型有一个名为quantity
的字段)这就是为什么这不会导致编译器抱怨而是{{1在运行时生成,因为它显然不是属性。
@Phoog&amp; Tilak - 谢谢你的帮助。你的两个答案都帮助我解决了这个问题,这就是为什么我对你的两个答案进行了投票,尽管这是“正确的”答案。