Megosztás a következőn keresztül:


Modell navigálása és frissítése a programkódban

Írhat kódot a modellelemek létrehozásához és törléséhez, a tulajdonságok beállításához, valamint az elemek közötti hivatkozások létrehozásához és törléséhez. Minden módosítást egy tranzakción belül kell végrehajtani. Ha az elemeket egy diagramon tekinti meg, a diagram automatikusan "javítva" lesz a tranzakció végén.

Példa DSL-definícióra

Ez a DslDefinition.dsl fő része a jelen témakör példáihoz:

DSL-definíciós diagram – családfamodell

Ez a modell ennek a DSL-nek az egy konkrét példája.

Tudor családfamodell

Hivatkozások és névterek

A kód ebben a témakörben való futtatásához a következőre kell hivatkoznia:

Microsoft.VisualStudio.Modeling.Sdk.11.0.dll

A kód a következő névteret fogja használni:

using Microsoft.VisualStudio.Modeling;

Ezenkívül, ha a kódot egy másik projektben írja, mint amelyikben a DSL definiálva van, importálja a Dsl-projekt által létrehozott szerelvényt.

Tulajdonságok

A DSL-definícióban definiált tartománytulajdonságok a programkódban elérhető tulajdonságokká válnak:

Person henry = ...;

if (henry.BirthDate < 1500) ...

if (henry.Name.EndsWith("VIII")) ...

Ha egy tulajdonságot szeretne beállítani, ezt egy tranzakción belül kell megtennie:

henry.Name = "Henry VIII";

Ha a DSL-definícióban egy tulajdonság TípusaSzámított, akkor nem állíthatja be. További információ: Számított és egyéni tárolótulajdonságok.

Relationships

A DSL-definícióban definiált tartománykapcsolatok tulajdonságok párjaivá válnak, amelyek a kapcsolat mindkét végén az osztályban vannak. A tulajdonságok neve a DslDefinition diagramban címkékként jelenik meg a kapcsolat mindkét oldalán lévő szerepkörökön. A szerepkör többszöröződésétől függően a tulajdonság típusa lehet a kapcsolat másik végén lévő osztály, vagy az osztály gyűjteménye.

foreach (Person child in henry.Children) { ... }

FamilyTreeModel ftree = henry.FamilyTreeModel;

A kapcsolat ellentétes végén lévő tulajdonságok mindig kölcsönösek. Hivatkozás létrehozásakor vagy törlésekor a szerepkör tulajdonságai mindkét elemen frissülnek. A következő kifejezés (amely a bővítményeket System.Linqhasználja) mindig igaz a ParentsHaveChildren kapcsolatra a példában:

(Person p) => p.Children.All(child => child.Parents.Contains(p))

&& p.Parents.All(parent => parent.Children.Contains(p));

ElementLinks. A kapcsolatokat egy hivatkozásnak nevezett modellelem is képviseli, amely a tartománykapcsolattípus egy példánya. A hivatkozásoknak mindig van egy forráseleme és egy céleleme. A forráselem és a célelem lehet ugyanaz.

A hivatkozásokat és azok tulajdonságait a következő címen érheti el:

ParentsHaveChildren link = ParentsHaveChildren.GetLink(henry, edward);

// This is now true:

link == null || link.Parent == henry && link.Child == edward

Alapértelmezés szerint egy kapcsolat egynél több példánya nem kapcsolhat össze modellelemeket. Ha azonban a DSL-definícióban a Allow Duplicates jelölő igaz a kapcsolatra, akkor több hivatkozás is lehet, és a következőt kell használnia GetLinks:

foreach (ParentsHaveChildren link in ParentsHaveChildren.GetLinks(henry, edward)) { ... }

A hivatkozások elérésére más módszerek is léteznek. Például:

foreach (ParentsHaveChildren link in ParentsHaveChildren.GetLinksToChildren(henry)) { ... }

Rejtett szerepkörök. Ha a DSL-definícióban a Generált tulajdonsághamis egy adott szerepkörhöz, akkor a rendszer nem hoz létre olyan tulajdonságot, amely megfelel az adott szerepkörnek. Azonban továbbra is elérheti a hivatkozásokat, és a kapcsolat módszereivel lépkedhet a hivatkozások között:

foreach (Person p in ParentsHaveChildren.GetChildren(henry)) { ... }

A leggyakrabban használt példa a PresentationViewsSubject kapcsolat, amely egy modellelemet a diagramon megjelenítő alakzathoz kapcsol:

PresentationViewsSubject.GetPresentation(henry)[0] as PersonShape

Az elemkönyvtár

Az elemkönyvtár használatával elérheti az áruház összes elemét:

store.ElementDirectory.AllElements

Az elemek keresésére is vannak módszerek, például a következők:

store.ElementDirectory.FindElements(Person.DomainClassId);

store.ElementDirectory.GetElement(elementId);

Az osztályadatok elérése

Információt kaphat a DSL-definíció osztályairól, kapcsolatairól és egyéb aspektusairól. Például:

DomainClassInfo personClass = henry.GetDomainClass();

DomainPropertyInfo birthProperty =

personClass.FindDomainProperty("BirthDate")

DomainRelationshipInfo relationship =

link.GetDomainRelationship();

DomainRoleInfo sourceRole = relationship.DomainRole[0];

A modellelemek elődosztályai a következők:

  • ModelElement – minden elem és kapcsolat egy ModelElement

  • ElementLink – minden kapcsolat ElementLinks

Módosítások végrehajtása tranzakción belül

Amikor a programkód bármit módosít az Áruházban, azt tranzakción belül kell megtennie. Ez az összes modellelemre, kapcsolatra, alakzatra, diagramra és azok tulajdonságaira vonatkozik. További információért lásd Transaction.

A tranzakciók kezelésének legkényelmesebb módja egy using utasítás egy try...catch utasításban:

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.
}

Egy tranzakción belül tetszőleges számú módosítást hajthat végre. Egy aktív tranzakción belül új tranzakciókat nyithat meg.

A módosítások véglegesítéséhez a tranzakciót a véglegesítés előtt kell Commit végrehajtania. Ha olyan kivétel történik, amely nem jelenik meg a tranzakcióban, az Áruház a módosítások előtt visszaáll az állapotára.

Modellelemek létrehozása

Ez a példa egy elemet ad hozzá egy meglévő modellhez:

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!
}

Ez a példa az elemek létrehozásával kapcsolatos alapvető szempontokat mutatja be:

  • Hozza létre az új elemet az Áruház adott partíciójában. Modellelemek és kapcsolatok esetén, de alakzatok nem, ez általában az alapértelmezett partíció.

  • Tegye az beágyazási kapcsolat célpontjává. A példa DslDefinition elemében minden személynek a FamilyTreeHasPeople kapcsolat beágyazásának célja kell lennie. Ennek eléréséhez beállíthatjuk a Személy objektum FamilyTreeModel szerepkörtulajdonságát, vagy hozzáadhatjuk a Személyt a FamilyTreeModel objektum Személyek szerepkörtulajdonságához.

  • Adja meg egy új elem tulajdonságait, különösen azt a tulajdonságot, amely IsName a DslDefinition tulajdonságban igaz. Ez a jelző jelöli azt a tulajdonságot, amely az elem tulajdonoson belüli egyedi azonosítására szolgál. Ebben az esetben a Name tulajdonságban ez a jelző szerepel.

  • Ennek a DSL-nek a DSL-definícióját be kellett volna tölteni az Áruházba. Ha egy bővítményt, például egy menüparancsot ír, ez általában már igaz lesz. Más esetekben explicit módon betöltheti a modellt az Áruházba, vagy a ModelBus használatával is betöltheti. További információért lásd: Hogyan nyissunk meg egy modellt fájlból programkódban?.

    Amikor ilyen módon hoz létre egy elemet, a rendszer automatikusan létrehoz egy alakzatot (ha a DSL-nek van diagramja). Automatikusan hozzárendelt helyen jelenik meg, alapértelmezett alakzattal, színnel és egyéb funkciókkal. Ha azt szeretné szabályozni, hogy a társított alakzat hol és hogyan jelenik meg, olvassa el az Elem és alakzat létrehozása című témakört.

A DSL-definícióban két kapcsolat van definiálva. Minden kapcsolat meghatároz egy szerepkörtulajdonságot az osztályban a kapcsolat mindkét végén.

Háromféleképpen hozhat létre egy kapcsolatpéldányt. A három módszer mindegyikének ugyanaz a hatása:

  • Állítsa be a forrásszereplő tulajdonságát. Például:

    • familyTree.People.Add(edward);

    • edward.Parents.Add(henry);

  • Állítsa be a célszerepkör résztvevőjének tulajdonságát. Például:

    • edward.familyTreeModel = familyTree;

      Ennek a szerepkörnek a szorzása az 1..1, ezért hozzárendeljük az értéket.

    • henry.Children.Add(edward);

      Ez a szerepkör többszörös előfordulása, így hozzáadjuk a gyűjteményhez.

  • Hozza létre a kapcsolat egy példányát explicit módon. Például:

    • FamilyTreeHasPeople edwardLink = new FamilyTreeHasPeople(familyTreeModel, edward);

    • ParentsHaveChildren edwardHenryLink = new ParentsHaveChildren(henry, edward);

    Az utolsó módszer akkor hasznos, ha a kapcsolat tulajdonságait szeretné beállítani.

    Ha így hoz létre egy elemet, a rendszer automatikusan létrehoz egy összekötőt a diagramon, de az alapértelmezett alakzat, szín és egyéb funkciókkal rendelkezik. A társított összekötő létrehozásának szabályozásához tekintse meg az Elem és alakzat létrehozása című témakört.

Elemek törlése

Elem törlése hívással Delete():

henry.Delete();

Ez a művelet a következőt is törli:

  • Kapcsolati hivatkozások az elemhez és az elemből. Például a edward.Parents továbbiakban nem fog tartalmazni henry.

  • A szerepkörök azon elemei, amelyekre a PropagatesDelete jelölő igaz. Az elemet megjelenítő alakzat például törlődik.

Alapértelmezés szerint minden beágyazási kapcsolat PropagatesDelete igaz a cél szerepkörre. A henry törlés nem törli a familyTreeelemet, hanem familyTree.Delete() az Personsösszeset törli.

Alapértelmezés szerint PropagatesDelete nem igaz a hivatkozási kapcsolatok szerepköreire nézve.

Előfordulhat, hogy a törlési szabályok bizonyos propagálásokat hagynak ki egy objektum törlésekor. Ez akkor hasznos, ha egy elemet egy másikra helyettesít. Adja meg egy vagy több szerepkör GUID-jait, amelyekhez a törlést nem szabad propagálni. A GUID a kapcsolatosztályból kérhető le:

henry.Delete(ParentsHaveChildren.SourceDomainRoleId);

(Ennek a konkrét példának nincs hatása, mert PropagatesDeletefalse a kapcsolat szerepköreihez ParentsHaveChildren tartozik.)

Bizonyos esetekben a törlést megakadályozza egy zárolás, akár az elemen, akár egy olyan elemen, amelyet propagálással törölnének. element.CanDelete() Ezzel ellenőrizheti, hogy az elem törölhető-e.

Kapcsolathivatkozást úgy törölhet, ha eltávolít egy elemet egy szerepkörtulajdonságból:

henry.Children.Remove(edward); // or:

edward.Parents.Remove(henry); // or:

A hivatkozást explicit módon is törölheti:

edwardHenryLink.Delete();

Ennek a három módszernek ugyanaz a hatása. Csak egyet kell használnia.

Ha a szerepkör 0..1 vagy 1...1 szorzású, beállíthatja a következő értékre nullvagy másik értékre:

edward.FamilyTreeModel = null; vagy:

edward.FamilyTreeModel = anotherFamilyTree;

Kapcsolat hivatkozásainak újrarendezése

Egy adott modellelemhez forrásként vagy célként megadott kapcsolat hivatkozásai egy adott sorozatot tartalmaznak. A hozzáadásuk sorrendjében jelennek meg. Ez az állítás például mindig ugyanabban a sorrendben adja meg a gyermekeket:

foreach (Person child in henry.Children) ...

Módosíthatja a hivatkozások sorrendjét:

ParentsHaveChildren link = GetLink(henry,edward);

ParentsHaveChildren nextLink = GetLink(henry, elizabeth);

DomainRoleInfo role =

link.GetDomainRelationship().DomainRoles[0];

link.MoveBefore(role, nextLink);

Locks

A módosítások zárolással akadályozhatók meg. A zárolások beállíthatók az egyes elemeken, a partíciókon és az áruházban. Ha ezen szintek bármelyike olyan zárolással rendelkezik, amely megakadályozza a módosítani kívánt módosítást, a rendszer kivételt jelezhet a kísérlet során. Az elem.GetLocks() bővítménymetódusának használatával megállapíthatja, hogy a zárolások aktívak-e, amely a névtérben Microsoft.VisualStudio.Modeling.Immutability van definiálva.

További információ: Zárolási szabályzat definiálása Read-Only szegmensek létrehozásához.

Másolás és beillesztés

Elemeket vagy elemcsoportokat másolhat a IDataObjectkövetkezőbe:

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>());

Az elemek szerializált elemcsoportként vannak tárolva.

Az IDataObject elemeit egy modellbe egyesítheti:

using (Transaction t = targetDiagram.Store.
        TransactionManager.BeginTransaction("paste"))
{
  adopterShape.Diagram.ElementOperations.Merge(adopter, data);
}

Merge () elfogadhatja az a PresentationElement vagy a ModelElement. Ha ad neki egy PresentationElementhelyet, a céldiagramon is megadhat egy pozíciót harmadik paraméterként.

Diagramok navigálása és frissítése

A DSL-ben a tartománymodell eleme, amely egy olyan fogalmat jelöl, mint a Személy vagy a Dal, elkülönül az alakzatelemtől, amely a diagramon látható elemeket jelöli. A tartománymodell eleme tárolja a fogalmak fontos tulajdonságait és kapcsolatait. Az alakzatelem tárolja az objektum nézetének méretét, pozícióját és színét a diagramon, valamint az összetevők elrendezését.

Bemutatóelemek

Az alapalakzatok és elemtípusok osztálydiagramja

A DSL-definícióban minden megadott elem létrehoz egy osztályt, amely a következő standard osztályok egyikéből származik.

Elem típusa Alaposztály
Tartományosztály ModelElement
Tartománykapcsolat ElementLink
Shape NodeShape
Connector BinaryLinkShape
Diagram Diagram

A diagram egy eleme általában egy modellelemet jelöl. Általában (de nem mindig) egy NodeShape egy tartományosztálypéldányt, és egy BinaryLinkShape egy tartománykapcsolati példányt jelöl. A PresentationViewsSubject kapcsolat összekapcsol egy csomópontot vagy egy csatolási alakzatot az általa képviselt modellelemhez.

Minden csomópont- vagy csatolási alakzat egy diagramhoz tartozik. A bináris csatolású alakzatok két csomópontalakzatot kötnek össze.

Az alakzatok gyermekalakzatokat tartalmazhatnak két halmazban. A NestedChildShapes halmazban lévő alakzat a szülő határolódobozában van korlátozva. A RelativeChildShapes listában szereplő alakzat a szülő határain kívül vagy részben kívül is megjelenhet – például címke vagy port. A diagramnak nincs RelativeChildShapes és Parent.

Navigálás alakzatok és elemek között

A tartománymodell elemei és az alakzatelemek kapcsolatban állnak a PresentationViewsSubject kapcsolattal.

// using Microsoft.VisualStudio.Modeling;
// using Microsoft.VisualStudio.Modeling.Diagrams;
// using System.Linq;
Person henry = ...;
PersonShape henryShape =
  PresentationViewsSubject.GetPresentation(henry)
    .FirstOrDefault() as PersonShape;

Ugyanez a kapcsolat összekapcsolja a kapcsolatokat a diagramon lévő összekötőkkel:

Descendants link = Descendants.GetLink(henry, edward);
DescendantConnector dc =
   PresentationViewsSubject.GetPresentation(link)
     .FirstOrDefault() as DescendantConnector;
// dc.FromShape == henryShape && dc.ToShape == edwardShape

Ez a kapcsolat a modell gyökerét a diagramhoz is csatolja:

FamilyTreeDiagram diagram =
   PresentationViewsSubject.GetPresentation(familyTree)
      .FirstOrDefault() as FamilyTreeDiagram;

A modellelem alakzattal való ábrázolásához használja a következőt:

henryShape.ModelElement as Person

diagram.ModelElement as FamilyTreeModel

Általában nem ajánlott navigálni az alakzatok és összekötők között a diagramon. Jobb, ha a modellben lévő kapcsolatokat navigálja, és csak akkor lépked az alakzatok és összekötők között, ha a diagram megjelenésén kell dolgoznia. Ezek a metódusok összekapcsolják az összekötőket a két végén lévő alakzatokkal:

personShape.FromRoleLinkShapes, personShape.ToRoleLinkShapes

connector.FromShape, connector.ToShape

Sok alakzat összetett; egy szülőalakzatból és egy vagy több gyermekrétegből állnak. A másik alakzathoz viszonyítva elhelyezett alakzatokat a rendszer gyermekeknek tekinti. Amikor a szülőalakzat mozog, a gyerekek vele együtt mozognak.

A relatív gyermekek a szülőalakzat határolókeretén kívül is megjelenhetnek. Beágyazott gyermekek szigorúan a szülői határokon belül jelennek meg.

A diagramon lévő alakzatok felső készletének beszerzéséhez használja a következőt:

Diagram.NestedChildShapes

Az alakzatok és összekötők elődosztályai a következők:

ModelElement

-- PresentationElement

-- ShapeElement

----- NodeShape

------- Diagram

------- YourShape

----- LinkShape

------- BinaryLinkShape

--------- YourConnector

Alakzatok és összekötők tulajdonságai

A legtöbb esetben nem szükséges explicit módosításokat végezni az alakzatokon. Amikor módosította a modellelemeket, a "kiigazítás" szabályok frissítik az alakzatokat és az összekötőket. További információt a Változások megválaszolása és propagálása című témakörben talál.

Hasznos azonban, ha a modellelemekétől független tulajdonságok alakzatait explicit módon módosítja. Módosíthatja például ezeket a tulajdonságokat:

  • Size - meghatározza az alakzat magasságát és szélességét.

  • Location - a szülőalakzathoz vagy diagramhoz viszonyított pozíció

  • StyleSet - az alakzat vagy összekötő rajzolásához használt tollak és ecsetek készlete

  • Hide - az alakzatot láthatatlanná teszi

  • Show - az alakzat láthatóvá válik a Hide() után

Egy elem és annak formájának létrehozása

Amikor létrehoz egy elemet, és összekapcsolja a kapcsolatok beágyazásának fájával, a rendszer automatikusan létrehoz és társít egy alakzatot. A tranzakció végén lefuttatott "fixup" szabályok hajtják ezt végre. Az alakzat azonban automatikusan hozzárendelt helyen jelenik meg, és az alakzat, a szín és más funkciók alapértelmezett értékekkel rendelkeznek. Az alakzat létrehozásának szabályozásához használhatja az egyesítési függvényt. Először fel kell vennie a hozzáadni kívánt elemeket egy Elemcsoportba, majd egyesítenie kell a csoportot a diagramban.

Ez a módszer:

  • Beállítja a nevet, ha elemnévként hozzárendelt egy tulajdonságot.

  • Megfigyeli a DSL-definícióban megadott elemegyesítési irányelveket.

Ez a példa létrehoz egy alakzatot az egér pozíciójában, amikor a felhasználó duplán kattint a diagramra. A minta DSL-definíciójában FillColor a ExampleShape tulajdonság ki lett téve.

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();
    }
  }
}

Ha egynél több alakzatot ad meg, állítsa be a relatív pozícióikat a AbsoluteBounds.

Ezzel a módszerrel az összekötők színét és egyéb közzétett tulajdonságait is beállíthatja.

Tranzakciók használata

Az alakzatok, összekötők és diagramok a ModelElement altípusai, és az Áruházban találhatók. Ezért csak egy tranzakción belül kell módosítania őket. További információért lásd: Hogyan használjuk a tranzakciókat a modell frissítéséhez.

Dokumentumnézet és dokumentumadatok

Standard diagramtípusok osztálydiagramja

Tárolópartíciók

A modell betöltésekor a kapcsolódó diagram egyszerre lesz betöltve. A modell betöltése általában a Store.DefaultPartition szolgáltatásba történik, a diagram tartalma pedig egy másik partícióba töltődik be. Az egyes partíciók tartalma általában egy külön fájlba van betöltve és mentve.