1

我正在为我的 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;
        }
    }
}

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 的输出窗口提供了更多信息,似乎最初的异常是 aInvalidCastException虽然我不确定为什么因为两个表达式属于同一类型。

任何人都可以阐明并帮助我吗?

4

3 回答 3

1

按照 Matti Virkkunen 的评论,Member 属性返回一个可以表示属性或字段的 MemberInfo 对象。如果这是您的问题,请不要担心;Name 属性实际上是 MemberInfo 类的成员,因此无需强制转换为 PropertyInfo。

如果发生其他事情,我们需要更多信息来提供帮助;你能用你传递的实际 lambda 表达式发布调用站点吗?

于 2012-12-31T03:33:11.533 回答
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;
    }
于 2012-12-31T04:11:33.990 回答
0

事实证明,不知何故,我设法输入了错误的属性名称,例如我想要的位置:

p => p.Quantity

我实际上输入了:

p => p.quantity

注意小写的 q。由于我在构造函数中调用它的事实,我还可以访问我的所有私有成员(所以我的类型有一个名为 的字段quantity),这就是为什么这不会导致编译器抱怨而是InvalidCastException在运行时生成它是显然不是财产。

@Phoog & Tilak - 谢谢你的帮助。您的两个答案都帮助我隔离了问题,这就是为什么我对您的两个答案都投了赞成票,尽管这是“正确”的答案。

于 2012-12-31T11:42:57.290 回答