考虑以下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站点上提交了一个错误报告,以防其他任何人遇到此问题并希望加入:
解决方法
<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>它适用于我,没有它它没有.