none
Customize Class color by stereotype in UML class diagram RRS feed

  • Question

  • Hi,

     I use VS 2010 ultimate and I have uml class diagrams, with a lot of classes. These classes are stereotyped by differents Stereotype.

    I'd like to know if it's possible to customize class appearance by stereotype ? I have not the courage to change for example the color of each class one by one...

    Thank you.

    Friday, October 8, 2010 3:53 PM

Answers

  • Hi

    As Phil says, the UML extensibility API doesn't have a way to tap into the necessary events for this - we'll put that on the backlog to consider for a future enhancement - but we can hack into it a bit to achieve the effect you want.

    The sample below uses some of the classes from the underlying DSL implementation. It sets a couple of events handlers that trigger whenever stereotype instances are created or deleted. See " Event Handlers ". The handlers need to be set up when the model opens, so we abuse the "Validate on open" feature to set them: instead of doing any validation, we just register the handlers.

    One slight drawback of using event handlers is that the handlers are called after the triggering transaction completes. This means that the colors are changed in a separate transaction. So if the user does an "Undo" after setting a stereotype, the color change is undone, but it requires another Undo to un-apply the stereotype. This might or might not be what you want. To do the whole thing in one transaction would require a Rule rather than an event handler; but registering rules is a hack involving so much reflection and invocation of internal stuff that I'm too embarrassed to show it to you. :)  - But if you really need this, ask again and I'll post it separately.

     To use this, insert it in a VSIX model extension. Start by creating a new project from the "Model Validation Extension" template (CTRL+SHIFT+O, then click Modeling Projects...) When you hit F5, you get a new VS in which you can test this on a class diagram. To deploy, copy the VSIX out of bin/debug. More at Extending UML .

    This sample just colors UML classes and interfaces, and I've picked out some of the standard stereotypes that are applicable by default.

      

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.Drawing;
    using System.Linq;
    using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
    using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
    using Microsoft.VisualStudio.Modeling;
    using Microsoft.VisualStudio.Modeling.Diagrams;
    using Microsoft.VisualStudio.Modeling.Validation;
    using Microsoft.VisualStudio.Uml.AuxiliaryConstructs;
    using Microsoft.VisualStudio.Uml.Classes;
    using Microsoft.VisualStudio.Uml.Profiles;
    using Microsoft.VisualStudio.Uml.UseCases;

    /// <summary> /// Wraps a UML model to add stereotype coloring etc. /// </summary> public partial class ColoringModelAdapter { /// <summary> /// This isn't actually validation. It's to couple this adapter to the model before we start. /// The validation package creates an instance of this class and then calls this method. /// See "Validation": http://msdn.microsoft.com/library/bb126413.aspx /// </summary> /// <param name="vcontext"></param> /// <param name="model"></param> [Export(typeof(System.Action<ValidationContext, object>))] [ValidationMethod(ValidationCategories.Open)] public void ConnectAdapterToModel(ValidationContext vcontext, IModel model) { // This is the underlying DSL store, not the wrapping UML ModelStore: store = (model as ModelElement).Store; // Add an event that triggers on creating a stereotype instance. // See "Event handlers": http://msdn.microsoft.com/library/bb126250.aspx DomainClassInfo siClass = store.DomainDataDirectory.FindDomainClass("Microsoft.VisualStudio.Uml.Classes.StereotypeInstance"); store.EventManagerDirectory.ElementAdded.Add(siClass, new EventHandler<ElementAddedEventArgs>(StereotypeInstanceAdded)); // For the deletion, we need to trigger from the deleted link // between the stereotype instance and the model element - // because after deletion we can't find the element from the stereotype instance. DomainRelationshipInfo linkToStereotypeClass = store.DomainDataDirectory.FindDomainRelationship ("Microsoft.VisualStudio.Uml.Classes.ElementHasAppliedStereotypeInstances"); store.EventManagerDirectory.ElementDeleted.Add(linkToStereotypeClass, new EventHandler<ElementDeletedEventArgs>(StereotypeInstanceDeleted)); } /// <summary> /// Called whenever a stereotype instance is linked to a uml model element. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void StereotypeInstanceAdded(object sender, ElementAddedEventArgs e) { // Don't handle changes in undo or load from file: if (store.InUndoRedoOrRollback || store.InSerializationTransaction) return; IStereotypeInstance si = e.ModelElement as IStereotypeInstance; IElement umlElement = si.Element; // I'm only interested in coloring classes and interfaces: if (!(umlElement is IType)) return; Color? color = ColorForStereotype(si.Name); if (color.HasValue) { SetColorOfShapes(si.Element, color.Value); } } /// <summary> /// Called whenever a stereotype instance is deleted - well, actually, /// when the link between the stereotype instance and the uml model element is deleted. /// Triggering on the link deletion allows us to get both ends. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void StereotypeInstanceDeleted(object sender, ElementDeletedEventArgs e) { // Don't handle changes in undo or load from file: if (store.InUndoRedoOrRollback || store.InSerializationTransaction) return; // Use the generic link type to avoid unearthing the UML implementation DLL: ElementLink elementToStereotypeLink = e.ModelElement as ElementLink; IElement umlElement = elementToStereotypeLink.LinkedElements[0] as IElement; IStereotypeInstance si = elementToStereotypeLink.LinkedElements[1] as IStereotypeInstance; // We're here either because a stereotype is being un-applied, // or because the uml element is being deleted. // Don't bother if the element is being deleted: if ((umlElement as ModelElement).IsDeleting) return; // We're only interested in classes and interfaces: if (!(umlElement is IType)) return; // Because more than one stereotype can be applied to an element, // we should check to see if there are any remaining: Color newColor = Color.WhiteSmoke; // Default if there aren't. foreach (IStereotypeInstance remainingSi in umlElement.AppliedStereotypes) { Color? color = ColorForStereotype(remainingSi.Name); if (color.HasValue) { newColor = color.Value; break; } } SetColorOfShapes(umlElement, newColor); } private void SetColorOfShapes(IElement element, Color color) { foreach (IShape shape in element.Shapes()) { shape.Color = color; } } private Color? ColorForStereotype(string name) { switch (name) { case "focus": return Color.AliceBlue; case "auxiliary": return Color.Bisque; case "specification": return Color.OliveDrab; case "realization": return Color.LightSeaGreen; case "implementationClass": return Color.PaleGoldenrod; } return null; } private Store store;
      }
     

    Friday, November 5, 2010 5:19 PM
  • Thanks for raising this though, because if you saw it that way, others will have. I shouldha made it more clear.

    Re the proper solution - the dev team are heavily involved in other stuff now, so it looks likely that this is the best we can do for the time being. I'll put up a page about this on the MSDN library next month.

    Cheers

    Alan


    - Alan -MSFT
    Friday, January 21, 2011 5:59 PM

All replies

  • Hi Rmagna7.

    First, my apology for taking so long to get back to you.  Our internal notification mechanism had a problem.

    Second, I need to confirm what you're asking.  I think you're asking that you want to automatically customize a shape's appearance when a specific stereotype is applied.  Is this correct?  This is possible, but will require that you write some code.  Unfortunately this code will probably span two different layers in our architecture.  Basically, you need two bits of code -

    1. runs as an add-in or an extension - registers an event handler with teh DSL Tools IMS.
    2. some code that is triggered by a DSL Tools IMS model change event (to capture the stereotype change) and then access the UML model store to get the class shape and change its color.

    I'm working with the team to figure out the best way to do this.  I'll try to post some code that show's an example.

    Thanks.

    Phil


    Phil Lee | Program Manager | Visual Studio Architecture Tools
    Monday, October 25, 2010 11:06 PM
  • Hi

    As Phil says, the UML extensibility API doesn't have a way to tap into the necessary events for this - we'll put that on the backlog to consider for a future enhancement - but we can hack into it a bit to achieve the effect you want.

    The sample below uses some of the classes from the underlying DSL implementation. It sets a couple of events handlers that trigger whenever stereotype instances are created or deleted. See " Event Handlers ". The handlers need to be set up when the model opens, so we abuse the "Validate on open" feature to set them: instead of doing any validation, we just register the handlers.

    One slight drawback of using event handlers is that the handlers are called after the triggering transaction completes. This means that the colors are changed in a separate transaction. So if the user does an "Undo" after setting a stereotype, the color change is undone, but it requires another Undo to un-apply the stereotype. This might or might not be what you want. To do the whole thing in one transaction would require a Rule rather than an event handler; but registering rules is a hack involving so much reflection and invocation of internal stuff that I'm too embarrassed to show it to you. :)  - But if you really need this, ask again and I'll post it separately.

     To use this, insert it in a VSIX model extension. Start by creating a new project from the "Model Validation Extension" template (CTRL+SHIFT+O, then click Modeling Projects...) When you hit F5, you get a new VS in which you can test this on a class diagram. To deploy, copy the VSIX out of bin/debug. More at Extending UML .

    This sample just colors UML classes and interfaces, and I've picked out some of the standard stereotypes that are applicable by default.

      

    using System;
    using System.Collections.Generic;
    using System.ComponentModel.Composition;
    using System.Drawing;
    using System.Linq;
    using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
    using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
    using Microsoft.VisualStudio.Modeling;
    using Microsoft.VisualStudio.Modeling.Diagrams;
    using Microsoft.VisualStudio.Modeling.Validation;
    using Microsoft.VisualStudio.Uml.AuxiliaryConstructs;
    using Microsoft.VisualStudio.Uml.Classes;
    using Microsoft.VisualStudio.Uml.Profiles;
    using Microsoft.VisualStudio.Uml.UseCases;

    /// <summary> /// Wraps a UML model to add stereotype coloring etc. /// </summary> public partial class ColoringModelAdapter { /// <summary> /// This isn't actually validation. It's to couple this adapter to the model before we start. /// The validation package creates an instance of this class and then calls this method. /// See "Validation": http://msdn.microsoft.com/library/bb126413.aspx /// </summary> /// <param name="vcontext"></param> /// <param name="model"></param> [Export(typeof(System.Action<ValidationContext, object>))] [ValidationMethod(ValidationCategories.Open)] public void ConnectAdapterToModel(ValidationContext vcontext, IModel model) { // This is the underlying DSL store, not the wrapping UML ModelStore: store = (model as ModelElement).Store; // Add an event that triggers on creating a stereotype instance. // See "Event handlers": http://msdn.microsoft.com/library/bb126250.aspx DomainClassInfo siClass = store.DomainDataDirectory.FindDomainClass("Microsoft.VisualStudio.Uml.Classes.StereotypeInstance"); store.EventManagerDirectory.ElementAdded.Add(siClass, new EventHandler<ElementAddedEventArgs>(StereotypeInstanceAdded)); // For the deletion, we need to trigger from the deleted link // between the stereotype instance and the model element - // because after deletion we can't find the element from the stereotype instance. DomainRelationshipInfo linkToStereotypeClass = store.DomainDataDirectory.FindDomainRelationship ("Microsoft.VisualStudio.Uml.Classes.ElementHasAppliedStereotypeInstances"); store.EventManagerDirectory.ElementDeleted.Add(linkToStereotypeClass, new EventHandler<ElementDeletedEventArgs>(StereotypeInstanceDeleted)); } /// <summary> /// Called whenever a stereotype instance is linked to a uml model element. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void StereotypeInstanceAdded(object sender, ElementAddedEventArgs e) { // Don't handle changes in undo or load from file: if (store.InUndoRedoOrRollback || store.InSerializationTransaction) return; IStereotypeInstance si = e.ModelElement as IStereotypeInstance; IElement umlElement = si.Element; // I'm only interested in coloring classes and interfaces: if (!(umlElement is IType)) return; Color? color = ColorForStereotype(si.Name); if (color.HasValue) { SetColorOfShapes(si.Element, color.Value); } } /// <summary> /// Called whenever a stereotype instance is deleted - well, actually, /// when the link between the stereotype instance and the uml model element is deleted. /// Triggering on the link deletion allows us to get both ends. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void StereotypeInstanceDeleted(object sender, ElementDeletedEventArgs e) { // Don't handle changes in undo or load from file: if (store.InUndoRedoOrRollback || store.InSerializationTransaction) return; // Use the generic link type to avoid unearthing the UML implementation DLL: ElementLink elementToStereotypeLink = e.ModelElement as ElementLink; IElement umlElement = elementToStereotypeLink.LinkedElements[0] as IElement; IStereotypeInstance si = elementToStereotypeLink.LinkedElements[1] as IStereotypeInstance; // We're here either because a stereotype is being un-applied, // or because the uml element is being deleted. // Don't bother if the element is being deleted: if ((umlElement as ModelElement).IsDeleting) return; // We're only interested in classes and interfaces: if (!(umlElement is IType)) return; // Because more than one stereotype can be applied to an element, // we should check to see if there are any remaining: Color newColor = Color.WhiteSmoke; // Default if there aren't. foreach (IStereotypeInstance remainingSi in umlElement.AppliedStereotypes) { Color? color = ColorForStereotype(remainingSi.Name); if (color.HasValue) { newColor = color.Value; break; } } SetColorOfShapes(umlElement, newColor); } private void SetColorOfShapes(IElement element, Color color) { foreach (IShape shape in element.Shapes()) { shape.Color = color; } } private Color? ColorForStereotype(string name) { switch (name) { case "focus": return Color.AliceBlue; case "auxiliary": return Color.Bisque; case "specification": return Color.OliveDrab; case "realization": return Color.LightSeaGreen; case "implementationClass": return Color.PaleGoldenrod; } return null; } private Store store;
      }
     

    Friday, November 5, 2010 5:19 PM
  • Hi Phil,

    Did you make any progress on this?

    Colour coding diagrams by stereotypes is used by a lot of UML practictioners.

    The solution to use validation events does not appeal both because of the delays and using validation to do the updates.

    A DSL extension to do this at the time the stereotype is set or changed would be ideal, it would be great if the solution came from the development team.

    Kind Regards,

    Ozzy Geoff

    Friday, January 21, 2011 5:40 PM
  • Agreed that it would be better if the dev team produced a solution! This is the best we can do otherwise.

    As you say Ozzy, it's best to use the events at the time the stereotype is set or changed. This is in fact what the solution above actually does. The validation-on-open event is used only to set up the listener for the stereotype events initially.

    Alan


    - Alan -MSFT
    Friday, January 21, 2011 5:46 PM
  • Hi Alan,

    Apologies, I will study your solution in more depth, I saw the Validation attributes and jumped to my conclusions.

    Kind Regards,

    Ozzy

    Friday, January 21, 2011 5:55 PM
  • Thanks for raising this though, because if you saw it that way, others will have. I shouldha made it more clear.

    Re the proper solution - the dev team are heavily involved in other stuff now, so it looks likely that this is the best we can do for the time being. I'll put up a page about this on the MSDN library next month.

    Cheers

    Alan


    - Alan -MSFT
    Friday, January 21, 2011 5:59 PM