1

PropertyChangedゲッターで複数のイベントを発生させる標準的な方法よりもクリーンな方法でプロパティ間の依存関係を定義できるようにする、WPF ビューモデル基本クラスへの小さな拡張機能を作成しています。

したがって、代わりに:

public int ThisProperty
{  
    get
    {
        thisProperty = value;
        RaisePropertyChangedEvent("ThisProperty");
        RaisePropertyChangedEvent("FirstDependentProperty");
        RaisePropertyChangedEvent("SecondDependentProperty");
    }
}

ViewModels コンストラクターで次のようなことができるようにしたいと考えています。

RegisterDependencies("This Property", 
    "FirstDependentProperty", "SecondDependentProperty");

ビューモデルで次のメソッドを定義しました (コード量を減らすためにエラー チェックを削除しました)。

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それらのイベントも発生させます。これはすべて美しく機能し、これまでのところ非常に良好です。

私が本当に望んでいるのは、文字列ではなくラムダ式を使用して、オートコンプリートとリファクタリングのサポートを改善することです。私が求めているのは次のようなものです:

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 の出力ウィンドウにはもう少し詳しい情報が表示されます。最初の例外は a のようInvalidCastExceptionですが、両方の式が同じ型であるため理由はわかりません。

誰でも光を当てて、これを手伝ってもらえますか?

4

3 に答える 3

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 に答える
1

Matti Virkkunen のコメントに続き、Member プロパティは、プロパティまたはフィールドを表すことができる MemberInfo オブジェクトを返します。それが問題なら、心配はいりません。Name プロパティは実際には MemberInfo クラスのメンバーであるため、PropertyInfo にキャストする必要はありません。

何か他のことが起こっている場合は、さらに情報が必要です。あなたが渡している実際のラムダ式で呼び出しサイトを投稿できますか?

于 2012-12-31T03:33:11.533 に答える
0

どういうわけか、プロパティ名を間違って入力してしまったことがわかりました。

p => p.Quantity

私は実際に入力しました:

p => p.quantity

小文字の q に注意してください。コンストラクターでこれを呼び出していたという事実のために、すべてのプライベートメンバーにもアクセスできました (したがって、私の型には という名前のフィールドがありましたquantity)。InvalidCastException明らかにプロパティではありません。

@Phoog & Tilak - ご協力ありがとうございます。あなたの両方の回答は、問題を特定するのに役立ちました。これが、これが「正しい」回答であるにもかかわらず、両方の回答に賛成票を投じた理由です。

于 2012-12-31T11:42:57.290 に答える