Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Можно написать код для создания и удаления элементов модели, задания их свойств и создания и удаления связей между элементами. Все изменения должны быть внесены в транзакцию. Если элементы отображаются на схеме, схема будет "исправлена" автоматически в конце транзакции.
Пример определения 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));
ElementLinks. Связь также представлена элементом модели, который называется ссылкой, которая является экземпляром типа связи домена. Ссылка всегда имеет один исходный элемент и один целевой элемент. Исходный элемент и целевой элемент могут совпадать.
Вы можете получить доступ к ссылке и ее свойствам:
ParentsHaveChildren link = ParentsHaveChildren.GetLink(henry, edward);
// This is now true:
link == null || link.Parent == henry && link.Child == edward
По умолчанию разрешается связывать не более одного экземпляра связи для любой пары элементов модели. Но если в определении Allow Duplicates DSL флаг имеет значение 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];
Классы предков элементов модели приведены следующим образом:
Элемент модели — все элементы и связи являются элементами модели.
ElementLink — все связи — ElementLinks
Выполнение изменений внутри транзакции
Всякий раз, когда код программы изменяет что-либо в Магазине, он должен сделать это внутри транзакции. Это относится ко всем элементам модели, связям, фигурам, схемам и их свойствам. Дополнительные сведения см. в разделе Transaction.
Самый удобный способ управления транзакцией — это выражение using, заключенное в выражение try...catch.
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!
}
В этом примере показаны следующие основные моменты создания элемента:
Создайте новый элемент в определенной секции Store. Для элементов и связей модели, но не фигур, обычно это секция по умолчанию.
Сделайте его целевым объектом связи внедрения. В этом примере DslDefinition каждая персона должна быть целью включения отношений FamilyTreeHasPeople. Для этого можно задать свойство роли FamilyTreeModel объекта Person или добавить Person в свойство роли "Люди" объекта FamilyTreeModel.
Задайте свойства нового элемента, в частности для свойства, для которого
IsNameзадано значение true в DSL-определении. Этот флаг обозначает свойство, которое служит для уникальной идентификации элемента в рамках его владельца. В этом случае свойство Name имеет этот флаг.Определение DSL этого DSL должно быть загружено в хранилище. Если вы пишете расширение, например команду меню, это обычно будет верно. В других случаях можно явно загрузить модель в Магазин или использовать 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флаг имеет значение true. Например, фигура, отображающая элемент, будет удалена.
По умолчанию каждая связь внедрения имеет PropagatesDelete значение true в целевой роли. Удаление henry не удаляет familyTree, но familyTree.Delete() удаляет все Persons.
По умолчанию PropagatesDelete не соответствует ролям ссылочных связей.
Можно заставить правила удаления пропустить определенные распространения при удалении объекта. Это полезно, если вы заменяете один элемент на другой. Вы предоставляете GUID одной или нескольких ролей, для которых удаление не должно распространяться. GUID можно получить из класса связи:
henry.Delete(ParentsHaveChildren.SourceDomainRoleId);
(Этот конкретный пример не имел бы никакого эффекта, так как PropagatesDelete является false для ролей ParentsHaveChildren отношения.)
В некоторых случаях удаление предотвращается наличием блокировки либо в элементе, либо в элементе, который будет удален путем распространения. Можно использовать 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
Изменения могут быть запрещены блокировкой. Блокировки можно задать для отдельных элементов, секций и в хранилище. Если какой-либо из этих уровней имеет блокировку, которая предотвращает тот тип изменения, который вы хотите внести, исключение может быть сгенерировано при попытке сделать это. Вы можете выяснить, установлены ли блокировки, с помощью элемента.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, отделен от элемента фигуры, который представляет то, что вы видите на схеме. Элемент модели домена сохраняет важные свойства и связи понятий. Элемент фигуры сохраняет размер, положение и цвет представления объекта на схеме и макет его компонентов.
Элементы презентации
В определении DSL каждый элемент, указанный вами, создает класс, производный от одного из следующих стандартных классов.
| Тип элемента | Базовый класс |
|---|---|
| Класс домена | ModelElement |
| Взаимосвязь доменов | ElementLink |
| Shape | NodeShape |
| Connector | BinaryLinkShape |
| Diagram | Diagram |
Элемент на схеме обычно представляет элемент модели. Обычно (но не всегда), 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
Многие фигуры являются составными; они состоят из родительской фигуры и одного или нескольких слоев дочерних фигур. Фигуры, расположенные относительно другой фигуры, как говорят, являются его дочерними. При перемещении родительской фигуры дочерние элементы перемещаются вместе с ней.
Относительные дочерние элементы могут отображаться вне ограничивающего поля родительской фигуры. Вложенные дочерние элементы отображаются строго внутри границ родительского элемента.
Чтобы получить верхний набор фигур на схеме, используйте следующую команду:
Diagram.NestedChildShapes
Предковые классы фигур и соединителей:
-- ShapeElement
----- NodeShape
------- Diagram
------- YourShape
----- LinkShape
------- BinaryLinkShape
--------- YourConnector
Свойства фигур и соединителей
В большинстве случаев не требуется вносить явные изменения в фигуры. При изменении элементов модели правила "исправить" обновляют фигуры и соединители. Дополнительные сведения см. в статье "Реагирование на изменения" и "Распространение изменений".
Однако полезно внести некоторые явные изменения в свойства фигур, которые не зависят от элементов модели. Например, можно изменить следующие свойства:
Size — определяет высоту и ширину фигуры.
Location — положение относительно родительской фигуры или схемы
StyleSet — набор перьев и кистей, используемых для рисования фигуры или соединителя
Hide — делает фигуру невидимой
Show — делает фигуру видимой после
Hide()
Создание элемента и его формы
При создании элемента и его включении в дерево относящихся связей, автоматически создается форма и связывается с ним. Это делается правилами "fixup", которые выполняются в конце транзакции. Однако фигура будет отображаться в автоматически назначенном расположении, а ее фигура, цвет и другие функции будут иметь значения по умолчанию. Чтобы управлять созданием фигуры, можно использовать функцию объединения. Сначала необходимо добавить элементы, которые нужно добавить в ElementGroup, а затем объединить группу с диаграммой.
Этот метод:
Задает имя, если вы назначили свойство в качестве имени элемента.
Соблюдает любые директивы слияния элементов, указанные в определении DSL.
В этом примере создается фигура на позиции мыши, когда пользователь дважды щелкает схему. В определении DSL для этого примера свойство FillColor из ExampleShape было открыто.
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, а содержимое схемы загружается в другую секцию. Обычно содержимое каждой секции загружается и сохраняется в отдельный файл.