Partager via


Personnaliser la fenêtre Propriétés

Vous pouvez personnaliser l’apparence et le comportement de la fenêtre Propriétés dans votre langage dédié (DSL) dans Visual Studio. Dans votre définition DSL, vous définissez des propriétés de domaine sur chaque classe de domaine. Par défaut, quand vous sélectionnez une instance de la classe, sur un diagramme ou dans l’Explorateur de modèles, chaque propriété de domaine est listée dans la fenêtre Propriétés. Cela vous permet de voir et de modifier les valeurs des propriétés de domaine, même si vous ne les avez pas mappées aux champs de forme du diagramme.

Noms, descriptions et catégories

Nom et nom d’affichage. Dans votre définition d’une propriété de domaine, le Nom d’affichage de la propriété est le nom qui apparaît au moment de l’exécution dans la fenêtre Propriétés. En revanche, le Nom est utilisé quand vous écrivez du code de programme pour mettre à jour la propriété. Le nom doit être un nom alphanumérique CLR correct, mais le nom d’affichage peut contenir des espaces.

Quand vous définissez le nom d’une propriété dans la définition DSL, son nom d’affichage est automatiquement défini sur une copie du nom. Si vous écrivez un nom en casse Pascal, comme « FuelGauge », le nom d’affichage contient automatiquement un espace : « Fuel Gauge ». Toutefois, vous pouvez définir le nom d’affichage explicitement sur une autre valeur.

Description. La description d’une propriété de domaine s’affiche à deux endroits :

  • En bas de la fenêtre Propriétés, quand l’utilisateur sélectionne la propriété. Vous pouvez l’utiliser pour expliquer à l’utilisateur ce que représente la propriété.

  • Dans le code de programme généré. Si vous utilisez les fonctions de la documentation pour extraire la documentation de l’API, elle apparaît comme description de cette propriété dans l’API.

Catégorie. Une catégorie est un titre dans la fenêtre Propriétés.

Exposer des fonctionnalités de style

Certaines des fonctionnalités dynamiques des éléments graphiques peuvent être représentées ou exposées comme des propriétés de domaine. Une fonctionnalité exposée de cette manière peut être mise à jour par l’utilisateur et plus facilement mise à jour par le code de programme.

Cliquez avec le bouton droit sur une classe de forme dans la définition DSL, pointez sur Ajouter exposé, puis choisissez une fonctionnalité.

Sur les formes, vous pouvez exposer les propriétés FillColor, OutlineColor, TextColor, OutlineDashStyle, OutlineThickness et FillGradientMode. Sur les connecteurs, vous pouvez exposer les propriétés Color,TextColor, DashStyle et Thickness. Sur les diagrammes, vous pouvez exposer les propriétés FillColor et TextColor.

Quand l’utilisateur de votre DSL sélectionne un élément dans un modèle, les propriétés de cet élément s’affichent dans la fenêtre Propriétés. Toutefois, vous pouvez également afficher les propriétés des éléments associés spécifiés. Cela est utile si vous avez défini un groupe d’éléments qui fonctionnent ensemble. Par exemple, vous pouvez définir un élément principal et un élément de plug-in facultatif. Si l’élément principal est mappé à une forme et que l’autre ne l’est pas, il est utile de voir toutes leurs propriétés comme si elles se trouvaient sur un seul élément.

Cet effet est appelé transfert de propriété et il se produit automatiquement dans plusieurs cas. Dans d’autres cas, vous pouvez effectuer le transfert de propriété en définissant un descripteur de type de domaine.

Cas de transfert de propriété par défaut

Quand l’utilisateur sélectionne une forme ou un connecteur, ou un élément dans l’Explorateur, les propriétés suivantes s’affichent dans la fenêtre Propriétés :

  • Propriétés de domaine définies sur la classe de domaine de l’élément de modèle, y compris celles définies dans les classes de base. Ce n’est pas le cas des propriétés de domaine pour lesquelles vous avez défini Is Browsable sur False.

  • Noms d’éléments liés avec des relations qui ont une multiplicité de 0..1. C’est une méthode pratique pour voir les éléments éventuellement liés, même si vous n’avez pas défini de mappage de connecteur pour la relation.

  • Propriétés de domaine de la relation d’imbrication qui cible l’élément. Comme les relations d’imbrication ne sont généralement pas affichées explicitement, cela permet à l’utilisateur de voir leurs propriétés.

  • Propriétés de domaine définies sur la forme ou le connecteur sélectionnés.

Ajouter un transfert de propriété

Pour transférer une propriété, vous définissez un descripteur de type de domaine. Si vous avez une relation de domaine entre deux classes de domaine, vous pouvez utiliser un descripteur de type de domaine pour définir une propriété de domaine dans la première classe sur la valeur d’une propriété de domaine dans la deuxième classe de domaine. Par exemple, si vous avez une relation entre une classe de domaine Book et une classe de domaine Author, vous pouvez utiliser un descripteur de type de domaine pour faire apparaître la propriété Name d’un Author de Book dans la fenêtre Propriétés quand l’utilisateur sélectionne le Book.

Notes

Le transfert de propriété affecte uniquement la fenêtre Propriétés quand l’utilisateur modifie un modèle. Il ne définit pas de propriété de domaine sur la classe de réception. Si vous souhaitez accéder à la propriété de domaine transférée dans d’autres parties de la définition DSL ou dans le code du programme, vous devez accéder à l’élément de transfert.

La procédure suivante part du principe que vous avez créé un DSL. Les premières étapes récapitulent les prérequis.

Transférer une propriété à partir d’un autre élément

  1. Créez une solution Outils de langage dédié qui contient au moins deux classes, dans cet exemple, Book et Author. Il doit y avoir une relation d’un des deux types entre Book et Author.

    La multiplicité du rôle source (rôle côté Book) doit être 0..1 ou 1..1, pour que chaque Book ait un Author.

  2. Dans l’Explorateur DSL, cliquez avec le bouton droit sur la classe de domaine Book, puis cliquez sur Ajouter un nouveau DomainTypeDescriptor.

    Un nœud nommé Chemins des descripteurs de propriétés personnalisées apparaît sous le nœud Descripteur de type personnalisé.

  3. Cliquez avec le bouton droit sur le nœud Descripteur de type personnalisé, puis cliquez sur Ajouter un nouveau PropertyPath.

    Un nouveau chemin de propriété s’affiche sous le nœud Chemins des descripteurs de propriétés personnalisées.

  4. Sélectionnez le nouveau chemin de propriété et, dans la fenêtre Propriétés, définissez Chemin de la propriété sur le chemin de l’élément de modèle approprié.

    Vous pouvez modifier le chemin dans une arborescence en cliquant sur la flèche vers le bas à droite de cette propriété. Pour plus d’informations sur les chemins de domaine, consultez Syntaxe du chemin de domaine. Une fois que vous l’avez modifié, le chemin doit ressembler à BookReferencesAuthor.Author/!Author.

  5. Définissez Propriété sur la propriété de domaine Nom de Author.

  6. Définissez Nom d’affichage sur Author Name.

  7. Transformez tous les modèles, générez et exécutez le DSL.

  8. Dans un diagramme de modèle, créez un Book, un Author et liez-les en utilisant la relation de référence. Sélectionnez l’élément Book et, dans la fenêtre Propriétés, vous devez voir Author Name en plus des propriétés de Book. Changez le nom de l’Author lié ou liez le Book à un autre Author, et observez que le Author Name du Book change.

Éditeurs de propriétés personnalisées

La fenêtre Propriétés fournit une expérience de modification par défaut adaptée au type de chaque propriété de domaine. Par exemple, pour un type énuméré, l’utilisateur voit une liste déroulante et, pour une propriété numérique, l’utilisateur peut entrer des chiffres. Cela est vrai seulement pour les types intégrés. Si vous spécifiez un type externe, l’utilisateur peut voir les valeurs de la propriété, mais pas la modifier.

Toutefois, vous pouvez spécifier les éditeurs et types suivants :

  1. Un autre éditeur utilisé avec un type standard. Par exemple, vous pouvez spécifier un éditeur de chemin de fichier pour une propriété de chaîne.

  2. Un type externe pour la propriété de domaine et un éditeur pour celle-ci.

  3. Un éditeur .NET comme l’éditeur de chemin de fichier, sinon vous pouvez créer votre propre éditeur de propriétés personnalisées.

    Une conversion entre un type externe et un type comme String, qui a un éditeur par défaut.

    Dans un DSL, un type externe est un type qui n’est pas un des types simples (comme Boolean ou Int32) ou String.

Définir une propriété de domaine qui a un type externe

  1. Dans l’Explorateur de solutions, ajoutez une référence à l’assembly (DLL) qui contient le type externe, dans le projet Dsl.

    L’assembly peut être un assembly .NET ou un assembly que vous fournissez.

  2. Ajoutez le type à la liste Types de domaine, sauf si vous l’avez déjà fait.

    1. Ouvrez DslDefinition.dsl et, dans l’Explorateur DSL, cliquez avec le bouton droit sur le nœud racine, puis cliquez sur Ajouter un nouveau type externe.

      Une nouvelle entrée s’affiche sous le nœud Types de domaine.

      Avertissement

      L’élément de menu se trouve sur le nœud racine DSL et non sur le nœud Types de domaine.

    2. Définissez le nom et l’espace de noms du nouveau type dans la fenêtre Propriétés.

  3. Ajoutez une propriété de domaine à une classe de domaine de la manière habituelle.

    Dans la fenêtre Propriétés, sélectionnez le type externe dans la liste déroulante du champ Type.

    À ce stade, les utilisateurs peuvent voir les valeurs de la propriété, mais ils ne peuvent pas la modifier. Les valeurs affichées sont obtenues à partir de la fonction ToString(). Vous pouvez écrire du code de programme qui définit la valeur de la propriété, par exemple, dans une commande ou une règle.

Définir un éditeur de propriétés

Ajoutez un attribut CLR à la propriété de domaine, de la forme suivante :

[System.ComponentModel.Editor (
   typeof(AnEditor),
   typeof(System.Drawing.Design.UITypeEditor))]

Vous pouvez définir l’attribut sur une propriété en utilisant l’entrée Attribut personnalisé dans la fenêtre Propriétés.

Le type de AnEditor doit être dérivé du type spécifié dans le deuxième paramètre. Le deuxième paramètre doit être UITypeEditor ou ComponentEditor. Pour plus d’informations, consultez EditorAttribute.

Vous pouvez spécifier votre propre éditeur ou un éditeur .NET, par exemple FileNameEditor ou ImageEditor. Par exemple, utilisez la procédure suivante pour avoir une propriété dans laquelle l’utilisateur peut entrer un nom de fichier.

Définir une propriété de domaine de nom de fichier

  1. Ajoutez une propriété de domaine à une classe de domaine dans votre définition DSL.

  2. Sélectionnez la nouvelle propriété. Dans le champ Attribut personnalisé de la fenêtre Propriétés, entrez l’attribut suivant. Pour entrer cet attribut, cliquez sur les points de suspension [...], puis entrez le nom de l’attribut et les paramètres séparément :

    [System.ComponentModel.Editor (
       typeof(System.Windows.Forms.Design.FileNameEditor)
       , typeof(System.Drawing.Design.UITypeEditor))]
    
    
  3. Laissez le type de la propriété de domaine sur son paramètre par défaut String.

  4. Pour tester l’éditeur, vérifiez que les utilisateurs peuvent ouvrir l’éditeur de nom de fichier afin de modifier votre propriété de domaine.

    1. Appuyez sur Ctrl+F5 ou F5. Dans la solution de débogage, ouvrez un fichier de test. Créez un élément de la classe de domaine et sélectionnez-le.

    2. Dans la fenêtre Propriétés, sélectionnez la propriété de domaine. Le champ de valeur affiche des points de suspension [...].

    3. Cliquez sur les points de suspension. Une boîte de dialogue de fichiers s’affiche. Sélectionnez un fichier et fermez la boîte de dialogue. Le chemin du fichier est désormais la valeur de la propriété de domaine.

Définir votre propre éditeur de propriétés

Vous pouvez définir votre propre éditeur. Vous devez le faire pour permettre à l’utilisateur de modifier un type que vous avez défini ou de modifier un type standard d’une manière spéciale. Par exemple, vous pouvez autoriser l’utilisateur à entrer une chaîne qui représente une formule.

Vous définissez un éditeur en écrivant une classe dérivée de UITypeEditor. Votre classe doit remplacer :

  • EditValue, pour interagir avec l’utilisateur et mettre à jour la valeur de la propriété.

  • GetEditStyle, pour spécifier si votre éditeur ouvre une boîte de dialogue ou fournit un menu déroulant.

Vous pouvez également fournir une représentation graphique de la valeur de la propriété pour qu’elle s’affiche dans la grille des propriétés. Pour ce faire, remplacez GetPaintValueSupported et PaintValue. Pour plus d’informations, consultez UITypeEditor.

Notes

Ajoutez le code dans un fichier de code distinct dans le projet Dsl.

Par exemple :

internal class TextFileNameEditor : System.Windows.Forms.Design.FileNameEditor
{
  protected override void InitializeDialog(System.Windows.Forms.OpenFileDialog openFileDialog)
  {
    base.InitializeDialog(openFileDialog);
    openFileDialog.Filter = "Text files(*.txt)|*.txt|All files (*.*)|*.*";
    openFileDialog.Title = "Select a text file";
  }
}

Pour utiliser cet éditeur, définissez l’Attribut personnalisé d’une propriété de domaine sur :

[System.ComponentModel.Editor (
   typeof(MyNamespace.TextFileNameEditor)
   , typeof(System.Drawing.Design.UITypeEditor))]

Pour plus d’informations, consultez UITypeEditor.

Fournir une liste déroulante de valeurs

Vous pouvez fournir une liste de valeurs parmi lesquelles un utilisateur peut faire un choix.

Remarque

Cette technique fournit une liste de valeurs qui peut changer au moment de l’exécution. Si vous souhaitez fournir une liste qui ne change pas, utilisez plutôt un type énuméré comme type de votre propriété de domaine.

Pour définir une liste de valeurs standard, vous ajoutez à votre propriété de domaine un attribut CLR qui a la forme suivante :

[System.ComponentModel.TypeConverter
(typeof(MyTypeConverter))]

Définissez une classe qui dérive de TypeConverter. Ajoutez le code dans un fichier distinct dans le projet Dsl. Par exemple :

/// <summary>
/// Type converter that provides a list of values
/// to be displayed in the property grid.
/// </summary>
/// <remarks>This type converter returns a list
/// of the names of all "ExampleElements" in the
/// current store.</remarks>
public class MyTypeConverter : System.ComponentModel.TypeConverter
{
  /// <summary>
  /// Return true to indicate that we return a list of values to choose from
  /// </summary>
  /// <param name="context"></param>
  public override bool GetStandardValuesSupported
    (System.ComponentModel.ITypeDescriptorContext context)
  {
    return true;
  }

  /// <summary>
  /// Returns true to indicate that the user has
  /// to select a value from the list
  /// </summary>
  /// <param name="context"></param>
  /// <returns>If we returned false, the user would
  /// be able to either select a value from
  /// the list or type in a value that is not in the list.</returns>
  public override bool GetStandardValuesExclusive
      (System.ComponentModel.ITypeDescriptorContext context)
  {
    return true;
  }

  /// <summary>
  /// Return a list of the values to display in the grid
  /// </summary>
  /// <param name="context"></param>
  /// <returns>A list of values the user can choose from</returns>
  public override StandardValuesCollection GetStandardValues
      (System.ComponentModel.ITypeDescriptorContext context)
  {
    // Try to get a store from the current context
    // "context.Instance"  returns the element(s) that
    // are currently selected i.e. whose values are being
    // shown in the property grid.
    // Note that the user could have selected multiple objects,
    // in which case context.Instance will be an array.
    Store store = GetStore(context.Instance);

    List<string> values = new List<string>();

    if (store != null)
    {
      values.AddRange(store.ElementDirectory
        .FindElements<ExampleElement>()
        .Select<ExampleElement, string>(e =>
      {
        return e.Name;
      }));
    }
    return new StandardValuesCollection(values);
  }

  /// <summary>
  /// Attempts to get to a store from the currently selected object(s)
  /// in the property grid.
  /// </summary>
  private Store GetStore(object gridSelection)
  {
    // We assume that "instance" will either be a single model element, or
    // an array of model elements (if multiple items are selected).

    ModelElement currentElement = null;

    object[] objects = gridSelection as object[];
    if (objects != null && objects.Length > 0)
    {
      currentElement = objects[0] as ModelElement;
    }
    else
    {
        currentElement = gridSelection as ModelElement;
    }

    return (currentElement == null) ? null : currentElement.Store;
  }

}