Share via


事件處理常式傳播模型外的變更

在 Visualization and Modeling SDK 中,您可以定義存放區事件處理常式以將變更傳播至存放區外部的資源,例如非存放區變數、檔案、其他存放區中的模型,或其他 Visual Studio 延伸模組。 存放區事件處理常式會在發生觸發事件的交易結束後執行。 此外也會在復原或重做作業中執行。 因此,不同於存放區規則,存放區事件在更新存放區外部的值時效用最大。 不同於 .NET 事件,存放區事件處理常式在註冊後會接聽類別:您不需要為每個執行個體註冊個別的處理常式。 如需如何選擇不同的方式來處理變更的詳細資訊,請參閱回應及傳播變更

舉例來說,圖形化介面和其他使用者介面控制項都是可由存放區事件處理的外部資源。

定義存放區事件

  1. 選擇您要監視的事件類型。 如需完整清單,請查看 EventManagerDirectory 的屬性。 每個屬性都會對應至一個事件類型。 最常使用的事件類型包括:

    • ElementAdded - 在模型元素、關聯性連結、圖形或連接器建立時觸發。

    • ElementPropertyChanged - 在 Normal 領域屬性的值變更時觸發。 只有在新值與舊值不相等時,才會觸發此事件。 此事件無法套用至計算屬性和自訂儲存體屬性。

      它無法套用至對應於關聯性連結的角色屬性。 應使用 ElementAdded 來監視領域關聯性。

    • ElementDeleted - 在模型元素、關聯性、圖形或連接器刪除後觸發。 您仍可存取元素的屬性值,但它與其他元素將沒有關聯性。

  2. DslPackage 專案中的個別程式碼檔案,新增 YourDslDocData 的部分類別定義。

  3. 將事件的程式碼撰寫為方法,如下列範例所示。 除非您想要存取 DocData,否則可將其設為 static

  4. 覆寫 OnDocumentLoaded() 以註冊處理常式。 如果有多個處理常式,全都可在相同的位置註冊。

註冊碼的位置並不重要。 DocView.LoadView() 是替代位置。

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

使用事件在存放區中進行可復原的調整

存放區事件通常不會用來在存放區內傳播變更,因為事件處理常式在交易已認可後才會執行。 您應使用存放區規則。 如需詳細資訊,請參閱規則傳播模型內的變更

不過,如果您希望使用者能夠在原始事件外個別復原其他更新,您可以使用事件處理常式對存放區進行其他更新。 例如,假設小寫字元是專輯標題的常見慣例。 您可以撰寫存放區事件處理常式,在使用者以大寫輸入標題時將其更正為小寫。 但使用者可使用「復原」命令取消您的更正,而還原大寫字元。 再次使用「復原」會移除使用者的變更。

相較之下,如果您撰寫了存放區規則來執行相同動作,使用者的變更和您的更正將會在相同的交易中,因此使用者無法在不失去原始變更的情況下復原調整。

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.
}

如果您撰寫更新存放區的事件:

  • 使用 store.InUndoRedoOrRollback 以避免在「復原」中變更模型元素。 交易管理員會將存放區中的一切重設為其原始狀態。

  • 使用 store.InSerializationTransaction 以避免在從檔案載入模型時進行變更。

  • 您的變更會導致更多事件觸發。 請務必避免無限迴圈。

存放區事件類型

每個事件類型都會對應至 Store.EventManagerDirectory 中的一個集合。 您可以隨時新增或移除事件處理常式,但通常會在文件載入時加以新增。

EventManagerDirectory 屬性名稱 執行時機
ElementAdded 建立領域類別、領域關聯性、圖形、連接器或圖表的執行個體。
ElementDeleted 模型元素已從存放區的元素目錄中移除,且不再是任何關聯性的來源或目標。 元素實際上並未從記憶體中刪除,但保留以供日後執行「復原」時使用。
ElementEventsBegun 在外部交易結束時叫用。
ElementEventsEnded 在所有其他事件皆已處理時叫用。
ElementMoved 模型元素已從一個存放區分割區移至另一個分割區。

這與圖表上的圖形位置無關。
ElementPropertyChanged 領域屬性的值已變更。 只有在舊值與新值不相等時,才會執行此屬性。
RolePlayerChanged 關聯性的兩個角色 (端點) 之一會參考新元素。
RolePlayerOrderChanged 在多重性大於 1 的角色中,連結的順序已變更。
TransactionBeginning
TransactionCommitted
TransactionRolledBack

注意

文字範本轉換元件會作為 Visual Studio 延伸模組開發工作負載的一部分自動安裝。 您也可以從 Visual Studio 安裝程式的 [個別元件] 索引標籤加以安裝,其位於 [SDK、程式庫和架構] 底下。 從 [個別元件] 索引標籤安裝 [模型化 SDK] 元件。