Přidání obslužné rutiny operace podporující přetahování
Do dslu můžete přidat obslužné rutiny pro události přetažení, aby uživatelé mohli přetáhnout položky do diagramu z jiných diagramů nebo z jiných částí sady Visual Studio. Můžete také přidat obslužné rutiny pro události, jako jsou dvojité kliknutí. Společně se obslužné rutiny pro přetažení a poklikání označují jako obslužné rutiny gest.
Toto téma popisuje gesta přetažení, která pocházejí z jiných diagramů. U přesunu a kopírování událostí v jednom diagramu zvažte alternativu definování podtřídy ElementOperations
. Další informace naleznete v tématu Přizpůsobení chování kopírování. Můžete také přizpůsobit definici DSL.
Definování obslužných rutin gest přepsáním metod ShapeElement
OnDragDrop
, OnDoubleClick
, OnDragOver
a další metody lze přepsat.
Přidejte do projektu DSL nový soubor kódu. U obslužné rutiny gesta obvykle musíte mít alespoň následující using
direktivy:
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
using System.Linq;
V novém souboru definujte částečnou třídu pro třídu obrazce nebo diagramu, která by měla reagovat na operaci přetažení. Přepsat následující metody:
OnDragOver- Tato metoda je volána, když ukazatel myši přejde do obrazce během operace přetažení. Vaše metoda by měla zkontrolovat položku, kterou uživatel přetahuje, a nastavit Effect vlastnost, která označuje, zda uživatel může položku v tomto obrazci vyhodit. Vlastnost Effect určuje vzhled kurzoru, když je nad tímto obrazcem, a také určuje, zda
OnDragDrop()
bude volána, když uživatel uvolní tlačítko myši.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 - Tato metoda je volána, pokud uživatel uvolní tlačítko myši, zatímco ukazatel myši se nachází nad tímto obrazcem nebo diagramem, pokud
OnDragOver(DiagramDragEventArgs e)
byl dříve nastavene.Effect
na jinou hodnotu než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 – Tato metoda se volá, když uživatel dvakrát klikne na obrazec nebo diagram.
Další informace naleznete v tématu Postupy: Zachycení kliknutí na obrazec nebo dekorátor.
Definujte IsAcceptableDropItem(e)
, zda je přetažená položka přijatelná, a ProcessDragDropItem(e) pro aktualizaci modelu při vyřazení položky. Tyto metody musí nejprve extrahovat položku z argumentů události. Informace o tom, jak to udělat, naleznete v tématu Jak získat odkaz na přetaženou položku.
Definování obslužných rutin gest pomocí MEF
Tuto metodu použijte, pokud chcete, aby vývojáři třetích stran mohli definovat své vlastní obslužné rutiny do vašeho DSL. Uživatelé si můžou po instalaci dsl nainstalovat rozšíření třetích stran.
MEF (Managed Extensibility Framework) umožňuje definovat komponenty, které je možné nainstalovat s minimální konfigurací. Další informace najdete v tématu Rozhraní MEF (Managed Extensibility Framework).
Definování obslužné rutiny gesta MEF
Přidejte do svých projektů Dsl a DslPackage soubory MefExtension, které jsou popsány v rozšíření DSL pomocí MEF.
Obslužnou rutinu gesta teď můžete definovat jako součást 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); }
Můžete vytvořit více než jednu komponentu obslužné rutiny gest, například pokud máte různé typy přetahovaných objektů.
Přidejte částečné definice tříd pro cílový obrazec, spojnici nebo třídy diagramu a definujte metody
IsAcceptableDropItem()
aProcessDragDropItem()
. Tyto metody musí začínat extrahováním přetažené položky z argumentů události. Další informace naleznete v tématu Jak získat odkaz na přetaženou položku.
Jak dekódovat přetaženou položku
Prvky lze přetáhnout z libovolného okna nebo z plochy i z DSL.
Když uživatel přetáhne položku do diagramu nebo z jedné části diagramu do jiné, informace o přetahované položce jsou k dispozici v DiagramDragEventArgs
souboru . Vzhledem k tomu, že operace přetažení mohla být spuštěna u libovolného objektu na obrazovce, mohou být data k dispozici v libovolném z různých formátů. Váš kód musí rozpoznat formáty, se kterými dokáže pracovat.
Chcete-li zjistit formáty, ve kterých jsou k dispozici informace o zdroji přetažení, spusťte kód v režimu ladění, nastavte zarážku na položku OnDragOver()
nebo CanDragDrop()
. Zkontrolujte hodnoty parametru DiagramDragEventArgs
. Informace jsou uvedeny ve dvou formách:
IDataObject
Data
- Tato vlastnost nese serializované verze zdrojových objektů, obvykle ve více než jednom formátu. Její nejužitečnější funkce jsou:diagramEventArgs.Data.GetDataFormats() – uvádí formáty, ve kterých můžete přetahovaný objekt dekódovat. Pokud například uživatel přetáhne soubor z plochy, budou dostupné formáty obsahovat název souboru ("
FileNameW
").diagramEventArgs.Data.GetData(format)
- Dekóduje přetažený objekt v zadaném formátu. Přetypujte objekt na odpovídající typ. Příklad:string fileName = diagramEventArgs.Data.GetData("FileNameW") as string;
Můžete také přenášet objekty, jako jsou odkazy sběrnice modelu ze zdroje ve vlastním formátu. Další informace naleznete v tématu Odesílání odkazů sběrnice modelu v přetažení.
ElementGroupPrototype
Prototype
- Tuto vlastnost použijte, pokud chcete, aby uživatelé přetahovat položky z modelu DSL nebo UML. Prototyp skupiny prvků obsahuje jeden nebo více objektů, propojení a jejich hodnoty vlastností. Používá se také při operacích vložení a při přidávání prvku z panelu nástrojů. V prototypu jsou objekty a jejich typy identifikovány identifikátorem GUID. Tento kód například umožňuje uživateli přetáhnout prvky třídy z diagramu UML nebo Průzkumníka modelů 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 }
Chcete-li přijímat obrazce UML, určete identifikátory GUID tříd obrazců UML experimentem. Mějte na paměti, že v libovolném diagramu je obvykle více než jeden typ prvku. Nezapomeňte také, že objekt přetažený z diagramu DSL nebo UML je obrazec, nikoli prvek modelu.
DiagramDragEventArgs
má také vlastnosti, které označují aktuální pozici ukazatele myši a zda uživatel stiská klávesy CTRL, ALT nebo SHIFT.
Jak získat originál přetaženého elementu
Pokud přetažená položka je element DSL, můžete otevřít zdrojový model a získat přístup k prvku.
Argumenty Data
události a Prototype
vlastnosti obsahují pouze odkaz na přetažený obrazec. Obvykle, pokud chcete vytvořit objekt v cílovém DSL, který je odvozen z prototypu nějakým způsobem, potřebujete získat přístup k původnímu, například čtení obsahu souboru nebo přechod k prvku modelu reprezentovaný obrazcem. S tím můžete pomoct pomocí sady Visual Studio Model Bus.
Příprava projektu DSL pro modelovou sběrnici
Zpřístupnění zdrojového DSL pomocí sady Visual Studio Model Bus:
Otevřete definiční soubor DSL zdrojového DSL v návrháři DSL. Klepněte pravým tlačítkem myši na návrhovou plochu a potom klepněte na tlačítko Povolit Modelbus. V dialogovém okně zvolte jednu nebo obě možnosti. Klikněte na OK. Do řešení DSL se přidá nový projekt ModelBus.
Klikněte na TransformOvat všechny šablony a znovu sestavte řešení.
Odeslání objektu ze zdrojového DSL
V podtřídě ElementOperations přepište
Copy()
, aby kódoval odkaz na sběrnici modelu (MBR) do objektu IDataObject. Tato metoda bude volána, když uživatel začne přetahovat ze zdrojového diagramu. Kódovaný MBR pak bude k dispozici v objektu IDataObject, když uživatel klesne do cílového diagramu.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); } ...}
Získání odkazu na sběrnici modelu z DSL v cílovém projektu DSL nebo UML
V cílovém projektu DSL přidejte odkazy na projekt do:
Zdrojový projekt Dsl.
Zdrojový projekt ModelBus.
Do souboru kódu obslužné rutiny gest přidejte následující odkazy na obor názvů:
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;
Následující ukázka ukazuje, jak získat přístup k elementu zdrojového modelu:
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(); } } }
Přijetí elementu zdrojového z modelu UML
Následující ukázka kódu přijímá objekt vynechaný z diagramu 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 } } }
Použití akcí myši: Přetažení položek oddílu
Můžete napsat obslužnou rutinu, která zachycuje akce myši na polích obrazce. Následující příklad umožňuje uživateli změnit pořadí položek v oddílu přetažením myší.
Pokud chcete tento příklad sestavit, vytvořte řešení pomocí šablony řešení Diagramy tříd. Přidejte soubor kódu a přidejte následující kód. Upravte obor názvů tak, aby byl stejný jako váš vlastní.
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;
}
}
}
Související obsah
Poznámka:
Komponenta Transformace textové šablony se automaticky nainstaluje jako součást sady funkcí vývoje rozšíření sady Visual Studio. Můžete ho také nainstalovat z karty Jednotlivé komponenty Instalační program pro Visual Studio v kategorii sad SDK, knihoven a architektur. Nainstalujte komponentu Modeling SDK z karty Jednotlivé komponenty .