Procházení a aktualizace modelu v programovém kódu
Můžete napsat kód pro vytváření a odstraňování prvků modelu, nastavení jejich vlastností a vytváření a odstraňování propojení mezi prvky. Všechny změny musí být provedeny v rámci transakce. Pokud jsou prvky zobrazeny v diagramu, diagram se automaticky "opraví" na konci transakce.
Příklad definice DSL
Toto je hlavní část DslDefinition.dsl pro příklady v tomto tématu:
Tento model je instancí tohoto DSL:
Odkazy a obory názvů
Pokud chcete spustit kód v tomto tématu, měli byste odkazovat:
Microsoft.VisualStudio.Modeling.Sdk.11.0.dll
Váš kód bude používat tento obor názvů:
using Microsoft.VisualStudio.Modeling;
Kromě toho pokud píšete kód v jiném projektu než ten, ve kterém je definován dsl, měli byste importovat sestavení vytvořené projektem Dsl.
Navigace v modelu
Vlastnosti
Vlastnosti domény, které definujete v definici DSL, se stanou vlastnostmi, ke kterým máte přístup v kódu programu:
Person henry = ...;
if (henry.BirthDate < 1500) ...
if (henry.Name.EndsWith("VIII")) ...
Pokud chcete nastavit vlastnost, musíte to udělat uvnitř transakce:
henry.Name = "Henry VIII";
Pokud je v definici DSL, vlastnost Kind je Počítaný, nelze jej nastavit. Další informace naleznete v tématu Počítané a vlastní vlastnosti úložiště.
Relace
Relace domény, které definujete v definici DSL, se stanou páry vlastností, jedna ve třídě na každém konci relace. Názvy vlastností se v diagramu DslDefinition zobrazí jako popisky rolí na každé straně relace. V závislosti na násobnosti role je typ vlastnosti buď třída na druhém konci relace, nebo kolekce této třídy.
foreach (Person child in henry.Children) { ... }
FamilyTreeModel ftree = henry.FamilyTreeModel;
Vlastnosti na opačných koncích relace jsou vždy reciproční. Při vytvoření nebo odstranění odkazu se aktualizují vlastnosti role u obou prvků. Následující výraz (který používá rozšíření System.Linq
) je vždy pravdivý pro vztah ParentsHaveChildren v příkladu:
(Person p) => p.Children.All(child => child.Parents.Contains(p))
&& p.Parents.All(parent => parent.Children.Contains(p));
ElementLinks. Relace je také reprezentována elementem modelu označovaným jako propojení, což je instance typu relace domény. Odkaz má vždy jeden zdrojový prvek a jeden cílový prvek. Zdrojový prvek a cílový prvek mohou být stejné.
Můžete získat přístup k odkazu a jeho vlastnostem:
ParentsHaveChildren link = ParentsHaveChildren.GetLink(henry, edward);
// This is now true:
link == null || link.Parent == henry && link.Child == edward
Ve výchozím nastavení není povoleno propojení žádné dvojice prvků modelu více než jedna instance relace. Ale pokud v definici DSL, Allow Duplicates
příznak je pravdivý pro relaci, pak může existovat více než jeden odkaz a musíte použít GetLinks
:
foreach (ParentsHaveChildren link in ParentsHaveChildren.GetLinks(henry, edward)) { ... }
Existují také další metody pro přístup k odkazům. Příklad:
foreach (ParentsHaveChildren link in ParentsHaveChildren.GetLinksToChildren(henry)) { ... }
Skryté role. Pokud v definici DSL, Is Property Generated je false pro určitou roli, pak není generována žádná vlastnost, která odpovídá této roli. K odkazům ale můžete přistupovat a procházet je pomocí metod relace:
foreach (Person p in ParentsHaveChildren.GetChildren(henry)) { ... }
Nejčastěji používaným příkladem je PresentationViewsSubject relace, která propojuje prvek modelu s obrazcem, který ho zobrazuje v diagramu:
PresentationViewsSubject.GetPresentation(henry)[0] as PersonShape
Adresář elementů
Ke všem prvkům v úložišti můžete přistupovat pomocí adresáře elementů:
store.ElementDirectory.AllElements
Existují také metody pro hledání prvků, například:
store.ElementDirectory.FindElements(Person.DomainClassId);
store.ElementDirectory.GetElement(elementId);
Přístup k informacím o třídě
Můžete získat informace o třídách, relacích a dalších aspektech definice DSL. Příklad:
DomainClassInfo personClass = henry.GetDomainClass();
DomainPropertyInfo birthProperty =
personClass.FindDomainProperty("BirthDate")
DomainRelationshipInfo relationship =
link.GetDomainRelationship();
DomainRoleInfo sourceRole = relationship.DomainRole[0];
Nadřazené třídy prvků modelu jsou následující:
ModelElement – všechny prvky a relace jsou ModelElements
ElementLink – všechny relace jsou ElementLinks
Provádění změn uvnitř transakce
Pokaždé, když kód programu změní cokoli ve Storu, musí to udělat uvnitř transakce. To platí pro všechny prvky modelu, relace, obrazce, diagramy a jejich vlastnosti. Další informace najdete na webu Transaction.
Nejpohodlnější způsob správy transakce je s příkazem using
uzavřeným try...catch
v příkazu:
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.
}
V rámci jedné transakce můžete provést libovolný počet změn. Nové transakce můžete otevřít uvnitř aktivní transakce.
Chcete-li provést změny trvalé, měli Commit
byste transakci před odstraněním. Pokud dojde k výjimce, která není zachycena uvnitř transakce, Store se resetuje do stavu před změnami.
Vytváření elementů modelu
Tento příklad přidá prvek do existujícího modelu:
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!
}
Tento příklad ukazuje tyto základní body týkající se vytvoření elementu:
Vytvořte nový prvek v určitém oddílu Storu. U prvků modelu a relací, ale ne obrazců, je to obvykle výchozí oddíl.
Nastavte ho jako cíl relace vkládání. V DslDefinition tohoto příkladu musí být každá osoba cílem vkládání relace FamilyTreeHas Lidé. Abychom toho dosáhli, můžeme buď nastavit FamilyTreeModel vlastnost role Person objektu, nebo přidat Person do Lidé role vlastnost FamilyTreeModel objektu.
Nastavte vlastnosti nového prvku, zejména vlastnost, pro kterou
IsName
je true v DslDefinition. Tento příznak označuje vlastnost, která slouží k jednoznačné identifikaci prvku v rámci jeho vlastníka. V tomto případě vlastnost Name má tento příznak.Definice DSL této DSL musí být načtena do Storu. Pokud píšete rozšíření, jako je například příkaz nabídky, obvykle to bude platit. V jiných případech můžete model explicitně načíst do Storu nebo ho načíst pomocí modeluBus . Další informace naleznete v tématu Postupy: Otevření modelu ze souboru v kódu programu.
Když tímto způsobem vytvoříte prvek, obrazec se automaticky vytvoří (pokud má DSL diagram). Zobrazí se v automaticky přiřazené lokalitě s výchozím obrazcem, barvou a dalšími funkcemi. Pokud chcete určit, kde a jak se přidružený obrazec zobrazí, přečtěte si téma Vytvoření elementu a jeho obrazce.
Vytváření propojení relací
V ukázkové definici DSL jsou definovány dvě relace. Každá relace definuje vlastnost role třídy na každém konci relace.
Existují tři způsoby, jak vytvořit instanci relace. Každá z těchto tří metod má stejný účinek:
Nastavte vlastnost zdrojového hráče role. Příklad:
familyTree.People.Add(edward);
edward.Parents.Add(henry);
Nastavte vlastnost cílového hráče role. Příklad:
edward.familyTreeModel = familyTree;
Násobnost této role je
1..1
, takže přiřadíme hodnotu.henry.Children.Add(edward);
Násobnost této role je
0..*
, takže přidáme do kolekce.
Explicitně vytvořte instanci relace. Příklad:
FamilyTreeHasPeople edwardLink = new FamilyTreeHasPeople(familyTreeModel, edward);
ParentsHaveChildren edwardHenryLink = new ParentsHaveChildren(henry, edward);
Poslední metoda je užitečná, pokud chcete nastavit vlastnosti samotné relace.
Když tímto způsobem vytvoříte prvek, vytvoří se automaticky spojnice v diagramu, ale má výchozí tvar, barvu a další funkce. Informace o tom, jak je přidružená spojnice vytvořená, najdete v tématu Vytvoření elementu a jeho obrazce.
Odstranění elementů
Odstranění elementu voláním Delete()
:
henry.Delete();
Tato operace také odstraní:
Relace odkazuje na prvek a z elementu. Například
edward.Parents
již nebude obsahovathenry
.Prvky v rolích, pro které
PropagatesDelete
je příznak pravdivý. Například obrazec, který zobrazuje prvek, bude odstraněn.
Ve výchozím nastavení má PropagatesDelete
každá relace vkládání hodnotu true v cílové roli. Odstranění henry
neodstraní familyTree
, ale familyTree.Delete()
odstraní všechny Persons
.
Ve výchozím nastavení PropagatesDelete
neplatí pro role referenčních relací.
Při odstranění objektu můžete způsobit vynechání konkrétních šíření pravidel odstranění. To je užitečné, pokud vymýšlujete jeden prvek za jiný. Zadáte identifikátor GUID jedné nebo více rolí, pro které by se odstranění nemělo šířit. Identifikátor GUID lze získat z třídy relace:
henry.Delete(ParentsHaveChildren.SourceDomainRoleId);
(Tento konkrétní příklad by neměl žádný vliv, protože PropagatesDelete
se jedná false
o role ParentsHaveChildren
vztahu.)
V některýchpřípadechchmm elementu je v některých případech odstraněno odstraněním zámku. Můžete použít element.CanDelete()
ke kontrole, zda lze prvek odstranit.
Odstranění propojení relací
Propojení relací můžete odstranit odebráním prvku z vlastnosti role:
henry.Children.Remove(edward); // or:
edward.Parents.Remove(henry); // or:
Odkaz můžete také explicitně odstranit:
edwardHenryLink.Delete();
Tyto tři metody mají stejný účinek. Stačí použít jenom jeden z nich.
Pokud má role násobnost 0...1 nebo 1..1, můžete ji nastavit na null
hodnotu nebo na jinou hodnotu:
edward.FamilyTreeModel = null;
Nebo:
edward.FamilyTreeModel = anotherFamilyTree;
Změna pořadí propojení relace
Propojení konkrétní relace, která je zdrojem nebo cílem konkrétního prvku modelu, mají konkrétní sekvenci. Zobrazí se v pořadí, ve kterém byly přidány. Tento příkaz například vždy vrátí podřízené položky ve stejném pořadí:
foreach (Person child in henry.Children) ...
Můžete změnit pořadí odkazů:
ParentsHaveChildren link = GetLink(henry,edward);
ParentsHaveChildren nextLink = GetLink(henry, elizabeth);
DomainRoleInfo role =
link.GetDomainRelationship().DomainRoles[0];
link.MoveBefore(role, nextLink);
Zámky
Zámek může zabránit vašim změnám. Zámky je možné nastavit pro jednotlivé prvky, oddíly a úložiště. Pokud některá z těchto úrovní obsahuje zámek, který brání druhu změny, kterou chcete provést, může být při pokusu vyvolán výjimka. Pomocí elementu můžete zjistit, jestli jsou zámky nastavené. GetLocks(), což je rozšiřující metoda definovaná v oboru názvů Microsoft.VisualStudio.Modeling.Immutability.
Další informace naleznete v tématu Definování zásady uzamčení pro vytvoření segmentů jen pro čtení.
Kopírování a vkládání
Do objektu IDataObjectmůžete kopírovat prvky nebo skupiny prvků:
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>());
Prvky jsou uloženy jako serializovaná skupina elementů.
Prvky z objektu IDataObject můžete sloučit do modelu:
using (Transaction t = targetDiagram.Store.
TransactionManager.BeginTransaction("paste"))
{
adopterShape.Diagram.ElementOperations.Merge(adopter, data);
}
Merge ()
může přijmout buď aPresentationElement
, nebo .ModelElement
Pokud ji PresentationElement
dáte , můžete také zadat pozici v cílovém diagramu jako třetí parametr.
Navigace a aktualizace diagramů
V DSL je prvek doménového modelu, který představuje koncept, jako je Person nebo Song, oddělený od prvku obrazce, který představuje to, co vidíte v diagramu. Prvek doménového modelu ukládá důležité vlastnosti a vztahy konceptů. Prvek obrazce ukládá velikost, umístění a barvu zobrazení objektu v diagramu a rozložení jeho součástí.
Prvky prezentace
V definici DSL každý prvek, který zadáte, vytvoří třídu odvozenou z jedné z následujících standardních tříd.
Druh elementu | Základní třída |
---|---|
Domain – třída | ModelElement |
Vztah domény | ElementLink |
Tvar | NodeShape |
Konektor | BinaryLinkShape |
Obrázek | Diagram |
Prvek v diagramu obvykle představuje prvek modelu. Obvykle (ale ne vždy), NodeShape představuje instanci třídy domény a BinaryLinkShape představuje instanci vztahu domény. Relace PresentationViewsSubject propojí uzel nebo obrazec propojení s prvkem modelu, který představuje.
Každý obrazec uzlu nebo propojení patří do jednoho diagramu. Obrazec binárního propojení spojuje dva obrazce uzlů.
Obrazce můžou mít podřízené obrazce ve dvou sadách. Obrazec v NestedChildShapes
sadě je omezen na ohraničující rámeček nadřazeného objektu. Obrazec v RelativeChildShapes
seznamu se může objevit mimo hranice nadřazeného objektu nebo částečně mimo jeho hranice , například popisek nebo port. Diagram nemá žádné RelativeChildShapes
a ne Parent
.
Navigace mezi obrazci a prvky
Prvky doménového PresentationViewsSubject modelu a elementy obrazce souvisí vztahem.
// using Microsoft.VisualStudio.Modeling;
// using Microsoft.VisualStudio.Modeling.Diagrams;
// using System.Linq;
Person henry = ...;
PersonShape henryShape =
PresentationViewsSubject.GetPresentation(henry)
.FirstOrDefault() as PersonShape;
Stejná relace propojuje relace s spojnicemi v diagramu:
Descendants link = Descendants.GetLink(henry, edward);
DescendantConnector dc =
PresentationViewsSubject.GetPresentation(link)
.FirstOrDefault() as DescendantConnector;
// dc.FromShape == henryShape && dc.ToShape == edwardShape
Tato relace také propojuje kořen modelu s diagramem:
FamilyTreeDiagram diagram =
PresentationViewsSubject.GetPresentation(familyTree)
.FirstOrDefault() as FamilyTreeDiagram;
Pokud chcete získat prvek modelu reprezentovaný obrazcem, použijte:
henryShape.ModelElement as Person
diagram.ModelElement as FamilyTreeModel
Navigace kolem diagramu
Obecně se nedoporučuje přecházet mezi obrazci a spojnicemi v diagramu. Je lepší procházet relace v modelu, pohybovat se mezi obrazci a spojnicemi pouze v případě, že je nutné pracovat na vzhledu diagramu. Tyto metody propojují spojnice s obrazci na každém konci:
personShape.FromRoleLinkShapes, personShape.ToRoleLinkShapes
connector.FromShape, connector.ToShape
Mnoho obrazců je složených; jsou tvořeny nadřazeným tvarem a jednou nebo více vrstvami podřízených položek. Obrazce, které jsou umístěny vzhledem k jinému obrazci, jsou označeny jako jeho podřízené. Když se nadřazený obrazec přesune, podřízené objekty se s ním přesunou.
Relativní podřízené položky se můžou zobrazit mimo ohraničující rámeček nadřazeného obrazce. Vnořené podřízené položky se zobrazují přísně uvnitř hranic nadřazeného objektu.
K získání horní sady obrazců v diagramu použijte:
Diagram.NestedChildShapes
Nadřazené třídy obrazců a spojnic jsou:
-- ShapeElement
----- NodeShape
------- Diagram
------- Váš obrazec
----- LinkShape
------- BinaryLinkShape
--------- Váš Připojení or
Vlastnosti obrazců a Připojení orů
Ve většině případů není nutné provádět explicitní změny obrazců. Po změně prvků modelu pravidla "opravit" aktualizují obrazce a spojnice. Další informace naleznete v tématu Reakce na a šíření změn.
Je však užitečné provést některé explicitní změny obrazců ve vlastnostech, které jsou nezávislé na prvech modelu. Můžete například změnit tyto vlastnosti:
Size - určuje výšku a šířku obrazce.
Location - umístění vzhledem k nadřazeného obrazci nebo diagramu
StyleSet - sada per a štětců používaných k kreslení obrazce nebo spojnice
Hide - ztěžuje obrazec neviditelný
Show - zviditelní obrazec po
Hide()
Vytvoření elementu a jeho tvaru
Když vytvoříte prvek a propojíte ho se stromem relací vkládání, obrazec se automaticky vytvoří a přidružuje k němu. To se provádí pomocí pravidel "oprava", která se provádějí na konci transakce. Obrazec se ale zobrazí v automaticky přiřazené lokalitě a jeho obrazec, barva a další funkce budou mít výchozí hodnoty. K řízení způsobu vytvoření obrazce můžete použít funkci sloučení. Nejprve musíte přidat prvky, které chcete přidat do elementu ElementGroup, a pak skupinu sloučit do diagramu.
Tato metoda:
Nastaví název, pokud jste přiřadili vlastnost jako název elementu.
Sleduje všechny direktivy sloučení prvků, které jste zadali v definici DSL.
Tento příklad vytvoří obrazec na pozici myši, když uživatel poklikne na diagram. V definici DSL pro tuto ukázku FillColor
byla vlastnost ExampleShape
zpřístupněna.
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();
}
}
}
Pokud zadáte více než jeden obrazec, nastavte jejich relativní pozice pomocí .AbsoluteBounds
Pomocí této metody můžete také nastavit barvu a další vystavené vlastnosti konektorů.
Použití transakcí
Obrazce, spojnice a diagramy jsou podtypy ModelElement a jsou aktivní ve Storu. Proto je nutné provádět změny pouze uvnitř transakce. Další informace naleznete v tématu Postupy: Použití transakcí k aktualizaci modelu.
Zobrazení dokumentu a data dokumentu
Ukládání oddílů
Při načtení modelu se současně načte doprovodný diagram. Model se obvykle načte do Store.DefaultPartition a obsah diagramu se načte do jiného oddílu. Obsah každého oddílu se obvykle načte a uloží do samostatného souboru.