Partager via


Naviguer et mettre à jour un modèle dans le code du programme

Vous pouvez écrire du code pour créer et supprimer des éléments de modèle, définir leurs propriétés et créer et supprimer des liens entre des éléments. Toutes les modifications doivent être apportées dans une transaction. Si les éléments sont affichés sur un diagramme, le diagramme est « corrigé » automatiquement à la fin de la transaction.

Exemple de définition DSL

Il s’agit de la partie principale de DslDefinition.dsl pour les exemples de cette rubrique :

Diagramme de définition DSL - Modèle d’arbre de famille

Ce modèle est une instance de ce DSL :

Modèle d’arbre de famille Tudor

Références et espaces de noms

Pour exécuter le code dans cette rubrique, vous devez référencer :

Microsoft.VisualStudio.Modeling.Sdk.11.0.dll

Votre code utilise cet espace de noms :

using Microsoft.VisualStudio.Modeling;

En outre, si vous écrivez le code dans un projet différent de celui dans lequel votre DSL est défini, vous devez importer l’assembly généré par le projet Dsl.

Propriétés

Les propriétés de domaine que vous définissez dans la définition DSL deviennent des propriétés auxquelles vous pouvez accéder dans le code du programme :

Person henry = ...;

if (henry.BirthDate < 1500) ...

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

Si vous souhaitez définir une propriété, vous devez le faire à l’intérieur d’une transaction :

henry.Name = "Henry VIII";

Si, dans la définition DSL, le type d’une propriété est calculé, vous ne pouvez pas le définir. Pour plus d’informations, consultez Propriétés de stockage calculées et personnalisées.

Relations interpersonnelles

Les relations de domaine que vous définissez dans la définition DSL deviennent des paires de propriétés, une sur la classe à chaque fin de la relation. Les noms des propriétés apparaissent dans le diagramme DslDefinition sous forme d’étiquettes sur les rôles de chaque côté de la relation. Selon la multiplicité du rôle, le type de la propriété est soit la classe à l’autre extrémité de la relation, soit une collection de cette classe.

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

FamilyTreeModel ftree = henry.FamilyTreeModel;

Les propriétés à des extrémités opposées d’une relation sont toujours réciproques. Lorsqu’un lien est créé ou supprimé, les propriétés du rôle sur les deux éléments sont mises à jour. L’expression suivante (qui utilise les extensions de System.Linq) est toujours vraie pour la relation ParentsHaveChildren dans l’exemple :

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

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

ElementLinks. Une relation est également représentée par un élément de modèle appelé lien, qui est une instance du type de relation de domaine. Un lien a toujours un élément source et un élément cible. L’élément source et l’élément cible peuvent être identiques.

Vous pouvez accéder à un lien et à ses propriétés :

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

// This is now true:

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

Par défaut, aucune instance d’une relation n’est autorisée à lier n’importe quelle paire d’éléments de modèle. Mais si, dans la définition DSL, l’indicateur Allow Duplicates est vrai pour la relation, il peut y avoir plusieurs liens, et vous devez utiliser GetLinks:

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

Il existe également d’autres méthodes d’accès aux liens. Par exemple:

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

Rôles masqués. Si, dans la définition DSL, Is Property Generated a la valeur false pour un rôle particulier, aucune propriété n’est générée qui correspond à ce rôle. Toutefois, vous pouvez toujours accéder aux liens et parcourir les liens à l’aide des méthodes de la relation :

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

L’exemple le plus fréquemment utilisé est la PresentationViewsSubject relation, qui lie un élément de modèle à la forme qui l’affiche sur un diagramme :

PresentationViewsSubject.GetPresentation(henry)[0] as PersonShape

Répertoire d’éléments

Vous pouvez accéder à tous les éléments du magasin à l’aide du répertoire d’éléments :

store.ElementDirectory.AllElements

Il existe également des méthodes pour rechercher des éléments, comme les suivants :

store.ElementDirectory.FindElements(Person.DomainClassId);

store.ElementDirectory.GetElement(elementId);

Accès aux informations de classe

Vous pouvez obtenir des informations sur les classes, les relations et d’autres aspects de la définition DSL. Par exemple:

DomainClassInfo personClass = henry.GetDomainClass();

DomainPropertyInfo birthProperty =

personClass.FindDomainProperty("BirthDate")

DomainRelationshipInfo relationship =

link.GetDomainRelationship();

DomainRoleInfo sourceRole = relationship.DomainRole[0];

Les classes ancêtres des éléments de modèle sont les suivantes :

  • ModelElement : tous les éléments et relations sont des Éléments de Modèle

  • ElementLink : toutes les relations sont ElementLinks

Effectuer des modifications à l’intérieur d’une transaction

Chaque fois que votre code de programme change n’importe quoi dans le Windows Store, il doit le faire à l’intérieur d’une transaction. Cela s’applique à tous les éléments de modèle, relations, formes, diagrammes et leurs propriétés. Pour plus d’informations, consultez Transaction.

La méthode la plus pratique de gestion d’une transaction consiste à utiliser une using instruction placée dans une try...catch instruction :

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

Vous pouvez apporter n’importe quel nombre de modifications à l’intérieur d’une transaction. Vous pouvez ouvrir de nouvelles transactions à l’intérieur d’une transaction active.

Pour rendre vos modifications permanentes, vous devez Commit la transaction avant qu'elle ne soit supprimée. Si une exception se produit qui n’est pas interceptée dans la transaction, le Windows Store est réinitialisé à son état avant les modifications.

Création d’éléments de modèle

Cet exemple ajoute un élément à un modèle existant :

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

Cet exemple illustre ces points essentiels sur la création d’un élément :

  • Créez le nouvel élément dans une partition spécifique du Windows Store. Pour les éléments de modèle et les relations, mais pas les formes, il s’agit généralement de la partition par défaut.

  • Définissez-la comme cible d’une relation d’incorporation. Dans la DslDefinition de cet exemple, chaque Personne doit être la cible de la relation d’incorporation FamilyTreeHasPeople. Pour ce faire, nous pouvons définir la propriété de rôle FamilyTreeModel de l’objet Person ou ajouter la personne à la propriété People de l’objet FamilyTreeModel.

  • Définissez les propriétés d’un nouvel élément, en particulier la propriété pour laquelle IsName est true dans la DslDefinition. Cet indicateur marque la propriété qui sert à identifier l’élément de manière unique au sein de son propriétaire. Dans ce cas, la propriété Name a cet indicateur.

  • La définition DSL de cette DSL doit avoir été chargée dans le Windows Store. Si vous écrivez une extension telle qu’une commande de menu, cela sera généralement déjà vrai. Dans d’autres cas, vous pouvez charger explicitement le modèle dans le Store ou utiliser ModelBus pour le charger. Pour plus d’informations, consultez Guide pratique pour ouvrir un modèle à partir d’un fichier dans le code du programme.

    Lorsque vous créez un élément de cette façon, une forme est automatiquement créée (si la DSL a un diagramme). Il apparaît dans un emplacement attribué automatiquement, avec la forme, la couleur et d’autres fonctionnalités par défaut. Si vous souhaitez contrôler où et comment la forme associée s’affiche, consultez Création d’un élément et de sa forme.

Il existe deux relations définies dans l’exemple de définition DSL. Chaque relation définit une propriété de rôle sur la classe à chaque fin de la relation.

Il existe trois façons de créer une instance d’une relation. Chacune de ces trois méthodes a le même effet :

  • Définissez la propriété du lecteur de rôle source. Par exemple:

    • familyTree.People.Add(edward);

    • edward.Parents.Add(henry);

  • Définissez la propriété de l'acteur de rôle cible. Par exemple:

    • edward.familyTreeModel = familyTree;

      La multiplicité de ce rôle est 1..1, donc nous attribuons la valeur.

    • henry.Children.Add(edward);

      La multiplicité de ce rôle est 0..*, donc nous ajoutons cela à la collection.

  • Construisez explicitement une instance de la relation. Par exemple:

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

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

    La dernière méthode est utile si vous souhaitez définir des propriétés sur la relation elle-même.

    Lorsque vous créez un élément de cette façon, un connecteur sur le diagramme est automatiquement créé, mais il a une forme, une couleur et d’autres fonctionnalités par défaut. Pour contrôler la façon dont le connecteur associé est créé, consultez Création d’un élément et de sa forme.

Suppression d’éléments

Supprimez un élément en appelant Delete():

henry.Delete();

Cette opération supprime également :

  • Liens de relation vers et depuis l’élément. Par exemple, edward.Parents ne contiendra henryplus .

  • Éléments au niveau des rôles pour lesquels l’indicateur PropagatesDelete est vrai. Par exemple, la forme qui affiche l’élément est supprimée.

Par défaut, chaque relation d’incorporation a PropagatesDelete la valeur true au niveau du rôle cible. La suppression henry ne supprime pas le familyTree, mais familyTree.Delete() supprime tout le Persons.

Par défaut, PropagatesDelete n’est pas vrai pour les rôles des relations de référence.

Vous pouvez faire en sorte que les règles de suppression omettent certaines propagations lors de la suppression d’un objet. Cela est utile si vous remplacez un élément par un autre. Vous fournissez le GUID d’un ou plusieurs rôles pour lesquels la suppression ne doit pas être propagée. Le GUID peut être obtenu à partir de la classe de relation :

henry.Delete(ParentsHaveChildren.SourceDomainRoleId);

(Cet exemple particulier n’aurait aucun effet, car PropagatesDelete est false pour les rôles de la ParentsHaveChildren relation.)

Dans certains cas, la suppression est empêchée par l’existence d’un verrou, soit sur l’élément, soit sur un élément qui serait supprimé par propagation. Vous pouvez utiliser element.CanDelete() pour vérifier si l’élément peut être supprimé.

Vous pouvez supprimer un lien de relation en supprimant un élément d’une propriété de rôle :

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

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

Vous pouvez également supprimer le lien explicitement :

edwardHenryLink.Delete();

Ces trois méthodes ont tous le même effet. Vous n’avez besoin que d’utiliser l’une d’entre elles.

Si le rôle a une multiplicité de 0..1 ou 1..1, vous pouvez la définir à null ou à une autre valeur :

edward.FamilyTreeModel = null; ou:

edward.FamilyTreeModel = anotherFamilyTree;

Réorganiser les liens d’une relation

Les liens d'une relation particulière qui sont issus ou ciblés sur un élément de modèle particulier ont une séquence spécifique. Ils apparaissent dans l’ordre dans lequel ils ont été ajoutés. Par exemple, cette déclaration produira toujours les enfants dans le même ordre :

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

Vous pouvez modifier l’ordre des liens :

ParentsHaveChildren link = GetLink(henry,edward);

ParentsHaveChildren nextLink = GetLink(henry, elizabeth);

DomainRoleInfo role =

link.GetDomainRelationship().DomainRoles[0];

link.MoveBefore(role, nextLink);

Locks

Vos modifications peuvent être empêchées par un verrou. Les verrous peuvent être définis sur des éléments individuels, sur des partitions et sur le stockage. Si l’un de ces niveaux a un verrou qui empêche le type de modification que vous souhaitez effectuer, une exception peut être levée lorsque vous tentez de le faire. Vous pouvez découvrir si les verrous sont définis à l’aide de l’élément. GetLocks(), qui est une méthode d’extension définie dans l’espace de noms Microsoft.VisualStudio.Modeling.Immutability.

Pour plus d’informations, consultez Définition d’une stratégie de verrouillage pour créer des segments Read-Only.

Copier et coller

Vous pouvez copier des éléments ou des groupes d’éléments dans un 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>());

Les éléments sont stockés en tant que groupe d’éléments sérialisé.

Vous pouvez fusionner des éléments d’un IDataObject dans un modèle :

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

Merge () peut accepter un PresentationElement ou un ModelElement. Si vous lui donnez un PresentationElement, vous pouvez également spécifier une position sur le diagramme cible en tant que troisième paramètre.

Navigation et mise à jour des diagrammes

Dans une DSL, l’élément de modèle de domaine, qui représente un concept tel que Person ou Song, est distinct de l’élément de forme, qui représente ce que vous voyez sur le diagramme. L’élément de modèle de domaine stocke les propriétés et relations importantes des concepts. L’élément de forme stocke la taille, la position et la couleur de l’affichage de l’objet sur le diagramme et la disposition de ses composants.

Éléments de présentation

Diagramme de classes des types de formes et d’éléments de base

Dans votre définition DSL, chaque élément que vous spécifiez crée une classe dérivée de l’une des classes standard suivantes.

Type d’élément Classe de base
Classe de domaine ModelElement
Relation de domaine ElementLink
Graphique à base de formes NodeShape
Connector BinaryLinkShape
Diagramme Diagram

Un élément d’un diagramme représente généralement un élément de modèle. En règle générale (mais pas toujours), un NodeShape représentant une instance de classe de domaine et une BinaryLinkShape instance de relation de domaine. La PresentationViewsSubject relation lie un nœud ou une forme de lien à l’élément de modèle qu’il représente.

Chaque nœud ou forme de lien appartient à un diagramme. Une forme de lien binaire connecte deux formes de nœud.

Les formes peuvent avoir des formes enfants dans deux ensembles. Une forme dans l’ensemble NestedChildShapes est limitée à la boîte englobante de son parent. Une forme dans la RelativeChildShapes liste peut apparaître en dehors ou partiellement en dehors des limites du parent, par exemple une étiquette ou un port. Un diagramme n'a ni RelativeChildShapes ni Parent.

Navigation entre les formes et les éléments

Les éléments de modèle de domaine et les éléments de forme sont liés par la PresentationViewsSubject relation.

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

La même relation lie les relations aux connecteurs sur le diagramme :

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

Cette relation lie également la racine du modèle au diagramme :

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

Pour obtenir l’élément de modèle représenté par une forme, utilisez :

henryShape.ModelElement as Person

diagram.ModelElement as FamilyTreeModel

En général, il n’est pas recommandé de naviguer entre les formes et les connecteurs sur le diagramme. Il est préférable de parcourir les relations dans le modèle, de déplacer entre les formes et les connecteurs uniquement quand il est nécessaire de travailler sur l’apparence du diagramme. Ces méthodes lient les connecteurs aux formes à chaque extrémité :

personShape.FromRoleLinkShapes, personShape.ToRoleLinkShapes

connector.FromShape, connector.ToShape

De nombreuses formes sont des composites ; ils sont constitués d’une forme parente et d’une ou plusieurs couches d’enfants. Les formes positionnées par rapport à une autre forme sont considérées comme ses enfants. Lorsque la forme parente se déplace, les enfants se déplacent avec elle.

Les enfants relatifs peuvent apparaître en dehors de la zone englobante de la forme parente. Les enfants imbriqués apparaissent strictement à l’intérieur des limites du parent.

Pour obtenir le jeu de formes supérieur sur un diagramme, utilisez :

Diagram.NestedChildShapes

Les classes ancêtres de formes et de connecteurs sont les suivantes :

ModelElement

-- PresentationElement

-- ShapeElement

----- NodeShape

------- Diagram

------- YourShape

----- LinkShape

------- BinaryLinkShape

--------- YourConnector

Propriétés des formes et des connecteurs

Dans la plupart des cas, il n’est pas nécessaire d’apporter des modifications explicites aux formes. Lorsque vous avez modifié les éléments du modèle, les règles de « correction » mettent à jour les formes et les connecteurs. Pour plus d’informations, consultez Réponse aux modifications et propagation des modifications.

Toutefois, il est utile d’apporter des modifications explicites aux formes dans les propriétés indépendantes des éléments du modèle. Par exemple, vous pouvez modifier ces propriétés :

  • Size - détermine la hauteur et la largeur de la forme.

  • Location - position relative à la forme ou au diagramme parent

  • StyleSet - ensemble de stylets et de pinceaux utilisés pour dessiner la forme ou le connecteur

  • Hide - rend la forme invisible

  • Show - rend la forme visible après un Hide()

Création d’un élément et de sa forme

Lorsque vous créez un élément et liez-le dans l’arborescence des relations d’incorporation, une forme est automatiquement créée et associée. Cette opération est effectuée par les règles de « finalisation » qui s'exécutent à la fin de la transaction. Toutefois, la forme s’affiche à un emplacement attribué automatiquement, et sa forme, sa couleur et d’autres caractéristiques ont des valeurs par défaut. Pour contrôler la façon dont la forme est créée, vous pouvez utiliser la fonction de fusion. Vous devez d’abord ajouter les éléments que vous souhaitez ajouter à un ElementGroup, puis fusionner le groupe dans le diagramme.

Cette méthode :

  • Définit le nom, si vous avez affecté une propriété comme nom d’élément.

  • Observe les directives de fusion d’éléments que vous avez spécifiées dans la définition DSL.

Cet exemple crée une forme à la position de la souris lorsque l’utilisateur double-clique sur le diagramme. Dans la définition DSL pour cet exemple, la propriété FillColor d'une ExampleShape a été exposée.

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

Si vous fournissez plusieurs formes, définissez leurs positions relatives à l’aide du AbsoluteBounds.

Vous pouvez également définir la couleur et d’autres propriétés exposées des connecteurs à l’aide de cette méthode.

Utiliser des transactions

Les formes, les connecteurs et les diagrammes sont des sous-types de ModelElement et se trouvent dans le Store. Vous devez donc apporter des modifications uniquement à l’intérieur d’une transaction. Pour plus d’informations, consultez Guide pratique pour utiliser des transactions pour mettre à jour le modèle.

Vue de document et données de document

Diagramme de classes des types de diagrammes standard

Stocker des partitions

Lorsqu’un modèle est chargé, le diagramme associé est chargé en même temps. En règle générale, le modèle est chargé dans Store.DefaultPartition et le contenu du diagramme est chargé dans une autre partition. En règle générale, le contenu de chaque partition est chargé et enregistré dans un fichier distinct.