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.
public partial class AggregateGeometryShapeBase
{
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:
<Symbols>
<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.)
<Commands package="guidPkg">
<Groups>
<Group guid="guidCustomDiagramMenuCmdSet"
id="grpidDiagramMenuGroup" priority="0x0100">
<Parent guid="guidCmdSet" id="menuidContext" />
</Group>
</Groups>
<Buttons>
<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>
<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.
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;
protected override IList<MenuCommand> GetMenuCommands()
{
global::System.Collections.Generic.IList<global::System.ComponentModel.Design.MenuCommand>
commands = base.GetMenuCommands();
global::System.ComponentModel.Design.MenuCommand menuCommand = null;
menuCommand = new DynamicStatusMenuCommand(new EventHandler(OnStatusExpandCollapseAggregate),
new EventHandler(OnMenuExpandCollapseAggregate),
new CommandID(guidCustomDiagramMenuCmdSet,
cmdidExpandCollapseAggregateContextMenuCommand));
commands.Add(menuCommand);
return commands;
}
}
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.
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)
{
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
:
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");
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