2012-08-09 24 views
2

我想創建一個自定義標記擴展使用IMarkupExtension<T>有一些DependencyProperties進行綁定。但是,我正努力解決在XAML解析時解析標記擴展的問題,並且僅在稍後才解決綁定問題。我似乎無法通過綁定獲得某些東西:它們始終爲空,並且從不會調用其更改回調。IMarkupExtension與DependencyProperties

docs提到了一些關於返回標記擴展實例的內容(在「返回當前標記擴展實例」下),但似乎使東西爆炸,因爲它是目標的錯誤類型。 This SL5 MultiBinding似乎返回一個代理綁定到一個內部的源對象,但我無法設法得到這個工作:我的綁定仍然沒有設置。

我似乎無法找到任何可靠的信息如何真正實現標記擴展與DependencyProperties(即使它看起來像很多人對SL5感到興奮......)。任何人都可以提供任何指導或教程嗎?

具體來說,我想要做的就是創建一個標記擴展,可以動態地構造一個路徑做一個綁定到一個列表,像這樣:我想它基本上輸出

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

綁定看起來像{Binding List[Index]},其中索引是動態的。這樣做的目的就是說,在列表和索引上使用MultiBinding,以便我們直接綁定到對象並獲取更改通知。 (如果有更好的方法來做這個...)

回答

1

我已經擺弄了很多,我找到了解決方案。它基於我在問題中鏈接到的SL5 MultiBinding的實現。

訣竅是,對MarkupExtension的綁定永遠不會被評估,因爲它沒有DataContext或其他東西,但是如果你從它中取出BindingExpression並將其放入代理中,則附加屬性(附加到目標對象),那麼你可以得到綁定來解決。

下面是一個簡單的MarkupExtension,它演示了這一點。它所做的只是單個綁定並輸出其值(適當地遵守更改),但它顯示了它如何保持在一起。這可以擴展到解決我所討論的字典問題,以及一般的這個問題。

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}",但顯示的解決方案。

+0

現在我對它的理解更加全面了,我遇到了與原來的MultiBinding作者相同的問題:對於給定目標上的MarkupExtension *的所有實例*,您需要爲MarkupExtension中的每個綁定添加一個附加屬性,否則爲多個給定目標上的MarkupExtension實例將相互衝突。 – 2013-03-25 05:11:00

相關問題