2012-02-29 59 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; 
    } 
}