I suspect a XAML compiler and/or WPF bug, but I want to make sure I haven't done something wrong here (other than trusting the XAML compiler and/or WPF, that is :) ).
Consider the following XAML for a minimal WPF program that will reproduce the problem:
<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>
When the program is run, WPF throws an exception:
XamlParseException
: 'Add value to collection of type 'System.Windows.Controls.ItemCollection' threw an exception.' Line number '20' and line position '23'.
The InnerException
is:
InvalidOperationException
: Element already has a logical parent. It must be detached from the old parent before it is attached to a new one.
This is, of course, exactly what one would expect to see if one were trying to use the same control in multiple places. But using the x:Shared="False"
attribute should be causing a new instance of the MenuItem
resource object to be returned each time it's retrieved, avoiding that problem.
For some reason, having the x:Array
element before the MenuItem
element causes the x:Shared
attribute to be ignored, resulting in a single MenuItem
element being shared when the resource is referenced, and so causing the exception.
Other observations:
- Adding
x:Shared
to thex:Array
element doesn't help (it shouldn't, but I figured it was worth checking). - It does not matter whether the
MenuItem
element is actually referencing thex:Array
element or not. The mere presence of thex:Array
element is sufficient to cause the problem. - The example above uses
MenuItem
because this is where I ran across the problem, but the issue happens for other control types too.
Work-arounds include:
- Moving the
x:Array
to a differentResourceDictionary
(e.g. inApp.xaml
). Its presence appears to only affect the dictionary declaration in which it's found. (Doing so may or may not be feasible, depending on where the non-shared control resource needs to be declared). - Moving the
x:Array
to appear later in the dictionary declaration than the non-shared control resource's declaration. Of course, if the non-shared control resource requires thex:Array
element, this doesn't help. - Declaring the
x:Array
inline with the non-shared control rather than as a separate resource. This solves the dependency issues that might exist in the previous two work-arounds, but of course prevents sharing of thex:Array
with other dictionary entries that could use it, and exacerbates the issues surrounding the non-sharable control element (i.e. not only do you have multiple copies of the control element, you get multiple copies of the array it's dependent on as well). E.g:
<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>
- Defining the array in code-behind (e.g. as a C#
static readonly
field instead of in XAML). E.g.:
public static readonly string[] MenuItemValues = { "value #1", "value #2", "value #3" };
Then for example:
<MenuItem x:Key="menuItem1" x:Shared="False"
ItemsSource="{x:Static App.MenuItemValues}"
Header="Shared menu item"/>
- Declaring the collection using different markup. E.g. use
ArrayList
or a non-generic class that inheritsList<T>
(since XAML and generics don't play nicely together).
The XAML looks fine to me. Have I done something wrong? Did I violate some "by design" rule the XAML compiler and/or WPF imposes and which I'm not aware of?
This isn't the first time I've run into problems with the x:Array
markup extension causing problems (see XAML fails to compile, but without any error message, if user-defined object is first resource and followed immediately by x:Array resource and Spurious XAML errors in Visual Studio when declaring x:Array of x:Reference elements), but I want to check to make sure I'm not overlooking something here and that the XAML I wrote should in fact work as I expect.
Addendum:
For now, lacking an answer explaining that I've written the XAML incorrectly, I'm going to assume that I'm correct in my belief that this is a bug in the XAML compiler and/or WPF. I have submitted a bug report on the Microsoft Connect site, in case anyone else runs into this and would like to chime in: