【问题标题】:XDocument binding Elements and AttributesXDocument 绑定元素和属性
【发布时间】:2012-02-29 16:13:04
【问题描述】:

我有一个像这样的XDocument 设置为我的WindowDataContext

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

现在我在前端测试了几个不同的绑定路径。它们都适用于ElementsElementAttribute,但Attributes 似乎对我不起作用。我认为这很奇怪,因为ElementsIEnumerable&lt;XElement&gt;AttributesIEnumerable&lt;XAttribute&gt;——完全一样的集合和所有东西。

<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 之外的所有东西都能正确绑定吗?任何帮助表示赞赏。我认为这(可能)与一个事实有关,即ElementElements 是从XContainer 继承的,但这并不能解释为什么XElements 非常自己的Attribute 有效...

提前致谢! 亚伦

【问题讨论】:

    标签: wpf linq xaml binding linq-to-xml


    【解决方案1】:

    XElement 上没有属性 Attributes(只有方法 Attributes() 不能直接用于绑定),所以绑定不起作用也就不足为奇了。

    但是也没有属性Elements,那为什么会这样呢?这是因为 LINQ to XML 对象具有专门用于 WPF 的特殊“动态属性”,请参阅LINQ to XML Dynamic PropertiesXElement上有动态属性Elements,但没有Attributes

    还有一件事我不明白:Elements 动态属性被记录为只能以 elem.Elements[elementName] 的形式工作。因此,您的代码有效,我仍然感到惊讶。

    如果您想了解任何解决方法,我想不出任何解决方法,除了使用&lt;ObjectDataProvider&gt; 调用Attributes() 方法。

    【讨论】:

    • 谢谢,这是我问题的正确答案。再次感谢!
    • 感谢您的解释,因为我遇到了同样的情况,显然是在最初的帖子之后。 7 年后,这种奇怪仍然如上所述,我在下面使用 IValueConverter 添加了我的解决方法
    【解决方案2】:

    Svick 的回答是正确的。 Elements 之所以像您发现的那样工作,是因为 XElement 的自定义 CustomTypeDescriptor(由 XElement 上的 TypeDescriptionProviderAttribute 确定)提供了一个名为 Elements 的自定义 PropertyDescriptor,它返回一个 IEnumerable。如果索引器在绑定路径中跟随它,则返回的是 XContainer.Elements(XName),否则返回的是 XContainer.Elements()。 Attributes 不起作用的原因是没有提供这样的动态属性描述符。

    下面的代码以与动态 Elements 属性类似的方式提供了这个缺失的功能(以及 Nodes 属性)。

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

    以下类提供功能,此代码类似于 Elements 的工作方式。

    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;
        }
    }
    

    【讨论】:

      【解决方案3】:

      解决此问题的一种快速可移植解决方法是通过返回其属性结果的转换器运行 XElement。然后你可以简单地绑定到元素。

      我还过滤掉下面的命名空间,很容易删除。

      using System;
      using System.Collections.Generic;
      using System.Globalization;
      using System.Linq;
      using System.Text;
      using System.Threading.Tasks;
      using System.Windows.Data;
      using System.Xml.Linq;
      
      namespace FSW.Core.Utility
      {
          [ValueConversion(typeof(XElement), typeof(IEnumerable<XAttribute>))]
          public class XElementToXAttributesConverter : IValueConverter
          {
      
              public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
              {
                  var element = value as XElement;
                  return element?.Attributes().Where(x=>x.Name.LocalName != "xmlns");
              }
      
              public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
              {
                  throw new NotSupportedException();
              }
      
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2015-08-30
        • 1970-01-01
        • 2013-05-15
        • 1970-01-01
        • 2013-08-20
        • 1970-01-01
        • 2017-07-20
        • 1970-01-01
        • 2017-11-09
        相关资源
        最近更新 更多