Condividi tramite


Personalizzare il Finestra Proprietà

È possibile personalizzare l'aspetto e il comportamento della finestra delle proprietà nel linguaggio specifico del dominio in Visual Studio. Nella definizione DSL definire le proprietà di dominio in ogni classe di dominio. Per impostazione predefinita, quando si seleziona un'istanza della classe, in un diagramma o in Esplora modelli, ogni proprietà di dominio viene elencata nella finestra delle proprietà. In questo modo è possibile visualizzare e modificare i valori delle proprietà di dominio, anche se non sono state mappate ai campi di forma nel diagramma.

Nomi, descrizioni e categorie

Nome e nome visualizzato. Nella definizione di una proprietà di dominio, il nome visualizzato della proprietà è il nome visualizzato in fase di esecuzione nella finestra delle proprietà. Al contrario, il nome viene usato quando si scrive il codice del programma per aggiornare la proprietà. Il nome deve essere un nome alfanumerico CLR corretto, ma il nome visualizzato può contenere spazi.

Quando si imposta il nome di una proprietà nella definizione DSL, il relativo nome visualizzato viene impostato automaticamente su una copia del nome. Se si scrive un nome con maiuscole/minuscole Pascal, ad esempio "FuelDevicege", il nome visualizzato conterrà automaticamente uno spazio: "Misuratore carburante". Tuttavia, è possibile impostare il nome visualizzato in modo esplicito su un altro valore.

Descrizione. La descrizione di una proprietà di dominio viene visualizzata in due posizioni:

  • Nella parte inferiore della finestra delle proprietà quando l'utente seleziona la proprietà. È possibile usarlo per spiegare all'utente cosa rappresenta la proprietà.

  • Nel codice del programma generato. Se si usano le funzionalità della documentazione per estrarre la documentazione dell'API, verrà visualizzata come descrizione di questa proprietà nell'API.

Categoria. Una categoria è un'intestazione nella Finestra Proprietà.

Esporre le caratteristiche dello stile

Alcune delle funzionalità dinamiche degli elementi grafici possono essere rappresentate o esposte come proprietà di dominio. Una funzionalità esposta in questo modo può essere aggiornata dall'utente e può essere aggiornata più facilmente dal codice del programma.

Fare clic con il pulsante destro del mouse su una classe shape nella definizione DSL, scegliere Aggiungi esposto e quindi scegliere una funzionalità.

Nelle forme è possibile esporre le proprietà FillColor, OutlineColor, TextColor, OutlineDashStyle, OutlineThickness e FillGradientMode. Nei connettori è possibile esporre le proprietà Color,TextColor, DashStyle e Thickness. Nei diagrammi è possibile esporre le proprietà FillColor e TextColor .

Quando l'utente del linguaggio DSL seleziona un elemento in un modello, le proprietà dell'elemento vengono visualizzate nella finestra delle proprietà. Tuttavia, è anche possibile visualizzare le proprietà degli elementi correlati specificati. Ciò è utile se è stato definito un gruppo di elementi che interagiscono. Ad esempio, è possibile definire un elemento principale e un elemento plug-in facoltativo. Se l'elemento principale viene mappato a una forma e l'altro non lo è, è utile visualizzare tutte le relative proprietà come se fossero su un elemento.

Questo effetto è denominato inoltro proprietà e si verifica automaticamente in diversi casi. In altri casi, è possibile ottenere l'inoltro delle proprietà definendo un descrittore del tipo di dominio.

Casi di inoltro delle proprietà predefiniti

Quando l'utente seleziona una forma o un connettore o un elemento in Explorer, nella Finestra Proprietà vengono visualizzate le proprietà seguenti:

  • Proprietà di dominio definite nella classe di dominio dell'elemento del modello, incluse quelle definite nelle classi di base. Un'eccezione è costituita da proprietà di dominio per le quali è stato impostato Is Browsable su False.

  • Nomi di elementi collegati tramite relazioni con molteplicità pari a 0..1. In questo modo è disponibile un metodo pratico per visualizzare gli elementi collegati facoltativamente, anche se non è stato definito un mapping del connettore per la relazione.

  • Proprietà di dominio della relazione di incorporamento destinata all'elemento . Poiché le relazioni di incorporamento non vengono in genere visualizzate in modo esplicito, ciò consente all'utente di visualizzare le relative proprietà.

  • Proprietà di dominio definite nella forma o nel connettore selezionato.

Aggiungere l'inoltro delle proprietà

Per inoltrare una proprietà, definire un descrittore del tipo di dominio. Se si dispone di una relazione di dominio tra due classi di dominio, è possibile usare un descrittore del tipo di dominio per impostare una proprietà di dominio nella prima classe sul valore di una proprietà di dominio nella seconda classe di dominio. Ad esempio, se si dispone di una relazione tra una classe di dominio Book e una classe di dominio Author, è possibile utilizzare un descrittore del tipo di dominio per rendere la proprietà Name di un autore di un libro visualizzata nella Finestra Proprietà quando l'utente seleziona il Libro.

Nota

L'inoltro delle proprietà influisce solo sul Finestra Proprietà quando l'utente sta modificando un modello. Non definisce una proprietà di dominio nella classe ricevente. Se si desidera accedere alla proprietà di dominio inoltrato in altre parti della definizione DSL o nel codice del programma, è necessario accedere all'elemento di inoltro.

La procedura seguente presuppone che sia stato creato un linguaggio DSL. I primi passaggi riepilogano i prerequisiti.

Inoltrare una proprietà da un altro elemento

  1. Creare una soluzione Strumenti di linguaggio specifica del dominio che contiene almeno due classi, che in questo esempio sono denominate Book e Author. Dovrebbe esserci una relazione di tipo tra Libro e Autore.

    La molteplicità del ruolo di origine (il ruolo sul lato Libro) deve essere 0...1 o 1.1, in modo che ogni libro abbia un autore.

  2. In Esplora DSL fare clic con il pulsante destro del mouse sulla classe di dominio Book e quindi scegliere Aggiungi nuovo dominioTypeDescriptor.

    Nel nodo Descrittore di proprietà personalizzato viene visualizzato un nodo denominato Paths of Custom Property Descriptor .A node Custom Type Descriptor.

  3. Fare clic con il pulsante destro del mouse sul nodo Descrittore di tipo personalizzato e quindi scegliere Aggiungi nuovo Percorso proprietà.

    Nel nodo Percorsi dei descrittori di proprietà personalizzati viene visualizzato un nuovo percorso di proprietà.

  4. Selezionare il nuovo percorso della proprietà e nella finestra Proprietà impostare Path su Property sul percorso dell'elemento del modello appropriato.

    È possibile modificare il percorso in una visualizzazione albero facendo clic sulla freccia giù a destra di questa proprietà. Per altre informazioni sui percorsi di dominio, vedere Sintassi del percorso del dominio. Dopo averlo modificato, il percorso dovrebbe essere simile a BookReferencesAuthor.Author/! Autore.

  5. Impostare Proprietà sulla proprietà dominio Name di Author.

  6. Impostare Nome visualizzato su Nome autore.

  7. Trasformare tutti i modelli, compilare ed eseguire il linguaggio DSL.

  8. In un diagramma modello creare un libro, un autore e collegarli usando la relazione di riferimento. Selezionare l'elemento libro e nella Finestra Proprietà dovrebbe essere visualizzato Nome autore oltre alle proprietà del libro. Modificare il nome dell'autore collegato o collegare il libro a un autore diverso e osservare che il nome dell'autore del libro cambia.

Editor di proprietà personalizzati

La finestra delle proprietà offre un'esperienza di modifica predefinita appropriata per il tipo di ogni proprietà di dominio. Ad esempio, per un tipo enumerato, l'utente visualizza un elenco a discesa e, per una proprietà numerica, l'utente può immettere cifre. Questo vale solo per i tipi predefiniti. Se si specifica un tipo esterno, l'utente potrà visualizzare i valori della proprietà, ma non modificarli.

Tuttavia, è possibile specificare gli editor e i tipi seguenti:

  1. Un altro editor usato con un tipo standard. Ad esempio, è possibile specificare un editor di percorsi di file per una proprietà stringa.

  2. Tipo esterno per la proprietà di dominio e un editor.

  3. Un editor .NET, ad esempio l'editor del percorso del file, oppure è possibile creare un editor di proprietà personalizzato.

    Conversione tra un tipo esterno e un tipo, ad esempio String, con un editor predefinito.

    In un linguaggio DSL, un tipo esterno è qualsiasi tipo che non è uno dei tipi semplici (ad esempio Boolean o Int32) o String.

Definire una proprietà di dominio con un tipo esterno

  1. In Esplora soluzioni aggiungere un riferimento all'assembly (DLL) che contiene il tipo esterno, nel progetto Dsl.

    L'assembly può essere un assembly .NET o un assembly fornito dall'utente.

  2. Aggiungere il tipo all'elenco Tipi di dominio, a meno che non sia già stato fatto.

    1. Aprire DslDefinition.dsl e in Esplora DSL fare clic con il pulsante destro del mouse sul nodo radice e quindi scegliere Aggiungi nuovo tipo esterno.

      Viene visualizzata una nuova voce sotto il nodo Tipi di dominio.

      Avviso

      La voce di menu si trova nel nodo radice DSL, non nel nodo Tipi di dominio.

    2. Impostare il nome e lo spazio dei nomi del nuovo tipo nel Finestra Proprietà.

  3. Aggiungere una proprietà di dominio a una classe di dominio nel modo consueto.

    Nella Finestra Proprietà selezionare il tipo esterno dall'elenco a discesa nel campo Tipo.

    In questa fase, gli utenti possono visualizzare i valori della proprietà, ma non possono modificarli. I valori visualizzati vengono ottenuti dalla ToString() funzione . È possibile scrivere codice di programma che imposta il valore della proprietà, ad esempio in un comando o una regola.

Impostare un editor di proprietà

Aggiungere un attributo CLR alla proprietà di dominio nel formato seguente:

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

È possibile impostare l'attributo su una proprietà usando la voce Attributo personalizzato nella Finestra Proprietà.

Il tipo di AnEditor deve essere derivato dal tipo specificato nel secondo parametro. Il secondo parametro deve essere UITypeEditor o ComponentEditor. Per ulteriori informazioni, vedere EditorAttribute.

È possibile specificare un editor personalizzato o un editor .NET, ad esempio FileNameEditor o ImageEditor. Ad esempio, utilizzare la procedura seguente per avere una proprietà in cui l'utente può immettere un nome file.

Definire una proprietà di dominio del nome file

  1. Aggiungere una proprietà di dominio a una classe di dominio nella definizione DSL.

  2. Selezionare la nuova proprietà. Nel campo Attributo personalizzato nella Finestra Proprietà immettere l'attributo seguente. Per immettere questo attributo, fare clic sui puntini di sospensione [...] e quindi immettere il nome dell'attributo e i parametri separatamente:

    [System.ComponentModel.Editor (
       typeof(System.Windows.Forms.Design.FileNameEditor)
       , typeof(System.Drawing.Design.UITypeEditor))]
    
    
  3. Lasciare l'impostazione predefinita Tipo della proprietà di dominio string.

  4. Per testare l'editor, verificare che gli utenti possano aprire l'editor dei nomi file per modificare la proprietà del dominio.

    1. Premere CTRL+F5 o F5. Nella soluzione di debug aprire un file di test. Creare un elemento della classe di dominio e selezionarlo.

    2. Nella Finestra Proprietà selezionare la proprietà di dominio. Il campo valore mostra i puntini di sospensione [...].

    3. Fare clic sui puntini di sospensione. Viene visualizzata una finestra di dialogo file. Selezionare un file e chiudere la finestra di dialogo. Il percorso del file è ora il valore della proprietà di dominio.

Definire un editor di proprietà personalizzato

È possibile definire un editor personalizzato. Questa operazione consente all'utente di modificare un tipo definito o di modificare un tipo standard in modo speciale. Ad esempio, è possibile consentire all'utente di immettere una stringa che rappresenta una formula.

Per definire un editor, scrivere una classe derivata da UITypeEditor. La classe deve eseguire l'override di:

  • EditValue, per interagire con l'utente e aggiornare il valore della proprietà.

  • GetEditStyle, per specificare se l'editor aprirà una finestra di dialogo o fornirà un menu a discesa.

È anche possibile fornire una rappresentazione grafica del valore della proprietà che verrà visualizzata nella griglia delle proprietà. A tale scopo, eseguire l'override GetPaintValueSupporteddi e PaintValue. Per ulteriori informazioni, vedere UITypeEditor.

Nota

Aggiungere il codice in un file di codice separato nel progetto Dsl .

Ad esempio:

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";
  }
}

Per usare questo editor, impostare l'attributo personalizzato di una proprietà di dominio su:

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

Per ulteriori informazioni, vedere UITypeEditor.

Specificare un elenco a discesa di valori

È possibile specificare un elenco di valori tra cui scegliere un utente.

Nota

Questa tecnica fornisce un elenco di valori che possono cambiare in fase di esecuzione. Se si desidera fornire un elenco che non cambia, è consigliabile usare invece un tipo enumerato come tipo della proprietà del dominio.

Per definire un elenco di valori standard, aggiungere alla proprietà del dominio un attributo CLR con il formato seguente:

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

Definire una classe che deriva da TypeConverter. Aggiungere il codice in un file separato nel progetto Dsl . Ad esempio:

/// <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;
  }

}