Search code examples
wpfxamlstylesdatatemplatemenuitem

How do I dynamically bind and statically add MenuItems?


I'm binding the ItemsSource of my MenuItem to an ObservableCollection in my ViewModel. Here is my xaml:

<MenuItem Header="_View"
          ItemsSource="{Binding Windows}">
  <MenuItem.ItemContainerStyle>
    <Style>
      <Setter Property="MenuItem.Header"
              Value="{Binding Title}" />
    </Style>
  </MenuItem.ItemContainerStyle>
</MenuItem>

This part works great, but now I also want to add some static MenuItems to the same View MenuItem, separated with a separator. Something like this, even though I know this won't work because I can't set the items twice.

<MenuItem Header="_View"
          ItemsSource="{Binding Windows}">
  <MenuItem.ItemContainerStyle>
    <Style>
      <Setter Property="MenuItem.Header"
              Value="{Binding Title}" />
    </Style>
  </MenuItem.ItemContainerStyle>
  <Separator />
  <MenuItem Header="item 1" />
  <MenuItem Header="item 2" />
</MenuItem>

For now I have created a work around by adding another level to the MenuItem like this:

<MenuItem Header="_View">
  <MenuItem Header="Windows"
            ItemsSource="{Binding Windows}">
    <MenuItem.ItemContainerStyle>
      <Style>
        <Setter Property="MenuItem.Header"
                Value="{Binding Title}" />
      </Style>
    </MenuItem.ItemContainerStyle>
  </MenuItem>
  <MenuItem Header="Load Layout" />
  <MenuItem Header="Save Layout" />
</MenuItem>

This works fine, but I'd rather not have a sub menu if at all possible. Oh, and I'd also prefer to do this in xaml instead of code behind. Any ideas?


Solution

  • You can use a CompositeCollection to do this, you can combine different Collections and add static items in the xaml.

    Example:

    Xaml:

    <Window x:Class="WpfApplication8.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="233" Width="143" Name="UI">
        <Window.Resources>
            <CollectionViewSource Source="{Binding ElementName=UI, Path=Windows}" x:Key="YourMenuItems"/>
         </Window.Resources>
    
        <Grid DataContext="{Binding ElementName=UI}">
            <Menu Height="24" VerticalAlignment="Top">
                <MenuItem Header="_View" >
                    <MenuItem Header="Windows">
                        <MenuItem.ItemsSource>
                            <CompositeCollection>
                                <CollectionContainer Collection="{Binding Source={StaticResource YourMenuItems}}" />
                                <MenuItem Header="Menu Item 1" />
                                <MenuItem Header="Menu Item 2" />
                                <MenuItem Header="Menu Item 3" />
                            </CompositeCollection>
                        </MenuItem.ItemsSource>
                        <MenuItem.ItemContainerStyle>
                            <Style>
                                <Setter Property="MenuItem.Header" Value="{Binding Title}"/>
                            </Style>
                        </MenuItem.ItemContainerStyle>
                    </MenuItem>
                </MenuItem>
            </Menu>
        </Grid>
    </Window>
    

    Code

    public partial class MainWindow : Window
    {
        private ObservableCollection<MyObject> _windows = new ObservableCollection<MyObject>();
    
        public MainWindow()
        {
            InitializeComponent();
            Windows.Add(new MyObject { Title = "Collection Item 1" });
            Windows.Add(new MyObject { Title = "Collection Item 2" });
        }
    
        public ObservableCollection<MyObject> Windows
        {
            get { return _windows; }
            set { _windows = value; }
        }
    }
    
    public class MyObject
    {
        public string Title { get; set; }
    }
    

    Result:

    enter image description here