Jak: Přidat ukazatel a přetažení
Obslužné rutiny událostí a přetažení můžete přidat do vašeho DSL tak, aby uživatelé můžete přetáhnout položky diagramu z jiných diagramech nebo z jiných částí Visual Studio.Můžete také přidat obslužné rutiny pro události, například poklepání.Společně se nazývají obslužné rutiny a přetažení a poklepejte na gesto obslužné rutiny.
Toto téma popisuje v jiných diagramů a přetažení tahy, které pocházejí.Pro přesun a kopírování události do jednoho diagramu Zvažte alternativu definování podtřídu ElementOperations.Další informace naleznete v tématu Jak: Program zkopírovat a vložit chování - přesměrování.Je také možné přizpůsobit definici DSL.
V tomto tématu
První dvě části popisují alternativní metody definování Obsluha speciálního tahu:
Definování obslužné rutiny gesto přepsání ShapeElement metodami.OnDragDrop, OnDoubleClick, OnDragOver, a jiné metody mohou být potlačeny.
Definování obslužné rutiny gesta pomocí MEF.Tuto metodu použijte, pokud chcete, aby vývojáři třetích stran mohli definovat své vlastní obslužné rutiny pro váš modem DSL.Uživatelé mohou zvolit instalaci rozšíření třetích stran po instalaci vašeho DSL.
Dekódování přetažená položka.Prvky můžete přetáhnout z kteréhokoli okna nebo z plochy, z DSL.
Jak získat původní přetáhnout položky.Přetažená položka je prvek DSL, můžete otevřít zdrojový model a přístup k prvku.
Pomocí akce myši: Přetažení položek prostoru.Tento příklad znázorňuje popisovač nižší úrovně, který zachycuje akce myši na pole obrazce.Příklad umožňuje uživateli změnit pořadí položek v krabici přetažením pomocí myši.
Definování obslužné rutiny gesto přepsáním ShapeElement metody
Přidání nového souboru kód projektu DSL.Pro obsluhu gesto obvykle musíte mít alespoň následující using příkazy:
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
using System.Linq;
V novém souboru definujte částečné třídy pro obrazec nebo diagramu třídy, která by měla odpovědět na operaci přetažení.Přepsat následující metody:
OnDragOver-Tato metoda se nazývá ukazatel myši na obrazec zadá při operaci přetažení.Metodu by měly kontrolovat zboží, které je uživatel tažení a nastavte vlastnost efekt označuje, zda uživatel může umístit položku na tento obrazec.Vlastnost efekt určuje vzhled kurzoru 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 v případě, že uživatel uvolní tlačítko myši při umístění ukazatele myši přes tento obrazec nebo diagram, pokud OnDragOver(DiagramDragEventArgs e) dříve e.Effect na 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 Postup: klepněte na obrazec nebo Decorator zachytit.
Definovat IsAcceptableDropItem(e) určit, zda přetažená položka je přijatelné a ProcessDragDropItem(e) při přetažení položky aktualizace modelu.Tyto metody musí nejprve extrahovat položku z argumentů události.Informace o postupu naleznete v tématu jak odkazovat na přetažená položka.
Definování pomocí MEF obslužné rutiny gesto
MEF (spravované Extensibility Framework) umožňuje definovat součásti, které lze nainstalovat s minimální konfigurace.Další informace naleznete v tématu Managed Extensibility Framework (MEF).
Definovat speciální tah pro obsluhu MEF
Přidat do vašeho Dsl a DslPackage projekty MefExtension soubory, které jsou popsány v Rozšířit vaše DSL pomocí MEF.
Nyní můžete definovat speciální tah pro obsluhu 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 popisovač gesto jako když mají různé typy objektů přetažené.
Přidat dílčí třídy definice cílového obrazce, spojnice nebo diagramu třídy a definovat metody IsAcceptableDropItem() a ProcessDragDropItem().Tyto metody musí nejprve extrahování přetažená položka z argumentů události.Další informace naleznete v tématu jak odkazovat na přetažená položka.
Dekódování přetažená položka
Když uživatel přetáhne položky do diagramu nebo z jedné části diagramu na jiné informace o zboží, které je přetažení je k dispozici v [DiagramDragEventArgs].Protože operace přetažení by mohla začít na libovolný objekt na obrazovce, data mohou být k dispozici v jednom z různých formátů.Kód musí rozpoznat formáty, které je schopné obchodování.
Chcete-li zjistit formáty, ve kterých je k dispozici zdroj informací přetáhněte spustit kód v režim ladění, nastavením zarážky v položce OnDragOver() nebo CanDragDrop().Zkontrolovat hodnoty DiagramDragEventArgs parametr.Informace jsou poskytovány ve dvou formách:
IDataObject Data– Serializované verze zdrojové objekty tato vlastnost obvykle nese ve více než jednom formátu.Nejužitečnější funkce jsou:
diagramEventArgs.Data.GetDataFormats() – seznam formátů, které lze dekódovat taženého objektu.Například, pokud uživatel přetáhne souboru z pracovní plochy, dostupné formáty zahrnují název souboru ("FileNameW").
diagramEventArgs.Data.GetData(format)– Dekóduje taženého objektu v určeném formátu.Nádech příslušný typ objektu.Příklad:
string fileName = diagramEventArgs.Data.GetData("FileNameW") as string;
Objekty jako model sběrnice odkazy ze zdroje také přenášet ve vlastní formát.Další informace naleznete v tématu jak odeslat modelu sběrnice odkazy v přetáhnout.
ElementGroupPrototypePrototype– Pomocí této vlastnosti, pokud chcete, aby uživatelé přetahovat položky z DSL nebo modelu UML.Skupiny prototypu prvek obsahuje jeden nebo více objektů, odkazy a jejich hodnoty vlastností.Používá se také v operace vkládání a jsou při přidání prvku z panelu nástrojů.V prototypu objekty a jejich typy jsou označeny Guid.Například tento kód umožňuje uživateli přetáhnout prvky třídy z diagramu UML nebo Průzkumník modelů:
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řijmout obrazce UML stanovit identifikátory GUID třídy obrazce UML.Nezapomeňte, že obvykle existuje více než jeden typ prvku na libovolný diagram.Nezapomeňte, že je objekt přetaženým z DSL nebo UML diagramu obrazec prvek modelu.
DiagramDragEventArgsmá také vlastnosti, které označují aktuální umístění ukazatele myši a zda je uživatel klávesy CTRL, ALT nebo SHIFT klávesy.
Jak získat původní přetažené prvek
Data a Prototype vlastnosti argumentů události obsahovat pouze odkaz na přetažené obrazce.Obvykle Pokud chcete vytvořit objekt v cílové DSL, odvozených z prototypu nějakým způsobem, je třeba získat přístup k původní, například čtení obsahu souboru nebo přechod na prvek modelu zastoupena obrazcem.Můžete pomoci s tímto sběrnice modelu Visual Studio.
Příprava projektu DSL modelu sběrnice
Zpřístupnit zdroje DSL ve Visual Studio modelu sběrnice:
Stáhněte a nainstalujte rozšíření sběrnice modelu Visual Studio, pokud již není nainstalována.Další informace naleznete v vizualizaci a modelování SDK.
Otevřete soubor definice DSL zdroje DSL v Návrháři DSL.Pravým tlačítkem myši na plochu návrhu a klepněte na tlačítko Povolení Modelbus.V dialogovém okně zvolte jednu nebo obě možnosti.Klikněte na tlačítko OK.Do řešení DSL je přidán nový projekt "ModelBus".
Klepněte na tlačítko Transformace všechny šablony a znovu vytvořit řešení.
K odeslání objektu ze zdroje DSL
Přepsat vlastní podtřídě ElementOperations Copy() tak, aby jej zakóduje referenční Model sběrnice (MBR) do IDataObject.Tato metoda bude volána při spuštění uživatelem přetáhnout z diagramu zdrojů.Kódování MBR pak bude k dispozici IDataObject při přerušení uživatele v terčový diagram.
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); } ...}
Odkaz na Model sběrnice přijímat od DSL v cílový projekt DSL nebo UML
Cíl projektu DSL přidáte projekt odkazy na:
Zdrojový projekt Dsl.
Zdroj ModelBus projektu.
V souboru kód popisovač gesto 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 prvku 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 prvek pocházející z modelu UML
Následující ukázka kódu přijímá objekt z diagramu UML vynechány.
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 psát obslužné rutiny, která zachycuje akce myši na pole obrazce.Následující příklad umožňuje uživateli změnit pořadí položek v krabici přetažením pomocí myši.
Chcete-li vytvořit tento příklad vytvoření řešení pomocí Diagramy třídy roztok šablony.Kód souboru přidat a přidejte následující kód.Upravte stejnou jako vlastní obor názvů.
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;
}
}
}