我怀疑是XAML编译器和/或
WPF错误,但我想确保我在这里没有做错(除了信任XAML编译器和/或WPF,即:)).
考虑以下XAML,以获得将重现问题的最小WPF程序:
<Window x:Class="TestxSharedMenuItem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<x:Array x:Key="menuItemValues1" Type="{x:Type s:String}">
<s:String>value #1</s:String>
<s:String>value #2</s:String>
<s:String>value #3</s:String>
</x:Array>
<MenuItem x:Key="menuItem1" x:Shared="False"
ItemsSource="{StaticResource menuItemValues1}"
Header="Shared menu item"/>
</Window.Resources>
<StackPanel>
<Menu HorizontalAlignment="Left" VerticalAlignment="Top">
<StaticResource ResourceKey="menuItem1"/>
<StaticResource ResourceKey="menuItem1"/>
</Menu>
</StackPanel>
</Window>
程序运行时,WPF会抛出异常:
XamlParseException
: ‘Add value to collection of type ‘System.Windows.Controls.ItemCollection’ threw an exception.’ Line number ’20’ and line position ’23’.
InnerException是:
InvalidOperationException
: Element already has a logical parent. It must be detached from the old parent before it is attached to a new one.
当然,这正是人们在多个地方尝试使用相同控件时所期望的.但是使用x:Shared =“False”属性应该导致每次检索时都返回MenuItem资源对象的新实例,从而避免该问题.
出于某种原因,在MenuItem元素之前使用x:Array元素会导致忽略x:Shared属性,从而导致在引用资源时共享单个MenuItem元素,从而导致异常.
其他观察:
>添加x:共享到x:Array元素没有帮助(它不应该,但我认为值得检查).
> MenuItem元素是否实际引用x:Array元素并不重要.仅仅存在x:Array元素就足以引起问题.
>上面的示例使用MenuItem,因为这是我遇到问题的地方,但问题也发生在其他控件类型上.
解决方法包括:
>将x:Array移动到不同的ResourceDictionary(例如,在App.xaml中).它的存在似乎只影响它所在的字典声明. (这样做可能会也可能不可行,具体取决于需要声明非共享控制资源的位置).
>将x:Array移动到字典声明中,而不是非共享控件资源的声明.当然,如果非共享控制资源需要x:Array元素,这没有帮助.
>使用非共享控件声明x:Array内联,而不是作为单独的资源.这解决了前两种解决方案中可能存在的依赖性问题,但当然会阻止x:Array与可能使用它的其他字典条目共享,并加剧围绕不可共享控制元素的问题(即不仅你有多个控制元素的副本,你得到它依赖的数组的多个副本).例如:
<MenuItem x:Key="menuItem1" x:Shared="False"
Header="Shared menu item">
<MenuItem.ItemsSource>
<x:Array Type="{x:Type s:String}">
<s:String>value #1</s:String>
<s:String>value #2</s:String>
<s:String>value #3</s:String>
</x:Array>
</MenuItem.ItemsSource>
</MenuItem>
>在代码隐藏中定义数组(例如,作为C#静态只读字段而不是在XAML中).例如.:
public static readonly string[] MenuItemValues = { "value #1", "value #2", "value #3" };
然后例如:
<MenuItem x:Key="menuItem1" x:Shared="False"
ItemsSource="{x:Static App.MenuItemValues}"
Header="Shared menu item"/>
>使用不同的标记声明集合.例如.使用ArrayList或继承List< T>的非泛型类(因为XAML和泛型不能很好地协同工作).
XAML对我来说很好看.我做错了什么吗?我是否违反了XAML编译器和/或WPF强加的某些“按设计”规则而我不知道的内容?
这不是我第一次遇到x:Array标记扩展导致问题的问题(参见XAML fails to compile, but without any error message, if user-defined object is first resource and followed immediately by x:Array resource和Spurious XAML errors in Visual Studio when declaring x:Array of x:Reference elements),但我想检查以确保我没有忽略这里的东西以及我写的XAML事实上应该像我期望的那样工作.
附录:
目前,由于缺少解释我已经错误地编写了XAML的答案,我将假设我认为这是XAML编译器和/或WPF中的错误.我已经在Microsoft Connect站点上提交了一个错误报告,以防其他任何人遇到此问题并希望加入:
最佳答案 写< ResourceDictionary>明确地进入< Window.Resources>:
<Window x:Class="TestxSharedMenuItem.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib"
Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary> <!-- modified here -->
<x:Array x:Key="menuItemValues1" Type="{x:Type s:String}">
<s:String>value #1</s:String>
<s:String>value #2</s:String>
<s:String>value #3</s:String>
</x:Array>
<MenuItem x:Key="menuItem1" x:Shared="False"
ItemsSource="{StaticResource menuItemValues1}"
Header="Shared menu item"/>
</ResourceDictionary> <!-- modified here -->
</Window.Resources>
<StackPanel>
<Menu HorizontalAlignment="Left" VerticalAlignment="Top">
<StaticResource ResourceKey="menuItem1"/>
<StaticResource ResourceKey="menuItem1"/>
</Menu>
</StackPanel>
</Window>
我有一个非常相似的问题(实际上,完全相同,只有UserControl).当我尝试上述解决方法时,我非常绝望:),但它确实有效.
我现在用你的示例代码尝试了它,并使用了明确的< ResourceDictionary>它适用于我,没有它它没有.