WPFのメソッドにバインドしますか?


90

WPFのこのシナリオでは、オブジェクトメソッドにどのようにバインドしますか?

public class RootObject
{
    public string Name { get; }

    public ObservableCollection<ChildObject> GetChildren() {...}
}

public class ChildObject
{
    public string Name { get; }
}

XAML:

<TreeView ItemsSource="some list of RootObjects">
    <TreeView.Resources>
        <HierarchicalDataTemplate DataType="{x:Type data:RootObject}" 
                                  ItemsSource="???">
            <TextBlock Text="{Binding Path=Name}" />
        </HierarchicalDataTemplate>
        <HierarchicalDataTemplate DataType="{x:Type data:ChildObject}">
            <TextBlock Text="{Binding Path=Name}" />
        </HierarchicalDataTemplate>
    </TreeView.Resources>
</TreeView>

ここでは、GetChildrenRootObjectツリーのメソッドにバインドしたいと思います。

EDITのへのバインドは、ObjectDataProviderアイテムのリストにバインドしているため機能しないようでObjectDataProvider、静的メソッドが必要か、それが独自のインスタンスを作成してそれを使用しています。

たとえば、マットの答えを使用すると、次のようになります。

System.Windows.Dataエラー:33:ObjectDataProviderはオブジェクトを作成できません。Type = 'RootObject'; エラー= 'コンストラクタのパラメータが間違っています。'

System.Windows.Dataエラー:34:ObjectDataProvider:タイプのメソッドを呼び出そうとして失敗しました。Method = 'GetChildren'; Type = 'RootObject'; エラー= '指定されたメンバーをターゲットで呼び出すことはできません。TargetException: 'System.Reflection.TargetException:非静的メソッドにはターゲットが必要です。


ええ、あなたは正しいです。ObjectDataProviderには(特定のインスタンスでそのメソッドを呼び出すための)ObjectInstanceプロパティがありますが、これは依存関係プロパティではないため、バインドできません(AFAIK)。
マットハミルトン

1
はい、ObjectInstanceにバインドしようとしたところ、依存関係プロパティではないことがわかりました。
Cameron MacFarland、

いずれにしても、私の回答はそのまま残しておきます。更新情報を提供するためと、この質問を見つけた他の誰かが同様の十分な問題を抱えている場合のためにです。
Matt Hamilton、

実際にObjectInstanceにバインドする必要がありますか?(変更されます)代わりに、独自の変更イベント処理を作成し、ObjectDataProviderをコードで更新できると仮定します...
Tim Lovell-Smith

1
事実の1年後、いくつかのソースコードで私の回答を更新しました。
Drew Noakesが

回答:


71

次のIValueConverterように使用できるように、メソッド名をパラメーターとして取るカスタムを作成することもできます。

ItemsSource="{Binding 
    Converter={StaticResource MethodToValueConverter},
    ConverterParameter='GetChildren'}"

このコンバーターは、リフレクションを使用してメソッドを見つけて呼び出します。これには、メソッドに引数がないことが必要です。

このようなコンバータのソースの例を次に示します。

public sealed class MethodToValueConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var methodName = parameter as string;
        if (value==null || methodName==null)
            return value;
        var methodInfo = value.GetType().GetMethod(methodName, new Type[0]);
        if (methodInfo==null)
            return value;
        return methodInfo.Invoke(value, new object[0]);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException("MethodToValueConverter can only be used for one way conversion.");
    }
}

そして対応する単体テスト:

[Test]
public void Convert()
{
    var converter = new MethodToValueConverter();
    Assert.AreEqual("1234", converter.Convert(1234, typeof(string), "ToString", null));
    Assert.AreEqual("ABCD", converter.Convert(" ABCD ", typeof(string), "Trim", null));

    Assert.IsNull(converter.Convert(null, typeof(string), "ToString", null));

    Assert.AreEqual("Pineapple", converter.Convert("Pineapple", typeof(string), "InvalidMethodName", null));
}

このコンバーターはtargetTypeパラメーターを強制しないことに注意してください。


6
うーん、...ハックのようですが、これが唯一の方法かもしれないと私は考え始めています。間違いなく一番簡単な方法です!
EightyOne Unite

25

シナリオでどのように機能するかはわかりませんが、MethodNameプロパティon ObjectDataProviderを使用して、(プロパティの特定のパラメーターMethodParametersを使用して)特定のメソッドを呼び出し、データを取得できます。

以下は、MSDNページから直接抜粋したスニペットです。

<Window.Resources>
    <ObjectDataProvider ObjectType="{x:Type local:TemperatureScale}"
        MethodName="ConvertTemp" x:Key="convertTemp">
        <ObjectDataProvider.MethodParameters>
            <system:Double>0</system:Double>
            <local:TempType>Celsius</local:TempType>
        </ObjectDataProvider.MethodParameters>
    </ObjectDataProvider>
</Window.Resources>

つまり、クラスのインスタンスでメソッドをObjectDataProvider呼び出して、2つのパラメータ(と)を渡しています。ConvertTempTemperatureScale0TempType.Celsius


10

メソッドにバインドする必要がありますか?

メソッドを取得するプロパティにバインドできますか?

public ObservableCollection<ChildObject> Children
{
   get
   {
      return GetChildren();
   }
}

2
私はキャメロンのコメントを、彼がプロパティを追加できないタイプにバインドしていることを意味すると解釈します。
Drew Noakes、

2
メソッドが長時間実行される可能性がある場合は、メソッドを呼び出すプロパティへのバインドを回避する必要があります。コードの利用者はプロパティがローカル変数にのみアクセスすることを期待しているので、そのようなメソッドがあるとプロパティは良い設計にはなりません。
markmnl 2010

@markmnlでは、関数に直接バインドするポイントは何ですか?OPの質問はあなたのケースでは意味がありませんか?
Teoman Shipahi、2015

4

プロパティを追加してメソッドを呼び出す(またはそのプロパティを追加するラッパークラスを作成する)ことができない限り、私が知っている唯一の方法はValueConverterを使用することです。



3

を使用System.ComponentModelして、型のプロパティを動的に定義できます(コンパイルされたメタデータの一部ではありません)。フィールドへのバインドは不可能なので、フィールドに値を格納する型へのバインドを有効にするために、WPFでこのアプローチを使用しました。

タイプは、あなたが望むものを達成することが可能かもしれません。この記事によると:ICustomTypeDescriptorTypeDescriptionProvider

TypeDescriptionProviderを実装する別のクラスをICustomTypeDescriptor記述し、このクラスを他のタイプの記述のプロバイダーとして登録できます。

私はこの方法を自分で試したことはありませんが、あなたの場合に役立つことを願っています。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.