Практическое руководство. Добавление обработчика перетаскивания
Можно добавить обработчики событий перетаскивания в DSL, чтобы пользователи могли перетаскивания элементов на схему из других схем или из других частей Visual Studio.Также можно добавлять обработчики событий как дважды щелкнуть.Совместно, обработчики перетаскивания и дважды щелкает как обработчиков жестов.
Этот подраздел содержит жесты перетаскивания, возникающие на других схемах.Для событий перемещения и копий в одной схемы, рассмотрим альтернативу определение подкласс ElementOperations.Дополнительные сведения см. в разделе Практическое руководство. Программирование функций копирования и вставки — перенаправление.Можно также иметь возможность настраивать определение DSL.
Содержание раздела
Первые 2 раздела описаны альтернативные методы определения обработчика жестов.
Определение обработчиков жестов путем переопределения методов ShapeElement.OnDragDrop" OnDoubleClick" OnDragOverи другие методы можно переопределить.
Определение обработчиков жестов с помощью MEF.Используйте этот метод, если требуется сторонним разработчикам возможность определять свои собственные обработчики к вашему DSL.Пользователи могут выбирать для установки сторонние расширения после их устанавливается в DSL.
Как расшифровка перетаскиваемый элемент.Элементы можно перетащить из любого окна, либо с рабочего стола, а также от DSL.
Получение перетаскиваемый элемент оригиналом.Если перетаскиваемый элемент DSL, можно открыть начальное модель и получить доступ к элементу.
Использование действия мышью: Перетаскивание элементов секции.Этот образец демонстрирует обработчик действия низк-уровня который перехватывает мыши в полях формы.Пример позволяет пользователю изменять порядок элементов в секции путем перетаскивания мышью.
Определение обработчиков жестов путем переопределения методов ShapeElement
Добавьте новый файл кода в проект DSL.Для обработчика жестов обычно должны иметь хотя бы следующие действия using выписки:
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
using System.Linq;
В новом файле, определение разделяемого класса для класса формы или схемы, должен отвечать на операции перетаскивания.Переопределить следующие методы:
OnDragOver- Этот метод вызывается, когда указатель мыши входит в форму во время операции перетаскивания.Этот метод должен проверять элемент, который пользователь перетаскивает и задать свойство эффекта, чтобы указать, может ли пользователь удалить элемент на этой фигуре.Свойство эффекта внешний вид курсора во время его над этой формой, а также определяет определяет, является ли OnDragDrop() вызывается, когда выпуски пользователем кнопки мыши.
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 - Этот метод вызывается, если выпуски пользователем кнопки мыши, когда поместите указатель мыши над данным или схема, если фигура OnDragOver(DiagramDragEventArgs e) ранее set e.Effect значение, отличное от 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 - Этот метод вызывается, когда пользователь дважды щелкает фигуры или схема.
Дополнительные сведения см. в разделе Практическое руководство. Перехват щелчка фигуры или декоратора.
Определить IsAcceptableDropItem(e) определить, является ли перетаскиваемый элемент допустим, и ProcessDragDropItem (e) для обновления модели, когда элемент будет удален.Эти методы должны сначала извлечь элемент из аргументов события.Сведения о том, как сделать, см. в разделе Например, получите ссылку на перетащенному элементу.
Определение обработчиков жестов с помощью MEF
MEF (управляемую расширяемость .NET Framework) позволяет указать компоненты, которые можно устанавливать с минимальной конфигурацией.Дополнительные сведения см. в разделе Managed Extensibility Framework (MEF).
Определения обработчика жестов MEF
Добавьте к запросу Dsl и DslPackage проекты MefExtension файлы, описанных в Расширение доменного языка с помощью MEF.
Теперь можно определить обработчик жестов как компонент 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); }
Можно создать более одного компонента обработчика жестов, например при наличии различных типов перетащенных объектов.
Добавление определения разделяемого класса для формы целевого объекта, соединителя или классов схемы и определите методы IsAcceptableDropItem() и ProcessDragDropItem().Эти методы должны начинаться с извлечь перетаскиваемый элемент из аргументов события.Дополнительные сведения см. в разделе Например, получите ссылку на перетащенному элементу.
Как расшифровка перетаскиваемый элемент
Когда пользователь перетаскивает элемент на схеме или из одной части данной схемы в другую, сведения об элементе, перетащив доступно в пределах [DiagramDragEventArgs].Поскольку операция перетаскивания может запустить любого объекта на экране, данные могут быть недоступны в любом из разных форматов.Код должен распознать форматы, с которыми она может работать.
Для получения форматов, в которых ваша сведения об источнике перетаскивания, выполните код в режиме отладки, точки останова для записи в параметре OnDragOver() OR CanDragDrop().Проверьте значения DiagramDragEventArgs параметр.Сведения содержатся в формах: 2
IDataObject Data - Это свойство содержит сериализованные версии исходных объектов, обычно более чем в одном формате.Его самые полезные функции:
списки diagramEventArgs.Data.GetDataFormats () - форматы, в которых можно расшифровать перетаскиваемый объект.Например, если пользователь перетаскивает файл из рабочего стола, доступные форматы включают имя файла ("FileNameW").
diagramEventArgs.Data.GetData(format) - Декодирует перетаскиваемый объект в указанном формате.Приведение объекта к соответствующему типу.Примеры.
string fileName = diagramEventArgs.Data.GetData("FileNameW") as string;
Можно также передать объекты, шины модели ссылки из источника в собственном пользовательском формате.Дополнительные сведения см. в разделе Как отправить шины модели ссылки в сопротивлении и удалении.
ElementGroupPrototypePrototype - Это свойство используется, если нужно, чтобы пользователи перетаскивать элементы из модели UML или DSL.Заполнитель группы элементов содержит один или несколько объектов, ссылки и их значения свойства.Он также используется в операциях " вставить " и при добавлении элемент из области элементов.В прототипе объекты и их типы определяются по идентификаторам Guid.Например, этот код позволяет пользователю элементам класса перетаскивания из uml-схемы или обозреватель моделей 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 }
Чтобы принять фигуры UML, укажите Guids классов UML экспериментом фигуры.Помните, что, как правило, более одного типа элемента в любой схеме.Также следует помнить, что объект, перетаскиваемый из схем UML или DSL фигуры, а не элемент модели.
DiagramDragEventArgs также есть свойства, указывающие текущее положение указателя мыши и нажмите клавишу ли пользователь CTRL, SHIFT, ALT или клавиша.
Получение исходные перетаскиваемого элемента
Data и Prototype свойства аргументы события содержат только ссылку перетащенной на форму.Обычно, если нужно создать объект в целевом объекте DSL, который является производным из прототипа каким-либо образом требуется доступ к версию с оригиналом, например, при чтении содержимого файла или переходя к элементу модели, представленному формой.Шину Visual Studio можно использовать модели, чтобы помочь с данным.
Подготовка проекта для шины модели DSL
Сделайте доступной by source DSL Visual Studio Шина модели:
Загрузите и установите расширение шины модели Visual Studio, если она еще не устанавливается.Дополнительные сведения см. в разделе Пакет SDK для визуализации данных и моделирования.
Откройте файл определения DSL источника DSL в конструкторе DSL.Щелкните правой кнопкой мыши область конструктора и выберите пункт Включите Modelbus.В диалоговом окне выбор одного или обоих параметров.Нажмите кнопку ОК.Новый проект "ModelBus" добавляется в решение DSL.
Нажать Преобразовать все шаблоны и перестройте решение.
Для отправки объекта из источника DSL
В подклассе ElementOperations, переопределение Copy() таким образом, чтобы он кодирует ссылка шины модели (MBR) из IDataObject.Этот метод вызывается при запуске пользователя для перетаскивания из схемы источника.Закодированное MBR затем будет доступно из IDataObject, когда пользователь удаляет в схеме целевого объекта.
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); } ...}
Получить ссылку шины от модели DSL в целевом объекте или проекте UML DSL
DSL целевых объектов в проекте добавьте ссылки в проект:
Проект Dsl источника.
Проект ModelBus источника.
В файле кода обработчика жестов добавьте следующие ссылки пространства имен:
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;
Следующий пример показывает, как получить доступ к элементу исходной модели:
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(); } } }
Чтобы принять элемент источником из моделей UML
Следующий пример кода принимает объект, удаленный из 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 } } }
Использование действия мышью: Перетаскивание элементов секции
Можно написать обработчик этого перехваты действия мышью в полях формы.В следующем примере позволяет пользователю изменять порядок элементов в секции путем перетаскивания мышью.
Для построения этого примера создайте решение с помощью Схемы классов шаблон решения.Добавьте файл кода и добавьте следующий код.Измените пространство имен, чтобы быть такими же, как и пользовательских.
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;
}
}
}
См. также
Основные понятия
Практическое руководство. Программирование функций копирования и вставки — перенаправление