Contribute menu item to shell from module

Topics: Prism v2 - WPF 3.5
Jun 24, 2009 at 8:07 AM

I would like to place several menu items from different moduls in the shell's main menu at predefined places. I do it as follows:

<Menu>
<MenuItem Header="File">
<MenuItem Header="{x:Static properties:Gui.Menu_File_Exit}"
Command="{Binding Path=ExitCommand}"/>
<ContentControl cal:RegionManager.RegionName="{x:Static inf:RegionNames.MenuFileOnlineRegion}"/>
</MenuItem>
</Menu>
And in the module I add a UserControl to the above defined region.

 

_regionManager.RegisterViewWithRegion(RegionNames.MenuFileOnlineRegion,
() => _onlineMenuPresenter.View);

And this UserControl contains just a single MenuItem:
<UserControl x:Class="Views.Menus.OnlineMenuView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:properties="clr-namespace:Properties">
<MenuItem Header="{x:Static properties:Gui.Menu_File_Online}"
IsChecked="{Binding Path=IsConnected, Mode=OneWay}"
Command="{Binding Path=OnlineCommand}"/>
</UserControl>

This all works as expected, but the contributed MenuItem looks different from the the ones directly defined in the shell. That because the ContentControl gets wrapped inside a additional MenuItem, for whatever reason. How can I avoid this additional MenuItem? Are there other, better ways to achieve the same?

Thanks for help,

Dominik

 

 

Jun 25, 2009 at 6:26 PM

Hi Dominik,

I'm new to CAB. But I would try to put a Interface(IMenuCommand) in a Infrastructure Assembly.

Create a MenuCommandViewModel in the Module, wich Implements this Interface.

In the Shell I would create a DataTemplate rendering this Interface.

_regionManager.RegisterViewWithRegion(RegionNames.MenuFileOnlineRegion,
                () => MenuCommandViewModel);

But perhaps I'm overlooking something.

Gruß Timo

Jun 25, 2009 at 7:39 PM

Hi Dominik,

 

I have been able to reproduce the scenario you mention, but it seems to be a WPF problem and not related with prism (the same happens if you add the UserControl to the MenuItem Directly):

<Menu>
    <MenuItem Header="File">                            
                <MenuItem Header="{x:Static properties:Gui.Menu_File_Exit}" Command="{Binding Path=ExitCommand}"/>
        <Controls:MyUserControlWithOneMenuItem/>
    </MenuItem>
</Menu>

 

From the Prism point of View, you don't need to add a ContentControl to hold contributed menu items. As the MenuItem inherits from ItemsControl you can directly do as follows:

<Menu>
    <MenuItem Header="File" cal:RegionManager.RegionName="{x:Static inf:RegionNames.MenuFileOnlineRegion}">                            
        <MenuItem Header="{x:Static properties:Gui.Menu_File_Exit}" Command="{Binding Path=ExitCommand}"/>
    </MenuItem>
</Menu>

 

Now, the problem is because of the WPF Content Model. When you add an item to a Menu (or MenuItem) that does not inherit from MenuItem, a MenuItem is implicitly created.  In your scenario you are adding a UserControl, so a MenuItem is created to wrap it. Moreover, these implicitly created ItemContainer are part of the LogicalTree, so they don't inherit styles.

 

So, how can you workaround this?

1.       One possibility is to create a Custom Control (instead of a UserControl) to represent your view, and make it inherit from MenuItem. The drawback is that you will have to define the XAML for that control in the generic.xaml file.  You can read further about WPF controls in Control Authoring Overview.

For example, you could the following class (this will work as the codebehind):

public class MyCustomControl : MenuItem

{

    static MyCustomControl ()

    {

          DefaultStyleKeyProperty.OverrideMetadata(typeof(MyCustomControl), new FrameworkPropertyMetadata(typeof(MyCustomControl)));

    }

}

And then in the generic.xaml (this will work as the XAML file):

<Style TargetType="{x:Type local: MyCustomControl }">

  <Setter Property="Template">

    <Setter.Value>

      <ControlTemplate TargetType="{x:Type local: MyCustomControl }">

        <MenuItem Header="YourHeader" Command="{Binding Path=OnlineCommand}"></MenuItem>

      </ControlTemplate>

    </Setter.Value>

  </Setter>

</Style>

2.       If you are only going to have a MenuItem in your view, you can create it by code. So when you register the view you could do as follows:

MenuItem menuItem = new MenuItem() { Header = "Your Header" };

menuItem.Command = OnlineCommand;

regionManager.Regions["MenuFileOnlineRegion"].Add(menuItem);

 

 

Hope it helps! As this is a WPF problem, and I'm not a WPF expert, you might get better answer in the WPF Forum.

 

Matias Bonaventura

http://blogs.southworks.net/matiasb

Jun 25, 2009 at 7:47 PM
Edited Jun 25, 2009 at 8:56 PM

Checked my suggestion... and I couldn't get it to work.

Why?!-> http://www.dev102.com/2008/04/23/wpf-datatemplate-for-interfaces-not-supported/

Gruß Timo

Jun 26, 2009 at 6:27 AM

Hi Timo and Matias,

Thanks very much for your support! I will try one of the workarounds that Matias suggested.

Dominik

Oct 15, 2009 at 9:43 PM

@matiasbonaventura

Thank you so much, I tried the second solution and it worked instantly.

The first one seemed a bit complicated to me and since I'm simply creating MenuItems with Commands and the styling is done by the shell, the second workaround is very simple to do.

Again, thanks, saved me a lot of time that would have been spent researching for a solution, but in this case the first search brought your answer :)

Thorsten