4

I have TreeView which contains different item types. Item Styles are defined over a custom ItemContainerStyleSelector property.

My styles are all sharing a base style and only item specific stuff is defined in each style. It looks like this:

<Style x:Key="BaseStyle" TargetType="{x:Type TreeViewItem}">
...
</Style>

<Style x:Key ="SomeSpecificStyle" TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource BaseStyle}">
   <Setter Property="ContextMenu" Value="{StaticResource NodeContextMenu}"/>
   ...  
</Style>

<Style x:Key ="SomeSpecificStyle" TargetType="{x:Type TreeViewItem}" BasedOn="{StaticResource BaseStyle}">
   <Setter Property="ContextMenu" Value="{StaticResource AnotherNodeContextMenu}"/>
   ...  
</Style>

The context menu is defined like this

<ContextMenu x:Key="NodeContextMenu">
  <MenuItem Header="Select Views" Command="{Binding Path=OpenViewsCommand}" />
  ...other specific entries
  <MenuItem Header="Remove" Command="{Binding Path=DocumentRemoveCommand}" />
  ...other entries common for all menus
</ContextMenu>

Another context menu also should contain those common items like remove. These need to be replicated by copy paste every time the command properties, etc are changing. A hell for maintainability. Is there a way to define a context menu which contains the common items, and then "derive" specific context menus?

Edit: I found a solution with the hints from this thread: I define a Collection with the common items, and use a composite collection when defining a menu to include both new items and the common items collection

<CompositeCollection x:Key="CommonItems"> 
  <MenuItem Header="Remove" Command="{Binding Path=DocumentRemoveCommand}">
  ....Other common stuff
</CompositeCollection>

<ContextMenu x:Key="NodeContextMenu">
  <ContextMenu.ItemsSource>
    <CompositeCollection>
      <MenuItem Header="Select Views" Command="{Binding Path=OpenViewsCommand}" />
      <CollectionContainer Collection="{StaticResource CommonItems}" />
    </CompositeCollection>
  </ContextMenu.ItemsSource>
</ContextMenu>
Petr Osipov
  • 621
  • 6
  • 16

1 Answers1

5

You can declare the items as resource and reference them:

<Some.Resources>
    <MenuItem x:Key="mi_SelectViews" x:Shared="false"
              Header="Select Views" Command="{Binding Path=OpenViewsCommand}" />
    <MenuItem x:Key="mi_Remove" x:Shared="false"
              Header="Remove" Command="{Binding Path=DocumentRemoveCommand}" />
</Some.Resources>
<ContextMenu x:Key="NodeContextMenu">
  <StaticResource ResourceKey="mi_SelectViews" />
  ...other specific entries
  <StaticResource ResourceKey="mi_Remove" />
  ...other entries common for all menus
</ContextMenu>

(The x:Shared is important)


Another possibility would be to generate MenuItems via a object model approach, you just bind the ItemsSource to some list of objects which model the functionality of a MenuItem (i.e. properties for child items, header and command), then you can create one Remove model which can be part of multiple lists.

H.B.
  • 166,899
  • 29
  • 327
  • 400
  • Well, this is better then the original solution, but still can cause troubles as replication is necessary in case new items are added or entries are removed.. – Petr Osipov Nov 22 '11 at 13:32
  • @PetrOsipov: How so? You only have multiple references and you will always have at least that. – H.B. Nov 22 '11 at 13:38
  • Well, I would have loved something like: BaseMenu containing a dozen of common items, written once and only once. And each child menu referring to a base menu, taking over its items, and adding only node specific stuff. – Petr Osipov Nov 22 '11 at 13:41
  • @PetrOsipov: Well, i don't think that is easily possible, especially since you apparently want to add items in-between existing ones. – H.B. Nov 22 '11 at 13:43
  • Well, grouping the specific items at the top, and common items at the bottom of the menu would be ok... – Petr Osipov Nov 22 '11 at 13:48
  • @PetrOsipov: Then i would recommend the object model approach, you can use [`CompositeCollections`](http://msdn.microsoft.com/en-us/library/system.windows.data.compositecollection.aspx) to unite the common list with the new items for example. – H.B. Nov 22 '11 at 13:56