Share via


自訂項目的建立和移動

您可以允許將一個元素從工具箱或者透過貼上或移動作業拖曳到另一個元素上。 您可以使用指定的關聯性將移動的元素連結到目標元素。

元素合併指令 (EMD) 可指定將一個模型元素合併 到另一個模型元素時會發生什麼動作。 這會在以下情況中發生:

  • 使用者從工具箱中拖曳到圖表或圖形上。

  • 使用者使用總管或區間圖形中的 [新增] 功能表來建立元素。

  • 使用者將項目從一個泳道移到另一個泳道。

  • 使用者貼上元素。

  • 您的程式碼會呼叫元素合併指令。

雖然建立作業可能看起來與複製作業不同,但實際上它們的運作方式是相同的。 當新增一個元素時 (例如從工具箱中新增元素時),就會複寫它的原型。 該原型會以與從模型的其他部分複製元素相同的方式合併到模型中。

EMD 的責任是決定應如何將一個物件或一組物件合併到模型中的特定位置。 特別是,它會決定應具現化哪些關聯性以將合併的群組連結到模型中。 您也可以對其進行自訂,以設定屬性並建立其他物件。

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.

當您定義內嵌關聯性時,會自動產生 EMD。 當使用者將新的子實例新增到父實例時,此預設 EMD 會建立關聯性的實例。 您可以修改這些預設 EMD (例如透過新增自訂程式碼)。

您也可以在 DSL 定義中新增自己的 EMD,以讓使用者拖曳或貼上合併和接收類別的不同組合。

定義元素合併指令

您可以將元素合併指令新增至領域類別、領域關係性、圖形、連接器和圖表。 您可以在「DSL 總管」中的接收領域類別下新增或尋找它們。 接收類別是模型中已存在之元素,以及新的或複製的元素將合併到該元素上的領域類別。

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.

索引類別是可以合併到接收類別的成員中的元素的領域類別。 「索引類別」的子類別實例也將由此 EMD 合併 (除非您將 [套用至子類別] 設為 False)。

合併指令有兩種:

  • [處理合併] 指令可指定新元素應連結到樹狀結構的關聯性。

  • [轉遞合併] 指令會將新元素重新導向到另一個接收元素 (通常是父元素)。

您可以新增自訂程式碼來合併指令:

  • 設定 [使用自訂接受] 來新增您自己的程式碼,以確定是否應將索引元素的特定實例合併到目標元素中。 當使用者從工具箱中拖曳時,「無效的」指標會顯示您的程式碼是否不允許合併。

    例如,您可以只在接收元素處於特定狀態時允許合併。

  • 設定 [使用自訂合併] 以新增自己的程式碼來定義執行合併時對模型所做的變更。

    例如,您可以使用合併元素在模型中新位置的資料來設定合併元素的屬性。

注意

如果您撰寫自訂合併程式碼,它只會影響使用此 EMD 執行的合併。 如果有其他合併相同物件類型的 EMD,或者如果有其他自訂程式碼在不使用 EMD 的情況下建立這些物件,那麼它們將不會受到您的自訂合併程式碼的影響。

如果要確保新元素或新關係性一律由您的自訂程式碼處理,請考慮在內嵌關聯性上定義 AddRule,並在元素的領域類別上定義 DeleteRule。 如需詳細資訊,請參閱規則傳播模型內的變更

範例:定義不含自訂程式碼的 EMD

下列範例可讓使用者透過從工具箱拖曳到現有的圖形來同時建立元素和連接器。 此範例會將 EMD 新增至 DSL 定義中。 在進行此修改之前,使用者可以將工具拖曳到圖表上,但不能拖曳到現有的圖形上。

使用者也可以將元素貼到其他元素上。

讓使用者同時建立元素和連接器

  1. 使用最小語言解決方案範本來建立新的 DSL。

    當您執行此 DSL 時,它可讓您在圖形之間建立圖形和連接器。 您無法將新的 ExampleElement 圖形從工具箱中拖曳至現有的圖形。

  2. 若要讓使用者將元素合併至 ExampleElement 圖形,請在 ExampleElement 領域類別中建立新的 EMD:

    1. [DSL 總管]中,展開 [領域類別]。 以滑鼠右鍵按一下 ExampleElement,然後按一下 [新增元素合併指令]

    2. 請確定 [DSL 詳細資料] 視窗已開啟,以便您可以看到新 EMD 的詳細資料。 (功能表:[檢視][其他 Windows][DSL 詳細資料]。)

  3. 在 [DSL 詳細資料] 視窗中設定 [索引類別],以定義可以合併到 ExampleElement 物件上的元素類。

    在此範例中,選取 ExampleElements,以便使用者可以將新元素拖曳至現有的元素。

    請注意,索引類別會成為「DSL 總管」中的 EMD 名稱。

  4. [透過建立連結來處理合併] 下,新增兩個路徑:

    • 一個路徑會將新元素連結至父模型。 您需要輸入的路徑運算式會從現有的元素開始巡覽,沿著內嵌關聯性向上巡覽到父模型。 最後,它會指定新連結中新元素將被指派的角色。 該路徑如下所示:

      ExampleModelHasElements.ExampleModel/!ExampleModel/.Elements

    • 另一個路徑會將新元素連結到現有的元素。 該路徑運算式會指定參考關係性以及新元素將被指派的角色。 此路徑如下所示:

      ExampleElementReferencesTargets.Sources

      您可以使用路徑巡覽工具來建立每個路徑:

      1. [在路徑上建立連結以處理合併] 底下,按一下 <[新增路徑]>

      2. 按一下清單項目右側的下拉式箭頭。 一個樹狀檢視隨即出現。

      3. 展開樹狀結構中的節點來形成您要指定的路徑。

  5. 測試該 DSL:

    1. F5 來重建並執行該解決方案。

      重建需要比平常更長的時間,因為產生的程式碼會從文字範本中進行更新,以符合新的 DSL 定義。

    2. 當 Visual Studio 的實驗性實例啟動時,開啟 DSL 的模型檔案。 建立一些範例元素。

    3. 範例元素工具中拖曳到現有的圖形。

      一個新的圖形隨即出現,並透過連接器連結到現有的圖形。

    4. 複製現有的圖形。 選取另一個圖形並貼上。

      會建立第一個圖形的副本。 它有一個新名稱,並透過連接器連結到第二個圖形。

請注意此程序中的以下幾點:

  • 透過建立「元素合併指令」,您可以允許任何的元素類別接受任何其他的元素類別。 EMD 會在接收領域類別中建立,而已接受的領域類別會在 [索引類別] 欄位中指定。

  • 透過定義路徑,您可以指定應使用哪些連結來將新元素連接到現有的模型。

    您指定的連結應該包含一個內嵌關聯性。

  • EMD 會影響來自工具箱的建立動作以及貼上動作。

    如果您撰寫建立新元素的自訂程式碼,則可以使用 ElementOperations.Merge 方法來明確叫用 EMD。 這可確保您的程式碼會以與其他動作相同的方式將新元素連結到模型中。 如需詳細資訊,請參閱自訂複製行為

範例:將自訂接受程式碼新增至 EMD

透過為 EMD 新增自訂程式碼,您可以定義更複雜的合併行為。 這個簡單的範例可以防止使用者在圖表中新增超過固定數目的元素。 此範例可修改內嵌關聯性隨附的預設 EMD。

撰寫自訂接受程式碼來限制使用者可以新增的內容

  1. 使用最小語言解決方案範本來建立 DSL。 開啟 DSL 定義圖表。

  2. 在「DSL 總管」中,展開 [領域類別]ExampleModel[元素合併指令]。 選取名為 ExampleElement 的元素合併指令。

    此 EMD 可控制使用者如何在模型中建立新的 ExampleElement 物件 (例如透過從工具箱中拖曳)。

  3. [DSL 詳細資料] 視窗中,選取 [使用自訂接受]

  4. 重建方案。 這需要比平常更長的時間,因為產生的程式碼會從模型中進行更新。

    會回報一個建置錯誤,類似於:"Company.ElementMergeSample.ExampleElement does not contain a definition for CanMergeExampleElement..."

    您必須實作 CanMergeExampleElement 方法。

  5. Dsl 專案中建立一個新的程式碼檔案。 將其內容取代為以下程式碼,並將命名空間變更為您的專案的命名空間。

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

    這個簡單的範例會限制可以合併到父模型中的元素數目。 對於更引人注意的情況,該方法可以檢查接收物件的任何屬性和連結。 它也可以檢查 ElementGroupPrototype 中攜帶的合併元素的屬性。 如需 ElementGroupPrototypes 的詳細資訊,請參閱自訂複製行為。 如需如何撰寫讀取模型之程式碼的詳細資訊,請參閱在程式碼中巡覽和更新模型

  6. 測試該 DSL:

    1. F5 來重建該解決方案。 當 Visual Studio 的實驗性實例開啟時,請開啟 DSL 的實例。

    2. 以數種方式建立新元素:

      • 範例元素工具中拖曳到圖表上。

      • [範例模型總管]中,以滑鼠右鍵按一下根節點,然後按一下 [新增範例元素]

      • 複製圖表上的元素並貼上。

    3. 確認您無法使用這些方法中的任何一種來為模型新增四個以上的元素。 這是因為它們全都使用「元素合併指令」。

範例:將自訂合併程式碼新增至 EMD

在自訂合併程式碼中,您可以定義當使用者拖曳工具或貼上到元素上時會發生什麼動作。 有兩種方式可以定義自訂合併:

  1. 設定 [使用自訂合併] 並提供所需的程式碼。 您的程式碼會取代所產生的合併程式碼。 如果您想要完全重新定義合併的功能,請使用此選項。

  2. 覆寫 MergeRelate 方法,並選擇性地覆寫 MergeDisconnect 方法。 若要這樣做,您必須設定領域類別的 Generates Double Derived 屬性。 您的程式碼可以呼叫基底類別中產生的合併程式碼。 如果您想要在執行合併之後執行其他作業,請使用此選項。

    這些方法只會影響使用此 EMD 所執行的合併。 如果您想要影響可建立合併元素的所有方式,另一種方法是在內嵌關聯性上定義 AddRule,並在合併領域類別上定義 DeleteRule。 如需詳細資訊,請參閱規則傳播模型內的變更

覆寫 MergeRelate

  1. 在 DSL 定義中,確定您已定義您要在其中新增程式碼的 EMD。 如有需要,您可以新增路徑並定義自訂接受程式碼,如前幾節中所述。

  2. 在 DslDefinition 圖表中,選取合併的接收類別。 通常,它是內嵌關聯性來源端的類別。

    例如,在從 [最小語言] 解決方案產生的 DSL 中,選取 ExampleModel

  3. [屬性] 視窗中,將 Generates Double Derived 設為 true

  4. 重建方案。

  5. 檢查 Dsl\Generated Files\DomainClasses.cs 的內容。 搜尋名為 MergeRelate 的方法,並檢查其內容。 這可協助您撰寫您自己的版本。

  6. 在新的程式碼檔案中,撰寫接收類別的部分類別,並覆寫 MergeRelate 方法。 請記得呼叫基底方法。 例如:

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

撰寫自訂合併程式碼

  1. Dsl\Generated Code\DomainClasses.cs 中,檢查名為 MergeRelate 的方法。 這些方法會建立新元素與現有模型之間的連結。

    同時,檢查名為 MergeDisconnect 的方法。 這些方法會在要刪除元素時,將該元素從模型中取消連結。

  2. [DSL 總管] 中,選取或建立您想要自訂的「元素合併指令」。 在 [DSL 詳細資料] 視窗中,設定 [使用自訂合併]

    當您設定此選項時,會忽略 [處理合併][轉遞合併] 選項。 改用您的程式碼。

  3. 重建方案。 它需要比平常更長的時間,因為產生的程式碼檔案會從模型中進行更新。

    會出現錯誤訊息。 按兩下錯誤訊息,以查看產生的程式碼中的指示。 這些指示會要求您提供兩個方法:MergeRelateYourDomainClassMergeDisconnectYourDomainClass

  4. 將部分類別定義中的方法寫入個別的程式碼檔案中。 您稍早檢查的範例應該會建議您所需的內容。

    自訂合併程式碼不會影響直接建立物件和關聯性的程式碼,也不會影響其他 EMD。 為了確保無論元素如何建立,您的其他變更都會實作,請考慮改為撰寫 AddRuleDeleteRule。 如需詳細資訊,請參閱規則傳播模型內的變更

重新導向合併作業

轉遞合併指令會重新導向合併作業的目標。 通常,新目標是初始目標的內嵌父代目標。

例如,在使用元件圖表範本建立的 DSL 中,連接埠會內嵌在元件中。 連接埠會在元件圖形的邊緣上顯示為小圖形。 使用者可藉由將連接埠工具拖曳到元件圖形來建立連接埠。 但有時候,使用者會錯誤地將連接埠工具拖曳到現有的連接埠 (而不是元件) 上,而導致作業失敗。 當存在數個現有的連接埠時,這是一個很容易犯的錯誤。 為了協助使用者避免這種麻煩,您可以允許將連接埠拖曳到現有的連接埠上,但將動作重新導向到父元件。 該作業的運作方式就好像目標元素是元件一樣。

您可以在「元件模型」解決方案中建立轉遞合併指令。 如果您編譯並執行原始解決方案,您應該會看到使用者可以將任意數目的輸入連接埠輸出連接埠元素從 [工具箱] 中拖曳到元件元素。 但是,他們無法將連接埠拖曳到現有的連接埠。 「無法使用的」指標會提醒他們此移動未啟用。 但是,您可以建立一個轉遞合併指令,讓無意中丟到現有輸入連接埠上的連接埠轉遞到元件元素。

建立轉遞合併指令

  1. 使用「元件模型」範本建立一個「領域特定語言工具」解決方案。

  2. 開啟 DslDefinition.dsl 以顯示 [DSL 總管]

  3. [DSL 總管] 中,展開 [領域類別]

  4. ComponentPort 抽象領域類別是 InPortOutPort 的基底類別。 以滑鼠右鍵按一下 ComponentPort,然後按一下 [新增元素合併指令]

    新的元素合併指令節點會出現在 [元素合併指令] 節點底下。

  5. 選取該元素合併指令節點,然後開啟 [DSL 詳細資料] 視窗。

  6. 在 [索引類別] 清單中,選取 ComponentPort

  7. 選取 [將合併轉遞到不同的領域類別]

  8. 在路徑選取清單中,依序展開 ComponentPortComponentHasPorts,然後選取 [元件]

    新路徑應該會像這樣:

    ComponentHasPorts.Component/!Component

  9. 儲存該解決方案,然後按一下 [方案總管] 工具列上最右邊的按鈕來轉換範本。

  10. 建置並執行方案。 Visual Studio 的新實例隨即出現。

  11. [方案總管] 中,開啟 Sample.mydsl。 圖表和 [ComponentLanguage 工具箱] 隨即出現。

  12. 輸入連接埠從該工具箱中拖曳到另一個輸入連接埠。接下來,將輸出連接埠拖曳到輸入連接埠,然後再拖曳到另一個輸出連接埠

    您應該不會看到「無法使用的」指標,而您應該能夠將新的輸入連接埠丟到現有的輸入連接埠上。 選取新的輸入連接埠,然後將它拖曳到元件上的另一個點。