Share via


Navigeren en een model bijwerken in programmacode

U kunt code schrijven om modelelementen te maken en te verwijderen, hun eigenschappen in te stellen en koppelingen tussen elementen te maken en te verwijderen. Alle wijzigingen moeten binnen een transactie worden aangebracht. Als de elementen in een diagram worden bekeken, wordt het diagram automatisch 'opgelost' aan het einde van de transactie.

Een voorbeeld van EEN DSL-definitie

Dit is het belangrijkste onderdeel van DslDefinition.dsl voor de voorbeelden in dit onderwerp:

DSL-definitiediagram - familiestructuurmodel

Dit model is een exemplaar van deze DSL:

Tudor Family Tree Model

Verwijzingen en naamruimten

Als u de code in dit onderwerp wilt uitvoeren, moet u verwijzen naar:

Microsoft.VisualStudio.Modeling.Sdk.11.0.dll

Uw code gebruikt deze naamruimte:

using Microsoft.VisualStudio.Modeling;

Als u bovendien de code in een ander project schrijft dan het project waarin uw DSL is gedefinieerd, moet u de assembly importeren die is gebouwd door het Dsl-project.

Eigenschappen

Domeineigenschappen die u in de DSL-definitie definieert, worden eigenschappen die u kunt benaderen in programmacode.

Person henry = ...;

if (henry.BirthDate < 1500) ...

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

Als u een eigenschap wilt instellen, moet u dit doen binnen een transactie:

henry.Name = "Henry VIII";

Als in de DSL-definitie het type van een eigenschap wordt berekend, kunt u deze niet instellen. Zie Eigenschappen voor berekende en aangepaste opslag voor meer informatie.

Relationships

Domeinrelaties die u in de DSL-definitie definieert, worden paren van eigenschappen, één op de klasse aan elk einde van de relatie. De namen van de eigenschappen worden weergegeven in het DslDefinition-diagram als labels voor de rollen aan elke kant van de relatie. Afhankelijk van de multipliciteit van de rol is het type eigenschap de klasse aan het andere uiteinde van de relatie of een verzameling van die klasse.

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

FamilyTreeModel ftree = henry.FamilyTreeModel;

De eigenschappen aan tegenovergestelde uiteinden van een relatie zijn altijd wederkerig. Wanneer een koppeling wordt gemaakt of verwijderd, worden de roleigenschappen op beide elementen bijgewerkt. De volgende expressie (die gebruikmaakt van de extensies van System.Linq) is altijd waar voor de relatie ParentsHaveChildren in het voorbeeld:

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

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

ElementLinks. Een relatie wordt ook vertegenwoordigd door een modelelement dat een koppeling wordt genoemd. Dit is een exemplaar van het domeinrelatietype. Een koppeling heeft altijd één bronelement en één doelelement. Het bronelement en het doelelement kunnen hetzelfde zijn.

U hebt toegang tot een koppeling en de bijbehorende eigenschappen:

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

// This is now true:

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

Standaard mag niet meer dan één exemplaar van een relatie een paar modelelementen koppelen. Maar als in de DSL-definitie de Allow Duplicates vlag voor de relatie waar is, kan er meer dan één koppeling zijn en moet u GetLinks:

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

Er zijn ook andere methoden voor het openen van koppelingen. Voorbeeld:

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

Verborgen rollen. Als in de DSL-definitie Is eigenschap gegenereerdfalse is voor een bepaalde rol, dan wordt er geen eigenschap gegenereerd die overeenkomt met die rol. U kunt echter nog steeds toegang krijgen tot de koppelingen en de koppelingen doorlopen met behulp van de methoden van de relatie:

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

Het meest gebruikte voorbeeld is de PresentationViewsSubject relatie, die een modelelement koppelt aan de shape die het weergeeft in een diagram:

PresentationViewsSubject.GetPresentation(henry)[0] as PersonShape

Het Elementdirectory

U kunt alle elementen in de winkel openen met behulp van de elementendirectory.

store.ElementDirectory.AllElements

Er zijn ook methoden voor het vinden van elementen, zoals de volgende:

store.ElementDirectory.FindElements(Person.DomainClassId);

store.ElementDirectory.GetElement(elementId);

Toegang tot klasse-informatie

U kunt informatie krijgen over de klassen, relaties en andere aspecten van de DSL-definitie. Voorbeeld:

DomainClassInfo personClass = henry.GetDomainClass();

DomainPropertyInfo birthProperty =

personClass.FindDomainProperty("BirthDate")

DomainRelationshipInfo relationship =

link.GetDomainRelationship();

DomainRoleInfo sourceRole = relationship.DomainRole[0];

De bovenliggende klassen van modelelementen zijn als volgt:

  • ModelElement - alle elementen en relaties zijn ModelElements

  • ElementLink : alle relaties zijn ElementLinks

Wijzigingen in een transactie uitvoeren

Wanneer uw programmacode iets in de Store wijzigt, moet dit in een transactie worden gedaan. Dit geldt voor alle modelelementen, relaties, shapes, diagrammen en de bijbehorende eigenschappen. Zie Transaction voor meer informatie.

De meest handige methode om een transactie te beheren is met een using instructie in een try...catch instructie.

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

U kunt een willekeurig aantal wijzigingen aanbrengen binnen één transactie. U kunt nieuwe transacties openen binnen een actieve transactie.

Als u uw wijzigingen permanent wilt aanbrengen, moet Commit u de transactie uitvoeren voordat deze wordt verwijderd. Als er een uitzondering optreedt die niet binnen de transactie wordt gevangen, wordt de Store opnieuw ingesteld op de status voordat de wijzigingen worden doorgevoerd.

Modelelementen maken

In dit voorbeeld wordt een element toegevoegd aan een bestaand model:

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

In dit voorbeeld ziet u de volgende essentiële punten voor het maken van een element:

  • Maak het nieuwe element in een specifieke partitie van de Store. Voor modelelementen en relaties, maar niet voor vormen, is dit meestal de standaardpartitie.

  • Maak dit het doel van een embedderelatie. In de DslDefinition van dit voorbeeld moet elke persoon het doel zijn van het insluiten van relatie FamilyTreeHasPeople. Hiervoor kunnen we de eigenschap FamilyTreeModel-rol van het object Person instellen of de persoon toevoegen aan de eigenschap Personenrol van het object FamilyTreeModel.

  • Stel de eigenschappen van een nieuw element in, vooral de eigenschap waarvoor IsName waar is in de DslDefinition. Dit kenmerk markeert de eigenschap die dient om het element uniek te identificeren binnen zijn eigenaar. In dit geval heeft de eigenschap Name die vlag.

  • De DSL-definitie van deze DSL moet in de Store zijn geladen. Als u een extensie zoals een menuopdracht schrijft, is dit meestal al waar. In andere gevallen kunt u het model expliciet laden in de Store of ModelBus gebruiken om het te laden. Zie How to: Open a Model from File in Program Code voor meer informatie.

    Wanneer u op deze manier een element maakt, wordt er automatisch een vorm gemaakt (als de DSL een diagram heeft). Deze wordt weergegeven op een automatisch toegewezen locatie, met standaardvorm, kleur en andere functies. Zie Een element en de bijbehorende shape maken als u wilt bepalen waar en hoe de bijbehorende shape wordt weergegeven.

Er zijn twee relaties gedefinieerd in de voorbeeld-DSL-definitie. Elke relatie definieert een roleigenschap in de klasse aan elk einde van de relatie.

Er zijn drie manieren waarop u een instantie van een relatie kunt maken. Elk van deze drie methoden heeft hetzelfde effect:

  • Stel de eigenschap van de bronrolspeler in. Voorbeeld:

    • familyTree.People.Add(edward);

    • edward.Parents.Add(henry);

  • Stel de eigenschap van de doelrolspeler in. Voorbeeld:

    • edward.familyTreeModel = familyTree;

      De multipliciteit van deze rol is 1..1, dus wij wijzen de waarde toe.

    • henry.Children.Add(edward);

      De multipliciteit van deze rol is 0..*, dus voegen we toe aan de verzameling.

  • Maak expliciet een exemplaar van de relatie. Voorbeeld:

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

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

    De laatste methode is handig als u eigenschappen voor de relatie zelf wilt instellen.

    Wanneer u op deze manier een element maakt, wordt er automatisch een verbindingslijn in het diagram gemaakt, maar deze heeft een standaardvorm, kleur en andere functies. Zie Een element en de shape maken om te bepalen hoe de gekoppelde connector wordt gemaakt.

Elementen verwijderen

Een element verwijderen door aan te roepen Delete():

henry.Delete();

Met deze bewerking wordt ook het volgende verwijderd:

  • Relatiekoppelingen naar en van het element. Bijvoorbeeld, edward.Parents zal henry niet langer bevatten.

  • Elementen in rollen waarvoor de PropagatesDelete vlag waar is. De shape waarin het element wordt weergegeven, wordt bijvoorbeeld verwijderd.

Standaard staat elke insluitingsrelatie op PropagatesDelete waar bij de doelrol. Het verwijderen van henry verwijdert de familyTree niet, maar familyTree.Delete() zou alle Persons verwijderen.

Standaard is PropagatesDelete niet waar voor de rollen van referentierelaties.

U kunt ervoor zorgen dat de verwijderingsregels specifieke doorgiften weglaten wanneer u een object verwijdert. Dit is handig als u het ene element vervangt door een ander element. U geeft de GUID op van een of meer rollen waarvoor verwijdering niet mag worden doorgegeven. De GUID kan worden verkregen uit de relatieklasse:

henry.Delete(ParentsHaveChildren.SourceDomainRoleId);

(Dit specifieke voorbeeld heeft geen effect, omdat PropagatesDeletefalse is voor de rollen van de ParentsHaveChildren relatie.)

In sommige gevallen wordt verwijdering voorkomen door de aanwezigheid van een vergrendeling, hetzij op het element of op een element dat door overdracht zou worden verwijderd. U kunt gebruiken element.CanDelete() om te controleren of het element kan worden verwijderd.

U kunt een relatiekoppeling verwijderen door een element uit een roleigenschap te verwijderen:

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

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

U kunt de koppeling ook expliciet verwijderen:

edwardHenryLink.Delete();

Deze drie methoden hebben allemaal hetzelfde effect. U hoeft slechts een van deze te gebruiken.

Als de rol 0..1 of 1..1 multipliciteit heeft, kunt u deze nullinstellen op of op een andere waarde:

edward.FamilyTreeModel = null; of:

edward.FamilyTreeModel = anotherFamilyTree;

De koppelingen van een relatie opnieuw ordenen

De koppelingen van een bepaalde relatie die zijn afkomstig of gericht op een bepaald modelelement, hebben een specifieke reeks. Ze worden weergegeven in de volgorde waarin ze zijn toegevoegd. Met deze instructie worden de kinderen bijvoorbeeld altijd in dezelfde volgorde weergegeven:

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

U kunt de volgorde van de koppelingen wijzigen:

ParentsHaveChildren link = GetLink(henry,edward);

ParentsHaveChildren nextLink = GetLink(henry, elizabeth);

DomainRoleInfo role =

link.GetDomainRelationship().DomainRoles[0];

link.MoveBefore(role, nextLink);

Locks

Uw wijzigingen kunnen worden voorkomen door een vergrendeling. Vergrendelingen kunnen worden ingesteld op afzonderlijke elementen, op partities en op de opslag. Als een van deze niveaus een vergrendeling heeft die het soort wijziging voorkomt dat u wilt aanbrengen, kan er een uitzondering optreden wanneer u deze probeert uit te voeren. U kunt ontdekken of vergrendelingen zijn ingesteld met behulp van element. GetLocks(), een extensiemethode die is gedefinieerd in de naamruimte Microsoft.VisualStudio.Modeling.Immutability.

Zie Een vergrendelingsbeleid definiëren om Read-Only segmenten te maken voor meer informatie.

Kopiëren en plakken

U kunt elementen of groepen elementen kopiëren naar een 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>());

De elementen worden opgeslagen als een geserialiseerde elementgroep.

U kunt elementen uit een IDataObject samenvoegen in een model:

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

Merge () kan een PresentationElement of een ModelElement. Als u een PresentationElementwaarde opgeeft, kunt u ook een positie in het doeldiagram opgeven als een derde parameter.

Navigeren en bijwerken van diagrammen

In een DSL staat het domeinmodelelement, dat een concept zoals Person of Song vertegenwoordigt, los van het shape-element, dat aangeeft wat u in het diagram ziet. Het domeinmodelelement slaat de belangrijke eigenschappen en relaties van de concepten op. Het shape-element slaat de grootte, positie en kleur van de weergave van het object op in het diagram en de indeling van de onderdelen ervan.

Presentatie-elementen

Klassendiagram van basisvorm- en elementtypen

In uw DSL-definitie maakt elk element dat u opgeeft een klasse die is afgeleid van een van de volgende standaardklassen.

Soort element Basisklasse
Domeinklasse ModelElement
Domeinrelatie ElementLink
Shape NodeShape
Connector BinaryLinkShape
Diagram Diagram

Een element in een diagram vertegenwoordigt meestal een modelelement. Normaal gesproken (maar niet altijd), vertegenwoordigt een NodeShape een instantie van een domeinklasse en een BinaryLinkShape een instantie van een domeinrelatie. De PresentationViewsSubject relatie koppelt een knooppunt of koppelingsshape aan het modelelement dat wordt aangegeven.

Elke knooppunt- of koppelingsshape behoort tot één diagram. Een binaire verbindingsvorm verbindt twee knooppuntvormen.

Vormen kunnen onderliggende vormen in twee sets bevatten. Een vorm in de NestedChildShapes set is beperkt tot het omkadervlak van zijn bovenliggende element. Een vorm in de RelativeChildShapes lijst kan zich geheel of gedeeltelijk buiten de grenzen van de ouder bevinden, zoals bijvoorbeeld een label of een poort. Een diagram heeft geen RelativeChildShapes en geen Parent.

Navigeren tussen vormen en elementen

Domeinmodelelementen en shape-elementen zijn gerelateerd aan de PresentationViewsSubject relatie.

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

Dezelfde relatie verbindt relaties met connectoren op het diagram.

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

Deze relatie koppelt ook de wortel van het model aan het diagram.

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

Als u het modelelement wilt ophalen dat wordt vertegenwoordigd door een shape, gebruikt u:

henryShape.ModelElement as Person

diagram.ModelElement as FamilyTreeModel

Over het algemeen is het niet raadzaam om in het diagram tussen vormen en verbindingslijnen te navigeren. Het is beter om door de relaties in het model te navigeren en alleen tussen de shapes en verbindingslijnen te navigeren wanneer het nodig is om te werken aan het uiterlijk van het diagram. Met deze methoden worden connectoren aan de vormen aan elk uiteinde gekoppeld.

personShape.FromRoleLinkShapes, personShape.ToRoleLinkShapes

connector.FromShape, connector.ToShape

Veel vormen zijn samengesteld; ze bestaan uit een oudervorm en een of meer lagen van kindervormen. Vormen die ten opzichte van een andere vorm gepositioneerd zijn, worden de kinderen ervan genoemd. Wanneer de bovenliggende vorm wordt verplaatst, bewegen de kindvormen mee.

Relatieve kinderen kunnen buiten het begrenzingsvak van de bovenliggende vorm worden weergegeven. Geneste kinderen worden binnen de grenzen van de ouder weergegeven.

Als u de bovenste set shapes in een diagram wilt ophalen, gebruikt u:

Diagram.NestedChildShapes

De voorklassen van vormen en verbindingslijnen zijn:

ModelElement

-- PresentationElement

-- ShapeElement

----- NodeShape

------- Diagram

------- YourShape

----- LinkShape

------- BinaryLinkShape

--------- YourConnector

Eigenschappen van Vormen en Verbindingslijnen

In de meeste gevallen is het niet nodig om expliciete wijzigingen aan te brengen in vormen. Wanneer u de modelelementen hebt gewijzigd, passen de aanpasregels de vormen en verbindingslijnen aan. Zie Reageren op en doorgeven van wijzigingen voor meer informatie.

Het is echter handig om enkele expliciete wijzigingen aan te brengen in vormeigenschappen die los staan van de modelelementen. U kunt bijvoorbeeld deze eigenschappen wijzigen:

  • Size - bepaalt de hoogte en breedte van de vorm.

  • Location - positie ten opzichte van de oudervorm of het ouderdiagram

  • StyleSet - de set pennen en borstels die worden gebruikt voor het tekenen van de vorm of verbindingslijn

  • Hide - maakt de vorm onzichtbaar

  • Show - maakt de vorm zichtbaar na een Hide()

Een element en de bijbehorende shape maken

Wanneer u een element maakt en koppelt aan de boom van insluitende relaties, wordt er automatisch een vorm gemaakt en eraan gekoppeld. Dit wordt gedaan door de 'fixup'-regels die worden uitgevoerd aan het einde van de transactie. De shape wordt echter weergegeven op een automatisch toegewezen locatie en de vorm, kleur en andere functies hebben standaardwaarden. Als u wilt bepalen hoe de shape wordt gemaakt, kunt u de samenvoegfunctie gebruiken. U moet eerst de elementen toevoegen die u wilt toevoegen aan een ElementGroup en vervolgens de groep samenvoegen in het diagram.

Deze methode:

  • Hiermee stelt u de naam in, indien u een eigenschap als de naam van het element hebt toegewezen.

  • Bekijkt alle richtlijnen voor het samenvoegen van elementen die u hebt opgegeven in de DSL-definitie.

In dit voorbeeld wordt een vorm op de muispositie gemaakt wanneer de gebruiker dubbelklikt op het diagram. In de DSL-definitie voor dit voorbeeld is de FillColor-eigenschap van ExampleShape blootgelegd.

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

Als u meer dan één shape opgeeft, stelt u de relatieve posities in met behulp van de AbsoluteBounds.

U kunt ook de kleur en andere blootgestelde eigenschappen van verbindingslijnen instellen met behulp van deze methode.

Transacties gebruiken

Shapes, verbindingslijnen en diagrammen zijn subtypen van ModelElement en zijn opgeslagen in de Store. Daarom moet u ze alleen binnen een transactie wijzigen. Zie Procedure: Transacties gebruiken om het model bij te werken voor meer informatie.

Documentweergave en documentgegevens

Klassediagram van standaarddiagramtypen

Partities opslaan

Wanneer een model wordt geladen, wordt het bijbehorende diagram tegelijkertijd geladen. Normaal gesproken wordt het model geladen in Store.DefaultPartition en wordt de diagraminhoud in een andere partitie geladen. Meestal wordt de inhoud van elke partitie geladen en opgeslagen in een afzonderlijk bestand.