我已经对此进行了更多的摆弄,并且找到了解决方案。它基于我在问题中链接到的 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}"
,但显示了解决方案。