Postupy: Přidání obslužné rutiny operace přetažení myší
Obslužné rutiny pro události a přetažení můžete přidat do vašeho DSL tak, aby uživatelé můžete přetáhnout položky na diagramu z jiných diagramy nebo z jiných částí Visual Studio.Je-li přidat obslužné rutiny pro události, jako je například poklepání.Společně se nazývají obslužné rutiny přetahováním myší a dvakrát klikněte na položku speciálního tahu obslužné rutiny.
Toto téma popisuje gest a přetažení, které pocházejí v jiných diagramů.Pro přesunutí a zkopírování události v rámci jedné diagramu, zvažte alternativu definovat podtřídou třídy ElementOperations.Další informace naleznete v tématu Přizpůsobení chování kopírování.Také je možné upravit definici DSL.
V tomto tématu
První dvě části popisují alternativní metody definovat obslužnou rutinu gest:
Definování obslužných rutin gesto metodami přepsání ShapeElement.OnDragDrop, OnDoubleClick, OnDragOver, a ostatní metody lze přepsat.
Definování obslužných rutin gesto s použitím rozhraní MEF.Tuto metodu použijte, pokud chcete, aby třetí strany vývojářům nebude možné definovat své vlastní obslužné rutiny pro vaše DSL.Uživatelé mohou po nainstalovali vaše DSL nainstalovat rozšíření třetích stran.
Dekódování Taženou položku.Elementy lze přetáhnout z libovolného okna nebo z plochy, ale také DSL.
Jak získat původní přetáhnout položky.Je-li taženou položku DSL element, můžete otevřít zdrojový model a získat přístup k elementu.
Pomocí akce myši: Přetažení položek prostoru.Tento příklad znázorňuje obslužnou rutinu nižší úrovně, která zachycuje akce myši na pole obrazce.V příkladu umožňuje uživateli změnit pořadí položek v oddílu přetažením pomocí myši.
Definování obslužných rutin gesto přepsáním metody ShapeElement
Přidáte nový soubor kódu do svého projektu DSL.Pro obslužnou rutinu gesto obvykle musíte mít alespoň následující using příkazů:
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
using System.Linq;
V novém souboru definujte dílčí třídu pro třídy tvar nebo diagramu, kterou by měla odpovídat operaci přetažení.Přepište následujících metod:
OnDragOver-Tato metoda je volána, když ukazatel myši vstoupí tvar během operace přetažení.Způsob by měla zkontrolovat položku, která je přetažení uživatele a nastavte vlastnost efekt označuje, zda uživatel může umístit položku na tento obrazec.Vlastnost efekt určuje vzhled kurzoru, zatímco je nad tento obrazec 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 při umístění ukazatele myši myši tento tvar nebo diagramu, pokud OnDragOver(DiagramDragEventArgs e) dříve nastavit e.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 je volána při poklepání na obrazec nebo diagramu.
Další informace naleznete v tématu Postupy: Zachycení kliknutí na obrazec či dekorátor.
Definovat IsAcceptableDropItem(e) k určení, zda je přijatelné taženou položku a ProcessDragDropItem(e) k aktualizaci modelu, pokud položka je vyloučena.Tyto metody musí nejprve extrahovat položku z argumenty událostí.Informace o tom, jak provést naleznete v tématu jak získat odkaz na taženou položku.
Definování obslužných rutin gesto s použitím rozhraní MEF
Rozhraní MEF (Managed Extensibility Framework) umožňuje definovat součásti, které mohou být nainstalovány s minimální konfiguraci.Další informace naleznete v tématu Managed Extensibility Framework (MEF).
Chcete-li definovat obslužnou rutinu gesto MEF
Přidat do vašeho Dsl a DslPackage projektů MefExtension soubory, které jsou popsány v Rozšíření vašeho DSL pomocí MEF.
Nyní můžete definovat obslužnou rutinu gesto 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 součást obslužná rutina gesto, například když máte různé typy taženou objektů.
Přidat definice dílčí třída pro cíl tvaru, konektoru nebo diagramu třídy a definovat metody IsAcceptableDropItem() a ProcessDragDropItem().Tyto metody musí zahájíte extrahování taženou položku z argumenty událostí.Další informace naleznete v tématu jak získat odkaz na taženou položku.
Dekódování taženou položku
Když uživatel nastavuje tažením položku do diagramu nebo z jedné části diagramu do jiné informace o položce, která je přetažení je k dispozici v DiagramDragEventArgs.Vzhledem k tomu, že operace tažení může mít začal na libovolný objekt na obrazovce, data mohou být k dispozici v některém z různých formátů.Váš kód uzná formáty, s nimiž je schopen řešení.
Ke zjištění formáty, ve kterých je k dispozici vaše informace o zdroji přetažení, spustit svůj kód v režimu, nastavení zarážky na položku a ladění OnDragOver() nebo CanDragDrop().Zkontrolovat hodnoty DiagramDragEventArgs parametru.Informace jsou poskytovány ve dvou podobách:
IDataObject Data– Serializovaná verze zdrojových objektů Tato vlastnost obvykle nese ve více než jeden formát.Jeho nejužitečnějších funkcí jsou:
diagramEventArgs.Data.GetDataFormats() – obsahuje seznam formátů, ve kterých můžete dekódování taženou objektu.Například pokud uživatel nastavuje tažením souboru z plochy, k dispozici jsou názvu souboru ("FileNameW").
diagramEventArgs.Data.GetData(format)– Dekóduje taženou objekt v zadaném formátu.Objekt na odpovídající typ přetypování.Příklad:
string fileName = diagramEventArgs.Data.GetData("FileNameW") as string;
Můžete také přenášet objekty, jako jsou odkazy na sběrnici model ze zdroje v vlastní formát.Další informace naleznete v tématu jak odeslat odkazy na Model sběrnice v operace přetažení.
ElementGroupPrototypePrototype– Pomocí této vlastnosti, pokud chcete, aby uživatelé přetahování položek z linku DSL nebo modelu UML.Prototypu skupiny element obsahuje jeden nebo více objektů, odkazy a jejich hodnot vlastností.Používá se také v operace vkládání a pokud chcete přidat element z panelu nástrojů.V prototypu objekty a jejich typy jsou označeny identifikátor Guid.Například tento kód umožňuje uživateli přetáhnout elementy třídy z diagramů UML nebo Průzkumníka modelu 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 }
Potvrďte obrazce UML zjistěte identifikátory GUID třídy tvaru UML na základě testu.Mějte na paměti, že obvykle existuje více než jeden typ elementu na jakékoli diagramu.Nezapomeňte, že je objekt Přetažení z diagramu DSL nebo UML tvaru, není prvek modelu.
DiagramDragEventArgstaké obsahuje vlastnosti, které označují aktuální umístění ukazatele myši a zda je uživatel je stisknutím klávesy CTRL, ALT nebo SHIFT.
Jak získat původní taženou elementu
Data a Prototype vlastnosti argumenty událostí obsahují pouze odkaz na taženou tvaru.Obvykle Pokud chcete vytvořit objekt v cílové DSL, která je odvozena z prototypu nějakým způsobem, je třeba získat přístup k původní, například čtení obsahu souboru nebo navigaci na prvek modelu, která je reprezentována tvar.Chcete-li k tomu můžete sběrnice modelu Visual Studio.
Příprava DSL projektu pro Model sběrnice
Vytvořit zdroj DSL přístupný pro Visual Studio sběrnice modelu:
Stáhněte si a nainstalujte rozšíření sběrnice modelu Visual Studio, pokud ještě není nainstalována.Další informace naleznete v tématu pro vizualizaci a modelování SDK.
Otevřete soubor definice DSL zdroje DSL v Návrháři DSL.Klikněte pravým tlačítkem myši na návrhovou plochu a potom klikněte na tlačítko povolit Modelbus.V dialogovém okně vyberte jednu z možností.Klikněte na tlačítko OK.Nový projekt "ModelBus" je přidán do řešení DSL.
Klikněte na tlačítko transformace všechny šablony a znovu sestavte řešení.
Chcete-li odeslat objekt ze zdroje DSL
Ve vaší podtřídou ElementOperations přepsání Copy() tak, aby ji kóduje modelu sběrnice odkaz (MBR) objekt IDataObject.Tato metoda bude volána při spuštění uživatelem přetáhnout ze zdroje diagramu.Kódovaný MBR pak bude k dispozici v objekt IDataObject Jakmile uživatel přesune 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ískat odkaz na Model sběrnice od DSL v cílovém DSL nebo UML projektu
V projektu DSL cíl přidáte projekt odkazy na:
Zdrojový Dsl projekt.
Projekt ModelBus source.
V souboru s kódem obslužná rutina gest přidejte následující odkazy oboru 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í příklad ukazuje, jak získat přístup k elementu modelu zdroje:
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řijmout element pocházející z modelu UML
Následující příklad kódu přijímá objekt ze 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 } } }
Pomocí akce myši: Přetažení položek prostoru
Můžete napsat obslužnou rutinu, která zachycuje akce myši na pole obrazce.Následující příklad umožňuje uživateli změnit pořadí položek v oddílu přetažením pomocí myši.
K sestavení v tomto příkladu, vytvářet řešení s použitím diagramy tříd šablona řešení.Přidání souboru kódu a přidejte následující kód.Umožňuje upravte obor názvů, chcete-li být stejné jako 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;
}
}
}