在程序代码中导航和更新模型
可以创建和删除模型元素中编写代码,设置其属性,并创建和删除元素之间的链接。必须在事务内进行更改。如果元素在关系图中查看,关系图 “将自动已修复”在事务末尾。
主题内容
DSL 定义示例
导航模型
访问信息的类。
执行事务内的更改
创建模型元素
创建链接关系
删除元素
删除关系链接
重新排序的链接关系
锁定
复制和粘贴
导航和更新关系图
在形状和元素之间
形状和连接线属性
DocView 和 DocData
形状、连接和关系图及其关系到模型元素在单独的主题中进行说明。有关更多信息,请参见 [重定向] 如何:导航和更新图表。
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 定义,属性的 kind 是 计算,不能将其设置为。有关更多信息,请参见 计算的和自定义的存储属性。
关系
在 DSL 定义的字段关系成为对属性,一个在类在关系的每一端。属性的名称将显示 DslDefinition 关系图作为角色中的标签在关系的每一端。根据该角色的重数,属性的类型是类在关系的另一端或该类的集合。
foreach (Person child in henry.Children) { ... }
FamilyTreeModel ftree = henry.FamilyTreeModel;
属性在关系的相对端始终是相互的。当 LINK 创建或删除时,这两个元素的角色属性更新。使用 System.Linq扩展) 的以下表达式 (始终适用于该示例中的 ParentsHaveChildren 关系:
(Person p) => p.Children.All(child => child.Parents.Contains(p))
&& p.Parents.All(parent => parent.Children.Contains(p));
ElementLinks。关系由调用 链接的模型元素还表示,为域关系类型的实例。链接始终有一个源元素和一个目标元素。源元素和目标元素既可以是相同的。
可以访问链接及其属性:
ParentsHaveChildren link = ParentsHaveChildren.GetLink(henry, edward);
// This is now true:
link == null || link.Parent == henry && link.Child == edward
默认情况下,不超过关系的一个实例允许链接任何对模型元素。,但是,如果在 DSL 定义, Allow Duplicates 标志适用于该关系,然后可能有一个链接,因此,您必须使用 GetLinks:
foreach (ParentsHaveChildren link in ParentsHaveChildren.GetLinks(henry, edward)) { ... }
还具有访问的链接其他方法。例如:
foreach (ParentsHaveChildren link in ParentsHaveChildren.GetLinksToChildren(henry)) { ... }
**隐藏的角色。**如果在 DSL 定义, 是生成的属性 是某个特定角色的 错误 ,则对应于该角色的属性未生成。但是,使用该关系的方法,您仍然可以访问链接和遍历链接:
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 - 所有元素和关系是 ModelElements
ElementLink - 所有关系是 ElementLinks
执行事务内的更改
每当程序代码在存储更改任何内容,则必须先在事务内。这适用于任何模型元素、关系、形状、关系图及其特性。有关更多信息,请参见 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 事务。如果没有捕获在事务内发生异常,存储将重置为其更改前的状态。
创建模型元素
此示例将元素添加到现有模型:
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!
}
此示例演示以下基本指向有关创建元素:
创建新组件中存储的特定分区。对于模型元素和不显示关系,但是,形状,这通常是默认分区。
使其成为个目标一个嵌入的关系。在本示例的 DslDefinition,每个人都必须是嵌入关系的目标 FamilyTreeHasPeople。为此,我们可以设置 person 对象的 FamilyTreeModel 角色特性,或者添加用户。 FamilyTreeModel 对象的用户角色的属性。
设置新的元素的属性,特定属性 IsName 为 true。 DslDefinition。此标志指示服务在其所有者中唯一标识元素的属性。在这种情况下,属性名称具有该标志。
必须加载了此 DSL 的 DSL 定义到存储。如果要编写扩展 (如菜单命令,这通常是已经为 true。在某些情况下,您可以显式加载该模型来存储,或使用 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。
在 PropagatesDelete 标志真实的角色的元素。例如,显示该元素的形状将被删除。
默认情况下,每个嵌入的关系具有 PropagatesDelete true 在目标角色。删除 henry 不删除 familyTree,但是, familyTree.Delete() 将删除所有 Persons。有关更多信息,请参见 自定义删除行为。
默认情况下, PropagatesDelete 不适用于角色引用关系。
,则删除对象时,可以生成删除规则省略特定发送。,如果您在调用代码中,用一个元素时很有用。为删除不应传播一个或多个角色的 GUID。GUID 可以从关系类获取:
henry.Delete(ParentsHaveChildren.SourceDomainRoleId);
(此特定示例将不起作用,因为, PropagatesDelete 是 ParentsHaveChildren 关系的角色的 false 。)
有时,删除由固定存在防止,在元素或在将由发送移除的元素。可以使用 element.CanDelete() 检查该元素是否能删除。
删除关系链接
可以通过移除元素删除关系链接到角色特性:
henry.Children.Remove(edward); // or:
edward.Parents.Remove(henry); // or:
可以显式中删除该链接:
edwardHenryLink.Delete();
这三个方法都具有相同的效果。只需使用其中之一。
如果该角色具有或 1..1 重数 0..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);
锁定
更改可能受到锁定被禁止。锁可能设置在单独的组件,在分区和在存储。如果这些级别中的任何一个具有防止这将要进行的锁,异常可能会引发,如果尝试它。可以查看锁定通过使用元素,是否设置为。GetLocks(),是扩展方法。 Immutability定义。
有关更多信息,请参见 定义锁定策略以创建只读段。
复制和粘贴
可以复制元素的元素或组添加到 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 () 可以接受 PresentationElement 或 ModelElement。如果您赋予它 PresentationElement,还可以指定在目标关系图中的位置作为第三个参数。
导航和更新关系图
在 DSL,域模型元素,表示一个概念 (如人员或歌曲,与形状元素,表示想要在关系图参见。域模型元素存储概念的重要属性和关系。形状元素在关系图上存储大小、对象的视图的位置和颜色及其构成格式。
显示元素
在 DSL 定义,指定的每个元素创建从以下条件类之一派生的类。
元素类型 |
基类 |
---|---|
域类 |
|
域关系 |
|
形状 |
|
连接 |
|
关系图 |
关系图上的元素通常表示一个模型元素。通常 (,但不总是), NodeShape 表示字段类的实例,并且, 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
许多形状是复合;它们由父形状和子组成一个或多个层。将相对于其他形状的形状被视为其 子项。当父形状移动时, child 随之移动。
相对子级 可以显示外部父形状的边界框。嵌套 子强显示在父的边界内。
若要获取顶部设置关系图中的形状,请使用:
Diagram.NestedChildShapes
形状和连接线上级的类包括:
-- ShapeElement
----- NodeShape
------- Diagram
------- TheShape
----- LinkShape
------- BinaryLinkShape
--------- TheConnector
形状和连接线属性
在大多数情况下,对形状的显式更改并不是必需的。当您更改了模型元素时, “修复”规则更新形状和连接线。有关更多信息,请参见 响应并传播更改。
但是,对形状的这些显式更改在模型元素无关的属性是很有用的。例如,您可以更改以下属性:
Size - 定位形状的高度和宽度。
Location - 相对于父形状或关系图的位置
StyleSet - 设置钢笔和画笔进行绘制形状或使用了连接
Hide - 使形状可见
Show - 使形状显示在 Hide()之后
创建组件及其形状
当您创建元素并将其链接到嵌入关系时树,则形状自动创建并与它。这是通过对事务末尾的 “fixup”规则完成。但是,形状于一自动分配的位置将出现,并且,其形状、颜色和其他功能将具有默认值。若要控制形状如何创建,可以使用组合函数。您必须先添加要添加到 ElementGroup 的组件,然后将组添加到关系图。
此方法:
,如果分配了属性作为元素名称,将名称。
观察在 DSL 定义指定的所有元素合并指令。
,当用户双击关系图时,此示例创建形状在光标位置。本示例的 DSL 定义, ExampleShapeFillColor 属性显示。
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,设置它们的相对位置。
使用此方法,还可以设置颜色和其他连接线显示的属性。
使用事务
形状、连接和关系图是 ModelElement 子类型并存储在中。因此必须对仅其更改在事务内。有关更多信息,请参见 如何:使用事务更新模型。
文档视图和文档数据
存储分区
当设计加载时,附带的关系图同时加载。通常,模型加载到 Store.DefaultPartition,并且,关系图内容加载到另一个分区。通常,每个分区目录加载和保存到一个单独的文件中。
请参见
参考
概念
使用 Visual Studio Modelbus 集成模型