c# – 为什么x:Array元素会导致忽略它后面的任何x:Shared属性?

我怀疑是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 resourceSpurious XAML errors in Visual Studio when declaring x:Array of x:Reference elements),但我想检查以确保我没有忽略这里的东西以及我写的XAML事实上应该像我期望的那样工作.

附录:

目前,由于缺少解释我已经错误地编写了XAML的答案,我将假设我认为这是XAML编译器和/或WPF中的错误.我已经在Microsoft Connect站点上提交了一个错误报告,以防其他任何人遇到此问题并希望加入:

https://connect.microsoft.com/VisualStudio/feedback/details/2443920/declaring-x-array-element-in-resources-causes-x-shared-attribute-on-later-element-to-be-ignored

最佳答案 写< 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>它适用于我,没有它它没有.

点赞