Skip to content
Nov 30 / Hugo Ribeiro

DSL Tools #16 – Show/Hide Connectors

One of the major issues in the DSL Tools today is that you can’t have a model be defined in two or more diagrams (files). This is an issue for many reasons. One of them is that it is very easy to have models becoming very hard to manipulate (or even read) because they have lots and lots of shapes and connectors.

Thinking about this a bit, one of the things that contributes more to make a model hard to read are connectors, that is when you have a lot of them.

So a very useful thing that diagrams should have is the ability to hide and show all the connectors at the user’s discretion.

It turns out that this is very easy to implement just by adding two extensions to the Diagram class.

Here is how I’ve done it.

1. Extend Diagram

I’ve created a static class called DiagramExtensions that provides extension methods for the Diagram class. This class adds two extensions:

  • ShowAllLinks
  • HideAllLinks

The magic happens in the ShowHideAllLinks private method:

  • First I get an IEnumerable of all the shapes in the diagram that are of type BinaryLinkShape through another extension method called NestedChildBinaryLinkShapes (defined for ShapeElement).
  • NestedChildBinaryLinkShapes just iterates through NestedChildShapes and returns only the shapes that are of type BinaryLinkShape (a connector in the DSL Tools).
  • Finally, the IEnumerable is enumerated and the connector is shown if it is hidden or hidden if it is visible.

Here is the source code for DiagramExtensions:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;

namespace Experimental.Core.Extensions
{
    /// <summary>
    /// Provides extension methods for the Diagram class.
    /// </summary>
    public static class DiagramExtensions
    {
        #region Public Methods

        /// <summary>
        /// Shows all the links in the current diagram.
        /// </summary>
        /// <param name="diagram">The diagram.</param>
        public static void ShowAllLinks(this Diagram diagram)
        {
            ShowHideAllLinks(diagram, true);
        }

        /// <summary>
        /// Hides all the links in the current diagram.
        /// </summary>
        /// <param name="diagram">The diagram.</param>
        public static void HideAllLinks(this Diagram diagram)
        {
            ShowHideAllLinks(diagram, false);
        }

        #endregion

        #region Private Methods

        private static void ShowHideAllLinks(this Diagram diagram, bool show)
        {
            ShowHideAllLinks(diagram, null, show);
        }

        private static void ShowHideAllLinks(this Diagram diagram, NodeShape nodeShape, bool show)
        {
            // Validation

            if (diagram == null)
            {
                throw new ArgumentNullException("diagram");
            }

            // Get the links that should be shown or hidden

            IEnumerable<BinaryLinkShape> links = diagram.NestedChildBinaryLinkShapes(nodeShape);

            // Show/hide the links

            foreach (BinaryLinkShape link in links)
            {
                if (show)
                {
                    if (!link.IsVisible)
                    {
                        link.Show();
                    }
                }
                else
                {
                    if (link.IsVisible)
                    {
                        link.Hide();
                    }
                }
            }
        }

        #endregion
    }
}

And the source code for ShapeElementExtensions:

using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Microsoft.VisualStudio.Modeling.Diagrams;

namespace Experimental.Core.Extensions
{
    /// <summary>
    /// Provides extension methods for the ShapeElement class.
    /// </summary>
    public static class ShapeElementExtensions
    {
        #region Public Methods

        /// <summary>
        /// Gets a list of the nested binary link shapes.
        /// </summary>
        /// <param name="shape">The shape.</param>
        /// <returns>A list of the nested binary link shapes.</returns>
        public static IEnumerable<BinaryLinkShape> NestedChildBinaryLinkShapes(this ShapeElement shape)
        {
            return NestedChildBinaryLinkShapes(shape, null);
        }

        /// <summary>
        /// Gets a list of the nested binary link shapes that are connected to the specified node shape.
        /// </summary>
        /// <param name="shape">The shape.</param>
        /// <param name="toOrFromShape">The node shape to which the link shape must be connected to appear in the result.</param>
        /// <returns>A list of the nested binary link shapes that are connected to the specified node shape.</returns>
        public static IEnumerable<BinaryLinkShape> NestedChildBinaryLinkShapes(this ShapeElement shape, NodeShape toOrFromShape)
        {
            // Validation

            if (shape == null)
            {
                throw new ArgumentNullException("shape");
            }

            // Search all nested child shapes

            Collection<BinaryLinkShape> result = new Collection<BinaryLinkShape>();
            foreach (ShapeElement child in shape.NestedChildShapes)
            {
                // Is it a link shape?

                BinaryLinkShape linkShape = child as BinaryLinkShape;
                if (linkShape != null)
                {
                    // If the node shape is not specified, just add to the result

                    if (toOrFromShape == null)
                    {
                        result.Add(linkShape);
                    }
                    else
                    {
                        // Otherwise the link shape must connect to the specified node shape

                        if (linkShape.FromLinkConnectsToNode.Nodes.Equals(toOrFromShape) ||
                            linkShape.ToLinkConnectsToNode.Nodes.Equals(toOrFromShape))
                        {
                            result.Add(linkShape);
                        }
                    }
                }
            }

            return result.AsEnumerable<BinaryLinkShape>();
        }

        #endregion
    }
}

2. Add Context Menus

The two new operations are available to the user through context menus (implemented as explained here). The corresponding commands just need to call the extension methods defined in the previous step.

private void ExecuteShowAllConnectors()
{
    // Validation

    if (this.CurrentDocView == null || this.CurrentDocView.CurrentDiagram == null)
    {
        return;
    }

    // Execute

    this.CurrentDocView.CurrentDiagram.ShowAllLinks();
}

private void ExecuteHideAllConnectors()
{
    // Validation

    if (this.CurrentDocView == null || this.CurrentDocView.CurrentDiagram == null)
    {
        return;
    }

    // Execute

    this.CurrentDocView.CurrentDiagram.HideAllLinks();
}

Easy.

Share:
  • RSS
  • LinkedIn
  • Facebook
  • Twitter