我有XDocument
这样的DataContext
设置为Window
的{{1}}:
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
是IEnumerable<XElement>
而Attributes
是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
有效......
提前致谢! 亚伦
答案 0 :(得分: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>
方法外,我无法想到任何变通方法。
答案 1 :(得分:0)
Svick对他的回答是正确的。 Elements之所以如你所知,是因为XElement的自定义CustomTypeDescriptor(由XElement上TypeDescriptionProviderAttribute的存在决定)提供了一个名为Elements的自定义PropertyDescriptor,它返回一个IEnumerable&lt; XElement&gt;。如果索引器在绑定路径中遵循了这一点,那么返回的是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;
}
}
答案 2 :(得分:0)
此问题的快速可移植解决方案是通过返回其Attributes结果的转换器运行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();
}
}
}