Cómo: Agregar un controlador para arrastrar y colocar
Puede agregar controladores para los eventos de arrastrar y colocar a DSL, de modo que los usuarios pueden arrastrar elementos al diagrama de otros diagramas o de otras partes de Visual Studio.También puede agregar controladores para los eventos como doble clic.Juntos, conocen controladores de arrastrar y colocar y haga doble clic en como controladores de gestos.
Este tema describe los gestos de arrastrar y colocar que se originan en otros diagramas.Para los eventos de desplazamiento y copiar en un único diagrama, considere la posibilidad de definir una subclase de ElementOperations.Para obtener más información, vea Cómo: Comportamiento de copiar y pegar de los programas - redirigir.Puede que también pueden personalizar la definición ADSL.
En este tema
Las primeras dos secciones describen métodos para definir un controlador de gestos:
Definir los controladores de gestos reemplazando los métodos de ShapeElement.OnDragDrop, OnDoubleClick, OnDragOver, y otros métodos pueden reemplazar.
Definir controladores de gestos utilizando MEF.Utilice este método si desea que los programadores de otros fabricantes para poder definir sus propios controladores a ADSL.Los usuarios pueden elegir para instalar las extensiones de terceros después de que haya instalado ADSL.
cómo descodificar el elemento arrastrado.Los elementos se pueden arrastrar desde cualquier ventana o de escritorio, así como ADSL.
cómo obtener el elemento arrastrado original.Si el elemento arrastrado es un elemento ADSL, puede abrir el modelo de origen y tener acceso al elemento.
Usando acciones del mouse: Arrastrar elementos de compartimiento.Este ejemplo muestra un controlador de nivel inferior que las acciones del mouse de las intersecciones de los campos de una forma.El ejemplo permite al usuario cambiar el orden de los elementos en un compartimiento arrastrando con el mouse.
Definir los controladores de gestos reemplazando los métodos de ShapeElement
Agregue un nuevo archivo de código al proyecto ADSL.Para un controlador de gestos, es normalmente debe tener al menos las siguientes instrucciones de using :
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
using System.Linq;
En el nuevo archivo, defina una clase parcial para la clase shape o de diagrama que debe responder a la operación de arrastre.invalide los métodos siguientes:
OnDragOver- se llama al método de Finalización cuando el puntero del mouse entra en la forma durante una operación de arrastre.El método debe inspeccionar el elemento que el usuario está arrastrando, y establecer la propiedad del efecto para indicar si el usuario puede quitar el elemento de esta forma.La propiedad de efecto determina la apariencia del cursor mientras está sobre esta forma, y también determina si OnDragDrop() se le llama cuando el usuario suelta el botón del 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 – se llama al método de Finalización si el usuario suelta el botón del mouse mientras los restos del puntero del mouse sobre esto forma o diagrama, si OnDragOver(DiagramDragEventArgs e) establece previamente e.Effect en un valor distinto 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 – se llama al método de Finalización cuando el usuario hace doble clic en la forma o el diagrama.
Para obtener más información, vea Cómo: Interceptar un clic en una forma o decorador.
defina IsAcceptableDropItem(e) para determinar si el elemento arrastrado es aceptable, y ProcessDragDropItem (e) actualizar el modelo cuando se quita el elemento.Estos métodos primero deben extraer el elemento de argumentos del evento.Para obtener información sobre cómo hacerlo, vea Cómo obtener una referencia al elemento arrastrado.
Definir controladores de gestos utilizando MEF
MEF (managed extensibility framework) permite definir los componentes que se pueden instalar con la configuración mínima.Para obtener más información, vea Managed Extensibility Framework (MEF).
Para definir un controlador de gestos MEF
agregue a Dsl y DslPackage proyecta los archivos de MefExtension que se describen en Ampliar DSL mediante MEF.
Ahora puede definir un controlador de gestos como 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); }
Puede crear a más de un componente del controlador de gestos, por ejemplo cuando hay diferentes tipos de objetos arrastrados.
Agregue las definiciones de clase parcial para la forma de destino, el conector o clases del diagrama, y definir los métodos IsAcceptableDropItem() y ProcessDragDropItem().Estos métodos se deben iniciar mediante el elemento arrastrado de argumentos del evento.Para obtener más información, vea Cómo obtener una referencia al elemento arrastrado.
cómo descodificar el elemento arrastrado
Cuando el usuario arrastra un elemento al diagrama, o entre un elemento del diagrama a otra, la información sobre el elemento que se están arrastrando está disponible en [DiagramDragEventArgs].Dado que la operación de arrastre podría haber iniciar en cualquier objeto de la pantalla, los datos pueden estar disponibles en una variedad de formatos.El código debe reconocer los formatos que es capaz de control.
Para detectar los formatos en los que la información del origen de arrastre está disponible, ejecute el código en modo de depuración, el valor un punto de interrupción en la entrada a OnDragOver() o CanDragDrop().Inspeccione los valores del parámetro de DiagramDragEventArgs .La información se proporciona de dos formas:
IDataObjectData (la propiedad de Esta contiene las versiones serializadas de los objetos de origen, normalmente en más de un formato.Las funciones más útiles son:
diagrama EventArgs.Data.GetDataFormats() – listas formatos en los que puede descodificar el objeto arrastrado.Por ejemplo, si el usuario arrastra un archivo del escritorio, los formatos disponibles incluyen el nombre de archivo (“FileNameW").
diagramEventArgs.Data.GetData(format) – descodifica el objeto arrastrado en el formato especificado.Convierta el objeto al tipo adecuado.Por ejemplo:
string fileName = diagramEventArgs.Data.GetData("FileNameW") as string;
También puede transmitir objetos como referencias pertenecen al bus de origen en dispone de formato personalizado.Para obtener más información, vea Cómo a las referencias del bus del modelo de Envío en un arrastrar y colocar.
ElementGroupPrototypePrototype – utilice esta propiedad si desea que los usuarios arrastrar elementos ADSL o de un modelo UML.Un prototipo del grupo de elementos contiene uno o más objetos, vínculos, y los valores de propiedad.También se utiliza en operaciones de pegar y al agregar un elemento de cuadro de herramientas.En un prototipo, los objetos y sus tipos identifican Guid.Por ejemplo, este código permite que el usuario arrastre elementos de clase de un diagrama UML o en el Explorador 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 aceptar las formas UML, determine el GUID de las clases de la forma UML por el experimente.Recuerde que hay normalmente más de un tipo de elemento en un diagrama.Recuerde también que un objeto arrastrado de un diagrama ADSL o UML es la forma, no el elemento de modelo.
DiagramDragEventArgs también tiene propiedades que indican el puntero del mouse actual colocar y si el usuario está presionando CTRL, ALT, o las teclas MAYÚS.
cómo obtener el original de un elemento arrastrado
Las propiedades de Data y de Prototype de los argumentos de evento contienen sólo una referencia a la forma arrastrada.Normalmente, si desea crear un objeto en el destino ADSL que es derivado de prototipo de alguna manera, necesita obtener acceso al original, por ejemplo, al leer el contenido del archivo, o navegando al elemento modelo representado por una forma.Puede utilizar model bus de Visual Studio para ayudar con esto.
Para preparar un proyecto ADSL para model bus
Cree el origen ADSL accesible en bus del modelo de Visual Studio :
Descargar e instalar la extensión de megabus del modelo de Visual Studio, si no está instalado.Para obtener más información, vea El SDK de visualización y modelado.
Abra el archivo de definición ADSL de origen ADSL en ADSL Designer.Haga clic con el botón secundario en la superficie de diseño y haga clic en permiso Modelbus.en el cuadro de diálogo, elija una o ambas opciones.Haga clic en Aceptar.Un nuevo proyecto “ModelBus” se agrega a la solución ADSL.
Haga clic en Transformar todas las plantillas y recompile la solución.
Para enviar un objeto de un origen ADSL
En la subclase de ElementOperations, override Copy() de modo que lo codifica una referencia del modelo de (MBR) bus en el IDataObject.Este método se llama cuando el usuario empieza a arrastrar el diagrama de origen.El MBR codificadas se disponibles en el IDataObject cuando el usuario penetra en el 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 recibir una referencia del modelo de bus ADSL en un destino DSL o el proyecto UML
En el proyecto ADSL de destino, agregue referencias a:
El proyecto de ADSL de origen.
El proyecto de ModelBus de origen.
En el archivo de código del controlador de gestos, agregue referencias de espacio de nombres siguientes:
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;
El ejemplo siguiente se muestra cómo obtener acceso al elemento del modelo de origen:
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(); } } }
Acepte un elemento originó de un modelo UML
El siguiente ejemplo de código acepta un objeto quitado de un 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 acciones del mouse: Arrastrar elementos de compartimiento
Puede escribir un controlador ese intersecciones las acciones del mouse en los campos de una forma.El ejemplo siguiente permite al usuario cambiar el orden de los elementos en un compartimiento arrastrando con el mouse.
Para compilar este ejemplo, cree una solución con la plantilla de solución de diagramas de clases .agregue un archivo de código y agregue el código siguiente.Ajuste el espacio de nombres sea igual que propietario.
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;
}
}
}
Vea también
Conceptos
Personalizar comportamiento de copia
Cómo: Comportamiento de copiar y pegar de los programas - redirigir