您可以撰寫程式碼來建立和刪除模型元素、設定其屬性,以及建立和刪除元素之間的連結。 所有變更都必須在交易內進行。 如果在圖表上查看元素,則圖表將在交易結束時自動「修復」。
DSL 定義範例
這是本主題範例的 DslDefinition.dsl 主要部分:
此模型是此 DSL 的範例:
參考和命名空間
若要執行本主題中的程式碼,您應該參考:
Microsoft.VisualStudio.Modeling.Sdk.11.0.dll
您的程式碼將使用此命名空間:
using Microsoft.VisualStudio.Modeling;
此外,如果您在與定義 DSL 的專案不同的專案中撰寫程式碼,則應匯入由 Dsl 專案建置的組件。
導覽模型
屬性
您在 DSL 定義中定義的網域屬性會成為您可以在程式程式碼中存取的內容:
Person henry = ...;
if (henry.BirthDate < 1500) ...
if (henry.Name.EndsWith("VIII")) ...
如果您想要設定屬性,則必須在 交易內執行此操作:
henry.Name = "Henry VIII";
如果在 DSL 定義中,屬性的 [種類] 已 計算,則無法設定它。 如需詳細資訊,請參閱 計算和自訂儲存屬性。
關係
您在 DSL 定義中定義的網域關聯性會變成屬性配對,在關聯性每一端的類別上各一個。 屬性的名稱會在 DslDefinition 圖中顯示為關係雙方角色上的標籤。 視角色的多重性而定,屬性的類型是關係另一端的類別,或是該類別的集合。
foreach (Person child in henry.Children) { ... }
FamilyTreeModel ftree = henry.FamilyTreeModel;
關係兩端的屬性總是互惠的。 建立或刪除連結時,這兩個元素上的角色屬性都會更新。 下列運算式(使用 System.Linq 的擴展功能)在範例中的 ParentsHaveChildren 關係中始終為真:
(Person p) => p.Children.All(child => child.Parents.Contains(p))
&& p.Parents.All(parent => parent.Children.Contains(p));
元素連結。 關係也會由稱為 連結的模型元素表示,這是網域關係類型的實例。 連結一律有一個來源元素和一個目標元素。 來源元素和目標元素可以相同。
您可以存取連結及其屬性:
ParentsHaveChildren link = ParentsHaveChildren.GetLink(henry, edward);
// This is now true:
link == null || link.Parent == henry && link.Child == edward
依預設,不允許超過一個關係實例連結任何一對模型元素。 但是,如果在 DSL 定義中, Allow Duplicates 關係的旗標為 true,則可能有多個連結,您必須使用 GetLinks:
foreach (ParentsHaveChildren link in ParentsHaveChildren.GetLinks(henry, edward)) { ... }
還有其他存取連結的方法。 例如:
foreach (ParentsHaveChildren link in ParentsHaveChildren.GetLinksToChildren(henry)) { ... }
隱藏角色。 如果在 DSL 定義中,特定角色的 [已產生屬性] 為 false ,則不會產生對應至該角色的內容。 不過,您仍然可以使用關係的方法存取這些連結並瀏覽連結。
foreach (Person p in ParentsHaveChildren.GetChildren(henry)) { ... }
最常用的範例是 PresentationViewsSubject 關聯性,它會將模型元素連結至在圖表上顯示它的圖形:
PresentationViewsSubject.GetPresentation(henry)[0] as PersonShape
元素目錄
您可以使用元素目錄存取存放區中的所有元素:
store.ElementDirectory.AllElements
還有一些尋找元素的方法,例如:
store.ElementDirectory.FindElements(Person.DomainClassId);
store.ElementDirectory.GetElement(elementId);
存取班級資訊
您可以取得 DSL 定義的類別、關聯性及其他層面的相關資訊。 例如:
DomainClassInfo personClass = henry.GetDomainClass();
DomainPropertyInfo birthProperty =
personClass.FindDomainProperty("BirthDate")
DomainRelationshipInfo relationship =
link.GetDomainRelationship();
DomainRoleInfo sourceRole = relationship.DomainRole[0];
模型元素的祖先類別如下:
ModelElement - 所有元素和關聯性都是 ModelElement
ElementLink - 所有關係都是 ElementLink
在交易內執行變更
每當您的程式碼更改資料庫中的任何內容時,必須在一個交易中完成此操作。 這適用於所有模型元素、關聯性、圖形、圖表及其屬性。 如需詳細資訊,請參閱Transaction。
管理交易最方便的方法是將 try...catch 語句包含在 using 語句中:
Store store; ...
try
{
using (Transaction transaction =
store.TransactionManager.BeginTransaction("update model"))
// Outermost transaction must always have a name.
{
// Make several changes in Store:
Person p = new Person(store);
p.FamilyTreeModel = familyTree;
p.Name = "Edward VI";
// end of changes to Store
transaction.Commit(); // Don't forget this!
} // transaction disposed here
}
catch (Exception ex)
{
// If an exception occurs, the Store will be
// rolled back to its previous state.
}
您可以在一筆交易中進行任意數量的更改。 您可以在活動中的交易內開啟新交易。
在處置交易之前,若要使您的變更永久化,您應該確認Commit該交易。 如果發生未在交易內攔截的例外狀況,Store 將會重設為變更前的狀態。
建立模型元素
此範例將元素新增至現有模型:
FamilyTreeModel familyTree = ...; // The root of the model.
using (Transaction t =
familyTree.Store.TransactionManager
.BeginTransaction("update model"))
{
// Create a new model element
// in the same partition as the model root:
Person edward = new Person(familyTree.Partition);
// Set its embedding relationship:
edward.FamilyTreeModel = familyTree;
// same as: familyTree.People.Add(edward);
// Set its properties:
edward.Name = "Edward VII";
t.Commit(); // Don't forget this!
}
此範例說明有關建立元素的這些要點:
在 Store 的特定分割區中建立新元素。 對於模型元素和關聯性,但不是圖形,這通常是預設分割區。
讓它成為嵌入關係的對象。 在此範例的 DslDefinition 中,每個 Person 都必須是內嵌於 FamilyTreeHasPeople 關係中的目標。 若要達成此目的,我們可以設定 Person 物件的 FamilyTreeModel 角色屬性,或將 Person 新增至 FamilyTreeModel 物件的 People 角色屬性。
設定新元素的屬性,特別是 DslDefinition 中 true 的屬性
IsName。 此旗標標示屬性,用於在其擁有者中唯一識別此元素。 在此情況下,Name 屬性具有該旗標。此 DSL 的 DSL 定義必須已載入到資料儲存庫中。 如果您正在撰寫功能表命令等擴充功能,這通常已經成立。 在其他情況下,您可以明確將模型載入 Store,或使用 ModelBus 來載入模型。 如需詳細資訊,請參閱 如何:從程式程式碼中的檔案開啟模型。
當您以這種方式建立元素時,會自動建立圖形 (如果 DSL 有圖表)。 它出現在自動分配的位置,具有默認形狀、顏色和其他功能。 如果您想要控制相關聯形狀的顯示位置和方式,請參閱 建立元素及其形狀。
建立關係連結
範例 DSL 定義中定義了兩種關係。 每個關聯性都會在關聯性每一端的類別上定義 一個角色屬性 。
您可以透過三種方式建立某個關係的實體。 這三種方法中的每一個都有相同的效果:
設定來源角色成員的屬性。 例如:
familyTree.People.Add(edward);edward.Parents.Add(henry);
設定目標角色的屬性。 例如:
edward.familyTreeModel = familyTree;這個角色的多重性是
1..1,所以我們分配值。henry.Children.Add(edward);這個角色的多樣性是
0..*,所以我們添加到集合中。
明確地構造一個關係的實例。 例如:
FamilyTreeHasPeople edwardLink = new FamilyTreeHasPeople(familyTreeModel, edward);ParentsHaveChildren edwardHenryLink = new ParentsHaveChildren(henry, edward);
如果您想要在關係本身上設定屬性,最後一種方法很有用。
當您以這種方式建立元素時,會自動在圖表上建立連接器,但它具有預設的形狀、顏色和其他功能。 若要控制關聯連接器的建立方式,請參閱 建立元素及其造型。
刪除元素
透過呼叫 Delete()刪除元素:
henry.Delete();
此作業也會刪除:
與該元素相關的來往關係連結。 例如,
edward.Parents將不再包含henry。旗標為 true 的角色
PropagatesDelete中的元素。 例如,顯示元素的形狀將被刪除。
依預設,每個內嵌關係在目標角色處都有 PropagatesDelete true。 刪除henry不會刪除familyTree,但familyTree.Delete()會刪除所有Persons。
依預設,PropagatesDelete 在參照關係的角色中不為真。
當您刪除物件時,您可以讓刪除規則省略特定傳播。 如果您要將一個元素替換為另一個元素,這很有用。 您提供一或多個角色的 GUID,這些角色的刪除不應被傳播。 GUID 可以從關聯性類別取得:
henry.Delete(ParentsHaveChildren.SourceDomainRoleId);
(這個特定的例子不會有任何效果,因為在 ParentsHaveChildren 關係中,PropagatesDelete 是 false 的角色。)
在某些情況下,刪除會因為在元素上或在藉由傳播而被刪除的元素上存在鎖定而被阻止。 您可以使用element.CanDelete()檢查元素是否可以刪除。
刪除關係連結
您可以從角色屬性中移除元素,以刪除關係連結:
henry.Children.Remove(edward); // or:
edward.Parents.Remove(henry); // or:
您也可以明確刪除連結:
edwardHenryLink.Delete();
這三種方法都有相同的效果。 您只需要使用其中一個。
如果角色具有 0..1 或 1..1 多重性,您可以將其設定為 null,或設定為另一個值:
edward.FamilyTreeModel = null; 或:
edward.FamilyTreeModel = anotherFamilyTree;
重新排序關係的連結
來源或以特定模型元素為目標的特定關係的鏈結具有特定的順序。 它們會依新增的順序顯示。 例如,這個陳述式總是以相同順序生出子項:
foreach (Person child in henry.Children) ...
您可以變更連結的順序:
ParentsHaveChildren link = GetLink(henry,edward);
ParentsHaveChildren nextLink = GetLink(henry, elizabeth);
DomainRoleInfo role =
link.GetDomainRelationship().DomainRoles[0];
link.MoveBefore(role, nextLink);
Locks
您的變更可能會被鎖定所阻止。 可以對單個元素、分區以及整個存儲設置鎖定。 如果其中任何層級具有防止您要進行的變更類型的鎖定,則嘗試變更時可能會拋出例外。 您可以使用 element.GetLocks() 來探索鎖定是否已設定,這是一個在命名空間 Microsoft.VisualStudio.Modeling.Immutability 中定義的擴充方法。
如需詳細資訊,請參閱 定義鎖定原則以建立 Read-Only 區段。
複製和貼上
您可以將元素或元素群組複製到:IDataObject
Person person = personShape.ModelElement as Person;
Person adopter = adopterShape.ModelElement as Person;
IDataObject data = new DataObject();
personShape.Diagram.ElementOperations
.Copy(data, person.Children.ToList<ModelElement>());
元素會儲存為序列化的元素群組。
您可以將 IDataObject 中的元素合併至模型:
using (Transaction t = targetDiagram.Store.
TransactionManager.BeginTransaction("paste"))
{
adopterShape.Diagram.ElementOperations.Merge(adopter, data);
}
Merge () 可以接受 a PresentationElement 或 a ModelElement。 如果您給它 PresentationElement一個 ,您也可以指定目標圖表上的位置作為第三個參數。
導覽和更新圖表
在 DSL 中,代表 Person 或 Song 等概念的領域模型元素與代表您在圖表上看到的內容的 shape 元素是分開的。 領域模型元素儲存概念的重要屬性和關係。 形狀元素會儲存圖表上物件視圖的大小、位置和顏色,以及其元件組件的佈局。
簡報元素
在 DSL 定義中,您指定的每個元素都會建立衍生自下列其中一個標準類別的類別。
| 元素種類 | 基類 |
|---|---|
| 網域類別 | ModelElement |
| 網域關係 | ElementLink |
| Shape | NodeShape |
| Connector | BinaryLinkShape |
| 圖表 | Diagram |
圖表上的元素通常代表模型元素。 通常 (但並非總是如此) ,a NodeShape 代表網域類別實例,而 a BinaryLinkShape 代表網域關係實例。 PresentationViewsSubject 關係將節點或連結圖形關聯到它所表示的模型元素。
每個節點或連結圖形都屬於一個圖表。 二進位連結圖形會連接兩個節點圖形。
圖形可以有兩組中的子圖形。 集合中的 NestedChildShapes 形狀僅限於其父項的邊界框。 清單中的 RelativeChildShapes 形狀可以出現在父項的邊界之外或部分之外,例如標籤或埠。 圖表沒有RelativeChildShapes和Parent。
在形狀和元素之間導航
領域模型元素和形狀元素是通過PresentationViewsSubject的關係來相關聯的。
// using Microsoft.VisualStudio.Modeling;
// using Microsoft.VisualStudio.Modeling.Diagrams;
// using System.Linq;
Person henry = ...;
PersonShape henryShape =
PresentationViewsSubject.GetPresentation(henry)
.FirstOrDefault() as PersonShape;
相同的關聯性會將關聯性連結至圖表上的連接器:
Descendants link = Descendants.GetLink(henry, edward);
DescendantConnector dc =
PresentationViewsSubject.GetPresentation(link)
.FirstOrDefault() as DescendantConnector;
// dc.FromShape == henryShape && dc.ToShape == edwardShape
此關聯性也會將模型的根連結至圖:
FamilyTreeDiagram diagram =
PresentationViewsSubject.GetPresentation(familyTree)
.FirstOrDefault() as FamilyTreeDiagram;
若要取得由圖形表示的模型元素,請使用:
henryShape.ModelElement as Person
diagram.ModelElement as FamilyTreeModel
在圖表中導航
一般而言,不建議在圖表上的圖形和連接器之間導覽。 最好在模型中導航關係,僅在需要處理圖表外觀時才在形狀和連接器之間移動。 這些方法會將連接器連接到兩端的圖形上。
personShape.FromRoleLinkShapes, personShape.ToRoleLinkShapes
connector.FromShape, connector.ToShape
許多形狀是複合形狀,由父形狀和一層或多層的子層組成。 相對於另一個圖形定位的圖形稱為其 子系。 當父圖形移動時,子圖形也會隨之移動。
相對子系 可以出現在父圖形的邊界框之外。 巢狀 子系嚴格出現在父系的界限內。
若要取得圖表上最頂端的圖形集,請使用:
Diagram.NestedChildShapes
圖形和連接器的祖先類別為:
-- ShapeElement
----- NodeShape
------- Diagram
------- YourShape
----- LinkShape
------- BinaryLinkShape
--------- 你的連接器
形狀和連接器的屬性
在大部分情況下,不需要對圖形進行明確變更。 變更模型元素後,「修復」規則會更新造型和連接器。 如需詳細資訊,請參閱 回應和傳播變更。
不過,對與模型元素無關的屬性中的圖形進行一些明確變更會很有用。 例如,您可以變更下列屬性:
Size - 決定形狀的高度和寬度。
Location - 相對於父圖形或圖表的位置
StyleSet - 用於繪製形狀或連接器的鋼筆和畫筆組
Hide - 使形狀不可見
Show - 使形狀在
Hide()之後可見
建立元素及其形狀
當您建立元素並將其連結至內嵌關係樹狀結構時,會自動建立形狀並與其相關聯。 這是透過在交易結束時執行的「修復」規則來完成的。 但是,該形狀將出現在自動分配的位置,其形狀、顏色和其他特徵將具有預設值。 若要控制圖形的建立方式,您可以使用合併功能。 您必須先將要新增至 ElementGroup 的元素,然後將群組合併到圖表中。
此方法:
如果您已將屬性指派為元素名稱,則設定該名稱。
監控您在 DSL 定義中指定的任何元素合併指令。
此範例會在使用者按兩下圖表時,在滑鼠位置建立圖形。 在此範例的 DSL 定義中,ExampleShape 的 FillColor 屬性已公開。
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
partial class MyDiagram
{
public override void OnDoubleClick(DiagramPointEventArgs e)
{
base.OnDoubleClick(e);
using (Transaction t = this.Store.TransactionManager
.BeginTransaction("double click"))
{
ExampleElement element = new ExampleElement(this.Store);
ElementGroup group = new ElementGroup(element);
{ // To use a shape of a default size and color, omit this block.
ExampleShape shape = new ExampleShape(this.Partition);
shape.ModelElement = element;
shape.AbsoluteBounds = new RectangleD(0, 0, 1.5, 1.0);
shape.FillColor = System.Drawing.Color.Azure;
group.Add(shape);
}
this.ElementOperations.MergeElementGroupPrototype(
this,
group.CreatePrototype(),
PointD.ToPointF(e.MousePosition));
t.Commit();
}
}
}
如果您提供多個形狀,請使用 AbsoluteBounds設定其相對位置。
您也可以使用此方法設定連接器的顏色和其他公開屬性。
使用交易
圖形、連接器和圖表是 Store 的 ModelElement 子類型,並存在於 Store 中。 因此,您只能在交易內對它們進行變更。 如需詳細資訊,請參閱 如何:使用交易來更新模型。
文件檢視和文件資料
儲存分割區
載入模型時,會同時載入隨附的圖表。 一般而言,模型會載入 Store.DefaultPartition,而圖表內容會載入到另一個 Partition 中。 通常,每個分割區的內容都會載入並儲存到單獨的檔案中。