2010-02-27 92 views
3

這種感覺應該是這樣一種簡單的解決方案,但我認爲我通過考慮WPF術語中的問題而陷入癱瘓。如何使用綁定綁定到Silverlight中的祖父元素?

在我看來模型我有模式,其中一個容器包含項目集合(例如組和用戶)。所以我創建了3個類,「Group」,「User」和「UserCollection」。在XAML中,我使用一個ItemsControl重複所有的用戶,例如:

<StackPanel DataContext="{Binding CurrentGroup}"> 
    <ItemsControl ItemsSource="{Binding UsersInGroup}"> 
    <ItemsControl.ItemTemplate> 
     <DataTemplate> 
     <TextBlock Text="{Binding UserName"></TextBlock> 
     </DataTemplate> 
    </ItemsControl.ItemTemplate> 
    </ItemsControl> 
</StackPanel> 

現在,DataTemplate中內,我希望綁定到恩在CurrentGroup元素。在WPF中,我會用FindAncestor如:

<TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Group}}, Path=GroupName}"></TextBlock> 

如何,在Silverlight中,我可以使綁定到祖父母的財產?我猜測有一種我看不見的簡單方法。

(我想我已經學會了Silverlight的第一次,而不是WPF,這樣我就不會繼續努力在Silverlight應用程序使用WPF具體的解決方案。)

回答

1

呀,SL是偉大的,但它是很難使用它在學習WPF之後,就像你寫的一樣。

我沒有針對一般問題的解決方案。

對於這個特定的一個,既然你有一個視圖模型,你可以有一個從用戶的組指向一個回指針?如果用戶可以屬於不同的組,這意味着要爲每個UserCollection創建一個特定副本。

+0

我明白你的意思,並會作出合理的變通。仍然希望有一個通用的解決方案,雖然... – Adrian 2010-02-28 23:39:23

+0

100%同意,也許SL 5 ... – Timores 2010-03-01 00:20:58

2

是的,不幸的是,RelativeSource標記擴展在Silverlight中仍然是一種殘缺......它所支持的全部是TemplatedParent和Self,如果內存服務的話。而且由於Silverlight不支持用戶創建的標記擴展(尚未),所以沒有直接模擬FindAncestor語法。

現在認識到這是一個無用的評論,讓我們看看我們是否可以找出一個不同的方式來做到這一點。我想直接將WPF中的FindAncestor語法移植到silverlight的問題與Silverlight沒有真正的邏輯樹的事實有關。我不知道你是否可以使用ValueConverter或附加的行爲,以創造「的VisualTree行走」模擬...

(發生一些google搜索)

嘿,看起來像其他人試圖在Silverlight 2.0這樣做是爲了實現的ElementName - 這可能是一種解決辦法是一個良好的開端: http://www.scottlogic.co.uk/blog/colin/2009/02/relativesource-binding-in-silverlight/

編輯: 好了,在這裏你去 - 適當的信貸應給予上述作家,但我已經調整了它刪除了一些錯誤,等等 - 還有很多需要改進的空間:

public class BindingProperties 
{ 
    public string SourceProperty { get; set; } 
    public string ElementName { get; set; } 
    public string TargetProperty { get; set; } 
    public IValueConverter Converter { get; set; } 
    public object ConverterParameter { get; set; } 
    public bool RelativeSourceSelf { get; set; } 
    public BindingMode Mode { get; set; } 
    public string RelativeSourceAncestorType { get; set; } 
    public int RelativeSourceAncestorLevel { get; set; } 

    public BindingProperties() 
    { 
     RelativeSourceAncestorLevel = 1; 
    } 
} 

public static class BindingHelper 
{ 
    public class ValueObject : INotifyPropertyChanged 
    { 
     private object _value; 

     public object Value 
     { 
      get { return _value; } 
      set 
      { 
       _value = value; 
       OnPropertyChanged("Value"); 
      } 
     } 

     public event PropertyChangedEventHandler PropertyChanged; 

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

    public static BindingProperties GetBinding(DependencyObject obj) 
    { 
     return (BindingProperties)obj.GetValue(BindingProperty); 
    } 

    public static void SetBinding(DependencyObject obj, BindingProperties value) 
    { 
     obj.SetValue(BindingProperty, value); 
    } 

    public static readonly DependencyProperty BindingProperty = 
     DependencyProperty.RegisterAttached("Binding", typeof(BindingProperties), typeof(BindingHelper), 
     new PropertyMetadata(null, OnBinding)); 


    /// <summary> 
    /// property change event handler for BindingProperty 
    /// </summary> 
    private static void OnBinding(
     DependencyObject depObj, DependencyPropertyChangedEventArgs e) 
    { 
     FrameworkElement targetElement = depObj as FrameworkElement; 

     targetElement.Loaded += new RoutedEventHandler(TargetElement_Loaded); 
    } 

    private static void TargetElement_Loaded(object sender, RoutedEventArgs e) 
    { 
     FrameworkElement targetElement = sender as FrameworkElement; 

     // get the value of our attached property 
     BindingProperties bindingProperties = GetBinding(targetElement); 

     if (bindingProperties.ElementName != null) 
     { 
      // perform our 'ElementName' lookup 
      FrameworkElement sourceElement = targetElement.FindName(bindingProperties.ElementName) as FrameworkElement; 

      // bind them 
      CreateRelayBinding(targetElement, sourceElement, bindingProperties); 
     } 
     else if (bindingProperties.RelativeSourceSelf) 
     { 
      // bind an element to itself. 
      CreateRelayBinding(targetElement, targetElement, bindingProperties); 
     } 
     else if (!string.IsNullOrEmpty(bindingProperties.RelativeSourceAncestorType)) 
     { 
      Type ancestorType = Assembly.GetExecutingAssembly().GetTypes().FirstOrDefault(
       t => t.Name.Contains(bindingProperties.RelativeSourceAncestorType)); 

      if(ancestorType == null) 
      { 
       ancestorType = Assembly.GetCallingAssembly().GetTypes().FirstOrDefault(
            t => t.Name.Contains(bindingProperties.RelativeSourceAncestorType));      
      } 
      // navigate up the tree to find the type 
      DependencyObject currentObject = targetElement; 

      int currentLevel = 0; 
      while (currentLevel < bindingProperties.RelativeSourceAncestorLevel) 
      { 
       do 
       { 
        currentObject = VisualTreeHelper.GetParent(currentObject); 
        if(currentObject.GetType().IsSubclassOf(ancestorType)) 
        { 
         break; 
        } 
       } 
       while (currentObject.GetType().Name != bindingProperties.RelativeSourceAncestorType); 
       currentLevel++; 
      } 

      FrameworkElement sourceElement = currentObject as FrameworkElement; 

      // bind them 
      CreateRelayBinding(targetElement, sourceElement, bindingProperties); 
     } 
    } 

    private static readonly BindingFlags dpFlags = BindingFlags.Public | BindingFlags.Static | BindingFlags.FlattenHierarchy; 

    private struct RelayBindingKey 
    { 
     public DependencyProperty dependencyObject; 
     public FrameworkElement frameworkElement; 
    } 

    /// <summary> 
    /// A cache of relay bindings, keyed by RelayBindingKey which specifies a property of a specific 
    /// framework element. 
    /// </summary> 
    private static Dictionary<RelayBindingKey, ValueObject> relayBindings = new Dictionary<RelayBindingKey, ValueObject>(); 

    /// <summary> 
    /// Creates a relay binding between the two given elements using the properties and converters 
    /// detailed in the supplied bindingProperties. 
    /// </summary> 
    private static void CreateRelayBinding(FrameworkElement targetElement, FrameworkElement sourceElement, 
     BindingProperties bindingProperties) 
    { 

     string sourcePropertyName = bindingProperties.SourceProperty + "Property"; 
     string targetPropertyName = bindingProperties.TargetProperty + "Property"; 

     // find the source dependency property 
     FieldInfo[] sourceFields = sourceElement.GetType().GetFields(dpFlags); 
     FieldInfo sourceDependencyPropertyField = sourceFields.First(i => i.Name == sourcePropertyName); 
     DependencyProperty sourceDependencyProperty = sourceDependencyPropertyField.GetValue(null) as DependencyProperty; 

     // find the target dependency property 
     FieldInfo[] targetFields = targetElement.GetType().GetFields(dpFlags); 
     FieldInfo targetDependencyPropertyField = targetFields.First(i => i.Name == targetPropertyName); 
     DependencyProperty targetDependencyProperty = targetDependencyPropertyField.GetValue(null) as DependencyProperty; 


     ValueObject relayObject; 
     bool relayObjectBoundToSource = false; 

     // create a key that identifies this source binding 
     RelayBindingKey key = new RelayBindingKey() 
     { 
      dependencyObject = sourceDependencyProperty, 
      frameworkElement = sourceElement 
     }; 

     // do we already have a binding to this property? 
     if (relayBindings.ContainsKey(key)) 
     { 
      relayObject = relayBindings[key]; 
      relayObjectBoundToSource = true; 
     } 
     else 
     { 
      // create a relay binding between the two elements 
      relayObject = new ValueObject(); 
     } 


     // initialise the relay object with the source dependency property value 
     relayObject.Value = sourceElement.GetValue(sourceDependencyProperty); 

     // create the binding for our target element to the relay object, this binding will 
     // include the value converter 
     Binding targetToRelay = new Binding(); 
     targetToRelay.Source = relayObject; 
     targetToRelay.Path = new PropertyPath("Value"); 
     targetToRelay.Mode = bindingProperties.Mode; 
     targetToRelay.Converter = bindingProperties.Converter; 
     targetToRelay.ConverterParameter = bindingProperties.ConverterParameter; 

     // set the binding on our target element 
     targetElement.SetBinding(targetDependencyProperty, targetToRelay); 

     if (!relayObjectBoundToSource && bindingProperties.Mode == BindingMode.TwoWay) 
     { 
      // create the binding for our source element to the relay object 
      Binding sourceToRelay = new Binding(); 
      sourceToRelay.Source = relayObject; 
      sourceToRelay.Path = new PropertyPath("Value"); 
      sourceToRelay.Converter = bindingProperties.Converter; 
      sourceToRelay.ConverterParameter = bindingProperties.ConverterParameter; 
      sourceToRelay.Mode = bindingProperties.Mode; 

      // set the binding on our source element 
      sourceElement.SetBinding(sourceDependencyProperty, sourceToRelay); 

      relayBindings.Add(key, relayObject); 
     } 
    } 
} 

你會正是如此使用:

<TextBlock> 
    <SilverlightApplication1:BindingHelper.Binding> 
     <SilverlightApplication1:BindingProperties 
      TargetProperty="Text" 
      SourceProperty="ActualWidth" 
      RelativeSourceAncestorType="UserControl" 
      Mode="OneWay" 
      /> 
    </SilverlightApplication1:BindingHelper.Binding> 
</TextBlock> 
+0

不完全是我正在尋找,但它開始出現,我想要的東西不存在。這個機制看起來類似於我曾經見過的用於添加屬性雙向綁定到ASP.NET的機制。當我寫這個問題的時候,我想我有一個金髮的一天,只是錯過了一些微不足道的東西,顯然不是! – Adrian 2010-03-04 03:13:18