攔截圖形或裝飾項目上的點選
下列程序示範如何攔截圖形或圖示裝飾項目上的點選。 您可以攔截點選、按兩下、拖曳和其他手勢,並讓元素做出回應。
攔截圖形上的點選
在 Dsl 專案中,在與所產生程式碼檔案分開的程式碼檔案中,撰寫圖形類別的部分類別定義。 覆寫 OnDoubleClick()
或其中一個名稱以 On...
開頭的其他方法。 例如:
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
,除非您想要事件傳遞至包含圖形或圖表。
攔截裝飾項目上的點選
具有 OnDoubleClick
方法的 ImageField
類別執行個體上帶有影像裝飾項目。 如果撰寫 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) { } }
如果不想要將事件傳遞至包含圖形,您應該將
Handled
設定為true
。藉由新增下列部分類別定義來覆寫圖形類別中的
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 解決方案。
您也可以使用自己的解決方案,其中包含區間圖形。 此程式碼假設圖形所代表的模型元素與區間清單項目中所代表的元素之間有內嵌關聯性。
設定區間圖形的 Generates Double Derived 屬性。
在 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;
}
}
}