2012-02-29 3 views
1

У меня есть XDocument как этот набор как DataContext из моих Window: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 

Теперь в моем интерфейсе я испытываю несколько различных связующих путей. Все они работают с Elements, Element, Attribute, но Attributes, похоже, не работает для меня. Я считаю это довольно странным, потому что Elements is IEnumerable<XElement> и Attributes is IEnumerable<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? Любая помощь приветствуется. Я думаю, что есть еще (возможно) что-то делать с фактом, что Element и Elements унаследованы от XContainer, но это не объясняет, почему XElements очень собственные Attribute работы ...

Заранее спасибо! Аарон

ответ

1

Там нет собственности Attributes на XElement (единственный метод Attributes(), которые не могут быть использованы непосредственно в связывании), так что не удивительно, связывание не работает.

Но также нет собственности Elements, так почему это работает? Это связано с тем, что объекты LINQ to XML имеют специальные «динамические свойства» специально для использования в WPF, см. LINQ to XML Dynamic Properties on MSND. Существует динамическое свойство Elements на XElement, но нет Attributes.

Есть еще одна вещь, которую я не понимаю: динамическое свойство Elements документировано для работы только в форме elem.Elements[elementName]. Поэтому мне все еще удивительно, что ваш код работает.

Если вы хотите узнать о каких-либо обходных методах, я не могу придумать ни одного, кроме вызова метода Attributes() с использованием <ObjectDataProvider>.

+0

Спасибо, это правильный ответ на мой вопрос. Еще раз спасибо! –

0

Svick верен своим ответом. Причина, по которой Elements работает, как вы обнаружили, заключается в том, что пользовательский CustomTypeDescriptor для XElement (определяемый наличием TypeDescriptionProviderAttribute на XElement) предоставляет собственный PropertyDescriptor с именем Elements, который возвращает IEnumerable <XElement>. Если это выполняется в пути привязки индексом, то возвращаемое является XContainer.Elements (XName), иначе будет XContainer.Elements(). Причина, по которой атрибуты не работают, заключается в том, что такой динамический атрибут свойства отсутствует.

Код ниже предоставляет эту недостающую функциональность (а также свойство Nodes) аналогично динамическому свойству Elements.

//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; 
    } 
} 
Смежные вопросы