Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / .NET

DSL Modelling - Add a Context Sensitive Menu

0.00/5 (No votes)
15 Jul 2015CPOL2 min read 6.1K  
Add a context sensitive menu that only applies when the mouse is over a particular shape of your model

Introduction

When you use the DSL SDK to create a tool with which to draw out your domain, you may have functionality that only applies to one class (or diagram element) in that model. This tip allows you to create a context sensitive menu for that case.

Background

Creating a domain specific language requires the Visual Studio Modelling SDK to be installed.

Image 1

 

When you create a new domain specific language modelling project, Visual Studio creates a solution with two projects in it: a Dsl project that controls the diagramming component and a DslPackage project that controls how the IDE (or indeed any other modelling host) interacts with the diagram.

Image 2

The changes required to create a context sensitive menu go in the DslPackage project.

1. Create a New Menu for the IDE to Use

In the DslPackage project is a file called "Commands.vsct". You need to edit this to add any interactions you want the IDE to host. In an echo back to Visual C, you do this by creating a menu identifier and associating it to the command.

XML
<Symbols>
   <!-- Copy every menu identifier here -->
   <GuidSymbol name="guidCustomDiagramMenuCmdSet"
        value="{986DE2DB-3F48-429B-8474-1248E06D8C27}" >
    <IDSymbol name="grpidDiagramMenuGroup" value="0x01001"/>
    <IDSymbol name="cmdidExpandCollapseAggregateContextMenuCommand" value="0x00003"/>
  </GuidSymbol>
</Symbols>

This registers two identifiers - one for the top level command group the menus are going to go into and one for the command itself. The value= part must be unique for each item.

The actual menus are created in the Buttons tag:

XML
<Buttons>
  <!-- Expand or collapse the selected aggregate-->
  <Button guid="guidCustomDiagramMenuCmdSet"
   id="cmdidExpandCollapseAggregateContextMenuCommand"
    priority="0x0100" type="Button">
    <Parent guid="guidCustomDiagramMenuCmdSet" id="grpidDiagramMenuGroup"/>
    <CommandFlag>DefaultDisabled</CommandFlag>
    <CommandFlag>DefaultInvisible</CommandFlag>
    <CommandFlag>DynamicVisibility</CommandFlag>
    <Strings>
      <ButtonText>Expand/Collapse Aggregate</ButtonText>
    </Strings>
  </Button>
</Buttons>

Note that the ids of these buttons have to match the names of the symbols you created earlier, and the ButtonText indicates the caption that will be shown on the menu.

Image 3

2. Creating the Code for the Menus

When you rebuild this project, the partial class "CommandSet" will be created for these commands. You will need to add your own code to a partial class of the same name to implement the command menu itself. (Do not put this code in the automatically generated file or it will be lost when you rebuild the project).

The first thing you need to do in your class is restate the constants you put in the Symbols tag in step 1:

C#
internal partial class CommandSet
{
  private Guid guidCustomDiagramMenuCmdSet = new Guid("986DE2DB-3F48-429B-8474-1248E06D8C27");
  private int cmdidExpandCollapseAggregateContextMenuCommand = 0x00003;

Then you need to override the GetMenuCommands function so as to add your menus into the tool's menus:

C#
/// <summary>
/// Get the set of commands for this CQRS/DSL tool
/// </summary>
protected override IList<MenuCommand> GetMenuCommands()
{
  // Get the standard base commands
  global::System.Collections.Generic.IList<global::System.ComponentModel.Design.MenuCommand>
    commands = base.GetMenuCommands();

  // Add custom commands
  global::System.ComponentModel.Design.MenuCommand menuCommand = null;

  // Add handler for "Expand / CollapseAggregate" menu command
  menuCommand = new DynamicStatusMenuCommand(new EventHandler(OnStatusExpandCollapseAggregate),
   new EventHandler(OnMenuExpandCollapseAggregate),
   new CommandID(guidCustomDiagramMenuCmdSet,
        cmdidExpandCollapseAggregateContextMenuCommand));
   commands.Add(menuCommand);

  // return the resulting list
  return commands;
}

There are two methods you need to pass to this constructor for the new menu - the "OnStatus..." method which is called whenever the IDE is deciding to if (or how) show the menu and the "OnMenu..." method which is called when the menu is clicked.

In the case of the OnStatus method, we want the menu to be shown and enabled only if there is one and only one "Aggregate" selected (this being one of our diagram shape types defined in the Dsl project).

C#
internal virtual void OnStatusExpandCollapseAggregate(object sender, System.EventArgs e)
{
  global::System.ComponentModel.Design.MenuCommand cmd =
     sender as global::System.ComponentModel.Design.MenuCommand;

  cmd.Visible = true;
  cmd.Enabled = false;

  if (this.CurrentSelection.OfType<AggregateGeometryShape>().Count() == 1)
  {
    // One and only one "Aggregate" selected so show the Increment version command
    cmd.Enabled = true;
  }
}

History

  • 2015-07-15: Created first draft

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)