Share via


Gewusst wie: Reagieren auf Änderungen in einem UML-Modell

Sie können Code schreiben, der immer dann ausgeführt wird, wenn ein UML-Modell in Visual Studio verändert wird. Er reagiert auf Änderungen, die direkt vom Benutzer oder durch andere Visual Studio-Erweiterungen vorgenommen werden, gleichermaßen.

Warnung

Diese Funktion wird von der UML-Erweiterungs-API nicht direkt unterstützt. Daher müssen Sie etwas unkonventionelle Techniken verwenden. Der notwendige Code wird in diesem Thema bereitgestellt. Es ist jedoch möglich, dass in einigen Fällen unerwartete Effekte auftreten. Überdies ist möglich, dass die in diesem Thema beschriebenen Techniken durch zukünftige Änderungen in der UML-Implementierung von Visual Studio ungültig werden.

Für die Verwendung der in diesem Thema beschriebenen Techniken ist es hilfreich, mit dem Visualisierungs- und Modellierungs-SDK (VMSDK) vertraut zu sein, mit dem die UML-Tools implementiert werden. Weitere Informationen finden Sie unter Visualisierungs- und Modellierungs-SDK - Domänenspezifische Sprachen .

In diesem Thema:

  • Erstellen eines UML-Erweiterungsprojekts

  • Ereignisse und Regeln

  • Definieren von Ereignissen

  • Beispiel: Einfärben von Klassen von Stereotypen mithilfe von Ereignissen

  • Definieren von Regeln

  • Beispiel: Einfärben von Klassen von Stereotypen mithilfe von Regeln

  • Beispiel: Zuordnungen, die standardmäßig bidirektional sind

  • Haupt- und Ansichtsmodelle

Erstellen eines UML-Erweiterungsprojekts

In vielen Fällen fügen Sie einer Erweiterung, die bereits einen Befehl oder Gestenhandler implementiert, einen Ereignishandler hinzu. In diesem Fall können Sie den hier beschriebenen Code dem gleichen Visual Studio-Projekt hinzufügen. Weitere Informationen finden Sie unter Gewusst wie: Definieren eines Menübefehls in einem Modellierungsdiagramm und Gewusst wie: Definieren eines Handlers für Ablagevorgänge und Doppelklicks in einem Modellierungsdiagramm.

Wenn der Ereignishandler in einer separaten Visual Studio-Erweiterung erstellt werden soll, erstellen Sie zuerst ein neues UML-Validierungsprojekt. Klicken Sie im Dialogfeld Neues Projekt auf Modellierungsprojekte, und wählen Sie dann die Option für die Modellvalidierungserweiterung aus. Stattdessen können Sie auch das Verfahren Definieren einer Validierungserweiterung unter Gewusst wie: Definieren von Validierungseinschränkungen für UML-Modelle anwenden.

Sie müssen dem Projekt die folgenden Verweise hinzufügen:

  • \Program Files\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Microsoft.VisualStudio.Uml.dll

  • Microsoft.VisualStudio.ArchitectureTools.Extensibility.dll

  • Microsoft.VisualStudio.Modeling.Sdk.10.0dll

  • Microsoft.VisualStudio.Modeling.Sdk.Diagrams.10.0.dll

  • Microsoft.VisualStudio.Uml.Interfaces.dll

Ereignisse und Regeln

VMSDK stellt zwei Hauptmethoden zum Erkennen von Änderungen im Speicher bereit:

  • Ein Ereignishandler reagiert nach dem Ende der Transaktion, in der eine Änderung aufgetreten ist, auf die Änderung. Ereignishandler werden in der Regel verwendet, um Änderungen außerhalb des Modells an Benutzeroberflächen, Dateien oder Datenbanken weiterzugeben. Sie können auch einen Ereignishandler schreiben, der in einer neuen Transaktion eine zusätzliche Änderung am Modell vornimmt.

    Eine Regel reagiert innerhalb der Transaktion, in der eine Änderung auftritt, auf die Änderung. In der Regel werden Regeln verwendet, um Änderungen im Modell weiterzugeben, um die Konsistenz verschiedener Teile des Modells aufrechtzuerhalten. Sie können auch eine Regel schreiben, mit der unzulässige Änderungen durch den Abbruch der Transaktion verhindert werden.

Weitere Informationen zu Transaktionen finden Sie unter Gewusst wie: Verknüpfen von Modellaktualisierungen mithilfe von Transaktionen.

Definieren von Ereignishandlern

Damit der Ereignishandler aufgerufen wird, wenn eine Änderung auftritt, müssen Sie ihn registrieren. Sie müssen den Handler für jede Elementklasse, die überwacht werden soll, registrieren, z. B. UseCase oder Activity. Er muss nicht für jede Instanz registriert werden.

Im folgenden Beispiel wird die Farbe einer UML-Klasse nach dem Stereotyp festgelegt, das der Benutzer anwendet. Die Ereignishandler werden registriert, damit sie immer dann ausgelöst werden, wenn eine Stereotypinstanz erstellt oder gelöscht wird. Da in diesem Beispiel Ereignishandler und keine Regeln verwendet werden, werden die Handler nach dem Abschluss der Transaktion, in der das Stereotyp geändert wird, aufgerufen. Da die Farbe auch eine Änderung an der VMSDK-Klasse Store darstellt, muss sie in einer zweiten Transaktion ausgeführt werden.

Sie könnten mithilfe von Ereignishandlern auch Änderungen außerhalb der Store-Klasse durchführen.

Sie können den Code für das folgende Beispiel anpassen, um auf Ereignisse Ihrer Wahl zu reagieren. Bei diesem Code sind die folgenden wichtigen Punkte zu beachten:

  • Die Validierungs-API wird zum Registrieren der Ereignishandler verwendet. Das Validierungsframework stellt eine komfortable Möglichkeit bereit, Code beim Öffnen des Modells auszuführen. Die Validierung wird aber nicht im Code ausgeführt, und der Benutzer muss die Validierung nicht aufrufen, um Aktualisierungen durchzuführen.

  • Eventhandler sind Methoden, die Store.EventManagerDirectory hinzugefügt werden. Hierbei handelt es sich um die Store-Klasse der zugrunde liegenden VMSDK (DSL)-Implementierung, nicht das UML-Element ModelStore. EventManagerDirectory verfügt über einen festen Satz von Wörterbüchern für verschiedene Ereignisstypen, z. B. ElementAdded und ElementDeleted.

  • Um ein Ereignis zu registrieren, müssen Sie den Namen der Implementierungsklasse oder der Beziehung kennen, die überwacht werden soll. Diese Klassen werden in Microsoft.VisualStudio.Modeling.Uml.dll definiert, und Sie sehen die Klassennamen, wenn Sie Eigenschaften im Debugger beobachten. Sie können diese Klassenmember in die entsprechenden Schnittstellentypen, z. B. IClass, IStereotype, umwandeln. Eine Liste der Schnittstellentypen finden Sie unter Modellelementtypen.

    Der Klassenname der Implementierung kann in zukünftigen Versionen anders lauten.

  • Die Ereignishandler werden aufgerufen, wenn der Benutzer die Befehle Rückgängig und Wiederholen aufruft. Zum Beispiel wird nach einem Add-Ereignis durch den Befehl Rückgängig ein Delete-Ereignis ausgelöst. Der Ereignishandler sollte auf diese Ereignisse reagieren, wenn er Änderungen außerhalb der Store-Klasse weitergibt. Er sollte jedoch in Reaktion auf die Befehle Rückgängig oder Wiederholen keine Änderungen an der Store-Klasse vornehmen, und er sollte keine Änderungen vornehmen, wenn das Modell aus einer Datei gelesen wird. Sie können if (!store.InUndoRedoOrRollback && !store.InSerializationTransaction)... verwenden.

  • Im Beispiel werden Ereignishandler zum Hinzufügen und Löschen von Objekten im Modell veranschaulicht. Sie können auch Ereignishandler für Änderungen an Eigenschaftswerten erstellen. Weitere Informationen finden Sie unter Ereignishandler propagieren Änderungen außerhalb des Modells .

  • Ausführlichere Informationen zu Ereignissen finden Sie unter Ereignishandler propagieren Änderungen außerhalb des Modells.

Beispiel: Einfärben von Klassen nach Stereotyp mithilfe von Ereignissen

Für dieses Beispiel müssen Sie auch einen Projektverweis auf System.Drawing.dll hinzufügen.

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;

using Microsoft.VisualStudio.Uml.ModelStore; // in private assembly. Used for Get|IsElementDefinition()

namespace UmlEvents  // <<<< EDIT
{
/// <summary>
/// Wraps a UML model to add stereotype coloring.
/// </summary>
public partial class ColoringModelAdapter
{
  // This is the underlying DSL store, not the wrapping UML ModelStore:
  private Store store;

  /// <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": https://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": https://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));

    // Add here handlers for other events.
  }

  /// <summary>
  /// Event handler 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;

     // Work only with the core model, not the views:
     if (!umlElement.IsElementDefinition()) return;

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

     // Work only with the core model, not the views:
     if (!umlElement.IsElementDefinition()) return;

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

  /// <summary>
  /// This example deals only with a subset of the standard stereotypes.
  /// </summary>
  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;
  } 
}}

Definieren von Regeln

Sie können eine Regel definieren, um eine Änderung innerhalb der VMSDK-Klasse Store weiterzugeben. Sowohl die auslösende Änderung als auch die Regel werden innerhalb derselben Transaktion ausgeführt. Wenn der Benutzer Rückgängig aufruft, werden beide Änderungen zusammen rückgängig gemacht.

Ein Nachteil des vorherigen Beispiels ist, dass die Farbe der Formen mithilfe von Ereignishandlern geändert wird. Die Farbe wird auch einem Feld in der VMSDK-Klasse Store angefügt, und daher muss die Änderung in einer Transaktion ausgeführt werden. Infolgedessen gilt, wenn der Benutzer den Befehl Rückgängig aufruft, nachdem ein Stereotyp übernommen wurde, dann wird die Farbänderung rückgängig gemacht, das Stereotyp wird aber weiterhin angewendet. Ein weiterer Aufruf von Rückgängig ist erforderlich, um die Anwendung des Stereotyps rückgängig zu machen. In manchen Fällen mag dies dem von Ihnen beabsichtigten Effekt entsprechen. Ist das jedoch nicht der Fall, dann können Sie sämtliche Änderungen in einer Transaktion weitergeben, indem Sie Regeln definieren.

Regeln sind nicht so gut zum Weitergeben von Änderungen außerhalb des Speichers geeignet, da sie nicht aufgerufen werden, wenn der Benutzer den Befehl Rückgängig oder Wiederholen ausführt.

Eine Regel ist eine Klasse, die beim Regel-Manager registriert wird. Wenn Sie VMSDK-Code schreiben, registrieren Sie Regeln für gewöhnlich, indem Sie der Klasse ein Attribut anfügen und die Klasse in eine Liste aufnehmen, die beim Laden des Erweiterungscodes gelesen wird. Da die UML-Implementierung bereits kompiliert ist, müssen Sie die Regel dem Regel-Manager dynamisch hinzufügen. Der im Beispiel dargestellte Code ist stark von der Regelverwaltung der aktuellen Implementierung abhängig, die sich in zukünftigen Versionen ändern könnte.

Um eine Regel hinzufügen zu können, müssen Sie die Namen der Implementierungsklassen kennen. Diese können sich in zukünftigen Versionen ändern. So weit wie möglich sollten Sie Elemente in die UML-API-Typen, z. B. IClass, IProfile, umwandeln.

Im Beispiel werden Regeln veranschaulicht, mit deren Hilfe Hinzufügungen und Löschungen von Objekten im UML-Modell behandelt werden. Sie können auch Regeln erstellen, die auf Änderungen an den Eigenschaften von Objekten reagieren. Weitere Informationen finden Sie unter Regeln propagieren Änderungen im Modell .

Beispiel: Einfärben von Klassen nach Stereotyp mithilfe von Regeln

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Drawing;
using System.Linq;
using System.Text;
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.UseCases; 

using Microsoft.VisualStudio.Uml.ModelStore; // in private assembly. Used for Get|IsElementDefinition()


namespace UmlRules
{
  class ColorByStereotype
  {
    /// <summary>
    /// Singleton wrappers: one per model.
    /// </summary>
    private static Dictionary<IPackage, ColorByStereotype > modelAdapters = 
        new Dictionary<IPackage, ColorByStereotype >();

    private class Wrapper
    {
      /// <summary>
      /// This isn't actually validation. 
      /// It sets up some store rules.
      /// The validation package creates an instance of this class and then calls this method.
      /// See "Validation": https://msdn.microsoft.com/library/bb126413.aspx
      /// </summary>
      /// <param name="vcontext"></param>
      /// <param name="model"></param>
      [Export(typeof(System.Action<ValidationContext, object>))]
      [ValidationMethod(ValidationCategories.Open)]
      private void ConnectAdapterToModel(ValidationContext vcontext, IModel model)
      {
        modelAdapters.Add(model, new ColorByStereotype (model));
      }
    }

    private IModel model;
    private Store store;
    private ColorByStereotype (IModel model)
    {
      this.model = model;
      // This is the underlying DSL store, not the wrapping UML ModelStore:
      store = (model as ModelElement).Store;

      SetRule<StereotypeInstanceAddedRule>(
        store.DomainDataDirectory.FindDomainClass(
          "Microsoft.VisualStudio.Uml.Classes.StereotypeInstance"));
      
      // 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.
      
      SetRule<StereotypeInstanceDeletedRule>(
        store.DomainDataDirectory.FindDomainRelationship(
        "Microsoft.VisualStudio.Uml.Classes.ElementHasAppliedStereotypeInstances"));
    }

    /// <summary>
    /// Register a rule. 
    /// Normally, you set a rule by prefixing the rule class with 
    /// [RuleOn(typeof(TargetClass))]
    /// but we are setting up the rule at runtime, so must add
    /// the rules to the relevant dictionaries.
    /// </summary>
    /// <typeparam name="T">Rule class</typeparam>
    /// <param name="classInfo">Class or relationship to which to attach the rule.</param>
    private void SetRule<T>(DomainClassInfo classInfo) where T : Rule, new()
    {
      T rule = new T();
      rule.FireTime = TimeToFire.TopLevelCommit;

      System.Type tt = typeof(T);
      string ruleSet = (typeof(AddRule).IsAssignableFrom(tt)) ? "AddRules" :
        (typeof(ChangeRule).IsAssignableFrom(tt)) ? "ChangeRules" :
        (typeof(DeleteRule).IsAssignableFrom(tt)) ? "DeleteRules" :
        (typeof(DeletingRule).IsAssignableFrom(tt)) ? "DeletingRules" : "";

      // The rest of this method uses reflection to achieve the following:
      // store.RuleManager.RegisterRule(rule);
      // classInfo.AddRules.Add(rule);

      System.Reflection.BindingFlags privateBinding = 
          System.Reflection.BindingFlags.Instance 
        | System.Reflection.BindingFlags.NonPublic;
      System.Reflection.MethodInfo mi = 
        typeof(RuleManager).GetMethod("RegisterRule", privateBinding);
      mi.Invoke(store.RuleManager, new object[] { rule });

      store.RuleManager.EnableRule(typeof(T));

      System.Reflection.PropertyInfo pi = 
        typeof(DomainClassInfo).GetProperty(ruleSet, privateBinding);
      dynamic rules = pi.GetValue(classInfo, null);
      System.Type ruleListType = rules.GetType();
      System.Reflection.FieldInfo listpi = 
        ruleListType.GetField("list", privateBinding);
      dynamic list = listpi.GetValue(rules);
      System.Type listType = list.GetType();
      System.Reflection.MethodInfo addmi = listType.GetMethod("Add");
      addmi.Invoke(list, new object[] { rule });


      System.Reflection.MethodInfo resetRulesCache = 
        typeof(DomainClassInfo).GetMethod("ResetRulesCache", privateBinding);
      resetRulesCache.Invoke(classInfo, null);

    }

    #region Rules.
    private class StereotypeInstanceAddedRule : AddRule
    {
      public override void ElementAdded(ElementAddedEventArgs e)
      {
        base.ElementAdded(e);
        Store store = e.ModelElement.Store;
        // Don't handle load from file:
        if (store.InSerializationTransaction)
          return;

        IStereotypeInstance si = e.ModelElement as IStereotypeInstance;
        IElement umlElement = si.Element;
        
         // Work only with the core model, not the views:
         if (!umlElement.IsElementDefinition()) return;

        // 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);
        }
      }
    }
    private class StereotypeInstanceDeletedRule : DeleteRule
    {
      public override void ElementDeleted(ElementDeletedEventArgs e)
      {
        base.ElementDeleted(e);
        Store store = e.ModelElement.Store;


        // Use the generic link type to avoid using the UML implementation DLL:
        ElementLink elementToStereotypeLink = e.ModelElement as ElementLink;
        IElement umlElement = elementToStereotypeLink.LinkedElements[0] as IElement;

         // Work only with the core model, not the views:
         if (!umlElement.IsElementDefinition()) return;

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

    /// <summary>
    /// Set the color of the shapes that display an element.
    /// </summary>
    /// <param name="element"></param>
    /// <param name="color"></param>
    private static void SetColorOfShapes(IElement element, Color color)
    {
      foreach (IShape shape in element.Shapes())
      {
        shape.Color = color;
      }
    }

    /// <summary>
    /// For this sample, we just deal with some of the standard stereotypes.
    /// </summary>
    /// <param name="name">Stereotype name</param>
    /// <returns></returns>
    private static 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;
    }
    #endregion
  }
}

Beispiel: Zuordnungen, die standardmäßig bidirektional sind

Wenn in einem Klassendiagramm eine Zuordnung gezeichnet wird, ist bei der neuen Zuordnung standardmäßig nur die Navigation in einer Richtung möglich. Sie hat an einem Ende eine Pfeilspitze. Zu einigen Zwecken ist es günstiger, bidirektionale Zuordnungen ohne Pfeilspitzen zu zeichnen. Sie können bidirektionale Zuordnungen als Standard festlegen, indem Sie die folgende Regel hinzuzufügen.

/// <summary>
/// Rule invoked when an Association is created.
/// This rule sets both ends navigable, which is convenient for representing requirements.
/// </summary>
private class AssociationAddRule : AddRule
{
  public override void ElementAdded(ElementAddedEventArgs e)
  {
    Store store = e.ModelElement.Store;
    IAssociation association = e.ModelElement as IAssociation;

    // Do not apply the rule if we are reading from file or undoing a deletion:
    if (association.MemberEnds.Count() == 0 
       || store.InSerializationTransaction || store.InUndoRedoOrRollback) return;

    // Do not apply the rule unless a containing package or model has imported 
    // a profile that defines the stereotype "Default Binary Associations" for associations:
    // if (!association.ApplicableStereotypes.Any
    //      (s => s.DisplayName == "Default Binary Associations")) return;

    // Don’t apply the rule to use cases:
    if (!(association.SourceElement is IUseCase && association.TargetElement is IUseCase))
    {
      association.OwnedEnds.First().SetNavigable(true);
      association.OwnedEnds.Last().SetNavigable(true);
    }
  }
}

Zum Registrieren der Regel müssen Sie die unter Definieren von Regeln beschriebene SetRule-Methode verwenden:

SetRule<AssociationAddRule>(store.DomainDataDirectory.
      FindDomainRelationship("Microsoft.VisualStudio.Uml.Classes.Association"));

Wenn Sie in der Lage sein möchten, diese Regel zu aktivieren und zu deaktivieren, besteht eine Möglichkeit darin, ein Profil zu definieren, in dem ein bestimmtes Stereotyp definiert wird. Sie konnten der Regel Code hinzufügen, um zu überprüfen, ob das Profil in einem enthaltenden Paket oder Modell aktiviert wurde. Weitere Informationen finden Sie unter Gewusst wie: Definieren eines Profils zum Erweitern von UML .

Haupt- und Ansichtsmodelle

Das UML-Modell besteht nicht nur aus einem VMSDK (DSL)-Modell:

  • Das Kernmodell enthält Darstellungen aller Elemente des UML-Modells. Der Benutzer kann diese Elemente im Fenster UML-Modell-Explorer sehen, und Sie können über die UML-API ModelStore auf sie zugreifen. Das Kernmodell nimmt eine VMSDK-Store-Partition ein.

  • Für jedes UML-Diagramm im UML-Projekt ist ein Ansichtsmodell vorhanden. Die Objekte in den einzelnen Ansichtsmodellen sind Proxys für Objekte im Kernmodell. Für jedes Element, das im UML-Diagramm angezeigt wird, ist ein Ansichtsmodellobjekt verfügbar. Jedes Ansichtsmodell belegt eine VMSDK-Store-Partition.

  • Jedem im Diagramm dargestellten Element ist ein VMSDK-Form-Objekt zugeordnet. Zwischen den Ansichtsmodellelementen und den Formen besteht eine 1:1 Beziehung.

Wenn Sie Regeln oder Ereignishandler definieren, werden beide aufgerufen, wenn sich Kern- und Ansichtsobjekte ändern. Sie sollten nur Änderungen an den Kernobjekten behandeln. Die Handler in diesen Beispielen bestimmen mithilfe von element.IsElementDefinition(), ob sie es mit dem Kernobjekt zu tun haben.

Um diese Methode zu verwenden, müssen Sie wie folgt einen Projektverweis hinzufügen:

%ProgramFiles%\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Microsoft.VisualStudio.Uml.dll

Warnung

IsElementDefinition und andere in der privaten Assembly definierten Methoden können sich in zukünftigen Versionen ändern.

using Microsoft.VisualStudio.Uml.ModelStore; 
  // in private assembly. Used for GetElementDefinition()
...
  // Determine whether an element is view or core:
  if (anElement.IsElementDefinition()) 
  { /* core */ }
  else
  { /* view */ }
...
  // If shapeElement is a shape on a diagram -
  // The VMSDK element connected to the shape is in the view:
  IElement viewModelElement = shapeElement.ModelElement as IElement;
  // Get the core element for which the view is a proxy:
  IElement coreModelElement = viewModelElement.GetElementDefinition();
...

Siehe auch

Aufgaben

Ereignishandler propagieren Änderungen außerhalb des Modells

Weitere Ressourcen

Gewusst wie: Navigieren im UML-Modell

Beispiel: Farbe nach Stereotyp

Änderungsprotokoll

Datum

Versionsgeschichte

Grund

März 2011

Thema erstellt.

Kundenfeedback.