none
How to align classes in the UML class designer? RRS feed

  • Question

  • I want to align my classes/interfaces and other elements in a VS 2010 UML class diagram. I can't seem to find out how to do this. Please help.
    Wednesday, October 27, 2010 8:47 PM

Answers

All replies

  • We don't have this out of the box.

    You can write a command to do it. Details on writing a command are here: How to Define a Menu Command on a Modeling Diagram

    In the command, go through the selected shapes and use the Move method on the shapes.

    foreach (IShape<IElement> shape in diagram.GetSelectedShapes<IElement>())
          { ... shape.Move(x,y); ... }

     


    - Alan -MSFT
    Wednesday, October 27, 2010 10:41 PM
  • using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.Linq;
    using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
    using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
    using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
    
    namespace AlignCommand
    {
     // Implements a command to align shapes in a UML class diagram.
     // The user first selects shapes that are roughly aligned either vertically or horizontally.
     // This command will straighten them up.
    
     // Place this file in a menu command extension project.
     // See http://msdn.microsoft.com/library/ee329481.aspx
    
     [Export(typeof(ICommandExtension))]
     [ClassDesignerExtension] // TODO: Add other diagram types if needed
     class CommandExtension : ICommandExtension
     {
      /// <summary>
      /// See http://msdn.microsoft.com/library/ee329481.aspx
      /// </summary>
      [Import]
      IDiagramContext context { get; set; }
    
      /// <summary>
      /// Transaction context.
      /// See http://msdn.microsoft.com/library/ee330926.aspx
      /// </summary>
      [Import]
      ILinkedUndoContext linkedUndo { get; set; }
    
      /// <summary>
      /// Called when the user selects the command.
      /// </summary>
      /// <param name="command"></param>
      public void Execute(IMenuCommand command)
      {
       Align(context.CurrentDiagram.SelectedShapes);
      }
    
      /// <summary>
      /// Called when the user right-clicks on the diagram.
      /// Determines whether the command is enabled.
      /// </summary>
      /// <param name="command"></param>
      public void QueryStatus(IMenuCommand command)
      {
       IEnumerable<IShape> currentSelection = context.CurrentDiagram.SelectedShapes;
       // Make it visible if there are shapes selected:
       command.Visible = currentSelection.Count() > 0 && !(currentSelection.FirstOrDefault() is IDiagram);
    
       // Make it enabled if there are two or more shapes that are roughly in line:
       command.Enabled = currentSelection.Count() > 1
        && (HorizontalAlignCenter(currentSelection) > 0.0
        || VerticalAlignCenter(currentSelection) > 0.0);
    
      }
    
      /// <summary>
      /// Title of the menu command.
      /// </summary>
      public string Text
      {
       get { return "Align Shapes"; }
      }
    
      /// <summary>
      /// Find a horizontal line that goes through a list of shapes.
      /// </summary>
      /// <param name="shapes"></param>
      /// <returns></returns>
      private static double HorizontalAlignCenter(IEnumerable<IShape> shapes)
      {
       double Y = -1.0;
       double top = 0.0, bottom = shapes.First().Bottom();
       foreach (IShape shape in shapes)
       {
        top = Math.Max(top, shape.Top());
        bottom = Math.Min(bottom, shape.Bottom());
       }
       if (bottom > top) Y = (bottom + top) / 2.0;
       return Y;
      }
    
      /// <summary>
      /// Find a vertical line that goes through a list of shapes.
      /// </summary>
      /// <param name="shapes"></param>
      /// <returns></returns>
      private static double VerticalAlignCenter(IEnumerable<IShape> shapes)
      {
       double X = -1.0;
       double left = 0.0, right = shapes.First().Right();
       foreach (IShape shape in shapes)
       {
        left = Math.Max(left, shape.Left());
        right = Math.Min(right, shape.Right());
       }
       if (right > left) X = (right + left) / 2.0;
       return X;
      }
    
      /// <summary>
      /// Line up those shapes that are roughly aligned.
      /// </summary>
      /// <param name="shapes"></param>
      private void Align(IEnumerable<IShape> shapes)
      {
       if (shapes.Count() > 1)
       {
        // The shapes must all overlap either horizontally or vertically.
        // Find a horizontal line that is covered by all the shapes:
        double Y = HorizontalAlignCenter(shapes);
        if (Y > 0.0) // Negative if they don't overlap.
        {
         // Adjust all the shape positions in one transaction:
         using (ILinkedUndoTransaction t = linkedUndo.BeginTransaction("align"))
         {
          foreach (IShape shape in shapes)
          {
           shape.AlignYCenter(Y);
          }
          t.Commit();
         }
        }
        else
        {
         // Find a vertical line that is covered by all the shapes:
         double X = VerticalAlignCenter(shapes);
         if (X > 0.0) // Negative if they don't overlap.
         {
          // Adjust all the shape positions in one transaction:
          using (ILinkedUndoTransaction t = linkedUndo.BeginTransaction("align"))
          {
           foreach (IShape shape in shapes)
           {
            shape.AlignXCenter(X);
           }
           t.Commit();
          }
         }
        }
       }
      }
     }
     
     /// <summary>
     /// Convenience extensions for IShape.
     /// </summary>
     public static class IShapeExtension
     {
      public static double Bottom(this IShape shape)
      {
       return shape.YPosition + shape.Height;
      }
    
      public static double Top(this IShape shape)
      {
       return shape.YPosition;
      }
    
      public static double Left(this IShape shape)
      {
       return shape.XPosition;
      }
    
      public static double Right(this IShape shape)
      {
       return shape.XPosition + shape.Width;
      }
    
      public static void AlignYCenter(this IShape shape, double Y)
      {
       shape.Move(shape.XPosition, Y - shape.YCenter());
      }
    
      public static void AlignXCenter(this IShape shape, double X)
      {
       shape.Move(X - shape.XCenter(), shape.YPosition);
      }
    
      /// <summary>
      /// We can adjust what bit of the shape we want to be aligned.
      /// The default is the center of the shape.
      /// </summary>
      /// <param name="shape"></param>
      /// <returns></returns>
      public static double YCenter(this IShape shape)
      {
        return shape.Height / 2.0;
      } 
      
      /// <summary>
      /// We can adjust what bit of the shape we want to be aligned.
      /// The default is the center of the shape.
      /// </summary>
      /// <param name="shape"></param>
      /// <returns></returns>
      public static double XCenter(this IShape shape)
      {
        return shape.Width / 2.0;
      }
     }
    }
    
    

    - Alan -MSFT
    Thursday, February 3, 2011 7:01 PM
  • Thanks! This is great!
    Friday, February 4, 2011 6:58 PM
  • I've uploaded this to MSDN Samples: Align UML Shapes on the Diagram


    - Alan -MSFT
    Tuesday, February 22, 2011 2:41 AM