2

我正在尝试使用IMarkupExtension<T>具有一些 DependencyProperties 进行绑定的自定义标记扩展。但是,我正在努力解决在 XAML 解析时解决标记扩展的问题,以及稍后才解决绑定的问题。我似乎从来没有通过绑定得到任何东西:它们总是为空并且从不调用它们的更改回调。

文档提到了一些关于返回标记扩展实例的内容(在“返回当前标记扩展实例”下),但这似乎使事情爆炸,因为它是目标的错误类型。这个 SL5 MultiBinding似乎将代理绑定返回到内部源对象,但我无法让它工作:我的绑定仍然没有设置。

我似乎找不到任何可靠的信息如何使用 DependencyProperties 实际实现标记扩展(尽管看起来很多人对 SL5 感到兴奋......)。任何人都可以提供任何指导或教程吗?

具体来说,我想要做的是创建一个标记扩展,它可以动态构造一个路径来绑定到一个列表,如下所示:

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

我希望它基本上输出一个看起来像的 Binding {Binding List[Index]},其中 Index 是动态的。这样做的目的是对列表和索引上的 MultiBinding 进行此操作,以便我们直接绑定到对象并获得更改通知。(如果有更好的方法来做到这一点......)

4

1 回答 1

1

我已经对此进行了更多的摆弄,并且找到了解决方案。它基于我在问题中链接到的 SL5 MultiBinding 的实现。

诀窍是永远不会评估 MarkupExtension 上的绑定,因为它没有 DataContext 或其他东西,但是如果您从中获取 BindingExpression 并将其放入代理附加属性(附加到目标对象)中,那么您可以让绑定解决。

下面是一个简单的 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 回答