Personalizar a janela Propriedades

Você pode personalizar a aparência e o comportamento da janela de propriedades em sua DSL (linguagem específica do domínio) no Visual Studio. Na definição de DSL, você define as propriedades de domínio em cada classe de domínio. Por padrão, quando você seleciona uma instância da classe, seja em um diagrama ou no Gerenciador de Modelos, cada propriedade de domínio é listada na janela de propriedades. Isso permite ver e editar os valores das propriedades do domínio, mesmo que você não os tenha mapeado para formar campos no diagrama.

Nomes, descrições e categorias

ID e nome para exibição. Na definição de uma propriedade de domínio, o Nome de Exibição da propriedade é o nome que aparece em tempo de execução na janela de propriedades. Por outro lado, o Nome é usado quando você escreve o código do programa para atualizar a propriedade. O Nome deve ser um nome alfanumérico CLR correto, mas o Nome de Exibição pode conter espaços.

Quando você define o Nome de uma propriedade na Definição de DSL, seu Nome de Exibição é definido automaticamente como uma cópia do Nome. Se você escrever um nome com maiúsculas e minúsculas pascal, como "FuelGauge", o Nome de Exibição conterá automaticamente um espaço: "Fuel Gauge". No entanto, você pode definir o Nome de Exibição explicitamente como outro valor.

Descrição. A Descrição de uma propriedade de domínio aparece em dois locais:

  • Na parte inferior da janela de propriedades quando o usuário seleciona a propriedade. Você pode usá-lo para explicar ao usuário o que a propriedade representa.

  • No código do programa gerado. Se você usar as instalações de documentação para extrair a documentação da API, ela aparecerá como a descrição dessa propriedade na API.

Categoria. Uma categoria é um título no janela Propriedades.

Expor recursos de estilo

Alguns dos recursos dinâmicos dos elementos gráficos podem ser representados ou expostos como propriedades de domínio. Um recurso que foi exposto dessa maneira pode ser atualizado pelo usuário e pode ser atualizado com mais facilidade pelo código do programa.

Clique com o botão direito do mouse em uma classe de forma na Definição de DSL, aponte para Adicionar Exposto e escolha um recurso.

Em formas, você pode expor as propriedades FillColor, OutlineColor, TextColor, OutlineDashStyle, OutlineThickness e FillGradientMode. Em conectores, você pode expor as propriedades Color,TextColor, DashStyle e Thickness. Em diagramas, você pode expor as propriedades FillColor e TextColor.

Quando o usuário da DSL seleciona um elemento em um modelo, as propriedades desse elemento são exibidas na janela de propriedades. No entanto, você também pode exibir as propriedades dos elementos relacionados especificados. Isso será útil se você tiver definido um grupo de elementos que funcionam juntos. Por exemplo, você pode definir um elemento principal e um elemento plug-in opcional. Se o elemento principal for mapeado para uma forma e o outro não for, será útil ver todas as suas propriedades como se estivessem em um elemento.

Esse efeito é chamado de encaminhamento de propriedade e ocorre automaticamente em vários casos. Em outros casos, você pode obter o encaminhamento de propriedade definindo um descritor de tipo de domínio.

Casos de encaminhamento de propriedade padrão

Quando o usuário seleciona uma forma ou conector ou um elemento no Explorer, as seguintes propriedades são exibidas no janela Propriedades:

  • As propriedades de domínio definidas na classe de domínio do elemento de modelo, incluindo aquelas definidas em classes base. Uma exceção são as propriedades de domínio para as quais você definiu Is Browsable como False.

  • Os nomes de elementos que são vinculados por meio de relações que têm uma multiplicidade de 0..1. Isso fornece um método conveniente de ver elementos vinculados opcionalmente, mesmo que você não tenha definido um mapeamento de conector para a relação.

  • Propriedades de domínio do relacionamento de incorporação que visa o elemento. Como os relacionamentos de incorporação geralmente não são exibidos explicitamente, isso permite que o usuário veja suas propriedades.

  • Propriedades de domínio definidas na forma ou conector selecionado.

Adicionar encaminhamento de propriedade

Para encaminhar uma propriedade, defina um descritor de tipo de domínio. Se você tiver uma relação de domínio entre duas classes de domínio, poderá usar um descritor de tipo de domínio para definir uma propriedade de domínio na primeira classe para o valor de uma propriedade de domínio na segunda classe de domínio. Por exemplo, se você tiver uma relação entre uma classe de domínio Book e uma classe de domínio Author, poderá usar um descritor de tipo de domínio para fazer com que a propriedade Name de um Author do Livro apareça na janela Propriedades quando o usuário selecionar o Livro.

Observação

O encaminhamento de propriedades afeta apenas a janela Propriedades quando o usuário está editando um modelo. Ele não define uma propriedade de domínio na classe receptora. Se você deseja acessar a propriedade de domínio encaminhado em outras partes da definição de DSL ou no código do programa, deve acessar o elemento de encaminhamento.

O procedimento a seguir pressupõe que você tenha criado uma DSL. As primeiras etapas resumem os pré-requisitos.

Encaminhar uma propriedade de outro elemento

  1. Crie uma solução Ferramentas de Linguagem Específica de Domínio que contenha pelo menos duas classes, que neste exemplo são chamadas de Book e Author. Deve haver uma relação de qualquer tipo entre Book e Author.

    A multiplicidade da função de origem (a função no lado do Book ) deve ser 0..1 ou 1..1, para que cada Book tenha um Autor.

  2. No Gerenciador de DSL, clique com o botão direito do mouse na classe de domínio Livro e clique em Adicionar Novo DomainTypeDescriptor.

    Um nó chamado Caminhos de Descritores de Propriedade Personalizada aparece no nó Descritor de Tipo Personalizado.

  3. Clique com o botão direito do mouse no nó Descritor de Tipo Personalizado e clique em Adicionar Novo PropertyPath.

    Um novo caminho de propriedade aparece no nó Caminhos de Descritores de Propriedade Personalizada.

  4. Selecione o novo caminho de propriedade e, na janela Propriedades, defina Caminho como Propriedade como o caminho do elemento de modelo apropriado.

    Você pode editar o caminho em um modo de exibição de árvore clicando na seta para baixo à direita dessa propriedade. Para obter mais informações sobre caminhos de domínio, confira Sintaxe de caminho de domínio. Quando você o editou, o caminho deve ser semelhante a BookReferencesAuthor.Author/!Author.

  5. Defina Propriedade como a propriedade de domínio Name de Author.

  6. Defina Nome de Exibição como Nome do Autor.

  7. Transforme Todos os Modelos, compile e execute a DSL.

  8. Em um diagrama de modelo, crie um livro, um autor e vincule-os usando o relacionamento de referência. Selecione o elemento do livro e, na janela Propriedades, você verá o Nome do autor, além das propriedades do livro. Altere o nome do autor vinculado ou vincule o livro a um autor diferente e observe que o Nome do autor do livro muda.

Editores de propriedades personalizados

A janela de propriedades fornece uma experiência de edição padrão apropriada para o tipo de cada propriedade de domínio. Por exemplo, para um tipo enumerado, o usuário vê uma lista suspensa e, para uma propriedade numérica, o usuário pode inserir dígitos. Isso só é verdadeiro para os tipos internos. Se você especificar um tipo externo, o usuário poderá ver os valores da propriedade, mas não editá-la.

No entanto, você pode especificar os seguintes editores e tipos:

  1. Outro editor que é usado com um tipo padrão. Por exemplo, você pode especificar um editor de caminho de arquivo para uma propriedade de cadeia de caracteres.

  2. Um tipo externo para a propriedade de domínio e um editor para ela.

  3. Um editor do .NET, como o editor de caminho de arquivo, ou você pode criar seu próprio editor de propriedades personalizado.

    Uma conversão entre um tipo externo e um tipo como Cadeia de caracteres, que tem um editor padrão.

    Em uma DSL, um tipo externo é qualquer tipo que não seja um dos tipos simples (como Booliano ou Int32) ou Cadeia de caracteres.

Definir uma propriedade de domínio que tenha um tipo externo

  1. Em Gerenciador de Soluções, adicione uma referência ao assembly (DLL) que contém o tipo externo, no projeto Dsl.

    O assembly pode ser um assembly .NET ou um assembly fornecido por você.

  2. Adicione o tipo à lista Tipos de Domínio, a menos que você já tenha feito isso.

    1. Abra DslDefinition.dsl e, no Gerenciador de DSL, clique com o botão direito do mouse no nó raiz e clique em Adicionar Novo Tipo Externo.

      Uma nova entrada aparece no nó Tipos de Domínio.

      Aviso

      O item de menu está no nó raiz DSL, não no nó Tipos de Domínio.

    2. Defina o nome e o namespace do novo tipo no janela Propriedades.

  3. Adicione uma propriedade de domínio a uma classe de domínio da maneira usual.

    Na janela Propriedades, selecione o tipo externo na lista suspensa no campo Tipo.

    Neste estágio, os usuários podem exibir os valores da propriedade, mas não podem editá-la. Os valores exibidos são obtidos da função ToString(). Você pode escrever um código de programa que define o valor da propriedade, por exemplo, em um comando ou regra.

Definir um editor de propriedades

Adicione um atributo CLR à propriedade de domínio, na seguinte forma:

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

Você pode definir o atributo em uma propriedade usando a entrada Atributo Personalizado na janela Propriedades.

O tipo de AnEditor deve ser derivado do tipo especificado no segundo parâmetro. O segundo parâmetro deve ser UITypeEditor ou ComponentEditor. Para obter mais informações, consulte EditorAttribute.

Você pode especificar seu próprio editor ou um editor do .NET, como FileNameEditor ou ImageEditor. Por exemplo, use o procedimento a seguir para ter uma propriedade na qual o usuário pode inserir um nome de arquivo.

Definir uma propriedade de domínio de nome de arquivo

  1. Adicione uma propriedade de domínio a uma classe de domínio em sua definição de DSL.

  2. Selecione a nova propriedade. No campo Atributo Personalizado na janela Propriedades, insira o atributo a seguir. Para inserir esse atributo, clique nas reticências [...] e insira o nome do atributo e os parâmetros separadamente:

    [System.ComponentModel.Editor (
       typeof(System.Windows.Forms.Design.FileNameEditor)
       , typeof(System.Drawing.Design.UITypeEditor))]
    
    
  3. Deixe o Tipo da propriedade de domínio em sua configuração padrão de Cadeia de caracteres.

  4. Para testar o editor, verifique se os usuários podem abrir o editor de nomes de arquivo para editar sua propriedade de domínio.

    1. Pressione CTRL+F5 ou F5. Na solução de depuração, abra um arquivo de teste. Crie um elemento da classe de domínio e selecione-o.

    2. Na janela Propriedades, selecione a propriedade do domínio. O campo de valor mostra reticências [...].

    3. Clique no botão de reticências. Uma caixa de diálogo de arquivo é exibida. Selecione um arquivo e feche a caixa de diálogo. O caminho do arquivo agora é o valor da propriedade de domínio.

Definir seu próprio editor de propriedades

Você pode definir seu próprio editor. Você faria isso para permitir que o usuário editasse um tipo que você definiu ou editasse um tipo padrão de uma maneira especial. Por exemplo, você pode permitir que o usuário insira uma cadeia de caracteres que representa uma fórmula.

Você define um editor escrevendo uma classe derivada de UITypeEditor. Sua classe deve substituir:

  • EditValue, para interagir com o usuário e atualizar o valor da propriedade.

  • GetEditStyle, para especificar se o editor abrirá uma caixa de diálogo ou fornecerá um menu suspenso.

Você também pode fornecer uma representação gráfica do valor da propriedade que será exibida na grade de propriedades. Para fazer isso, substitua GetPaintValueSupportede PaintValue. Para obter mais informações, consulte UITypeEditor.

Observação

Adicione o código em um arquivo de código separado no projeto Dsl.

Por exemplo:

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

Para usar esse editor, defina o Atributo Personalizado de uma propriedade de domínio como:

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

Para obter mais informações, consulte UITypeEditor.

Fornecer uma lista suspensa de valores

Você pode fornecer uma lista de valores para um usuário escolher.

Observação

Essa técnica fornece uma lista de valores que podem ser alterados em tempo de execução. Se você quiser fornecer uma lista que não seja alterada, considere usar um tipo enumerado como o tipo da sua propriedade de domínio.

Para definir uma lista de valores padrão, adicione à sua propriedade de domínio um atributo CLR que tenha o seguinte formulário:

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

Defina uma classe derivada de TypeConverter. Adicione o código em um arquivo de código separado no projeto Dsl. Por exemplo:

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

}