如何:截获对形状或修饰器的单击
下面的过程演示如何在中截获在形状或图标修饰器中单击。 您可以截获单击、双击、拖动和其他操作,并将该元素响应。
若要截获单击形状
在 DSL 项目,于生成的代码文件中的代码文件,编写形状类的分部类定义。 重写具有名称从 On...启动其他方法的 OnDoubleClick() 或之一。 例如:
public partial class MyShape // change
{
public override void OnDoubleClick(DiagramPointEventArgs e)
{
base.OnDoubleClick(e);
System.Windows.Forms.MessageBox.Show("Click");
e.Handled = true;
} }
备注
,除非您希望该操作传递到包含的形状或关系图,设置 e.Handled 到 true。
若要截获单击修饰器
图像修饰器在 ImageField 类的实例带有,具有 OnDoubleClick 方法。 您可以截获单击,如果您编写 ImageField 子类。 字段。 InitializeShapeFields 方法设置为。 因此,必须更改该方法实例化自己的子类而不是使用普通的 ImageField。 InitializeShapeFields 方法在形状类的生成代码。 ,如果您设置其属性 Generates Double Derived 如以下过程所述,您可以重写形状类。
虽然 InitializeShapeFields 为实例方法时,会针对每个类只调用一次。 因此,只有 ClickableImageField 一个实例为每个类的每个字段,并非每个形状的一个实例在 " 关系图。 当用户双击实例时,必须标识要实例被命中,因为,在示例中的代码演示了。
截获在图标修饰器中单击
打开或创建 DSL 解决方案。
选择或创建有一个图标修饰器的形状,并将其映射到域类。
位于独立于 GeneratedCode 文件夹中的文件的代码文件,请创建 ImageField 新子类的情况:
using Microsoft.VisualStudio.Modeling; using Microsoft.VisualStudio.Modeling.Design; using Microsoft.VisualStudio.Modeling.Diagrams; using System.Collections.Generic; using System.Linq; namespace Fabrikam.MyDsl { // Change to your namespace internal class ClickableImageField : ImageField { // You can also override OnClick and so on. public override void OnDoubleClick(DiagramPointEventArgs e) { base.OnDoubleClick(e); // Work out which instance was hit. MyShape shapeHit = e.HitDiagramItem.Shape as MyShape; if (shapeHit != null) { MyDomainClass element = shapeHit.ModelElement as MyDomainClass; System.Windows.Forms.MessageBox.Show( "Double click on shape for " + element.Name); // If we do not set Handled, the event will // be passed to the containing shape: e.Handled = true; } } public ClickableImageField(string fieldName) : base(fieldName) { } }
,如果您不希望该操作传递到包含的形状,应将处理为 true。
通过添加以下分部类定义将重写在形状 classs 的 InitializeShapeFields 方法。
public partial class MyShape // change { protected override void InitializeShapeFields (IList<ShapeField> shapeFields) { base.InitializeShapeFields(shapeFields); // You can see the above method in MyShapeBase // in the generated Shapes.cs // It has already added fields for the Icons. // So you will have to retrieve them and replace with your own. ShapeField unwantedField = shapeFields.First (field => field.Name == "IconDecorator1"); shapeFields.Remove(unwantedField); // Now replicate the generated code from the base class // in Shape.cs, but with your own image constructor. ImageField field2 = new ClickableImageField("IconDecorator1"); field2.DefaultImage = ImageHelper.GetImage( MyDslDomainModel.SingletonResourceManager .GetObject("MyShapeIconDecorator1DefaultImage")); shapeFields.Add(field2); }
生成并运行解决方案。
双击图标形状的实例。 您的测试消息来应显示。
截获单击和拖动到 CompartmentShape 列表
下面的示例允许用户通过拖动将重新排列在隔离舱形状的项目。 运行此代码:
使用 类图 解决方案模板,创建一个新的 DSL 解决方案。
还可以使用包含形状隔离舱您的解决方案一起使用。 此代码假定,具有形状表示的模型元素之间的一个嵌入的关系,因此,在隔离舱表示的元素列表项。
设置形状隔离舱的 生成派生的二进制文件 属性。
将一个文件的此代码在 Dsl 项目。
调整此代码的域类和形状名称与 DSL 的。
总之,代码按以下方式。 在此示例中, ClassShape 是形状隔离舱的名称。
,则在创建集合时,设置鼠标事件处理程序附加到每隔离舱实例。
ClassShape.MouseDown 事件存储当前项目。
当鼠标从当前项目时, MouseAction 创建实例,将光标并捕获鼠标,直到其释放。
若要避免干扰其他鼠标事件,如选择的项的文本, MouseAction 未创建,直到鼠标离开原始项目。
用于创建 MouseAction 的替代项为侦听 MouseUp。 但是,这不会正确工作,如果用户将它后释放鼠标在隔离舱之外。 MouseAction 可以执行相应的操作,这样不论释放鼠标。
释放鼠标, MouseAction.MouseUp 重新排列链接的顺序模型元素之间的。
角色顺序的更改激发规则更新显示。 此行为已定义,因此,不需要其他代码。
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
{
/// <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.
/// </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;
}
}
}