Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#5.0

DSL Modelling - Expand or Collapse All of a Diagram Shape's Children

0.00/5 (No votes)
1 Oct 2015CPOL2 min read 6.9K  
Expand/collapse your DSL diagram elements to make them easier to work with

Introduction

When you use the DSL SDK, you can create a tool with which you draw out your domain and it is subsequently turned into code with the T4 templates and code generation. However, the diagram can soon become very cluttered so having a way to expand/collapse your entities makes it easier to find what you are looking for and work on that.

Background

Creating a domain specific language model requires the Visual Studio Modelling SDK.

1. Add a Shown/Hidden Property to Your Shape

When you create a shape in the DSL designer, it generates a class corresponding to that shape in the GeneratedCode folder of the DSL project. However, this class is auto-generated so any code you add to it will be lost if the model is rebuilt.

Instead, you need to create a different folder (I use the name CustomCode) and in that folder, create a partial class with the same name as the auto-generated shape class.

Into this partial class, add a property to indicate if the shape's children are hidden or visible.

C#
public partial class AggregateGeometryShapeBase
{
    /// <summary>
    /// Are the linked events/projections/queries/commands visible
    /// </summary>
    private bool m_childrenHidden = false;
    public bool ChildrenHidden
    {
        get
        {
            return m_childrenHidden;
        }
        set
        {
            m_childrenHidden = value ;
        }
    }
}

2. Add a Context Menu to Your DSL Designer

In your DSLPackage project, there will be a file called "Commands.vsct". This is an XML file that defines the menus (and other user interface functions) that your package will add into the Visual Studio designer when your DSL is being designed graphically.

Add a new command to show/hide the shape children to this file:

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

(Obviously replace the GUID with one you generate yourself.)

XML
<Commands package="guidPkg">
   <Groups>
     <Group guid="guidCustomDiagramMenuCmdSet"
     id="grpidDiagramMenuGroup" priority="0x0100">
       <!-- These symbols are defined in GeneratedVSCT.vsct -->
       <Parent guid="guidCmdSet" id="menuidContext" />
     </Group>
   </Groups>
   <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>
 </Commands>
 <VisibilityConstraints>
   <!-- Ensure that the diagram commands are only loaded for this DSL -->
   <VisibilityItem guid="guidCustomDiagramMenuCmdSet"
     id="cmdidExpandCollapseAggregateContextMenuCommand"
     context="guidEditor"/>
 </VisibilityConstraints>

At this point, your menu won't actually appear - you have to create a matching bit of code which has the same class name as the code automatically generated from this XML file.

C#
 internal partial class CQRSdslCommandSet
 {
     private Guid guidCustomDiagramMenuCmdSet = new Guid("986DE2DB-3E48-429B-8474-1248E06D8C27");
     private int cmdidShowHideModelTipsContextMenuCommand = 0x00001;
     private int cmdidIncrementEventVersionContextMenuCommand = 0x00002;
     private int cmdidExpandCollapseAggregateContextMenuCommand = 0x00003;

     private int cmdidGenerateCQRSModelCode =  0x00011;

     private int cmdidGenerateCQRSModelDocumentation = 0x00021;

     /// <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;

     }
// - - - -8< - - - - - - - -
}

You will notice that there are two functions passed in when creating a command - an OnStatus... function which determines if the menu is shown, hidden, enabled or disabled and an OnMenu.. function which performs the code when the menu is clicked.

In this case, we want the menu to be available if one and only one of our top level shapes is currently selected.

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;
    }
}

3. Execute the Showing/Hiding

The menu handler itself is going to update the diagram view itself so its actions must be performed inside a Microsoft.VisualStudio.Modeling.Transaction:

C#
internal virtual void OnMenuExpandCollapseAggregate(object sender, global::System.EventArgs e)
{
    AggregateGeometryShape agg = this.CurrentSelection.OfType<AggregateGeometryShape>()
        .FirstOrDefault();
    if (null != agg)
    {
        agg.ChildrenHidden = !agg.ChildrenHidden;

        Microsoft.VisualStudio.Modeling.Transaction tShowHide =
              agg.Store.TransactionManager.BeginTransaction("Show or hide children");
        // Show/hide all the child links and shapes
        foreach (BinaryLinkShape linkedChild in agg.FromRoleLinkShapes.OfType<BinaryLinkShape >() )
        {
            linkedChild.SetShowHideState(!agg.ChildrenHidden);
            if (linkedChild.ToShape != null)
            {
                linkedChild.ToShape.SetShowHideState(!agg.ChildrenHidden);
            }
        }
        tShowHide.Commit();
    }
}

History

  • 2015-10-01: Initial version

License

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