3

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:

  1. Adding x:Shared to the x:Array element doesn't help (it shouldn't, but I figured it was worth checking).
  2. It does not matter whether the MenuItem element is actually referencing the x:Array element or not. The mere presence of the x:Array element is sufficient to cause the problem.
  3. 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:

  1. Moving the x:Array to a different ResourceDictionary (e.g. in App.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).
  2. 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 the x:Array element, this doesn't help.
  3. 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 the x: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>
  1. 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"/>
  1. Declaring the collection using different markup. E.g. use ArrayList or a non-generic class that inherits List<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:

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

Community
  • 1
  • 1
Peter Duniho
  • 68,759
  • 7
  • 102
  • 136
  • Hi Peter, Another workaround is to use ArrayList instead of Array. See http://stackoverflow.com/questions/13630645/how-do-i-reuse-item-children-via-a-style-in-xaml – Taterhead Mar 05 '16 at 10:33
  • Looks like a bug to me. If replacing `x:Array` with a different collection type (something that inherits `List` for instance) solves the issue, then I'd definitely call that a bug. – Lucas Trzesniewski Mar 05 '16 at 13:38
  • @Tater: thanks...yes, `ArrayList` or a class inheriting `List` (as Lucas suggests) would also be a useful workaround. (btw, I undid your tags edit, because while there are secondary connections to those tags, this question isn't actually _about_ reusability or the `MenuItem` object...indeed, the issue happens whatever type of element one is trying to set `x:Shared="False"` on, not just `MenuItem` elements). – Peter Duniho Mar 06 '16 at 06:34
  • 1
    @Lucas: thanks...I'm inclined to agree with you, and yes using a different collection type works fine. I'll give the question another day, just in case someone else has additional input, and then will go ahead and post the bug to Connect (I'll include a link in the question for reference). – Peter Duniho Mar 06 '16 at 06:38
  • @Peter - no hard feelings about the tag removals. But I might have something to point that this might be a `MenuItem` issue after all and rule out the `Shared` property. In my code where I replaced `Array` with `ArrayList`, I today removed the `x:Shared="False"` and reran. This resulted in your original `XMLParseException` with inner 'Element already has a logical parent. ..' . So perhaps using a `StaticResource` more than once in a `Menu` definition is the issue? thoughts? – Taterhead Mar 06 '16 at 10:17
  • @Lucas, using the `ArrayList` works, but today I took a closer look. For each of the menus, the first menu has items, but the second Menu is blank. – Taterhead Mar 06 '16 at 10:21
  • @Taterhead I just tried it with `ArrayList`, and it [works for me](https://i.imgur.com/VQYI1Tw.png) - both menus have items. – Lucas Trzesniewski Mar 06 '16 at 10:50
  • @Tater: I think you might have something funny going on in your test. I'm able to reproduce the problem using only a single `TextBlock` resource, used twice in the layout, and an `x:Array` preceding it in the resources (which isn't even referenced anywhere else in the XAML). I.e. it really isn't just a `MenuItem` thing. And when I use `ArrayList` (or any other work-around), I get two completely-populated menus. Works fine. – Peter Duniho Mar 06 '16 at 17:02
  • @Lucas - I just retested and I get menu items the first time I click; but when I mouse back and forth, one of them the looses their entries and the menu turns empty. Ill retest at a different machine tomorrow. – Taterhead Mar 06 '16 at 19:58
  • @Lucas, I was able to reproduce this on other machines. Ill post a question about it. – Taterhead Mar 07 '16 at 20:36
  • @Taterhead hmm ok, you may want to post a link here in the comments since it's related. – Lucas Trzesniewski Mar 07 '16 at 20:45
  • @Tater: assuming your test uses code that is just like the code I've used here, when you post your question you should make sure to include all relevant configuration information: versions for OS, .NET, C#, etc. Also, be sure to state clearly whether in your test across different machines you actually _compiled_ the code using different machines, or if you simply ran the same executable on different machines. I.e. describe the experiment as precisely as you can. Thanks! – Peter Duniho Mar 07 '16 at 20:52
  • Will do, @Peter. Stay tuned. – Taterhead Mar 07 '16 at 20:57
  • 1
    OK Peter and @Lucas, I created the question here: http://stackoverflow.com/questions/35878836/wpf-xaml-defined-menuitem-reuse-starts-working-then-disappears – Taterhead Mar 08 '16 at 22:01

1 Answers1

2

Write <ResourceDictionary> explicitely into <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>

I had a very similar issue (actually, quite exactly the same, only with UserControl). I was desperate when I tried the above workaround :), but it worked.
I've just tried it out with your example code now, with explicit <ResourceDictionary> it works by me, without it it doesn't.

nvirth
  • 1,599
  • 1
  • 13
  • 21
  • While this post doesn't really answer the question I asked, I think it's about as close as I'm going to get. The behavior is likely a bug (Microsoft never commented on it, but it seems to be), and this work-around is probably the best way to get the code to work. So I'm going to go ahead and accept this (finally), to avoid cluttering up "unanswered questions" searches and to credit the author with being the only respondent with a useful answer (or any, for that matter). – Peter Duniho May 16 '20 at 16:37