2

IMarkupExtension<T>バインディング用のDependencyPropertiesを持つカスタムマークアップ拡張機能を作成しようとしています。ただし、マークアップ拡張機能がXAML解析時に解決され、バインディングが後で解決されるという問題を解決するのに苦労しています。バインディングを介して何かを取得することはないようです。バインディングは常にnullであり、変更コールバックを呼び出すことはありません。

ドキュメントには、マークアップ拡張機能のインスタンスを返すことについての説明がありますが(「現在のマークアップ拡張機能インスタンスを返す」の下)、ターゲットのタイプが間違っているため、爆発するようです。このSL5MultiBindingは、内部ソースオブジェクトへのプロキシバインディングを返すようですが、それを機能させることができません。バインディングがまだ設定されていません。

DependencyPropertiesを使用してマークアップ拡張機能を実際に実装する方法についての確かな情報を見つけることができないようです(多くの人がSL5に興奮しているように見えましたが...)。誰かがガイダンスやチュートリアルを提供できますか?

具体的には、次のように、リストへのバインドを行うためのパスを動的に構築できるマークアップ拡張機能を作成しようとしています。

{my:ListLookup ListPath='List' Index={Binding Index}}

{Binding List[Index]}基本的に、インデックスが動的であるようなバインディングを出力するようにしたいと思います。たとえば、リストとインデックスのMultiBindingでこれを行う目的は、オブジェクトに直接バインドして変更通知を受け取るようにすることです。(これを行うためのより良い方法がある場合...)

4

1 に答える 1

1

私はこれをもっといじって、解決策を見つけました。これは、質問でリンクした SL5 MultiBinding の実装に基づいています。

トリックは、DataContext などがないため、MarkupExtension の Binding が評価されないことですが、そこから BindingExpression を取得して、プロキシの添付プロパティ (ターゲット オブジェクトに添付) にスローすると、次のことができます。 Binding を取得して解決します。

以下は、これを示す簡単な MarkupExtension です。単一の Binding を取り、その値を出力するだけです (適切に変更に従います)。これは、私が話していた辞書の問題と、この問題全般を解決するために拡張できます。

public class SimpleBindingMarkupExtension : DependencyObject, IMarkupExtension<object>, INotifyPropertyChanged
{
    public object Binding
    {
        get { return (object)GetValue(BindingProperty); }
        set { SetValue(BindingProperty, value); }
    }

    public static readonly DependencyProperty BindingProperty =
        DependencyProperty.Register(
            "Binding",
            typeof(object),
            typeof(SimpleBindingMarkupExtension),
            new PropertyMetadata(null));

    public static readonly DependencyProperty ProxyAttachedBindingProperty =
        DependencyProperty.RegisterAttached(
            "ProxyAttachedBinding",
            typeof(object),
            typeof(SimpleBindingMarkupExtension),
            new PropertyMetadata(null, OnProxyAttachedBindingChanged));

    public static readonly DependencyProperty AttachedMarkupExtensionProperty =
        DependencyProperty.RegisterAttached(
            "AttachedMarkupExtension",
            typeof(SimpleBindingMarkupExtension),
            typeof(SimpleBindingMarkupExtension),
            new PropertyMetadata(null));

    private object _bindingSource;
    public object BindingSource
    {
        get { return _bindingSource; }
        set
        {
            _bindingSource = value;
            OnPropertyChanged("BindingSource");
        }
    }

    private static void OnProxyAttachedBindingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        // Pull the MarkupExtension from the attached property
        var markupExtension = (SimpleBindingMarkupExtension) d.GetValue(AttachedMarkupExtensionProperty);
        markupExtension.ProxyAttachedBindingChanged(e.NewValue);
    }

    private void ProxyAttachedBindingChanged(object value)
    {
        BindingSource = value;
    }

    public object ProvideValue(IServiceProvider serviceProvider)
    {
        IProvideValueTarget target = (IProvideValueTarget) serviceProvider.GetService(typeof (IProvideValueTarget));

        DependencyObject targetObject = target.TargetObject as DependencyObject;
        if (targetObject == null)
            return null;

        // Attach this MarkupExtension to the object so we can find it again from attached property change callbacks
        targetObject.SetValue(AttachedMarkupExtensionProperty, this);

        // Put binding onto proxy attached property, so it actually evaluates
        var localValue = ReadLocalValue(BindingProperty);
        var bindingExpression = localValue as BindingExpression;
        if (bindingExpression == null)
        {
            return localValue;
        }

        Binding originalBinding = bindingExpression.ParentBinding;
        BindingOperations.SetBinding(targetObject, ProxyAttachedBindingProperty, originalBinding);

        // Give the target a proxy Binding that binds to a property on the MarkupExtension
        Binding binding = new Binding
        {
            Path = new PropertyPath("BindingSource"),
            Source = this
        };

        return binding.ProvideValue(serviceProvider);
    }

    #region INotifyPropertyChanged

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    #endregion
}

使用法:

<TextBlock Text="{local:SimpleBindingMarkupExtension Binding={Binding Text}}"/>

前述のように、この例は とだけ言ったのと同じ結果になりText="{Binding Text}"ますが、解決策を示しています。

于 2013-03-22T05:48:48.077 に答える