简体   繁体   中英

MVVM Ribbon Application Menu Implementation (WPF 4.5 Ribbon)

I am trying to Implement the Application Menu of a Ribbon Control in WPF 4.5 using the MVVM Pattern. Therefore I created an ApplicationMenuViewModel which contains an observable collection of AbstractApplicationMenuItemViewModel which is an abstract class used to unify the classes ...SplitItem and ...Item. The SplitItem shall be able to contain other RibbonApplicationMenuSplitItems or just plain RibbonApplicationMenuItems to an arbitrary level of hierarchy. Therefore it contains itself an ObservableCollection which may contain ...SplitItems or plain ...Items. The ApplicationMenu itself is provided through a Factory Class due to other Architectural Reasons. The problem I am experiencing now is that nothing is shown in the ApplicationMenu at all. The ViewModel is instantiated correctly so I think that the problem is mainly due to my abstraction of the two different items that may be contained inside ApplicationMenuSplitItemViewModel and ApplicationMenuViewModel. So the question is what is the most flexible, maintainable and easy way to implement the templating and/or styling to get the RibbonApplicationMenu to show up? At the moment the following template is applied:

<!--Ribbon Application Menu Styling and Templating-->
<DataTemplate DataType="{x:Type rvmmnu:ApplicationMenuViewModel}">
    <RibbonApplicationMenu ItemsSource="{Binding Children}"/>
</DataTemplate>

<DataTemplate DataType="{x:Type rvmmnu:ApplicationMenuItemViewModel}">
    <RibbonApplicationMenuItem Header="{Binding DisplayName}" 
                               ImageSource="{Binding ImageSource}" 
                               Command="{Binding Command}"/>
</DataTemplate>

<DataTemplate DataType="{x:Type rvmmnu:ApplicationMenuSplitItemViewModel}">
    <RibbonApplicationSplitMenuItem ItemsSource="{Binding Children}"
                                    Header="{Binding DisplayName}"/>
</DataTemplate>

The Ribbon itself is instantiated with this XAML:

    <Ribbon DockPanel.Dock="Top" ItemContainerStyle="{StaticResource    ResourceKey=ribbonTabStyle}" 
            ItemsSource="{Binding Path=Ribbon.Tabs}" 
            ApplicationMenu="{Binding Path=Ribbon.ApplicationMenu}"/>

With this configuration the Application Menu shows nothing at the moment. I suppose the problem is steming from the fact that I am abstracting away the difference between the two hierarchical items contained in the menu. I was thinking about using a custom template selector for RibbonApplicationMenu and RibbonApplicationMenuSplitItem. Another approach I was thinking about was to divide the different object types (...SplitItem and ...Item) into two different collections and use a multi binding for the ItemsSource Property. But I am not really satisfied with these approaches because I think that there is an easier way to achieve what I want.

I finally implemented the whole thing by using a Template and a Style Selector checking the type of the object provided to the ribbon and returning the templates provided via the XAML instantiation. The XAML for this is now the follow:

    <!--Ribbon Application Menu Styling and Templating-->

<Style x:Key="appMnuItemStyle">
    <Setter Property="RibbonApplicationMenuItem.Header" Value="{Binding DisplayName}"/>
    <Setter Property="RibbonApplicationMenuItem.ImageSource" Value="{Binding ImageSource}"/>
    <Setter Property="RibbonApplicationMenuItem.Command" Value="{Binding Command}"/>
</Style>

<Style x:Key="appMnuSplitItemStyle">
    <Setter Property="RibbonApplicationSplitMenuItem.ItemsSource" Value="{Binding Children}"/>
    <Setter Property="RibbonApplicationSplitMenuItem.Header" Value="{Binding DisplayName}"/>
    <Setter Property="RibbonApplicationSplitMenuItem.ItemTemplateSelector" Value="{DynamicResource appMnuItemTemplateSelector}"/>
    <Setter Property="RibbonApplicationSplitMenuItem.ImageSource" Value="{Binding Image}"/>
</Style>

<HierarchicalDataTemplate x:Key="appMnuSplitItemTemplate" DataType="{x:Type rvmmnu:ApplicationMenuSplitItemViewModel}"
                          ItemsSource="{Binding Children}"
                          ItemContainerStyle="{StaticResource ResourceKey=appMnuSplitItemStyle}"/>


<DataTemplate x:Key="appMnuItemTemplate"/>


<rvmmnu:ApplicationMenuItemTemplateSelector x:Key="appMnuItemTemplateSelector" 
                                            PlainItemTemplate="{StaticResource ResourceKey=appMnuItemTemplate}" 
                                            SplitItemTemplate="{StaticResource ResourceKey=appMnuSplitItemTemplate}"/>

<rvmmnu:ApplicationMenuStyleSelector x:Key="appMnuItemStyleSelector"
                                     PlainItemStyle="{StaticResource ResourceKey=appMnuItemStyle}" 
                                     SplitItemStyle="{StaticResource ResourceKey=appMnuSplitItemStyle}"/>

The Ribbon is now instantiated like this:

        <Ribbon DockPanel.Dock="Top" ItemContainerStyle="{StaticResource ResourceKey=ribbonTabStyle}" 
            ItemsSource="{Binding Path=Tabs}" DataContext="{Binding Ribbon}">
        <Ribbon.ApplicationMenu>
            <RibbonApplicationMenu ItemsSource="{Binding ApplicationMenu.Children}"
                                   ItemTemplateSelector="{StaticResource ResourceKey=appMnuItemTemplateSelector}"
                                   ItemContainerStyleSelector="{StaticResource appMnuItemStyleSelector}"/>
        </Ribbon.ApplicationMenu>
    </Ribbon>

I am not really sure if this is the best solution. But it works for me.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM