Anpassen der Elementerstellung und -verschiebung

Sie können das Ziehen eines Elements auf ein anderes zulassen, entweder aus der Toolbox oder in einem Einfüge- oder Verschiebevorgang. Sie können die verschobenen Elemente mithilfe der von Ihnen angegebenen Beziehungen mit den Zielelementen verknüpfen lassen.

Eine Elementmerge-Anweisung (Element Merge Directive, EMD) gibt an, was geschieht, wenn ein Modellelement mit einem anderen Modellelement zusammengeführt wird. Dies kann in folgenden Fällen erforderlich sein:

  • Der Benutzer zieht aus der Toolbox auf das Diagramm oder ein Shape.

  • Der Benutzer erstellt ein Element mithilfe eines Menüs „Hinzufügen“ im Explorer oder einer Depotform.

  • Der Benutzer verschiebt ein Element von einer Swimlane in eine andere.

  • Der Benutzer fügt ein Element ein.

  • Ihr Programmcode ruft die Elementmerge-Anweisung auf.

Obwohl sich die Erstellvorgänge augenscheinlich von den Kopiervorgängen unterscheiden, funktionieren sie tatsächlich auf die gleiche Weise. Wenn ein Element hinzugefügt wird, z. B. aus der Toolbox, wird ein Prototyp davon repliziert. Der Prototyp wird auf die gleiche Weise mit dem Modell zusammengeführt wie Elemente, die aus einem anderen Teil des Modells kopiert wurden.

Die Zuständigkeit einer EMD besteht in der Entscheidung, wie ein Objekt oder eine Gruppe von Objekten an einer bestimmten Stelle im Modell zusammengeführt werden soll. Insbesondere entscheidet sie, welche Beziehungen instanziiert werden sollen, um die zusammengeführte Gruppe mit dem Modell zu verknüpfen. Sie können sie darüber hinaus anpassen, um Eigenschaften festzulegen und zusätzliche Objekte zu erstellen.

Diagram showing a before and after look at a tree of elements and their reference relationships when An E M D determines how a new element is added.

Eine EMD wird automatisch generiert, wenn Sie eine Embedding Relationship definieren. Diese Standard-EMD erstellt eine Instanz der Beziehung, wenn Benutzer dem übergeordneten Element neue untergeordnete Instanzen hinzufügen. Sie können diese Standard-EMDs ändern, z. B. durch Hinzufügen von benutzerdefiniertem Code.

Sie können außerdem eigene EMDs zur DSL-Definition hinzufügen, damit Benutzer verschiedene Kombinationen von zusammengeführten und empfangenden Klassen ziehen oder einfügen können.

Definieren einer Elementmerge-Anweisung

Sie können Elementmerge-Anweisungen zu Domänenklassen, Domänenbeziehungen, Formen, Connectors und Diagrammen hinzufügen. Sie können sie im DSL-Explorer unter der empfangenden Domänenklasse hinzufügen oder suchen. Die empfangende Klasse ist die Domänenklasse des Elements, das sich bereits im Modell befindet und mit dem das neue oder kopierte Element zusammengeführt wird.

Screenshot of DSL Explorer showing an E M D being added with ExampleElement selected as the Indexing class and the Applies to subclasses option checked.

Die Indizierungsklasse ist die Domänenklasse von Elementen, die mit Mitgliedern der empfangenden Klasse zusammengeführt werden können. Instanzen von Unterklassen der Indizierungsklasse werden ebenfalls von dieser EMD zusammengeführt, es sei denn, Sie legen Auf Unterklassen anwenden auf False fest.

Es gibt zwei Arten von Merge-Anweisung:

  • Eine Prozessmerge-Anweisung gibt die Beziehungen an, über die das neue Element mit der Struktur verknüpft werden soll.

  • Eine Forward Merge-Anweisung leitet das neue Element auf ein anderes empfangendes Element um, normalerweise ein übergeordnetes Element.

Sie können Merge-Anweisungen benutzerdefinierten Code hinzufügen:

  • Legen Sie Verwendet benutzerdefiniertes Akzeptieren fest, um Ihren eigenen Code hinzuzufügen, um zu bestimmen, ob eine bestimmte Instanz des Indizierungselements mit dem Zielelement zusammengeführt werden soll. Wenn der Benutzer aus der Toolbox zieht, wird mit dem Zeiger „ungültig“ angezeigt, ob der Code die Zusammenführung verbietet.

    Beispielsweise können Sie die Zusammenführung nur dann zulassen, wenn sich das empfangende Element in einem bestimmten Zustand befindet.

  • Legen Sie Verwendet benutzerdefinierten Merge fest, um eigenen Code hinzuzufügen, um die Änderungen zu definieren, die beim Zusammenführen am Modell vorgenommen werden.

    Beispielsweise können Sie Eigenschaften im zusammengeführten Element festlegen, indem Sie Daten aus seinem neuen Speicherort im Modell verwenden.

Hinweis

Wenn Sie benutzerdefinierten Mergecode schreiben, wirkt sich dies nur auf Zusammenführungen aus, die mit dieser EMD ausgeführt werden. Wenn es andere EMDs gibt, die zur Zusammenführung denselben Objekttyps dienen, oder wenn es weiteren benutzerdefinierten Code gibt, der diese Objekte erstellt, ohne die EMD zu verwenden, sind sie von Ihrem benutzerdefinierten Mergecode nicht betroffen.

Wenn Sie sicherstellen möchten, dass ein neues Element oder eine neue Beziehung immer von Ihrem benutzerdefinierten Code verarbeitet wird, sollten Sie eine AddRule für die Einbettungsbeziehung und eine DeleteRule für die Domänenklasse des Elements definieren. Weitere Informationen finden Sie unter Regeln propagieren Änderungen im Modell.

Beispiel: Definieren einer EMD ohne benutzerdefinierten Code

Im folgenden Beispiel können Benutzer durch Ziehen aus der Toolbox auf eine vorhandene Form zugleich ein Element und einen Connector erstellen. Im Beispiel wird der DSL-Definition eine EMD hinzugefügt. Vor dieser Änderung können Benutzer Tools auf das Diagramm ziehen, aber nicht auf vorhandene Formen.

Benutzer können außerdem Elemente in andere Elemente einfügen.

So können Benutzer ein Element und zugleich einen Connector erstellen

  1. Erstellen Sie eine neue DSL mithilfe der Projektmappenvorlage Minimale Sprache.

    Wenn Sie diese DSL ausführen, können Sie Formen und Connectors zwischen den Formen erstellen. Sie können keine neue ExampleElement-Form aus der Toolbox auf eine vorhandene Form ziehen.

  2. Damit Benutzer Elemente mit ExampleElement-Formen zusammenführen können, erstellen Sie eine neue EMD in der ExampleElement-Domänenklasse:

    1. Erweitern Sie im DSL-ExplorerDomänenklassen. Klicken Sie mit der rechten Maustaste auf ExampleElement, und klicken Sie dann auf Neue Elementmerge-Anweisung hinzufügen.

    2. Vergewissern Sie sich, dass das Fenster DSL-Details geöffnet ist, damit Sie die Details der neuen EMD sehen können. (Menü: Ansicht, Andere Fenster, DSL-Details.)

  3. Legen Sie die Indizierungsklasse im DSL-Detailfenster fest, um zu definieren, welche Klasse von Elementen mit ExampleElement-Objekten zusammengeführt werden kann.

    Wählen Sie für dieses Beispiel ExampleElements aus, damit der Benutzer neue Elemente auf vorhandene Elemente ziehen kann.

    Beachten Sie, dass die Indizierungsklasse zum Namen im DSL-Explorer der EMD wird.

  4. Fügen Sie unter Merge durch Erstellen von Links verarbeiten zwei Pfade hinzu:

    • Ein Pfad verknüpft das neue Element mit dem übergeordneten Modell. Der Pfadausdruck, den Sie eingeben müssen, navigiert vom vorhandenen Element durch die Einbettungsbeziehung zum übergeordneten Modell. Schließlich gibt er die Rolle im neuen Link an, dem das neue Element zugewiesen wird. Der Pfad lautet wie folgt:

      ExampleModelHasElements.ExampleModel/!ExampleModel/.Elements

    • Der andere Pfad verknüpft das neue Element mit dem vorhandenen Element. Der Pfadausdruck gibt die Verweisbeziehung und die Rolle an, der das neue Element zugewiesen wird. Dieser Pfad lautet folgendermaßen:

      ExampleElementReferencesTargets.Sources

      Zum Erstellen der Pfade können Sie das Pfadnavigationstool verwenden:

      1. Klicken Sie unter Merge durch Erstellen von Links in Pfaden verarbeiten auf <Pfad hinzufügen>.

      2. Klicken Sie auf den Dropdownpfeil rechts neben dem Listenelement. Eine Strukturansicht wird angezeigt.

      3. Erweitern Sie die Knoten in der Struktur, um den Pfad zu bilden, den Sie angeben möchten.

  5. Testen Sie die DSL:

    1. Drücken Sie F5, um die Projektmappe erneut zu erstellen und auszuführen.

      Die Neuerstellung dauert länger als gewohnt, da der generierte Code aus Textvorlagen aktualisiert wird, um der neuen DSL-Definition zu entsprechen.

    2. Wenn die experimentelle Instanz von Visual Studio gestartet wurde, öffnen Sie eine Modelldatei Ihrer DSL. Erstellen Sie einige Beispielelemente.

    3. Ziehen Sie aus dem Beispielelement-Tool auf eine vorhandene Form.

      Eine neue Form wird angezeigt, die durch einen Connector mit der vorhandenen Form verknüpft ist.

    4. Kopieren Sie eine vorhandene Form. Wählen Sie eine andere Form aus, und fügen Sie sie ein.

      Es wird eine Kopie der ersten Form erstellt. Sie hat einen neuen Namen und ist mit der zweiten Form durch einen Connector verknüpft.

Beachten Sie die folgenden Punkte aus diesem Verfahren:

  • Durch das Erstellen von Elementmerge-Anweisungen können Sie jeder Klasse von Elementen erlauben, jede andere zu akzeptieren. Die EMD wird in der empfangenden Domänenklasse erstellt, und die akzeptierte Domänenklasse wird im Feld Indexklasse angegeben.

  • Durch das Definieren von Pfaden können Sie angeben, welche Links verwendet werden sollen, um das neue Element mit dem vorhandenen Modell zu verbinden.

    Die von Ihnen angegebenen Links sollten eine Einbettungsbeziehung enthalten.

  • Die EMD wirkt sich sowohl auf die Erstellung aus der Toolbox als auch auf Einfügevorgänge aus.

    Wenn Sie benutzerdefinierten Code schreiben, der neue Elemente erstellt, können Sie mithilfe der ElementOperations.Merge-Methode die EMD explizit aufrufen. Dadurch wird sichergestellt, dass Ihr Code neue Elemente auf die gleiche Weise mit dem Modell verknüpft wie andere Vorgänge. Weitere Informationen finden Sie unter Anpassen des Kopierverhaltens.

Beispiel: Hinzufügen von benutzerdefiniertem Akzeptierungscode zu einer EMD

Durch Hinzufügen von benutzerdefiniertem Code zu einer EMD können Sie ein komplexeres Mergeverhalten definieren. In diesem einfachen Beispiel wird der Benutzer daran gehindert, dem Diagramm mehr als eine feste Anzahl von Elementen hinzuzufügen. Im Beispiel wird die Standard-EMD geändert, die eine Einbettungsbeziehung begleitet.

So schreiben Sie benutzerdefinierten Akzeptierungscode, um einzuschränken, was Benutzer hinzufügen können

  1. Erstellen Sie eine DSL mithilfe der Projektmappenvorlage Minimale Sprache. Öffnen Sie das DSL-Definitionsdiagramm.

  2. Erweitern Sie im DSL-Explorer Domänenklassen, ExampleModel, Elementmerge-Anweisungen. Wählen Sie die Elementmerge-Anweisung mit dem Namen ExampleElement aus.

    Diese EMD steuert, wie der Benutzer neue ExampleElement-Objekte im Modell erstellen kann, beispielsweise durch Ziehen aus der Toolbox.

  3. Wählen Sie im Fenster DSL-Detailsdie Option Verwendet benutzerdefiniertes Akzeptieren aus.

  4. Generieren Sie die Projektmappe neu. Dies dauert länger als gewohnt, da der generierte Code aus dem Modell aktualisiert wird.

    Es wird ein Buildfehler ähnlich dem folgenden gemeldet: „Company.ElementMergeSample.ExampleElement enthält keine Definition für CanMergeExampleElement...“

    Sie müssen die CanMergeExampleElement-Methode implementieren.

  5. Erstellen Sie eine neue Codedatei im Dsl-Projekt. Ersetzen Sie ihren Inhalt durch den folgenden Code, und ändern Sie den Namespace in den Namespace Ihres Projekts.

    using Microsoft.VisualStudio.Modeling;
    
    namespace Company.ElementMergeSample // EDIT.
    {
      partial class ExampleModel
      {
        /// <summary>
        /// Called whenever an ExampleElement is to be merged into this ExampleModel.
        /// This happens when the user pastes an ExampleElement
        /// or drags from the toolbox.
        /// Determines whether the merge is allowed.
        /// </summary>
        /// <param name="rootElement">The root element in the merging EGP.</param>
        /// <param name="elementGroupPrototype">The EGP that the user wants to merge.</param>
        /// <returns>True if the merge is allowed</returns>
        private bool CanMergeExampleElement(ProtoElementBase rootElement, ElementGroupPrototype elementGroupPrototype)
        {
          // Allow no more than 4 elements to be added:
          return this.Elements.Count < 4;
        }
      }
    }
    

    In diesem einfachen Beispiel wird die Anzahl der Elemente eingeschränkt, die mit dem übergeordneten Modell zusammengeführt werden können. Für interessantere Bedingungen kann die Methode jede der Eigenschaften und jeden der Links des empfangenden Objekts überprüfen. Sie kann außerdem die Eigenschaften der zusammenführenden Elemente untersuchen, die in einem ElementGroupPrototype übertragen werden. Weitere Informationen zu ElementGroupPrototypes finden Sie unter Anpassen des Kopierverhaltens. Weitere Informationen zum Schreiben von Code, der ein Modell liest, finden Sie unter Navigieren in und Aktualisieren von Modellen im Programmcode.

  6. Testen Sie die DSL:

    1. Drücken Sie F5, um die App neu zu erstellen. Wenn die experimentelle Instanz von Visual Studio geöffnet wird, öffnen Sie eine Instanz Ihrer DSL.

    2. Erstellen Sie auf verschiedene Weise neue Elemente:

      • Ziehen Sie aus dem Beispielelement-Tool auf das Diagramm.

      • Klicken Sie im Beispielmodell-Explorer mit der rechten Maustaste auf den Stammknoten, und klicken Sie dann auf Neues Beispielelement hinzufügen.

      • Kopieren Sie ein Element, und fügen Sie es in das Diagramm ein.

    3. Bestätigen Sie durch Überprüfen, dass Sie auf keinem dieser Wege mehr als vier Elemente zum Modell hinzufügen können. Dies liegt daran, dass alle die Elementmerge-Anweisung verwenden.

Beispiel: Hinzufügen von benutzerdefiniertem Mergecode zu einer EMD

Im benutzerdefinierten Mergecode können Sie definieren, was geschieht, wenn der Benutzer ein Tool zieht oder es in ein Element einfügt. Benutzerdefinierte Merges können auf zwei Arten definiert werden:

  1. Legen Sie Verwendet benutzerdefinierten Merge fest, und geben Sie den erforderlichen Code an. Ihr Code ersetzt den generierten Mergecode. Verwenden Sie diese Option, wenn Sie die Funktionsweise der Zusammenführung vollständig neu definieren möchten.

  2. Setzen Sie die Methode MergeRelate und optional die Methode MergeDisconnect außer Kraft. Dazu müssen Sie die Eigenschaft Generiert doppelte Ableitungen der Domänenklasse festlegen. Ihr Code kann den generierten Mergecode in der Basisklasse aufrufen. Verwenden Sie diese Option, wenn Sie nach dem Merge zusätzliche Vorgänge ausführen möchten.

    Diese Ansätze wirken sich nur auf Zusammenführungen aus, die mithilfe dieser EMD durchgeführt werden. Wenn Sie alle Möglichkeiten zum Erstellen des zusammengeführten Elements beeinflussen möchten, besteht eine Alternative darin, eine AddRule für die Einbettungsbeziehung und eine DeleteRule für die zusammengeführte Domänenklasse zu definieren. Weitere Informationen finden Sie unter Regeln propagieren Änderungen im Modell.

So setzen Sie MergeRelate außer Kraft

  1. Stellen Sie in der DSL-Definition sicher, dass Sie die EMD definiert haben, der Sie Code hinzufügen möchten. Wenn Sie möchten, können Sie Pfade hinzufügen und benutzerdefinierten Akzeptierungscode definieren, wie in den vorherigen Abschnitten beschrieben.

  2. Wählen Sie im DslDefinition-Diagramm die empfangende Klasse des Merges aus. In der Regel handelt es sich um die Klasse auf der Quellseite einer Einbettungsbeziehung.

    Wählen Sie beispielsweise in einer DSL, die aus der Lösung „Minimale Sprache“ generiert wird, die Option ExampleModel aus.

  3. Legen Sie im EigenschaftenfensterGeneriert doppelte Ableitungen auf true fest.

  4. Generieren Sie die Projektmappe neu.

  5. Überprüfen Sie den Inhalt von Dsl\Generated Files\DomainClasses.cs. Suchen Sie nach Methoden mit dem Namen MergeRelate, und untersuchen Sie deren Inhalt. Dadurch können Sie Ihre eigenen Versionen schreiben.

  6. Schreiben Sie in einer neuen Codedatei eine partielle Klasse für die empfangende Klasse, und setzen Sie die MergeRelate-Methode außer Kraft. Denken Sie daran, die Basismethode aufzurufen. Zum Beispiel:

    partial class ExampleModel
    {
      /// <summary>
      /// Called when the user drags or pastes an ExampleElement onto the diagram.
      /// Sets the time of day as the name.
      /// </summary>
      /// <param name="sourceElement">Element to be added</param>
      /// <param name="elementGroup">Elements to be merged</param>
      protected override void MergeRelate(ModelElement sourceElement, ElementGroup elementGroup)
      {
        // Connect the element according to the EMD:
        base.MergeRelate(sourceElement, elementGroup);
    
        // Custom actions:
        ExampleElement mergingElement = sourceElement as ExampleElement;
        if (mergingElement != null)
        {
          mergingElement.Name = DateTime.Now.ToLongTimeString();
        }
      }
    }
    

So schreiben Sie benutzerdefinierten Mergecode

  1. Untersuchen Sie in Dsl\Generated Code\DomainClasses.cs Methoden mit dem Namen MergeRelate. Diese Methoden erstellen Links zwischen einem neuen Element und dem vorhandenen Modell.

    Überprüfen Sie außerdem Methoden mit dem Namen MergeDisconnect. Diese Methoden heben die Verknüpfung eines Elements mit dem Modell auf, wenn es gelöscht werden soll.

  2. Wählen Sie im DSL-Explorer die Elementmerge-Anweisung aus, die Sie anpassen möchten, oder erstellen Sie sie. Legen Sie im Fenster DSL-Detailsdie Option Verwendet benutzerdefinierten Merge fest.

    Wenn Sie diese Option festlegen, werden die Optionen Merge verarbeiten und Merge weiterleiten ignoriert. Stattdessen wird Ihr Code verwendet.

  3. Generieren Sie die Projektmappe neu. Dies dauert länger als gewohnt, da die generierten Codedateien aus dem Modell aktualisiert werden.

    Es werden Fehlermeldungen angezeigt. Doppelklicken Sie auf die Fehlermeldungen, um die Anweisungen im generierten Code anzuzeigen. In diesen Anweisungen werden Sie aufgefordert, zwei Methoden anzugeben, MergeRelateIhreDomänenklasse und MergeDisconnectIhreDomänenklasse

  4. Schreiben Sie die Methoden in eine partielle Klassendefinition in einer separaten Codedatei. Aus den Beispielen, die Sie zuvor untersucht haben, sollten Sie Vorschläge entnehmen können, was benötigt wird.

    Benutzerdefinierter Mergecode wirkt sich nicht auf Code aus, der Objekte und Beziehungen direkt erstellt, und er wirkt sich nicht auf andere EMDs aus. Zum Sicherstellen, dass Ihre zusätzlichen Änderungen unabhängig davon implementiert werden, wie das Element erstellt wird, sollten Sie stattdessen eine AddRule und eine DeleteRule schreiben. Weitere Informationen finden Sie unter Regeln propagieren Änderungen im Modell.

Umleiten eines Mergevorgangs

Eine Forward Merge-Anweisung leitet das Ziel eines Zusammenführungsvorgangs um. In der Regel ist das neue Ziel das einbettende übergeordnete Element des ursprünglichen Ziels.

Beispielsweise werden in einer DSL, die mit der Komponentendiagrammvorlage erstellt wurde, Ports in Komponenten eingebettet. Ports werden als kleine Formen am Rand einer Komponentenform angezeigt. Der Benutzer erstellt Ports, indem er das Porttool auf eine Komponentenform zieht. Manchmal zieht der Benutzer das Porttool jedoch versehentlich auf einen vorhandenen Port anstelle der Komponente, und der Vorgang schlägt fehl. Der Fehler tritt leicht auf, wenn mehrere Ports vorhanden sind. Um dem Benutzer zu helfen, diese Umstände zu vermeiden, können Sie zulassen, dass Ports auf einen vorhandenen Port gezogen werden, die Aktion aber an die übergeordnete Komponente umgeleitet wird. Der Vorgang funktioniert so, als ob das Zielelement die Komponente wäre.

Sie können eine Forward Merge-Anweisung in der Komponentenmodell-Projektmappe erstellen. Wenn Sie die ursprüngliche Projektmappe kompilieren und ausführen, sollten Sie sehen, dass Benutzer eine beliebige Anzahl von Eingabeport- oder Ausgabeportelementen aus der Toolbox auf ein Component-Element ziehen können. Jedoch können sie keinen Port auf einen vorhandenen Port ziehen. Der Zeiger „Nicht verfügbar“ warnt sie, dass diese Verschiebung nicht aktiviert ist. Sie können jedoch eine Forward Merge-Anweisung erstellen, sodass ein Port, der unbeabsichtigt auf einem vorhandenen Eingabeport abgelegt wird, an das Component-Element weitergeleitet wird.

So erstellen Sie eine Forward Merge-Anweisung

  1. Erstellen Sie mithilfe der Komponentenmodellvorlage eine Projektmappe für DSL-Tools (domänenspezifische Sprachtools).

  2. Zeigen Sie den DSL-Explorer an, indem Sie DslDefinition.dsl öffnen.

  3. Erweitern Sie im DSL-ExplorerDomänenklassen.

  4. Die abstrakte ComponentPort-Domänenklasse ist die Basisklasse von InPort und OutPort. Klicken Sie mit der rechten Maustaste auf ComponentPort, und klicken Sie dann auf Neue Elementmerge-Anweisung hinzufügen.

    Unter dem Knoten Elementmerge-Anweisungen wird ein neuer Knoten Elementmerge-Anweisung angezeigt.

  5. Wählen Sie den Knoten Elementmerge-Anweisung aus, und öffnen Sie das Fenster DSL-Details.

  6. Wählen Sie in der Liste Indizierungsklasse ComponentPort aus.

  7. Wählen Sie Merge an eine andere Domänenklasse weiterleiten aus.

  8. Erweitern Sie in der Pfadauswahlliste ComponentPort, erweitern Sie ComponentHasPorts, und wählen Sie dann Komponente aus.

    Der neue Pfad sollte ähnlich wie dieser aussehen:

    ComponentHasPorts.Component/!Component

  9. Speichern Sie die Projektmappe, und transformieren Sie dann die Vorlagen, indem Sie auf der Projektmappen-Explorer-Symbolleiste auf die Schaltfläche ganz rechts klicken.

  10. Erstellen Sie das Projekt, und führen Sie es aus. Eine neue Instanz von Visual Studio wird geöffnet.

  11. Öffnen Sie im Projektmappen-Explorer Sample.mydsl. Das Diagramm und die ComponentLanguage-Toolbox werden angezeigt.

  12. Ziehen Sie einen Eingabeport aus der Toolbox auf einen anderen Eingabeport. Ziehen Sie als Nächstes einen OutputPort auf einen InputPort und dann auf einen anderen OutputPort.

    Der Zeiger „Nicht verfügbar“ sollte nicht angezeigt werden, und Sie sollten den neuen Eingabeport auf dem vorhandenen Port ablegen können. Wählen Sie den neuen Eingabeport aus, und ziehen Sie ihn an einen anderen Punkt auf der Komponente.