Partilhar via


Navegando e atualizando um modelo no código do programa

Você pode escrever código para criar e excluir os elementos de modelo, defina suas propriedades e criar e excluir links entre elementos. Todas as alterações devem ser feitas dentro de uma transação. Se os elementos são exibidos em um diagrama, o diagrama será "corrigido" automaticamente no final da transação.

Neste tópico

Uma definição de DSL de exemplo

Navegando no modelo.

Acessando informações de classe

Realizar alterações dentro de uma transação.

Criação de elementos de modelo

Criação de Links de relacionamento

Excluindo elementos

Excluindo os vínculos de relacionamento

Reordenar os Links de um relacionamento

Bloqueios

Copiar e colar

Navegando e diagramas de atualização

Navegando entre as formas e elementos

Propriedades de formas e conectores

DocView e DocData

Formas, conectores e diagramas e seus relacionamentos com os elementos de modelo são descritos em um tópico separado. Para obter mais informações, consulte Como navegar e atualizar um diagrama [redirecionado].

Uma definição de DSL de exemplo

Esta é a parte principal da DslDefinition.dsl para os exemplos neste tópico:

Diagrama de definição de DSL - modelo de árvore genealógica

Este modelo é uma instância desse DSL:

Modelo de árvore da família Tudor

Referências e Namespaces

Para executar o código neste tópico, você deve fazer referência:

Microsoft.VisualStudio.Modeling.Sdk.11.0.dll

Seu código usará este espaço para nome:

using Microsoft.VisualStudio.Modeling;

Além disso, se você estiver escrevendo o código em um projeto diferente no qual seu DSL é definido, você deve importar o assembly que é compilado pelo projeto Dsl.

Propriedades

Propriedades de domínio que você define na definição de DSL tornam-se propriedades que você pode acessar no código do programa:

Person henry = ...;

if (henry.BirthDate < 1500) ...

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

Se você quiser definir uma propriedade, você deverá fazê-lo dentro de um transação:

henry.Name = "Henry VIII";

Se estiver na definição de DSL, uma propriedade tipo é calculado, você não pode defini-la. Para obter mais informações, consulte Propriedades calculadas e de armazenamento personalizado.

Relacionamentos

Relações de domínio que você define na definição de DSL se tornam os pares de propriedades, uma na classe em cada extremidade do relacionamento. Os nomes das propriedades aparecem no diagrama de DslDefinition como rótulos nas funções em cada lado do relacionamento. Dependendo da multiplicidade da função, o tipo da propriedade é a classe na outra extremidade do relacionamento, ou uma coleção de classe.

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

FamilyTreeModel ftree = henry.FamilyTreeModel;

As propriedades nas extremidades opostas de um relacionamento são sempre recíproco. Quando um link é criado ou excluído, as propriedades de função em ambos os elementos são atualizadas. A expressão a seguir (que usa as extensões de System.Linq) é sempre verdadeira para a relação de ParentsHaveChildren no exemplo:

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

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

ElementLinks. Uma relação também é representada por um elemento de modelo chamado um link, que é uma instância do tipo de relação de domínio. Um link sempre tem o elemento de uma fonte e o elemento de um destino. O elemento de origem e o elemento de destino pode ser o mesmo.

Você pode acessar um link e suas propriedades:

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

// This is now true:

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

Por padrão, não mais de uma instância de um relacionamento é permitida para vincular qualquer par de elementos de modelo. Mas se na definição de DSL, o Allow Duplicates sinalizador é verdadeiro para a relação, e em seguida, pode haver mais de um link, e você deve usar GetLinks:

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

Também existem outros métodos para acessar links. Por exemplo:

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

Funções ocultas. Se na definição de DSL, É gerado de propriedade é false para uma determinada função, em seguida, nenhuma propriedade é gerada que corresponde a essa função. No entanto, você ainda pode acessar os links e desviar os links usando os métodos da relação:

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

O exemplo usado com mais freqüência é o PresentationViewsSubject o relacionamento, que vincula a um elemento de modelo na forma que o exibe em um diagrama:

PresentationViewsSubject.GetPresentation(henry)[0] as PersonShape

O diretório do elemento

Você pode acessar todos os elementos no armazenamento usando o diretório de elemento:

store.ElementDirectory.AllElements

Também existem métodos para localizar elementos, como, por exemplo, o seguinte:

store.ElementDirectory.FindElements(Person.DomainClassId);

store.ElementDirectory.GetElement(elementId);

Acessando informações de classe

Você pode obter informações sobre as classes, relacionamentos e outros aspectos da definição de DSL. Por exemplo:

DomainClassInfo personClass = henry.GetDomainClass();

DomainPropertyInfo birthProperty =

personClass.FindDomainProperty("BirthDate")

DomainRelationshipInfo relationship =

link.GetDomainRelationship();

DomainRoleInfo sourceRole = relationship.DomainRole[0];

As classes de ancestral de elementos de modelo são as seguintes:

  • ModelElement - todos os elementos e relações são ModelElements

  • ElementLink - todos os relacionamentos são ElementLinks

Realizar alterações dentro de uma transação.

Sempre que seu código de programa altera qualquer coisa no armazenamento, ele deverá fazê-lo dentro de uma transação. Isso se aplica a todos os elementos de modelo, relacionamentos, formas, diagramas e suas propriedades. Para obter mais informações, consulte Transaction.

O método mais conveniente de gerenciar uma transação é com um using instrução incluído em um try...catch instrução:

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

Você pode fazer qualquer número de alterações dentro de uma transação. Você pode abrir novas transações dentro de uma transação ativa.

Para tornar as alterações permanentes, você deve Commit a transação antes que ele é descartado. Se ocorrer uma exceção não detectada dentro da transação, o armazenamento será redefinido para seu estado antes das alterações.

Criação de elementos de modelo

Este exemplo adiciona um elemento para um modelo existente:

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

Este exemplo ilustra esses pontos essenciais sobre a criação de um elemento:

  • Crie o novo elemento em uma partição específica do armazenamento. Para elementos de modelo e relacionamentos, mas não a formas, isso geralmente é a partição padrão.

  • Torne o destino de um relacionamento de incorporação. A DslDefinition deste exemplo, cada pessoa deve ser o destino da relação FamilyTreeHasPeople incorporada. Para conseguir isso, podemos pode definir a propriedade da função FamilyTreeModel do objeto pessoa ou adicionar a pessoa para a propriedade da função de pessoas do objeto FamilyTreeModel.

  • Definir as propriedades de um novo elemento, particularmente a propriedade para o qual IsName é verdadeiro para o DslDefinition. Esse sinalizador marca a propriedade que serve para identificar o elemento exclusivamente dentro de seu proprietário. Nesse caso, a propriedade Name tem esse sinalizador.

  • A definição de DSL dessa DSL deve ter sido carregada no armazenamento. Se você estiver escrevendo uma extensão como, por exemplo, um comando de menu, isso geralmente será já true. Em outros casos, você pode explicitamente carregar o modelo para o armazenamento, ou usar ModelBus para carregá-lo. Para obter mais informações, consulte Como abrir um modelo a partir de um arquivo no código do programa.

Quando você cria um elemento dessa maneira, uma forma é criada automaticamente (se o DSL tem um diagrama). Ela aparece em um local atribuído automaticamente, com a forma padrão, cor e outros recursos. Se você quiser controlar onde e como a forma associada é exibida, consulte a criação de um elemento e sua forma.

Existem dois relacionamentos definidos no exemplo, a definição de DSL. Cada relação define uma propriedade role na classe em cada extremidade do relacionamento.

Há três maneiras em que você pode criar uma instância de um relacionamento. Cada um desses três métodos tem o mesmo efeito:

  • Defina a propriedade do player de função de origem. Por exemplo:

    • familyTree.People.Add(edward);

    • edward.Parents.Add(henry);

  • Defina a propriedade de destino. Por exemplo:

    • edward.familyTreeModel = familyTree;

      A multiplicidade dessa função é 1..1, portanto, atribuímos o valor.

    • henry.Children.Add(edward);

      A multiplicidade dessa função é 0..*, portanto, podemos adicionar à coleção.

  • Construa uma instância do relacionamento explicitamente. Por exemplo:

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

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

O último método é útil se você deseja definir propriedades em que o próprio relacionamento.

Quando você cria um elemento dessa maneira, um conector no diagrama é criado automaticamente, mas ele tem uma forma padrão, cor e outros recursos. Para controlar como o conector associado é criado, consulte a criação de um elemento e sua forma.

Excluindo elementos

Excluir um elemento chamando Delete():

henry.Delete();

Esta operação também serão excluídos:

  • Links de relacionamento e para o elemento. Por exemplo, edward.Parents deixará de conter henry.

  • Elementos de funções para as quais o PropagatesDelete sinalizador é true. Por exemplo, a forma que exibe o elemento será excluída.

Por padrão, cada relacionamento incorporação tem PropagatesDelete verdadeira na função de destino. Excluindo henry não exclui o familyTree, mas familyTree.Delete() excluiria a todos os Persons. Para obter mais informações, consulte Personalizando o comportamento da operação de excluir.

Por padrão, PropagatesDelete não é verdadeiro para as funções de relações de referência.

Você pode fazer com que as regras de exclusão omitir propagações específicas quando você exclui um objeto. Isso é útil caso esteja substituindo um elemento para outro. Você fornece o GUID de uma ou mais funções para o qual não deverá ser propagada exclusão. O GUID pode ser obtido com a classe de relação:

henry.Delete(ParentsHaveChildren.SourceDomainRoleId);

(Esse exemplo específico não teria nenhum efeito, porque PropagatesDelete é false para as funções da ParentsHaveChildren relacionamento.)

Em alguns casos, a exclusão é evitada pela existência de um bloqueio no elemento ou em um elemento que seria excluído pela propagação. Você pode usar element.CanDelete() para verificar se o elemento pode ser excluído.

Excluindo os vínculos de relacionamento

Você pode excluir um link de relacionamento, removendo um elemento de uma propriedade de função:

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

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

Você também pode excluir o link explicitamente:

edwardHenryLink.Delete();

Todos esses três métodos têm o mesmo efeito. Você só precise usar uma delas.

Se a função tiver multiplicidade 0 ou 1.. 1, você pode defini-la null, ou a outro valor:

edward.FamilyTreeModel = null;/ / ou:

edward.FamilyTreeModel = anotherFamilyTree;

Os links de uma relação específica que são originados ou direcionado a um elemento de modelo em particular tem uma seqüência específica. Eles aparecem na ordem em que foram adicionados. Por exemplo, essa instrução sempre produzirá os filhos na mesma ordem:

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

Você pode alterar a ordem dos links:

ParentsHaveChildren link = GetLink(henry,edward);

ParentsHaveChildren nextLink = GetLink(henry, elizabeth);

DomainRoleInfo role =

link.GetDomainRelationship().DomainRoles[0];

link.MoveBefore(role, nextLink);

Bloqueios

Suas alterações poderão ser impedidas por um bloqueio. Bloqueios podem ser definidos em elementos individuais, em partições e no armazenamento. Se qualquer um desses níveis tem um bloqueio que impede que o tipo de alteração que você deseja tornar, uma exceção pode ser lançada quando você tenta executar. Você pode descobrir se os bloqueios são definidos usando o elemento.GetLocks(), que é um método de extensão é definido em Immutability.

Para obter mais informações, consulte Definindo uma política de bloqueio para criar segmentos somente leitura.

Copiar e colar

Você pode copiar elementos ou grupos de elementos para uma 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>());

Os elementos são armazenados como um grupo de elemento serializado.

Você pode mesclar os elementos de um IDataObject em um modelo:

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

Merge ()pode aceitar tanto um PresentationElement ou um ModelElement. Se você der a ele um PresentationElement, você também pode especificar uma posição no diagrama de destino como um terceiro parâmetro.

Uma DSL, o elemento de modelo de domínio, que representa um conceito, como música ou de pessoa, é separado do elemento de forma que representa o que você vê no diagrama. O elemento de modelo de domínio armazena as propriedades importantes e os relacionamentos dos conceitos. O elemento de forma armazena o tamanho, posição e cor do modo de exibição do objeto no diagrama e o layout dos seus componentes.

Elementos da apresentação

Diagrama de classes de tipos básicos de forma e o elemento

Em sua definição de DSL, cada elemento que você especificar cria uma classe que é derivada de uma das seguintes classes padrão.

Tipo de elemento

Classe base

Classe de domínio

ModelElement

Relação de domínio

ElementLink

Forma

NodeShape

Conector

BinaryLinkShape

Diagrama

Diagram

Normalmente, um elemento em um diagrama representa um elemento de modelo. Normalmente (mas nem sempre), um NodeShape representa uma instância de classe de domínio e um BinaryLinkShape representa uma instância de relação de domínio. O PresentationViewsSubject relação vincula uma forma de nó ou link para o elemento de modelo que ele representa.

Todas as formas de nó ou link pertence a um diagrama. Uma forma de link binário conecta duas formas de nó.

As formas podem ter formas filhas em dois conjuntos. Uma forma de NestedChildShapes conjunto é confinado à caixa delimitadora de seu pai. Uma forma de RelativeChildShapes lista pode aparecer fora ou parcialmente fora dos limites do pai – por exemplo, um rótulo ou uma porta. Não tem de um diagrama de RelativeChildShapes e não Parent.

Elementos de modelo de domínio e de forma estão relacionadas a PresentationViewsSubject relação.

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

A mesma relação vincula conectores no diagrama de relacionamentos:

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

Esse relacionamento também vincula a raiz do modelo de diagrama:

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

Para obter o elemento de modelo representado por uma forma, use:

henryShape.ModelElement as Person

diagram.ModelElement as FamilyTreeModel

Em geral não é aconselhável para navegar entre as formas e conectores no diagrama. É melhor navegar pelas relações no modelo, movendo-se entre as formas e conectores somente quando for necessário trabalhar na aparência do diagrama. Esses métodos link conectores às formas em cada extremidade:

personShape.FromRoleLinkShapes, personShape.ToRoleLinkShapes

connector.FromShape, connector.ToShape

Muitas formas são composições; eles são compostos de uma forma de pai e uma ou mais camadas de filhos. Formas que são posicionadas em relação à outra forma são consideradas seus filhos. Quando se move a forma pai, os filhos são movidas com ela.

Filhos relativos pode aparecer fora da caixa delimitadora da forma pai. Aninhado filhos estritamente aparecem dentro dos limites do pai.

Para obter o conjunto superior das formas em um diagrama, use:

Diagram.NestedChildShapes

As classes de ancestral de formas e conectores são:

ModelElement

-- PresentationElement

-- ShapeElement

----- NodeShape

------- Diagram

------- YourShape

----- LinkShape

------- BinaryLinkShape

--------- YourConnector

Propriedades de formas e conectores

Na maioria dos casos, não é necessário fazer alterações explícitas às formas. Quando você tiver alterado os elementos do modelo, as regras de "Corrigir" atualizar as formas e conectores. Para obter mais informações, consulte Respondendo a alterações e propagando-as.

No entanto, é útil fazer algumas alterações explícitas formas nas propriedades que são independentes dos elementos de modelo. Por exemplo, você pode alterar essas propriedades:

  • Size-Determina a altura e largura da forma.

  • Location-posição em relação à forma pai ou diagrama

  • StyleSet-o conjunto de canetas e pincéis usados para desenhar o conector ou forma

  • Hide-torna a forma invisível

  • Show-faz com que a forma visível após umHide()

Criação de um elemento e sua forma

Quando você cria um elemento e vinculá-lo na árvore de incorporar os relacionamentos, uma forma é automaticamente criada e associada a ele. Isso é feito pelas regras de "correção" que são executados no final da transação. No entanto, a forma aparecerá em um local atribuído automaticamente e sua forma, cor e outros recursos terão valores padrão. Para controlar como a forma é criada, você pode usar a função de mala direta. Você deve primeiro adicionar os elementos que você deseja adicionar em um ElementGroup e, em seguida, mesclar o grupo no diagrama.

Este método:

  • Define o nome, se você tiver atribuído uma propriedade como o nome do elemento.

  • Observa as diretivas de mesclar qualquer elemento que você especificou na definição de DSL.

Este exemplo cria uma forma na posição do mouse, quando o usuário clica duas vezes no diagrama. Na definição de DSL para este exemplo, o FillColor propriedade do ExampleShape foi exposto.

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

Se você fornecer mais de uma forma, definir suas posições relativas usando o AbsoluteBounds.

Você também pode definir a cor e outras propriedades expostas de conectores usando esse método.

Usar transações

Formas, conectores e diagramas são subtipos do ModelElement e ao vivo no armazenamento. Você deve, portanto, alterá-los dentro de uma transação. Para obter mais informações, consulte Como usar transações para atualizar o modelo.

Visualização de documentos e dados de documento

Diagrama de classes de tipos de diagrama padrão

Partições de armazenamento

Quando um modelo é carregado, o diagrama que acompanha é carregado ao mesmo tempo. Normalmente, o modelo é carregado no Store.DefaultPartition e o conteúdo do diagrama é carregado em outra partição. Normalmente, o conteúdo de cada partição é carregado e salvo em um arquivo separado.

Consulte também

Referência

ModelElement

Conceitos

Validação em uma linguagem específica do domínio

Como usar transações para atualizar o modelo

Integrando modelos por meio do Visual Studio Modelbus

Outros recursos

Gerando código a partir de uma linguagem específica do domínio

Respondendo a alterações e propagando-as