2012-02-29 56 views
1

我有一個XDocument這樣一組作爲我Window一個DataContext的XDocument結合元素和屬性

Class MainWindow 
    Public Sub New() 
     InitializeComponent() 
     Me.DataContext = <?xml version="1.0" encoding="utf-8"?> 
         <Sketch Format="A4" Author="Aaron" Created="..." Test="Value"> 
          <Item Kind="Line" X1="50" Y1="50" X2="150" Y2="150"> 
           <Item Kind="Rect" X="10" Y="10" Width="30" Height="30"/> 
          </Item> 
          <Item Kind="Line" X1="250" Y1="250" X2="250" Y2="50"> 
           <Item Kind="Ellipse" X="10" Y="10" Width="30" Height="30"/> 
          </Item> 
          <Test Param="Value"/> 
         </Sketch> 
    End Sub 
End Class 

現在在我的前端我測試幾個不同的結合路徑。他們都與ElementsElement,Attribute,但Attributes似乎並不適合我。我認爲它很奇怪,因爲ElementsIEnumerable<XElement>AttributesIEnumerable<XAttribute> - 完全一樣的集合和一切。

<Window Height="320" Title="Main Window" Width="640" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="MainWindow"> 
    <UniformGrid Columns="3"> 
     <StackPanel> 
      <Label Foreground="DimGray">Root.Elements.Count</Label> 
      <Label Content="{Binding Path=Root.Elements.Count, FallbackValue=Loading…}"/> 
      <Label Foreground="DimGray">Root.Attributes.Count</Label> 
      <Label Content="{Binding Path=Root.Attributes.Count, FallbackValue=Loading…}"/> 
      <Label Foreground="DimGray">Root.Element[Test]</Label> 
      <Label Content="{Binding Path=Root.Element[Test], FallbackValue=Loading…}"/> 
      <Label Foreground="DimGray">Root.Attribute[Test]</Label> 
      <Label Content="{Binding Path=Root.Attribute[Test], FallbackValue=Loading…}"/> 
     </StackPanel> 
     <StackPanel> 
      <Label Foreground="DimGray">Root.Elements</Label> 
      <ListBox ItemsSource="{Binding Root.Elements}"/> 
      <Label Foreground="DimGray">Root.Attributes</Label> 
      <ListBox ItemsSource="{Binding Root.Attributes}"/> 
     </StackPanel> 
     <StackPanel> 
      <TreeView ItemsSource="{Binding Root.Elements}"> 
       <TreeView.ItemTemplate> 
        <HierarchicalDataTemplate ItemsSource="{Binding Elements}"> 
         <Label Content="{Binding Name}"/> 
        </HierarchicalDataTemplate> 
       </TreeView.ItemTemplate> 
      </TreeView> 
     </StackPanel> 
    </UniformGrid> 
</Window> 

你知道爲什麼一切都能正確地綁定,除了Attributes嗎?任何幫助表示讚賞。我認爲這是有(也許)得到的東西做的一個事實,即ElementElementsXContainer繼承,但是這並不能解釋爲什麼XElements自己Attribute作品...

提前感謝! 亞倫

回答

1

上有XElement(唯一方法Attributes()不能在綁定直接使用)沒有財產Attributes,所以這並不奇怪綁定不起作用。

但也沒有財產Elements,那麼爲什麼這樣工作?這是因爲LINQ to XML對象具有專門用於WPF的特殊「動態屬性」,請參閱LINQ to XML Dynamic Properties on MSNDXElement上有一個動態屬性Elements,但沒有Attributes

雖然還有一件事我不明白:Elements動態屬性被記錄爲只能以elem.Elements[elementName]的形式工作。所以,我的代碼工作仍然令人驚訝。

如果你想知道任何解決方法,我想不出任何,除了調用Attributes()方法使用<ObjectDataProvider>

+0

謝謝你,這是我的問題的正確答案。再次感謝! – 2012-02-29 23:53:30

0

Svick對他的回答是正確的。您發現Elements的原因是XElement的自定義CustomTypeDescriptor(由XElement中TypeDescriptionProviderAttribute的存在確定)提供了一個名稱元素的自定義PropertyDescriptor,該元素返回IEnumerable <XElement>。如果在索引器的綁定路徑中遵循這一點,那麼返回的是XContainer.Elements(XName),否則將是XContainer.Elements()。爲什麼Attributes不起作用是因爲沒有提供這樣的動態屬性描述符。

下面的代碼以類似於動態Elements屬性的方式提供了這個缺失的功能(還有一個Nodes屬性)。

//Add this code in App start up  
TypeDescriptor.AddProvider(new XElementAdditionalDynamicPropertiesTypeDescriptionProvider(), 
typeof(XElement)); 

下面的類提供了功能,此代碼類似於元素的工作方式。

public class XDeferredAxis : IEnumerable<XAttribute> 
{ 
    internal XElement element; 
    private Func<XElement, XName, IEnumerable<XAttribute>> func; 
    private XName name; 

    public IEnumerator<XAttribute> GetEnumerator() 
    { 
     return this.func(this.element, this.name).GetEnumerator(); 
    } 

    public XDeferredAxis(Func<XElement, XName, IEnumerable<XAttribute>> func, XElement element, XName name) 
    { 
     if (func == null) 
     { 
      throw new ArgumentNullException("func"); 
     } 
     if (element == null) 
     { 
      throw new ArgumentNullException("element"); 
     } 
     this.func = func; 
     this.element = element; 
     this.name = name; 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return this.GetEnumerator(); 
    } 
} 

public class XElementNodesPropertyDescriptor : PropertyDescriptor 
{ 
    private XElement element; 
    private bool childRemoved; 
    public XElementNodesPropertyDescriptor() : base("Nodes", null) 
    { 

    } 
    public override void AddValueChanged(object component, EventHandler handler) 
    { 
     bool flag = base.GetValueChangedHandler(component) != null; 
     base.AddValueChanged(component, handler); 
     if (!flag) 
     { 
      XElement local = component as XElement; 
      if ((local != null) && (base.GetValueChangedHandler(component) != null)) 
      { 
       element = local; 
       local.Changing += new EventHandler<XObjectChangeEventArgs>(this.OnChanging); 
       local.Changed += new EventHandler<XObjectChangeEventArgs>(this.OnChanged); 
      } 
     } 
    } 

    private void OnChanging(object sender, XObjectChangeEventArgs e) 
    { 
     childRemoved = false; 
     if (e.ObjectChange == XObjectChange.Remove) 
     { 
      XObject senderNode = (XObject)sender; 
      if (senderNode.Parent == element) 
      { 
       childRemoved = true; 
      } 
     } 
    } 

    private void OnChanged(object sender, XObjectChangeEventArgs e) 
    { 
     XObject senderNode = (XObject)sender; 
     switch (e.ObjectChange) 
     { 
      case XObjectChange.Add: 
      case XObjectChange.Value: 
      case XObjectChange.Name: 
       if (senderNode.Parent == element) 
       { 
        this.OnValueChanged(element, EventArgs.Empty); 
       } 
       break; 
      case XObjectChange.Remove: 
       if (childRemoved) 
       { 
        this.OnValueChanged(element, EventArgs.Empty); 
       } 
       break; 

     } 
    } 
    public override void RemoveValueChanged(object component, EventHandler handler) 
    { 
     base.RemoveValueChanged(component, handler); 
     XElement local = component as XElement; 
     if ((local != null) && (base.GetValueChangedHandler(component) == null)) 
     { 
      local.Changed -= new EventHandler<XObjectChangeEventArgs>(this.OnChanged); 
     } 
    } 

    public override bool SupportsChangeEvents 
    { 
     get 
     { 
      return true; 
     } 
    } 
    public override Type ComponentType 
    { 
     get 
     { 
      return typeof(XElement); 
     } 
    } 

    public override bool IsReadOnly 
    { 
     get 
     { 
      return true; 
     } 
    } 

    public override Type PropertyType 
    { 
     get 
     { 
      return typeof(IEnumerable<XNode>); 
     } 
    } 

    public override bool CanResetValue(object component) 
    { 
     return false; 
    } 

    public override object GetValue(object component) 
    { 
     var nodes= (component as XElement).Nodes(); 
     return nodes; 
    } 

    public override void ResetValue(object component) 
    { 

    } 

    public override void SetValue(object component, object value) 
    { 

    } 

    public override bool ShouldSerializeValue(object component) 
    { 
     return false; 
    } 
} 

public class XElementAttributesPropertyDescriptor : PropertyDescriptor 
{ 
    private XDeferredAxis value; 
    private bool removalIsOwnAttribute; 
    public XElementAttributesPropertyDescriptor() : base("Attributes", null) { 

    } 
    public override void AddValueChanged(object component, EventHandler handler) 
    { 
     bool flag = base.GetValueChangedHandler(component) != null; 
     base.AddValueChanged(component, handler); 
     if (!flag) 
     { 
      XElement local = component as XElement; 
      if ((local != null) && (base.GetValueChangedHandler(component) != null)) 
      { 
       local.Changing += new EventHandler<XObjectChangeEventArgs>(this.OnChanging);    
       local.Changed += new EventHandler<XObjectChangeEventArgs>(this.OnChanged); 
      } 
     } 
    } 

    private void OnChanging(object sender, XObjectChangeEventArgs e) 
    { 
     removalIsOwnAttribute = false; 
     if (e.ObjectChange == XObjectChange.Remove) 
     { 
      var xAttribute = sender as XAttribute; 
      if (xAttribute != null && xAttribute.Parent == value.element) 
      { 
       removalIsOwnAttribute = true; 
      } 
     } 
    } 

    private void OnChanged(object sender, XObjectChangeEventArgs e) 
    { 
     var changeRequired = false; 
     var xAttribute = sender as XAttribute; 

     if (xAttribute != null) 
     { 
      switch (e.ObjectChange) 
      { 
       case XObjectChange.Name: 
       case XObjectChange.Add: 
        if (xAttribute.Parent == value.element) 
        { 
         changeRequired = true; 
        } 
        break; 
       case XObjectChange.Remove: 
        changeRequired = removalIsOwnAttribute; 
        break; 
      } 
      if (changeRequired) 
      { 
       this.OnValueChanged(value.element, EventArgs.Empty); 
      } 
     } 
    } 
    public override void RemoveValueChanged(object component, EventHandler handler) 
    { 
     base.RemoveValueChanged(component, handler); 
     XElement local = component as XElement; 
     if ((local != null) && (base.GetValueChangedHandler(component) == null)) 
     { 
      local.Changed -= new EventHandler<XObjectChangeEventArgs>(this.OnChanged); 
     } 
    } 

    public override bool SupportsChangeEvents 
    { 
     get 
     { 
      return true; 
     } 
    } 
    public override Type ComponentType 
    { 
     get 
     { 
      return typeof(XElement); 
     } 
    } 

    public override bool IsReadOnly 
    { 
     get 
     { 
      return true; 
     } 
    } 

    public override Type PropertyType 
    { 
     get 
     { 
      return typeof(IEnumerable<XAttribute>); 
     } 
    } 

    public override bool CanResetValue(object component) 
    { 
     return false; 
    } 

    public override object GetValue(object component) 
    { 
     return (object)(this.value = new XDeferredAxis((Func<XElement, XName, IEnumerable<XAttribute>>)((e, n) => 
     { 
      if (!(n != (XName)null)) 
       return e.Attributes(); 
      return e.Attributes(n); 
     }), component as XElement, (XName)null)); 
    } 

    public override void ResetValue(object component) 
    { 

    } 

    public override void SetValue(object component, object value) 
    { 

    } 

    public override bool ShouldSerializeValue(object component) 
    { 
     return false; 
    } 
} 

public class XElementAdditionalDynamicPropertiesTypeDescriptionProvider: TypeDescriptionProvider 
{ 
    public XElementAdditionalDynamicPropertiesTypeDescriptionProvider() : this(TypeDescriptor.GetProvider(typeof(XElement))) { } 

    protected XElementAdditionalDynamicPropertiesTypeDescriptionProvider(TypeDescriptionProvider parent) : base(parent) { } 

    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) 
    { 
     var baseTypeDescriptor= base.GetTypeDescriptor(objectType, instance); 
     return new XElementAdditionalDynamicPropertiesTypeDescriptor(baseTypeDescriptor); 
    } 
} 

public class XElementAdditionalDynamicPropertiesTypeDescriptor : CustomTypeDescriptor 
{ 
    public XElementAdditionalDynamicPropertiesTypeDescriptor(ICustomTypeDescriptor original) : base(original) { } 
    public override PropertyDescriptorCollection GetProperties() 
    { 
     return GetProperties(null); 
    } 
    public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) 
    { 
     PropertyDescriptorCollection descriptors = new PropertyDescriptorCollection(null); 
     if (attributes == null) 
     { 
      descriptors.Add(new XElementAttributesPropertyDescriptor()); 
      descriptors.Add(new XElementNodesPropertyDescriptor()); 
     } 


     foreach (PropertyDescriptor pd in base.GetProperties(attributes)) 
     { 
      descriptors.Add(pd); 
     } 
     return descriptors; 
    } 
}