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

时间:2016-03-05 01:21:30

标签: c# wpf xaml

我怀疑是一个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:&#39;将值添加到&#39; System.Windows.Controls.ItemCollection&#39;抛出异常。&#39;行号&#39; 20&#39;和行位置&#39; 23&#39;。

InnerException是:

  

InvalidOperationException:元素已有逻辑父级。在将旧父级附加到新父级之前,必须将其与旧父级分离。

当然,这正是人们期望在多个地方尝试使用相同控件的情况。但是使用x:Shared="False"属性导致每次检索MenuItem资源对象的新实例时都会返回,从而避免出现此问题。

出于某种原因,在x:Array元素之前使用MenuItem元素会导致x:Shared属性被忽略,导致在资源中共享一个MenuItem元素被引用,因此导致异常。

其他观察:

  1. x:Shared添加到x:Array元素没有帮助(它不应该,但我认为值得检查)。
  2. MenuItem元素是否实际引用x:Array元素并不重要。仅存在x:Array元素就足以导致问题。
  3. 上面的示例使用MenuItem,因为这是我遇到问题的地方,但问题也出现在其他控件类型中。
  4. 解决方法包括:

    1. x:Array移至其他ResourceDictionary(例如App.xaml)。它的存在似乎只影响它所找到的字典声明。 (这样做可能也可能不可行,具体取决于需要声明非共享控制资源的位置。)
    2. 移动x:Array以后出现在字典声明中,而不是非共享控制资源的声明。当然,如果非共享控制资源需要x:Array元素,那么这无济于事。
    3. 声明x:Array与非共享控件内联,而不是作为单独的资源。这解决了前两种解决方案中可能存在的依赖性问题,但当然会阻止x:Array与可能使用它的其他字典条目共享,并加剧围绕非可共享控制元素的问题(即你不仅拥有控制元素的多个副本,而且还获得了它所依赖的数组的多个副本。 E.g:
    4. <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>
      
      1. 在代码隐藏中定义数组(例如,作为C#static readonly字段而不是在XAML中)。 E.g:
      2. 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"/>
        
        1. 使用不同的标记声明集合。例如。使用ArrayList或继承List<T>的非泛型类(因为XAML和泛型不能很好地协同工作)。

        2. XAML对我来说很好看。我做错了什么吗?我是否违反了一些&#34;设计&#34;规则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

1 个答案:

答案 0 :(得分:1)

<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>它由我工作,没有它它没有。