Sdílet prostřednictvím


Navigace a aktualizace modelu v kódu programu

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:

Diagram definice DSL – model rodinného stromu

Tento model je instancí tohoto DSL:

Model rodinného stromu Tudor

Odkazy a obory názvů

Pokud chcete spustit kód v tomto tématu, měli byste uvést:

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.

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 KindPočítaná, nelze ji nastavit. Další informace naleznete v tématu Počítané a vlastní vlastnosti úložiště.

Relationships

Relace domény, které definujete v definici DSL, se stanou dvojicemi vlastností, jedna na 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í povolena více než jedna instance relace pro propojení jakékoli dvojice prvků modelu. Ale pokud v definici DSL je příznak Allow Duplicates 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. Napří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. Napří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 v tématu Transaction.

Nejpohodlnější způsob správy transakce je s příkazem using, který je uzavřen v příkazu 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.
}

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 trvale, měli byste před zrušením Commit transakci. 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 úložiště. U prvků modelu a relací, s výjimkou obrazců, je to obvykle oddíl výchozí.

  • 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 FamilyTreeHasPeople. Abychom toho dosáhli, můžeme buď nastavit vlastnost role u objektu Person ve FamilyTreeModel, nebo přidat Person do vlastnosti role People u objektu FamilyTreeModel.

  • 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 této DSL musí být načtena do úložiště. Pokud píšete rozšíření, jako je například příkaz nabídky, obvykle to již platí. V jiných případech můžete model explicitně načíst do Store nebo ho načíst pomocí ModelBus. 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.

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 můžete vytvořit instanci vztahu. Každá z těchto tří metod má stejný účinek:

  • Nastavte vlastnost zdrojového hráče role. Například:

    • familyTree.People.Add(edward);

    • edward.Parents.Add(henry);

  • Nastavte vlastnost cílového hráče role. Například:

    • edward.familyTreeModel = familyTree;

      Násobnost této role je 1..1, proto 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. Napří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.

Odstraňování prvků

Odstranění elementu voláním Delete():

henry.Delete();

Tato operace také odstraní:

  • Vztahy směřující k prvku a od prvku. Například edward.Parents již nebude obsahovat henry.

  • Prvky v rolích, kde je příznak PropagatesDelete platný. Například obrazec, který zobrazuje prvek, bude odstraněn.

Ve výchozím nastavení má každá relace vkládání ve své cílové roli hodnotu PropagatesDelete true. 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 pravidel odstranění. To je užitečné, pokud nahrazujete jeden prvek jiným. 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 vztahu:

henry.Delete(ParentsHaveChildren.SourceDomainRoleId);

(Tento konkrétní příklad by neměl žádný vliv, protože PropagatesDelete je false pro role ParentsHaveChildren vztahu.)

V některých případech je zabráněno odstranění existencí zámku buď na elementu, nebo na elementu, který by byl odstraněn propagací. Můžete použít element.CanDelete() ke kontrole, zda lze prvek odstranit.

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 nullhodnotu 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í potomky 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);

Locks

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í změně, kterou chcete provést, může být při pokusu vyvolána výjimka. Pomocí elementu.GetLocks(), rozšiřovací metody, která je definovaná v oboru názvů Microsoft.VisualStudio.Modeling.Immutability, můžete zjistit, jestli jsou zámky nastavené.

Další informace naleznete v tématu Definování zásady uzamčení pro vytvoření Read-Only segmentů.

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ď PresentationElement, nebo ModelElement. Pokud ji PresentationElementdá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

Diagram tříd základních typů obrazců a prvků

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
doménová třída ModelElement
Vztah domény ElementLink
Shape NodeShape
Connector BinaryLinkShape
Diagram 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. Vztah PresentationViewsSubject spojuje uzel nebo tvar 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 mohou mít podřízené tvary ve dvou sadách. Obrazec v NestedChildShapes sadě je omezen na ohraničující rámeček nadřazeného objektu. Obrazce v RelativeChildShapes seznamu se mohou objevit zcela nebo částečně mimo hranice nadřazeného objektu, 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

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 kompozitních; jsou tvořeny nadřazeným tvarem a jednou nebo více vrstvami dílčích prvků. Obrazce, které jsou umístěny vzhledem k jinému obrazci, jsou označeny jako jeho podřízené elementy. Když se nadřazený obrazec přesune, podřízené objekty se s ním přesunou.

Relativní potomci se můžou zobrazit mimo ohraničující rámeček nadřazeného obrazce. Vnořené prvky se zobrazují přísně uvnitř hranic rodičovského objektu.

K získání horní sady obrazců v diagramu použijte:

Diagram.NestedChildShapes

Nadřazené třídy obrazců a spojnic jsou:

ModelElement

-- PresentationElement

-- ShapeElement

----- NodeShape

------- Diagram

------- YourShape

----- LinkShape

------- BinaryLinkShape

--------- Váš konektor

Vlastnosti obrazců a spojnic

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ému obrazci nebo diagramu

  • StyleSet - sada per a štětců používaných k kreslení obrazce nebo spojnice

  • Hide - činí obrazec neviditelným

  • 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 byla zpřístupněna vlastnost 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();
    }
  }
}

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žijte transakce

Obrazce, spojnice a diagramy jsou podtypy ModelElement a jsou aktivní ve Store. 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

Diagram tříd standardních typů diagramů

Úložiště 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.