Como: adicionar um manipulador de arrastar-e-soltar
Você pode adicionar manipuladores para eventos de arrastar-e-soltar para seu DSL, para que os usuários podem arrastar itens para o diagrama de outros diagramas ou de outras partes do Visual Studio.Você também pode adicionar manipuladores de eventos, como clicar duas vezes.Juntos, os manipuladores de arrastar-e-soltar e clique duas vezes são conhecidos como manipuladores de gesto.
Este tópico discute os gestos de arrastar-e-soltar originados em outros diagramas.Para mover e copiar eventos dentro de um único diagrama, considere a alternativa da definição de uma subclasse de ElementOperations.Para obter mais informações, consulte Como: cópia do programa e o comportamento de colagem - redirecionar.Você também poderá personalizar a definição de DSL.
Neste tópico
As primeiras duas seções descrevem os métodos alternativos de definir um manipulador de gesto:
Definindo manipuladores de gesto pelos métodos de substituição ShapeElement.OnDragDrop, OnDoubleClick, OnDragOver, e outros métodos podem ser substituídos.
Definindo manipuladores de gesto usando MEF.Use esse método se desejar que os desenvolvedores de terceiros para poder definir seus próprios manipuladores de seu DSL.Os usuários podem escolher instalar as extensões de terceiros depois de terem instalado seu DSL.
Como decodificar o Item arrastado.Elementos podem ser arrastados a partir de qualquer janela ou da área de trabalho, bem como de uma DSL.
Como obter o Original arrastado o Item.Se o item arrastado é um elemento DSL, você pode abrir o modelo de origem e acessar o elemento.
Usando as ações do Mouse: Arrastando itens de compartimento.Este exemplo demonstra um manipulador de nível inferior que intercepta as ações do mouse em campos de uma forma.O exemplo permite ao usuário reordenar os itens em um compartimento, arrastando o mouse.
Definindo manipuladores de gesto, substituindo os métodos de ShapeElement
Adicione um novo arquivo de código ao seu projeto DSL.Para um manipulador de gesto, você geralmente deve ter pelo menos o seguinte using instruções:
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
using System.Linq;
No novo arquivo, defina uma classe parcial para a classe de forma ou o diagrama que deve responder para a operação de arrastar.Substitua os métodos a seguintes:
OnDragOver-Este método é chamado quando o ponteiro do mouse entra na forma durante uma operação de arrastar.Seu método deve inspecionar o item que o usuário está arrastando e defina a propriedade de efeito para indicar se o usuário pode soltar o item nessa forma.A propriedade de efeito determina a aparência do cursor enquanto ele está sobre esta forma e também determina se OnDragDrop() será chamado quando o usuário solta o botão do mouse.
partial class MyShape // MyShape generated from DSL Definition. { public override void OnDragOver(DiagramDragEventArgs e) { base.OnDragOver(e); if (e.Effect == System.Windows.Forms.DragDropEffects.None && IsAcceptableDropItem(e)) // To be defined { e.Effect = System.Windows.Forms.DragDropEffects.Copy; } }
OnDragDrop– Este método é chamado se o usuário libera o botão do mouse enquanto o ponteiro do mouse é posicionado sobre esta forma ou o diagrama, se OnDragOver(DiagramDragEventArgs e) definida anteriormente e.Effect para um valor diferente de None.
public override void OnDragDrop(DiagramDragEventArgs e) { if (!IsAcceptableDropItem(e)) { base.OnDragDrop(e); } else { // Process the dragged item, for example merging a copy into the diagram ProcessDragDropItem(e); // To be defined } }
OnDoubleClick– Esse método é chamado quando o usuário clica duas vezes na forma ou o diagrama.
Para obter mais informações, consulte Como: interceptar um clique em uma forma ou a decorador.
Definir IsAcceptableDropItem(e) para determinar se o item arrastado é aceitável e ProcessDragDropItem(e) para atualizar o seu modelo quando o item for solto.Esses métodos primeiro extraia o item do que os argumentos do evento.Para obter informações sobre como fazer isso, consulte como obter uma referência para o item arrastado.
Definindo manipuladores de gesto usando MEF
MEF (estrutura de extensibilidade gerenciado) permite definir componentes que podem ser instalados com a configuração mínima.Para obter mais informações, consulte Managed Extensibility Framework (MEF).
Definir um manipulador de gesto MEF
Adicionar à sua Dsl e DslPackage projetos do MefExtension arquivos que são descritos na Estender seu DSL usando MEF.
Agora você pode definir um manipulador de gesto como um componente MEF:
// This attribute is defined in the generated file // DslPackage\MefExtension\DesignerExtensionMetaDataAttribute.cs: [MyDslGestureExtension] public class MyGestureHandlerClassName : IGestureExtension { /// <summary> /// Called to determine whether a drag onto the diagram can be accepted. /// </summary> /// <param name="diagramDragEventArgs">Contains a link to the item that is being dragged</param> /// <param name="targetMergeElement">The shape or connector that the mouse is over</param> /// <returns>True if the item can be accepted on the targetMergeElement.</returns> public bool CanDragDrop(ShapeElement targetMergeElement, DiagramDragEventArgs diagramDragEventArgs) { MyShape target = targetMergeElement as MyShape; if (target == null) return false; if (target.IsAcceptableDropItem(diagramDragEventArgs)) return true; return false; } public void OnDragDrop(ShapeElement targetDropElement, DiagramDragEventArgs diagramDragEventArgs) { MyShape target = targetMergeElement as MyShape; if (target == null || ! target.IsAcceptableDropItem(diagramDragEventArgs)) return; // Process the dragged item, for example merging a copy into the diagram: target.ProcessDragDropItem(diagramDragEventArgs); }
Você pode criar mais de um componente de manipulador de gesto, como, por exemplo, quando você tiver diferentes tipos de objetos arrastados.
Adicionar definições de classe parcial para a forma de destino, conector ou classes de diagrama e definir os métodos IsAcceptableDropItem() e ProcessDragDropItem().Esses métodos devem começar extraindo o item arrastado dos argumentos do evento.Para obter mais informações, consulte como obter uma referência para o item arrastado.
Como decodificar o item arrastado
Quando o usuário arrasta um item para o diagrama ou de uma parte do seu diagrama para outro, informações sobre o item que está sendo arrastada estão disponíveis em [DiagramDragEventArgs].Porque a operação arrastar e poderia ter iniciado em qualquer objeto na tela, os dados podem ser disponíveis em qualquer um dos vários formatos.Seu código deve reconhecer os formatos com o qual ele é capaz de lidar.
Para descobrir os formatos em que suas informações de origem arrastar estão disponíveis, executar o código no modo de depuração, definindo um ponto de interrupção na entrada OnDragOver() ou CanDragDrop().Inspecione os valores da DiagramDragEventArgs parâmetro.As informações são fornecidas de duas formas:
IDataObject Data– Esta propriedade transporta serializadas versões dos objetos de origem, geralmente em mais de um formato.Suas funções mais úteis são:
diagramEventArgs.Data.GetDataFormats() – lista os formatos nos quais é possível decodificar o objeto arrastado.Por exemplo, se o usuário arrasta um arquivo da área de trabalho, os formatos disponíveis incluem o nome do arquivo ("FileNameW").
diagramEventArgs.Data.GetData(format)– Decodifica o objeto arrastado no formato especificado.Converta o objeto para o tipo apropriado.Por exemplo:
string fileName = diagramEventArgs.Data.GetData("FileNameW") as string;
Você também pode transmitir objetos como referências de barramento do modelo da fonte no seu próprio formato personalizado.Para obter mais informações, consulte como referências de barramento de modelo enviar em um arrastar e soltar.
ElementGroupPrototypePrototype– Use esta propriedade para que os usuários para arrastar itens de uma DSL ou um modelo UML.Um protótipo de grupo de elemento contém um ou mais objetos, links e seus valores de propriedade.Ele também é usado em operações de colagem e quando você estiver adicionando um elemento na caixa de ferramentas.Em um protótipo, objetos e seus tipos são identificados pelo Guid.Por exemplo, este código permite ao usuário arrastar elementos de classe de um diagrama UML ou o Gerenciador de modelos UML:
private bool IsAcceptableDropItem(DiagramDragEventArgs e) { return e.Prototype != null && e.Prototype.RootProtoElements.Any(element => element.DomainClassId.ToString() == "3866d10c-cc4e-438b-b46f-bb24380e1678"); // Accept UML class shapes. // Or, from another DSL: SourceNamespace.SourceShapeClass.DomainClassId }
Para aceitar formas UML, determine os Guids das classes de forma UML por experiência.Lembre-se de que geralmente é mais de um tipo de elemento em qualquer diagrama.Lembre-se também de que um objeto arrastado de um diagrama UML ou DSL é a forma, não o elemento de modelo.
DiagramDragEventArgstambém possui propriedades que indicam a posição atual do ponteiro do mouse e se o usuário está pressionando o CTRL, ALT ou SHIFT pressionadas.
Como obter o original de um elemento arrastado
O Data e Prototype propriedades dos argumentos de evento contêm apenas uma referência à forma arrastada.Normalmente, se você desejar criar um objeto no destino DSL é derivado do protótipo de alguma forma, você precisa obter acesso ao original, por exemplo, lendo o conteúdo do arquivo ou navegando até o elemento de modelo representado por uma forma.Você pode usar o barramento de modelo de Visual Studio para ajudar com isso.
Para preparar um projeto DSL para barramento de modelo
Disponibilize a fonte de DSL por Visual Studio o barramento de modelo:
Baixe e instale a extensão de barramento de modelo de Visual Studio, se já não estiver instalado.Para obter mais informações, consulte SDK de modelagem e visualização.
Abra o arquivo de definição de DSL da origem DSL no Designer de DSL.Clique com o botão direito na superfície de design e, em seguida, clique em Habilitar Modelbus.Na caixa de diálogo, escolha uma ou ambas as opções.Clique em OK.Um novo projeto "ModelBus" é adicionado à solução DSL.
Clique em Transformar todos os modelos de e recompile a solução.
Para enviar um objeto de uma fonte de DSL
Na sua subclasse de ElementOperations, substituir Copy() para que ele codifica uma referência de barramento de modelo (MBR) no IDataObject.Esse método será chamado quando o usuário começa a arrastar do diagrama de origem.O MBR codificado estarão disponível no IDataObject quando o usuário solta no diagrama de destino.
using Microsoft.VisualStudio.Modeling; using Microsoft.VisualStudio.Modeling.Shell; using Microsoft.VisualStudio.Modeling.Diagrams; using Microsoft.VisualStudio.Modeling.Integration; using Microsoft.VisualStudio.Modeling.Integration.Shell; using System.Drawing; // PointF using System.Collections.Generic; // ICollection using System.Windows.Forms; // for IDataObject ... public class MyElementOperations : DesignSurfaceElementOperations { public override void Copy(System.Windows.Forms.IDataObject data, System.Collections.Generic.ICollection<ModelElement> elements, ClosureType closureType, System.Drawing.PointF sourcePosition) { base.Copy(data, elements, closureType, sourcePosition); // Get the ModelBus service: IModelBus modelBus = this.Store.GetService(typeof(SModelBus)) as IModelBus; DocData docData = ((VSDiagramView)this.Diagram.ActiveDiagramView).DocData; string modelFile = docData.FileName; // Get an adapterManager for the target DSL: ModelBusAdapterManager manager = (modelBus.FindAdapterManagers(modelFile).First()); ModelBusReference modelReference = manager.CreateReference(modelFile); ModelBusReference elementReference = null; using (ModelBusAdapter adapter = modelBus.CreateAdapter(modelReference)) { elementReference = adapter.GetElementReference(elements.First()); } data.SetData("ModelBusReference", elementReference); } ...}
Para receber uma referência de barramento do modelo de uma DSL em um projeto DSL ou a UML de destino
No projeto DSL de destino, adicione referências de projeto para:
O projeto de Dsl de origem.
O projeto de ModelBus de origem.
No arquivo de código do manipulador de gesto, adicione as seguintes referências do namespace:
using Microsoft.VisualStudio.Modeling; using Microsoft.VisualStudio.Modeling.ExtensionEnablement; using Microsoft.VisualStudio.Modeling.Diagrams; using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement; using Microsoft.VisualStudio.Modeling.Integration; using SourceDslNamespace; using SourceDslNamespace.ModelBusAdapters;
O exemplo a seguir ilustra como acessar o elemento de modelo de origem:
partial class MyTargetShape // or diagram or connector { internal void ProcessDragDropItem(DiagramDragEventArgs diagramDragEventArgs) { // Verify that we're being passed an Object Shape. ElementGroupPrototype prototype = diagramDragEventArgs.Prototype; if (prototype == null) return; if (Company.InstanceDiagrams.ObjectShape.DomainClassId != prototype.RootProtoElements.First().DomainClassId) return; // - This is an ObjectShape. // - We need to access the originating Store, find the shape, and get its object. IModelBus modelBus = targetDropElement.Store.GetService(typeof(SModelBus)) as IModelBus; // Unpack the MBR that was packed in Copy: ModelBusReference reference = diagramDragEventArgs.Data.GetData("ModelBusReference") as ModelBusReference; using (SourceDslAdapter adapter = modelBus.CreateAdapter(reference) as SourceDslAdapter) { using (ILinkedUndoTransaction t = LinkedUndoContext.BeginTransaction("doing things")) { // Quickest way to get the shape from the MBR: ObjectShape firstShape = adapter.ResolveElementReference<ObjectShape>(reference); // But actually there might be several shapes - so get them from the prototype instead: IElementDirectory remoteDirectory = adapter.Store.ElementDirectory; foreach (Guid shapeGuid in prototype.SourceRootElementIds) { PresentationElement pe = remoteDirectory.FindElement(shapeGuid) as PresentationElement; if (pe == null) continue; SourceElement instance = pe.ModelElement as SourceElement; if (instance == null) continue; // Do something with the object: instance... } t.Commit(); } } }
Para aceitar um elemento originados de um modelo UML
O exemplo de código a seguir aceita um objeto descartado de um diagrama UML.
using Microsoft.VisualStudio.ArchitectureTools.Extensibility; using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml; using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation; using Microsoft.VisualStudio.Modeling; using Microsoft.VisualStudio.Modeling.Diagrams; using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement; using Microsoft.VisualStudio.Uml.Classes; using System; using System.ComponentModel.Composition; using System.Linq; ... partial class TargetShape { internal void ProcessDragDropItem(DiagramDragEventArgs diagramDragEventArgs) { EnvDTE.DTE dte = this.Store.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE; // Find the UML project foreach (EnvDTE.Project project in dte.Solution.Projects) { IModelingProject modelingProject = project as IModelingProject; if (modelingProject == null) continue; // not a modeling project IModelStore store = modelingProject.Store; if (store == null) return; foreach (IDiagram dd in store.Diagrams()) { // Get Modeling.Diagram that implements UML.IDiagram: Diagram diagram = dd.GetObject<Diagram>(); foreach (Guid elementId in e.Prototype.SourceRootElementIds) { ShapeElement shape = diagram.Partition.ElementDirectory.FindElement(elementId) as ShapeElement; if (shape == null) continue; // This example assumes the shape is a UML class: IClass classElement = shape.ModelElement as IClass; if (classElement == null) continue; // Now do something with the UML class element ... } } break; // don't try any more projects } } }
Usando as ações do Mouse: Arrastando itens de compartimento
Você pode escrever um manipulador que intercepta as ações do mouse em campos de uma forma.O exemplo a seguir permite que o usuário reordenar os itens em um compartimento, arrastando o mouse.
Para criar este exemplo, criar uma solução usando o Diagramas de classe o modelo de solução.Adicione um arquivo de código e o código a seguir.Ajuste o espaço para nome para ser o mesmo que o seu próprio.
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Design;
using Microsoft.VisualStudio.Modeling.Diagrams;
using System.Collections.Generic;
using System.Linq;
// This sample allows users to re-order items in a compartment shape by dragging.
// This example is built on the "Class Diagrams" solution template of VMSDK (DSL Tools).
// You will need to change the following domain class names to your own:
// ClassShape = a compartment shape
// ClassModelElement = the domain class displayed using a ClassShape
// This code assumes that the embedding relationships displayed in the compartments
// don't use inheritance (don't have base or derived domain relationships).
namespace Company.CompartmentDrag // EDIT.
{
/// <summary>
/// Manage the mouse while dragging a compartment item.
/// </summary>
public class CompartmentDragMouseAction : MouseAction
{
private ModelElement sourceChild;
private ClassShape sourceShape;
private RectangleD sourceCompartmentBounds;
public CompartmentDragMouseAction(ModelElement sourceChildElement, ClassShape sourceParentShape, RectangleD bounds)
: base (sourceParentShape.Diagram)
{
sourceChild = sourceChildElement;
sourceShape = sourceParentShape;
sourceCompartmentBounds = bounds; // For cursor.
}
/// <summary>
/// Call back to the source shape to drop the dragged item.
/// </summary>
/// <param name="e"></param>
protected override void OnMouseUp(DiagramMouseEventArgs e)
{
base.OnMouseUp(e);
sourceShape.DoMouseUp(sourceChild, e);
this.Cancel(e.DiagramClientView);
e.Handled = true;
}
/// <summary>
/// Ideally, this shouldn't happen. This action should only be active
/// while the mouse is still pressed. However, it can happen if you
/// move the mouse rapidly out of the source shape, let go, and then
/// click somewhere else in the source shape. Yuk.
/// </summary>
/// <param name="e"></param>
protected override void OnMouseDown(DiagramMouseEventArgs e)
{
base.OnMouseDown(e);
this.Cancel(e.DiagramClientView);
e.Handled = false;
}
/// <summary>
/// Display an appropriate cursor while the drag is in progress:
/// Up-down arrow if we are inside the original compartment.
/// No entry if we are elsewhere.
/// </summary>
/// <param name="currentCursor"></param>
/// <param name="diagramClientView"></param>
/// <param name="mousePosition"></param>
/// <returns></returns>
public override System.Windows.Forms.Cursor GetCursor(System.Windows.Forms.Cursor currentCursor, DiagramClientView diagramClientView, PointD mousePosition)
{
// If the cursor is inside the original compartment, show up-down cursor.
return sourceCompartmentBounds.Contains(mousePosition)
? System.Windows.Forms.Cursors.SizeNS // Up-down arrow.
: System.Windows.Forms.Cursors.No;
}
}
/// <summary>
/// Override some methods of the compartment shape.
/// *** GenerateDoubleDerived must be set for this shape in DslDefinition.dsl. ****
/// </summary>
public partial class ClassShape
{
/// <summary>
/// Model element that is being dragged.
/// </summary>
private static ClassModelElement dragStartElement = null;
/// <summary>
/// Absolute bounds of the compartment, used to set the cursor.
/// </summary>
private static RectangleD compartmentBounds;
/// <summary>
/// Attach mouse listeners to the compartments for the shape.
/// This is called once per compartment shape.
/// The base method creates the compartments for this shape.
/// </summary>
public override void EnsureCompartments()
{
base.EnsureCompartments();
foreach (Compartment compartment in this.NestedChildShapes.OfType<Compartment>())
{
compartment.MouseDown += new DiagramMouseEventHandler(compartment_MouseDown);
compartment.MouseUp += new DiagramMouseEventHandler(compartment_MouseUp);
compartment.MouseMove += new DiagramMouseEventHandler(compartment_MouseMove);
}
}
/// <summary>
/// Remember which item the mouse was dragged from.
/// We don't create an Action immediately, as this would inhibit the
/// inline text editing feature. Instead, we just remember the details
/// and will create an Action when/if the mouse moves off this list item.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void compartment_MouseDown(object sender, DiagramMouseEventArgs e)
{
dragStartElement = e.HitDiagramItem.RepresentedElements.OfType<ClassModelElement>().FirstOrDefault();
compartmentBounds = e.HitDiagramItem.Shape.AbsoluteBoundingBox;
}
/// <summary>
/// When the mouse moves away from the initial list item, but still inside the compartment,
/// create an Action to supervise the cursor and handle subsequent mouse events.
/// Transfer the details of the initial mouse position to the Action.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void compartment_MouseMove(object sender, DiagramMouseEventArgs e)
{
if (dragStartElement != null)
{
if (dragStartElement != e.HitDiagramItem.RepresentedElements.OfType<ClassModelElement>().FirstOrDefault())
{
e.DiagramClientView.ActiveMouseAction = new CompartmentDragMouseAction(dragStartElement, this, compartmentBounds);
dragStartElement = null;
}
}
}
/// <summary>
/// User has released the mouse button.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
void compartment_MouseUp(object sender, DiagramMouseEventArgs e)
{
dragStartElement = null;
}
/// <summary>
/// Forget the source item if mouse up occurs outside the
/// compartment.
/// </summary>
/// <param name="e"></param>
public override void OnMouseUp(DiagramMouseEventArgs e)
{
base.OnMouseUp(e);
dragStartElement = null;
}
/// <summary>
/// Called by the Action when the user releases the mouse.
/// If we are still on the same compartment but in a different list item,
/// move the starting item to the position of the current one.
/// </summary>
/// <param name="dragFrom"></param>
/// <param name="e"></param>
public void DoMouseUp(ModelElement dragFrom, DiagramMouseEventArgs e)
{
// Original or "from" item:
ClassModelElement dragFromElement = dragFrom as ClassModelElement;
// Current or "to" item:
ClassModelElement dragToElement = e.HitDiagramItem.RepresentedElements.OfType<ClassModelElement>().FirstOrDefault();
if (dragFromElement != null && dragToElement != null)
{
// Find the common parent model element, and the relationship links:
ElementLink parentToLink = GetEmbeddingLink(dragToElement);
ElementLink parentFromLink = GetEmbeddingLink(dragFromElement);
if (parentToLink != parentFromLink && parentFromLink != null && parentToLink != null)
{
// Get the static relationship and role (= end of relationship):
DomainRelationshipInfo relationshipFrom = parentFromLink.GetDomainRelationship();
DomainRoleInfo parentFromRole = relationshipFrom.DomainRoles[0];
// Get the node in which the element is embedded, usually the element displayed in the shape:
ModelElement parentFrom = parentFromLink.LinkedElements[0];
// Same again for the target:
DomainRelationshipInfo relationshipTo = parentToLink.GetDomainRelationship();
DomainRoleInfo parentToRole = relationshipTo.DomainRoles[0];
ModelElement parentTo = parentToLink.LinkedElements[0];
// Mouse went down and up in same parent and same compartment:
if (parentTo == parentFrom && relationshipTo == relationshipFrom)
{
// Find index of target position:
int newIndex = 0;
var elementLinks = parentToRole.GetElementLinks(parentTo);
foreach (ElementLink link in elementLinks)
{
if (link == parentToLink) { break; }
newIndex++;
}
if (newIndex < elementLinks.Count)
{
using (Transaction t = parentFrom.Store.TransactionManager.BeginTransaction("Move list item"))
{
parentFromLink.MoveToIndex(parentFromRole, newIndex);
t.Commit();
}
}
}
}
}
}
/// <summary>
/// Get the embedding link to this element.
/// Assumes there is no inheritance between embedding relationships.
/// (If there is, you need to make sure you've got the relationship
/// that is represented in the shape compartment.)
/// </summary>
/// <param name="child"></param>
/// <returns></returns>
ElementLink GetEmbeddingLink(ClassModelElement child)
{
foreach (DomainRoleInfo role in child.GetDomainClass().AllEmbeddedByDomainRoles)
{
foreach (ElementLink link in role.OppositeDomainRole.GetElementLinks(child))
{
// Just the assume the first embedding link is the only one.
// Not a valid assumption if one relationship is derived from another.
return link;
}
}
return null;
}
}
}
Consulte também
Conceitos
Personalizar o comportamento de cópia
Como: cópia do programa e o comportamento de colagem - redirecionar