Ereignishandler propagieren Änderungen außerhalb des Modells

Im SDK für Visualisierung und Modellierung können Sie Speicherereignishandler definieren, um Änderungen an Ressourcen außerhalb des Speichers weiterzugeben, z. B. Nicht-Speichervariablen, Dateien, Modelle in anderen Speichern oder andere Visual Studio-Erweiterungen. Speicherereignishandler werden nach dem Ende der Transaktion ausgeführt, in der das auslösende Ereignis aufgetreten ist. Sie werden auch in einem Rückgängig- oder Wiederholungsvorgang ausgeführt. Daher sind Speicherereignisse im Gegensatz zu Speicherregeln am nützlichsten, um Werte außerhalb des Speichers zu aktualisieren. Im Gegensatz zu .NET-Ereignissen werden Speicherereignishandler registriert, um auf eine Klasse zu lauschen: Sie müssen keinen separaten Handler für jede Instanz registrieren. Weitere Informationen zur Wahl zwischen verschiedenen Methoden zum Behandeln von Änderungen finden Sie unter Reagieren auf Änderungen und Weitergeben von Änderungen.

Die grafische Oberfläche und andere Steuerelemente der Benutzeroberfläche sind Beispiele für externe Ressourcen, die von Speicherereignissen verarbeitet werden können.

So definieren Sie ein Speicherereignis

  1. Wählen Sie den Ereignistyp aus, den Sie überwachen möchten. Eine vollständige Liste finden Sie in den Eigenschaften von EventManagerDirectory. Jede Eigenschaft entspricht einem Ereignistyp. Die am häufigsten verwendeten Ereignistypen sind:

    • ElementAdded: Wird ausgelöst, wenn ein Modellelement, eine Beziehungsverknüpfung, eine Form oder ein Verbinder erstellt wird.

    • ElementPropertyChanged: Wird ausgelöst, wenn der Wert einer Normal-Domäneneigenschaft geändert wird. Das Ereignis wird nur ausgelöst, wenn die neuen und alten Werte nicht gleich sind. Das Ereignis kann nicht auf berechnete und benutzerdefinierte Speichereigenschaften angewendet werden.

      Es kann nicht auf die Rolleneigenschaften angewendet werden, die Beziehungsverknüpfungen entsprechen. Verwenden Sie stattdessen ElementAdded, um die Domänenbeziehung zu überwachen.

    • ElementDeleted: Wird ausgelöst, nachdem ein Modellelement, eine Beziehung, eine Form oder ein Verbinder gelöscht wurde. Sie können weiterhin auf die Eigenschaftswerte des Elements zugreifen, es weist jedoch keine Beziehungen zu anderen Elementen auf.

  2. Fügen Sie eine partielle Klassendefinition für YourDslDocData in einer separaten Codedatei im DslPackage-Projekt hinzu.

  3. Schreiben Sie den Code des Ereignisses als Methode, wie im folgenden Beispiel gezeigt. Es kann sich um static handeln, es sei denn, Sie möchten auf DocData zugreifen.

  4. Überschreiben Sie OnDocumentLoaded(), um den Handler zu registrieren. Wenn Sie über mehrere Handler verfügen, können Sie sie alle am selben Ort registrieren.

Der Speicherort des Registrierungscodes ist nicht wichtig. DocView.LoadView() ist ein alternativer Speicherort.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.Modeling;

namespace Company.MusicLib
{
  partial class MusicLibDocData
  {
    // Register store events here or in DocView.LoadView().
    protected override void OnDocumentLoaded()
    {
      base.OnDocumentLoaded(); // Don't forget this.

      #region Store event handler registration.
      Store store = this.Store;
      EventManagerDirectory emd = store.EventManagerDirectory;
      DomainRelationshipInfo linkInfo = store.DomainDataDirectory
          .FindDomainRelationship(typeof(ArtistAppearsInAlbum));
      emd.ElementAdded.Add(linkInfo,
          new EventHandler<ElementAddedEventArgs>(AddLink));
      emd.ElementDeleted.Add(linkInfo,
          new EventHandler<ElementDeletedEventArgs>(RemoveLink));

      #endregion Store event handlers.
    }

    private void AddLink(object sender, ElementAddedEventArgs e)
    {
      ArtistAppearsInAlbum link = e.ModelElement as ArtistAppearsInAlbum;
      if (link != null)
            ExternalDatabase.Add(link.Artist.Name, link.Album.Title);
    }
    private void RemoveLink(object sender, ElementDeletedEventArgs e)
    {
      ArtistAppearsInAlbum link = e.ModelElement as ArtistAppearsInAlbum;
      if (link != null)
            ExternalDatabase.Delete(link.Artist.Name, link.Album.Title);
    }
  }
}

Verwenden von Ereignissen zum Vornehmen von Anpassungen im Speicher, die rückgängig gemacht werden können

Speicherereignisse werden normalerweise nicht für die Weitergabe von Änderungen innerhalb des Speichers verwendet, weil der Ereignishandler nach dem Committen der Transaktion ausgeführt wird. Stattdessen würden Sie eine Speicherregel verwenden. Weitere Informationen finden Sie unter Regeln geben Änderungen im Modell weiter.

Sie können jedoch einen Ereignishandler verwenden, um zusätzliche Aktualisierungen für den Speicher vorzunehmen, wenn der Benutzer in der Lage sein soll, die zusätzlichen Aktualisierungen getrennt vom ursprünglichen Ereignis rückgängig zu machen. Angenommen, Kleinbuchstaben sind die übliche Konvention für Albumtitel. Sie könnten einen Speicherereignishandler schreiben, der den Titel in Kleinbuchstaben umwandelt, nachdem der Benutzer ihn in Großbuchstaben eingegeben hat. Der Benutzer könnte jedoch den Befehl „Rückgängig“ verwenden, um die Korrektur abzubrechen und die Großbuchstaben wiederherzustellen. Ein zweiter Befehl „Rückgängig“ würde die Änderung des Benutzers entfernen.

Wenn Sie hingegen eine Speicherregel für denselben Vorgang geschrieben haben, würden sich die Änderung des Benutzers und Ihre Korrektur in derselben Transaktion befinden, sodass der Benutzer die Anpassung nicht rückgängig machen könnte, ohne die ursprüngliche Änderung zu verlieren.

partial class MusicLibDocView
{
    // Register store events here or in DocData.OnDocumentLoaded().
    protected override void LoadView()
    {
      /* Register store event handler for Album Title property. */
      // Get reflection data for property:
      DomainPropertyInfo propertyInfo =
        this.DocData.Store.DomainDataDirectory
        .FindDomainProperty(Album.TitleDomainPropertyId);
      // Add to property handler list:
      this.DocData.Store.EventManagerDirectory
        .ElementPropertyChanged.Add(propertyInfo,
        new EventHandler<ElementPropertyChangedEventArgs>
             (AlbumTitleAdjuster));

      /*
      // Alternatively, you can set one handler for
      // all properties of a class.
      // Your handler has to determine which property changed.
      DomainClassInfo classInfo = this.Store.DomainDataDirectory
           .FindDomainClass(typeof(Album));
      this.Store.EventManagerDirectory
          .ElementPropertyChanged.Add(classInfo,
        new EventHandler<ElementPropertyChangedEventArgs>
             (AlbumTitleAdjuster));
       */
      return base.LoadView();
    }

// Undoable adjustment after a property is changed.
// Method can be static since no local access.
private static void AlbumTitleAdjuster(object sender,
         ElementPropertyChangedEventArgs e)
{
  Album album = e.ModelElement as Album;
  Store store = album.Store;

  // We mustn't update the store in an Undo:
  if (store.InUndoRedoOrRollback
      || store.InSerializationTransaction)
      return;

  if (e.DomainProperty.Id == Album.TitleDomainPropertyId)
  {
    string newValue = (string)e.NewValue;
    string lowerCase = newValue.ToLowerInvariant();
    if (!newValue.Equals(lowerCase))
    {
      using (Transaction t = store.TransactionManager
            .BeginTransaction("adjust album title"))
      {
        album.Title = lowerCase;
        t.Commit();
      } // Beware! This could trigger the event again.
    }
  }
  // else other properties of this class.
}

Wenn Sie ein Ereignis schreiben, das den Speicher aktualisiert:

  • Verwenden Sie store.InUndoRedoOrRollback, um Änderungen an Modellelementen im Befehl „Rückgängig“ zu vermeiden. Der Transaktions-Manager versetzt alle Elemente im Speicher wieder in den ursprünglichen Zustand.

  • Verwenden Sie store.InSerializationTransaction, um Änderungen zu vermeiden, während das Modell aus der Datei geladen wird.

  • Ihre Änderungen führen dazu, dass weitere Ereignisse ausgelöst werden. Stellen Sie sicher, dass Sie eine Endlosschleife vermeiden.

Speicherereignistypen

Jeder Ereignistyp entspricht einer Sammlung in Store.EventManagerDirectory. Sie können Ereignishandler jederzeit hinzufügen oder entfernen. Es ist jedoch üblich, diese hinzuzufügen, wenn das Dokument geladen wird.

EventManagerDirectory-Eigenschaftsname Wird ausgeführt, wenn
ElementAdded Eine Instanz einer Domänenklasse, einer Domänenbeziehung, einer Form, eines Verbinders oder eines Diagramms wird erstellt.
ElementDeleted Ein Modellelement wurde aus dem Elementverzeichnis des Speichers entfernt und ist nicht mehr die Quelle oder das Ziel einer Beziehung. Das Element wird nicht tatsächlich aus dem Arbeitsspeicher gelöscht, sondern im Falle eines zukünftigen Rückgängig-Vorgangs beibehalten.
ElementEventsBegun Wird am Ende einer äußeren Transaktion aufgerufen.
ElementEventsBegun Wird aufgerufen, wenn alle anderen Ereignisse verarbeitet wurden.
ElementMoved Ein Modellelement wurde aus einer Speicherpartition in eine andere verschoben.

Dies hängt nicht mit der Position einer Form im Diagramm zusammen.
ElementPropertyChanged Der Wert einer Domäneneigenschaft hat sich geändert. Dies wird nur ausgeführt, wenn die alten und neuen Werte nicht gleich sind.
RolePlayerChanged Eine der beiden Rollen (Seiten) einer Beziehung verweist auf ein neues Element.
RolePlayerOrderChanged In einer Rolle mit einer Multiplizität größer als 1 hat sich die Reihenfolge der Verknüpfungen geändert.
TransactionBeginning
TransaktionCommitted
TransactionRolledBack

Hinweis

Die Komponente Textvorlagentransformation wird automatisch als Teil der Workload Visual Studio-Erweiterungsentwicklung installiert. Sie können die Installation auch über die Registerkarte Einzelne Komponenten des Visual Studio-Installers unter der Kategorie SDKs, Bibliotheken und Frameworks durchführen. Installieren Sie die Komponente Modellierungs-SDK auf der Registerkarte Einzelne Komponenten.