Search code examples
c#wpfribbonhierarchicaldatatemplate

WPF Ribbon Binding to ViewModel HierarchicalDataTemplate Strange behaviour


My plan is to build a large WPF application containing multiple user controls as modules which are loaded at need. These modules provide a own list of menu items to be displayed in the main window.

The menu provided is a list of MenuTab objects.

public class MenuTab
{
    private string _label;
    private List<MenuGroup> _menuGroups = new List<MenuGroup>();

    public string Label
    {
        get { return _label; }
        set { _label = value; }
    }

    public List<MenuGroup> MenuGroups
    {
        get { return _menuGroups; }
    }

    public MenuTab(string label)
    {
        Label = label;
    }
}

public class MenuGroup
{
    private string _label;
    private string _description;

    private List<MenuEntry> _menuEntries = new List<MenuEntry>();

    public string Label
    {
        get { return _label; }
        set { _label = value; }
    }

    public string Description
    {
        get { return _description; }
        set { _description = value; }
    }

    public List<MenuEntry> MenuEntries
    {
        get { return _menuEntries; }
    }

    public MenuGroup(string label)
    {
        Label = label;
    }
}

public class MenuEntry
{
    private string _label;
    private BitmapSource _largeImage;
    private BitmapSource _smallImage;
    private ICommand _command;

    public string Label
    {
        get { return _label; }
        set { _label = value; }
    }

    public BitmapSource LargeImage
    {
        get { return _largeImage; }
        set { _largeImage = value; }
    }


    public BitmapSource SmallImage
    {
        get { return _smallImage; }
        set { _smallImage = value; }
    }

    public ICommand Command
    {
        get { return _command; }
        set { _command = value; }
    }

    public MenuEntry(string label)
    {
        Label = label;
    }
}

I found multiple hints in the internet how to build the menu to add additional static menus and so on.

Here how I build the menu and the HierarchicalDataTemplate which basically result in the correct number of tabs, groups and items. Even command binding is working fine.

<RibbonWindow.Resources>
    <Style x:Key="ModuleGroup" TargetType="RibbonGroup">
        <Setter Property="Background" Value="AntiqueWhite" />
        <Setter Property="Foreground" Value="Green" />
    </Style>

    <HierarchicalDataTemplate DataType="{x:Type CommonModel:MenuTab}" ItemsSource="{Binding Path=MenuGroups}">
        <RibbonTab Header="{Binding Path=Label}" Background="Orange" />
    </HierarchicalDataTemplate>

    <HierarchicalDataTemplate DataType="{x:Type CommonModel:MenuGroup}" ItemsSource="{Binding Path=MenuEntries}">
        <RibbonGroup Header="{Binding Path=Label}" Style="{StaticResource ModuleGroup}" />
    </HierarchicalDataTemplate>

    <DataTemplate DataType="{x:Type CommonModel:MenuEntry}">
        <RibbonButton Label="{Binding Path=Label}" LargeImageSource="{Binding Path=LargeImage}" SmallImageSource="{Binding Path=SmallImage}" Command="{Binding Path=Command}" />
    </DataTemplate>

    <CollectionViewSource x:Key="ModuleMenuTabs" Source="{Binding ModuleMenu}"/>
</RibbonWindow.Resources>

And then between my static tabs:

<CollectionContainer Collection="{Binding Source={StaticResource ModuleMenuTabs}}"/>

But the tab header and background is not shown as excepted. And also the background of the generated groups is not as excepted (as example I added some background to one of the static groups which works fine.)

enter image description here

Can anybody give me a hint how to get the ribbons generated correctly?


Solution

  • After there were now responses I did further research and tests by myself. I found the solution working for me - maybe this helps other.

    My resources section in the XAML looks know like that:

    <RibbonWindow.Resources>
    <DataTemplate x:Key="buttonTempl">
        <RibbonButton Label="{Binding Path=Label}" LargeImageSource="{Binding Path=LargeImage}" SmallImageSource="{Binding Path=SmallImage}" Command="{Binding Path=Command}" />
    </DataTemplate>
    
    <Style TargetType="RibbonGroup" x:Key="groupStyle">
        <Setter Property="Header" Value="{Binding Label}"/>
        <Setter Property="ItemsSource" Value="{Binding MenuEntries}"/>
        <Setter Property="ItemTemplate" Value="{StaticResource buttonTempl}"/>
    </Style>
    <Style TargetType="RibbonTab" x:Key="tabStyle">
        <Setter Property="Header" Value="{Binding Label}"/>
        <Setter Property="ItemsSource" Value="{Binding MenuGroups}"/>
        <Setter Property="ItemContainerStyle" Value="{StaticResource groupStyle}"/>
    </Style>
    
    <CollectionViewSource x:Key="StaticMenuSrc" Source="{Binding StaticMenu}"/>
    <CollectionViewSource x:Key="ModuleMenuSrc" Source="{Binding ModuleMenu}"/>
    

    And the ribbon is defined as:

    <Ribbon DockPanel.Dock="Top" ItemContainerStyle="{StaticResource tabStyle}" >
            <Ribbon.ItemsSource>
                <CompositeCollection>
                    <CollectionContainer Collection="{Binding Source={StaticResource StaticMenuSrc}}" />
                    <CollectionContainer Collection="{Binding Source={StaticResource ModuleMenuSrc}}" />
                </CompositeCollection>
            </Ribbon.ItemsSource>
        </Ribbon>
    

    This way I can provide a static application menu and a module-specific menu using MVVM.