Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Você pode escrever código para criar e excluir elementos de modelo, definir suas propriedades e criar e excluir links entre elementos. Todas as alterações devem ser feitas dentro de uma transação. Se os elementos forem visualizados num diagrama, o diagrama será "corrigido" automaticamente no final da transação.
Um exemplo de definição DSL
Esta é a parte principal de DslDefinition.dsl para os exemplos neste tópico:
Este modelo é uma instância desta DSL:
Referências e namespaces
Para executar o código neste tópico, deve referenciar:
Microsoft.VisualStudio.Modeling.Sdk.11.0.dll
Seu código usará este namespace:
using Microsoft.VisualStudio.Modeling;
Além disso, se você estiver escrevendo o código em um projeto diferente daquele em que sua DSL está definida, você deve importar o assembly que é construído pelo projeto Dsl.
Navegando no modelo
Propriedades
As propriedades de domínio definidas na definição 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ê deve fazê-lo dentro de uma transação:
henry.Name = "Henry VIII";
Se na definição DSL, o Kind de uma propriedade for Calculado, você não poderá defini-lo. Para obter mais informações, consulte Propriedades de armazenamento calculadas e personalizadas.
Relações
As relações de domínio definidas na definição DSL tornam-se pares de propriedades, uma na classe em cada extremidade da relação. Os nomes das propriedades aparecem no diagrama DslDefinition como rótulos nas funções em cada lado da relação. Dependendo da multiplicidade da função, o tipo da propriedade é a classe na outra extremidade da relação ou uma coleção dessa classe.
foreach (Person child in henry.Children) { ... }
FamilyTreeModel ftree = henry.FamilyTreeModel;
As propriedades em extremos opostos de uma relação são sempre recíprocas. Quando um link é criado ou excluído, as propriedades da função em ambos os elementos são atualizadas. A seguinte expressão (que usa as extensões de System.Linq) é sempre verdadeira para a relação 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 link, que é uma instância do tipo de relação de domínio. Um link sempre tem um elemento de origem e um elemento de destino. O elemento de origem e o elemento de destino podem ser os mesmos.
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 do que uma instância de um relacionamento tem permissão para vincular qualquer par de elementos do modelo. Mas se na definição DSL o sinalizador Allow Duplicates for verdadeiro para o relacionamento, pode haver mais de um link, e você deve usar GetLinks:
foreach (ParentsHaveChildren link in ParentsHaveChildren.GetLinks(henry, edward)) { ... }
Existem também outros métodos para aceder a links. Por exemplo:
foreach (ParentsHaveChildren link in ParentsHaveChildren.GetLinksToChildren(henry)) { ... }
Papéis ocultos. Se, na definição DSL, Is Property Generated for false para um papel específico, nenhuma propriedade será gerada que corresponda a esse papel. No entanto, você ainda pode acessar os links e percorrer 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 relacionamento, que vincula um elemento de modelo à forma que o exibe em um diagrama:
PresentationViewsSubject.GetPresentation(henry)[0] as PersonShape
O diretório de elementos
Você pode acessar todos os elementos no repositório usando o diretório de elementos:
store.ElementDirectory.AllElements
Há também métodos para localizar elementos, como os seguintes:
store.ElementDirectory.FindElements(Person.DomainClassId);
store.ElementDirectory.GetElement(elementId);
Aceder a informações da classe
Você pode obter informações sobre as classes, relacionamentos e outros aspetos 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 ancestrais dos elementos do 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 o código do seu programa alterar algo no Store, deve fazê-lo dentro de uma transação. Isso se aplica a todos os elementos do modelo, relações, formas, diagramas e suas propriedades. Para obter mais informações, consulte Transaction.
O método mais conveniente de gerenciar uma transação é com uma using declaração anexada em uma try...catch declaraçã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 suas alterações permanentes, você deve Commit fazer a transação antes de ser descartada. Se ocorrer uma exceção que não seja detetada dentro da transação, a Loja será redefinida para seu estado antes das alterações.
Criando elementos de modelo
Este exemplo adiciona um elemento a 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 estes pontos essenciais sobre a criação de um elemento:
Crie o novo elemento em uma partição específica da Loja. Para elementos e relações de modelo, mas não formas, esta é geralmente a partição padrão.
Torne-o o alvo de uma relação de incorporação. No DslDefinition deste exemplo, cada Pessoa deve ser o alvo da incorporação do relacionamento FamilyTreeHasPeople. Para conseguir isso, podemos definir a propriedade de função do FamilyTreeModel no objeto Person, ou adicionar o objeto Person à propriedade de função People do objeto FamilyTreeModel.
Defina as propriedades de um novo elemento, particularmente a propriedade para a qual
IsNameé true no DslDefinition. Este 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 desta DSL deve ter sido carregada na Loja. Se você estiver escrevendo uma extensão, como um comando de menu, isso normalmente já será verdade. Em outros casos, você pode carregar explicitamente o modelo na Loja ou usar o ModelBus para carregá-lo. Para obter mais informações, consulte Como abrir um modelo a partir de arquivo no código do programa.
Quando você cria um elemento dessa maneira, uma forma é criada automaticamente (se a DSL tiver um diagrama). Ele aparece em um local atribuído automaticamente, com forma, cor e outros recursos padrão. Se você quiser controlar onde e como a forma associada aparece, consulte Criando um elemento e sua forma.
Criação de links de relacionamento
Há duas relações definidas na definição DSL de exemplo. Cada relação define uma propriedade de função em cada extremidade da relação na classe.
Há três maneiras pelas quais você pode criar uma instância de um relacionamento. Cada um destes 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 do jogador de função de destino. Por exemplo:
edward.familyTreeModel = familyTree;A multiplicidade deste papel é
1..1, por isso atribuímos o valor.henry.Children.Add(edward);A multiplicidade deste papel é
0..*, por isso acrescentamos à coleção.
Construa uma instância da relação explicitamente. Por exemplo:
FamilyTreeHasPeople edwardLink = new FamilyTreeHasPeople(familyTreeModel, edward);ParentsHaveChildren edwardHenryLink = new ParentsHaveChildren(henry, edward);
O último método é útil se você quiser definir propriedades no próprio relacionamento.
Quando você cria um elemento dessa maneira, um conector no diagrama é criado automaticamente, mas ele tem uma forma, cor e outros recursos padrão. Para controlar como o conector associado é criado, consulte Criando um elemento e sua forma.
Excluindo elementos
Exclua um elemento chamando Delete():
henry.Delete();
Esta operação também eliminará:
Links de relacionamento de e para o elemento . Por exemplo,
edward.Parentsnão conteráhenrymais .Elementos em funções para as quais o indicativo
PropagatesDeleteé verdadeiro. Por exemplo, a forma que exibe o elemento será excluída.
Por padrão, cada relação de incorporação tem PropagatesDelete true na função de destino. A exclusão henry não exclui o familyTree, mas familyTree.Delete() excluiria todo o Persons.
Por padrão, PropagatesDelete não é verdade para as funções de relações de referência.
Você pode fazer com que as regras de exclusão omitam propagações específicas quando você exclui um objeto. Isso é útil se você estiver substituindo um elemento por outro. Você fornece o GUID de uma ou mais funções para as quais a exclusão não deve ser propagada. O GUID pode ser obtido da classe de relacionamento:
henry.Delete(ParentsHaveChildren.SourceDomainRoleId);
(Este exemplo particular não teria efeito, porque PropagatesDelete é false para os papéis da ParentsHaveChildren relação.)
Em alguns casos, a exclusão é impedida pela existência de um bloqueio, seja no elemento ou em um elemento que seria excluído por propagação. Você pode usar element.CanDelete() para verificar se o elemento pode ser excluído.
Excluindo links 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();
Estes três métodos têm todos o mesmo efeito. Você só precisa usar um deles.
Se a função tiver multiplicidade de 0..1 ou 1..1, você poderá defini-la como null, ou com outro valor:
edward.FamilyTreeModel = null; ou:
edward.FamilyTreeModel = anotherFamilyTree;
Reordenando os links de um relacionamento
Os links de um relacionamento específico que são originados ou direcionados a um elemento de modelo específico têm uma sequência específica. Eles aparecem na ordem em que foram adicionados. Por exemplo, esta afirmação sempre renderá as crianças 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);
Locks
Suas alterações podem ser impedidas por um bloqueio. Os bloqueios podem ser definidos em elementos individuais, em partições e na loja. Se qualquer um desses níveis tiver um bloqueio que impeça o tipo de alteração que você deseja fazer, uma exceção poderá ser lançada quando você tentar. Você pode descobrir se os bloqueios são definidos usando o elemento . GetLocks(), que é um método de extensão definido no namespace Microsoft.VisualStudio.Modeling.Immutability.
Para obter mais informações, consulte Definir uma política de bloqueio para criar segmentos somente leitura.
Copiar e colar
Você pode copiar elementos ou grupos de elementos para um 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 elementos serializado.
Você pode mesclar elementos de um IDataObject em um modelo:
using (Transaction t = targetDiagram.Store.
TransactionManager.BeginTransaction("paste"))
{
adopterShape.Diagram.ElementOperations.Merge(adopter, data);
}
Merge () pode aceitar 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.
Navegando e atualizando diagramas
Em uma DSL, o elemento de modelo de domínio, que representa um conceito como Pessoa ou Canção, é separado do elemento forma, que representa o que você vê no diagrama. O elemento de modelo de domínio armazena as propriedades e relações importantes dos conceitos. O elemento de forma armazena o tamanho, a posição e a cor da exibição do objeto no diagrama e o layout de suas partes componentes.
Elementos de apresentação
Em sua definição de DSL, cada elemento especificado cria uma classe derivada de uma das seguintes classes padrão.
| Tipo de elemento | Classe base |
|---|---|
| Classe de domínio | ModelElement |
| Relação de domínio | ElementLink |
| Shape | NodeShape |
| Connector | BinaryLinkShape |
| Diagram | Diagram |
Um elemento em um diagrama geralmente representa um elemento de modelo. Normalmente (mas nem sempre), a NodeShape representa uma instância de classe de domínio e a BinaryLinkShape representa uma instância de relacionamento de domínio. A PresentationViewsSubject relação vincula um nó ou forma de ligação ao elemento de modelo que ele representa.
Cada nó ou forma de conexão 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 do conjunto NestedChildShapes está confinada à caixa delimitadora do seu progenitor. Uma forma na RelativeChildShapes lista pode aparecer fora ou parcialmente fora dos limites do pai - por exemplo, um rótulo ou uma porta. Um diagrama não tem RelativeChildShapes nem Parent.
Navegar entre formas e elementos
Os elementos do modelo de domínio e os elementos de forma estão relacionados através da relação PresentationViewsSubject.
// 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 liga relações a conectores no diagrama:
Descendants link = Descendants.GetLink(henry, edward);
DescendantConnector dc =
PresentationViewsSubject.GetPresentation(link)
.FirstOrDefault() as DescendantConnector;
// dc.FromShape == henryShape && dc.ToShape == edwardShape
Esta relação também liga a raiz do modelo ao 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
Navegando pelo diagrama
Em geral, não é aconselhável navegar entre formas e conectores no diagrama. É melhor navegar pelas relações no modelo, movendo-se entre as formas e conectores apenas quando é necessário trabalhar na aparência do diagrama. Esses métodos vinculam conectores às formas em cada extremidade:
personShape.FromRoleLinkShapes, personShape.ToRoleLinkShapes
connector.FromShape, connector.ToShape
Muitas formas são compostas; Eles são compostos por uma forma de pai e uma ou mais camadas de filhos. Diz-se que as formas que são posicionadas em relação a outra forma são seus filhos. Quando a forma dos pais se move, as crianças se movem com ela.
Os filhos relativos podem aparecer fora da caixa delimitadora da forma pai. As crianças aninhadas aparecem estritamente dentro dos limites do pai.
Para obter o conjunto superior de formas em um diagrama, use:
Diagram.NestedChildShapes
As classes ancestrais de formas e conectores são:
-- 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 nas formas. Quando se altera os elementos do modelo, as regras de "ajuste" atualizam as formas e os conectores. Para obter mais informações, consulte Respondendo e propagando alterações.
No entanto, é útil fazer algumas alterações explícitas em formas em propriedades que são independentes dos elementos do modelo. Por exemplo, você pode alterar estas propriedades:
Size - determina a altura e largura da forma.
Location - posição em relação à forma ou diagrama pai
StyleSet - o conjunto de canetas e pincéis utilizados para desenhar a forma ou conector
Hide - torna a forma invisível
Show - torna a forma visível após um
Hide()
Criando um elemento e sua forma
Quando você cria um elemento e o vincula à árvore de relações de incorporação, uma forma é automaticamente criada e associada a ele. Isso é feito pelas regras de "fixup" que são executadas 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 mesclagem. Você deve primeiro adicionar os elementos que deseja adicionar a 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 quaisquer diretivas de mesclagem de elementos especificadas 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 DSL para este exemplo, a FillColor propriedade de ExampleShape foi exposta.
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, defina 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 de ModelElement e estão disponíveis na Loja. Portanto, você deve fazer alterações neles apenas dentro de uma transação. Para obter mais informações, consulte Como usar transações para atualizar o modelo.
Visualização do documento e dados do documento
Divisórias de armazenamento
Quando um modelo é carregado, o diagrama que o acompanha é carregado ao mesmo tempo. Normalmente, o modelo é carregado em 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.