Region and ContextMenu

Sep 1, 2008 at 11:12 AM
I have attempted configuring a ContextMenu as a region using XAML but am running into a problem where the region is not registered until the ContextMenu is actually shown. I have moved the ContextMenu to the resources section of the window but still find that the region is not registered until the ContextMenu is shown.

It appears that attached properties on a context menu are not fired until the ContextMenu is shown even if the ContextMenu has successfully been instantiated in the resources section.

I would like to be able to add items to a ContextMenu from a module.
I have thought of using a service but run into the same problem with attached properties not firing until the ContextMenu is shown issue if I use a similar mechanism to the RegionManager and XAML.

Any ideas on how I can add items to a ContextMenu from a module?
Sep 2, 2008 at 12:18 AM
KerryRJ,

I believe that your region is not registered because of the way it was instatiated. If your control with the target ContextMenu, which we will call targetControl, is declared in Xaml on your main view, which we'll call parentView, then this would explain why the targetView's region has not been registered. Changing this do that your module registers both the parentView and the targetView then this should resolve your issue.

I do not know if this fits your scenario without code from your end, but if I am describing you use case, it is only because I ran into the same issue myself. Below is a link to another post that describes this a little more. I entered it today.

http://www.codeplex.com/CompositeWPF/Thread/View.aspx?ThreadId=34370

Hope this helps,

Andres Olivares
Sep 3, 2008 at 7:13 AM
Hi Andreas,

Just to clarify with an example

<Window x:Class="ServiceManagement.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:cal="http://www.codeplex.com/CompositeWPF"
    Title="Window1" Height="300" Width="300">
    <DockPanel LastChildFill="True">
        <TabControl DockPanel.Dock="Bottom" cal:RegionManager.RegionName="Test" TabStripPlacement="Bottom">
            <TabControl.ItemContainerStyle>
                <Style TargetType="{x:Type TabItem}">
                    <Setter Property="Header" Value="{Binding Header}" />
                </Style>
            </TabControl.ItemContainerStyle>
        </TabControl>
        <ListView Name="listView">
            <ListView.View>
                <GridView>
                    <GridViewColumn x:Name="gridViewColumnNumber" Header="Number" DisplayMemberBinding="{Binding XPath=@number}" />
                </GridView>
            </ListView.View>
            <ListView.ContextMenu>
                <ContextMenu
                        Name="documentsContextMenu"
                        DisplayMemberPath="@name"
                      cal:RegionManager.RegionName="TestMenu"/>
            </ListView.ContextMenu>
        </ListView>
    </DockPanel>
</Window>

This window is an example of the xaml used for my shell.
The toolbar region "Test" is visible from the region manager when I load a module, but the ContextMenu region "TestMenu" is not visible from the module using the RegionManager.
Stepping through the RegionManager attached properties shows that the region in a context menu is only registered when the context menu is about to be displayed.

I have moved the ContextMenu to the Resources section of the shell window. Debugging shows that the ContextMenu exists in the Resources section but does not exist in the RegionManager until it is about to be displayed.

I want modules to add MenuItems to this ContextMenu when the module is loaded using the RegionManager.
Sep 3, 2008 at 6:09 PM
KerryRJ,

This was a tricky one. The problem with this issue is that WPF does not have a relationship between objects and there property values, but this might be an all out separate discussion all together. nonetheless, I ran into the same problems you encountered so i choose to make your issue work with a little elbow grease: manual region attachment.

The only change I made to the Xaml was remove [cal:RegionManager.RegionName="TestMenu"] from the ContextMenu.

// Shell.cs
namespace ServiceManagement
{
  using System.Windows;
  using Microsoft.Practices.Composite.Wpf.Commands;
  using Microsoft.Practices.Composite.Regions;

  /// <summary>
  /// Interaction logic for Shell.xaml
  /// </summary>
  public partial class Shell
  {
    internal const string TestMenu = "TestMenu";

    public static readonly DelegateCommand<object> ShowMessageCommand =
      new DelegateCommand<object>(ShowMessage);

    public Shell(IRegionManager regionManager)
    {
      InitializeComponent();
      // Manually creating new region
      regionManager.AttachNewRegion(listView.ContextMenu, TestMenu);
    }

    static void ShowMessage(object value)
    {
      string message = "Do you see me";
      MessageBox.Show(message, "Manual...");
    }
  }
}

// Bootstrapper.cs
namespace ServiceManagement
{
  using System.Windows.Controls;
  using System.Windows;
  using Microsoft.Practices.Composite.Modularity;
  using Microsoft.Practices.Composite.UnityExtensions;
  using Microsoft.Practices.Composite.Regions;

  internal class Bootstrapper: UnityBootstrapper
  {
    protected override IModuleEnumerator GetModuleEnumerator()
    {
      return new StaticModuleEnumerator();
    }

    protected override DependencyObject CreateShell()
    {
      // Shell resolution
      Shell shell = Container.Resolve<Shell>();
      // Regionalization
      IRegionManager regionManager = Container.Resolve<IRegionManager>();
      // Getting menu items from somewhere
      MenuItem item = GetTestMenuItem();
      // Find manually created region
      IRegion region = regionManager.Regions[Shell.TestMenu];
      // Add and activate view
      region.Add(item);

      shell.Show();
      return shell;
    }

    static MenuItem GetTestMenuItem()
    {
      return new MenuItem
      {
        Header = "Do Something",
        Command = Shell.ShowMessageCommand,
      };
    }
  }
}

So basically while the Shell is being resolved, it attaches a region to the ContextMenu of the ListView (you should check for a null ContextMenu). After the Shell is resolved, the Bootstrapper adds the dynamic MenuItem.

Let me know if this solution works for you,

Andres Olivares

Sep 4, 2008 at 2:59 PM
Andres

This little line
regionManager.AttachNewRegion(listView.ContextMenu, TestMenu);
works wonders.

Thank you so very much

Feb 22, 2009 at 5:10 PM
I agree. This little line works wonders but the method isn’t anymore available in Composite WPF 2. Does anybody have an idea how to get this scenario working with the new CAL libraries?

I'm using this approach in my project www.codeplex.com/CompositeExtensions.

jbe
Feb 23, 2009 at 12:41 PM
The issue is caused because the RegionManager attached property is not flowing down to context menues (nor the shell is the parent of the context menu).

jbe, another way to workaround this, which also works in Prism V2, is to keep the original XAML as it was (keep the region name property on the context menu), and in the code behind for the window manually set the RegionManager attached property:

// Shell.cs
namespace ServiceManagement
{
  using System.Windows;
  using Microsoft.Practices.Composite.Wpf.Commands;
  using Microsoft.Practices.Composite.Regions;

  /// <summary>
  /// Interaction logic for Shell.xaml
  /// </summary>
  public partial class Shell
  {

    public Shell(IRegionManager regionManager)
    {
      InitializeComponent();

        // manually setting the region manager for the context menu (because it is not a logical child of the shell)

 

 

        RegionManager.SetRegionManager(this.documentsContextMenu, regionManager);

 

 

    }
  }
}

I hope this helps,
Julian Dominguez
http://blogs.southworks.net/jdominguez

Feb 25, 2009 at 7:43 AM
Hi Julian!

Thanks a lot. Your approach works in my scenario.

jbe