Comment : intercepter un événement Click sur une forme ou un décorateurHow to: Intercept a Click on a Shape or Decorator

Les procédures suivantes montrent comment intercepter un clic sur une forme ou un décorateur icône.The following procedures demonstrate how to intercept a click on a shape or an icon decorator. Vous pouvez intercepter les clics, double-clique sur, fait glisser, et d’autres mouvements et que l’élément de répondre.You can intercept clicks, double-clicks, drags, and other gestures, and make the element respond.

Pour intercepter les clics sur les formesTo Intercept Clicks on Shapes

Dans le projet Dsl, dans un fichier de code qui est distinct des fichiers de code générés, écrivez une définition de classe partielle pour la classe de forme.In the Dsl project, in a code file that is separate from the generated code files, write a partial class definition for the shape class. Substituer OnDoubleClick() ou l’une des autres méthodes qui a un nom commençant par On....Override OnDoubleClick() or one of the other methods that has a name beginning with On.... Par exemple :For example:

public partial class MyShape // change  
  {  
    public override void OnDoubleClick(DiagramPointEventArgs e)  
    {  
      base.OnDoubleClick(e);  
      System.Windows.Forms.MessageBox.Show("Click");  
      e.Handled = true;  
  }  }  

Note

Définissez e.Handled à true, sauf si vous souhaitez que l’événement à passer à la forme ou le schéma contenant.Set e.Handled to true, unless you want the event to be passed to the containing shape or diagram.

Pour intercepter les clics sur les éléments décoratifsTo Intercept Clicks on Decorators

Les décorateurs image sont exécutées sur une instance de la classe de ImageField, qui comporte une méthode OnDoubleClick.Image decorators are carried on an instance of ImageField class, which has an OnDoubleClick method. Vous pouvez intercepter les clics si vous écrivez une sous-classe de ImageField.You can intercept the clicks if you write an ImageField subclass. Les champs sont configurés dans la méthode InitializeShapeFields.The fields are set up in the InitializeShapeFields method. Par conséquent, vous devez modifier cette méthode pour instancier votre sous-classe au lieu du ImageField régulière.Therefore, you must change that method to instantiate your subclass instead of the regular ImageField. La méthode InitializeShapeFields est dans le code généré de la classe de forme.The InitializeShapeFields method is in the generated code of the shape class. Vous pouvez substituer la classe de forme si vous définissez son Generates Double Derived comme décrit dans la procédure suivante.You can override the shape class if you set its Generates Double Derived property as described in the following procedure.

Bien que InitializeShapeFields est une méthode d’instance, elle est appelée une seule fois pour chaque classe.Although InitializeShapeFields is an instance method, it is called only once for each class. Par conséquent, une seule instance de ClickableImageField existe pour chaque champ dans chaque classe, pas une instance de chaque forme dans le diagramme.Therefore, only one instance of ClickableImageField exists for each field in each class, not one instance for each shape in the diagram. Lorsque l’utilisateur double-clique sur une instance, vous devez identifier l’instance à laquelle a été atteint, que le code de l’exemple montre.When the user double-clicks an instance, you must identify which instance has been hit, as the code in the example demonstrates.

Pour intercepter un clic sur un élément décoratif icôneTo intercept a click on an icon decorator

  1. Ouvrez ou créez une solution DSL.Open or create a DSL solution.

  2. Choisir ou créer une forme qui possède un élément décoratif icône et le mapper à une classe de domaine.Choose or create a shape that has an icon decorator, and map it to a domain class.

  3. Dans un fichier de code distinct à partir des fichiers dans le GeneratedCode dossier, créer la sous-classe de ImageField :In a code file that is separate from the files in the GeneratedCode folder, create the new subclass of 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)  
       { }  
    }  
    

    Vous devez définir Handled sur « True » si vous ne souhaitez pas l’événement à passer à cette forme.You should set Handled to true if you do not want the event to be passed to the containing shape.

  4. Substituez la méthode de InitializeShapeFields dans votre classs de forme en ajoutant la définition de classe partielle suivante.Override the InitializeShapeFields method in your shape classs by adding the following partial class definition.

    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);  
    }  
    
  5. Générez et exécutez la solution.Build and run the solution.

  6. Double-cliquez sur l’icône sur une instance de la forme.Double-click the icon on an instance of the shape. Le message de test doit apparaître.Your test message should appear.

Interception clique et fait glisser sur des listes de CompartmentShapeIntercepting clicks and drags on CompartmentShape lists

L’exemple suivant permet aux utilisateurs de réorganiser les éléments dans une forme de compartiment en les faisant glisser.The following sample allows users to re-order items in a compartment shape by dragging them. Pour exécuter ce code :To run this code:

  1. Créez une solution DSL à l’aide de la diagrammes de classes modèle de solution.Create a new DSL solution by using the Class Diagrams solution template.

    Vous pouvez également travailler avec une solution qui contient des formes de compartiment de votre choix.You can also work with a solution of your own that contains compartment shapes. Ce code suppose qu’il existe une relation d’incorporation entre les éléments de modèle représentés par la forme et les éléments représentés dans les éléments de liste de compartiment.This code assumes that there is an embedding relationship between the model elements represented by the shape, and the elements represented in the compartment list items.

  2. Définir le génère une Double dérivée propriété de la forme de compartiment.Set the Generates Double Derived property of the compartment shape.

  3. Ajoutez ce code dans un fichier dans le Dsl projet.Add this code in a file in the Dsl project.

  4. Ajuster les noms de classe et la forme domaine dans ce code pour correspondre à votre propre DSL.Adjust the domain class and shape names in this code to match your own DSL.

    En résumé, le code fonctionne comme suit.In summary, the code works as follows. Dans cet exemple, ClassShape est le nom de la forme de compartiment.In this example, ClassShape is the name of the compartment shape.

  • Un ensemble de gestionnaires d’événements de souris est attaché à chaque instance de compartiment lors de sa création.A set of mouse event handlers is attached to each compartment instance when it is created.

  • Le ClassShape.MouseDown événement stocke l’élément actuel.The ClassShape.MouseDown event stores the current item.

  • Lorsque la souris se trouve en dehors de l’élément actuel, une instance de MouseAction est créée, qui définit le curseur et capture la souris jusqu'à ce qu’il est libéré.When the mouse moves out of the current item, an instance of MouseAction is created, which sets the cursor and captures the mouse until it is released.

    Pour éviter toute interférence avec d’autres actions de la souris, telles que la sélection du texte d’un élément, la MouseAction n’est pas créée tant que la souris a quitté l’élément d’origine.To avoid interfering with other mouse actions, such as selecting the text of an item, the MouseAction is not created until the mouse has left the original item.

    Une alternative à la création d’une MouseAction serait simplement pour écouter les MouseUp.An alternative to creating a MouseAction would be simply to listen for MouseUp. Toutefois, cela ne fonctionnera pas correctement si l’utilisateur relâche la souris après avoir en le faisant glisser à l’extérieur du compartiment.However, this would not work properly if the user releases the mouse after dragging it outside the compartment. L’énumération MouseAction est en mesure d’effectuer l’action appropriée, quel que soit l’endroit où la souris est relâchée.The MouseAction is able to perform the appropriate action no matter where the mouse is released.

  • Lorsque la souris est relâchée, MouseAction.MouseUp réorganise l’ordre des liens entre les éléments de modèle.When the mouse is released, MouseAction.MouseUp rearranges the order of the links between the model elements.

  • La modification de l’ordre de rôle déclenche une règle qui met à jour l’affichage.The change of role order fires a rule that updates the display. Ce comportement est déjà défini, et aucun code supplémentaire est nécessaire.This behavior is already defined, and no additional code is required.

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;  
  }  
 }  
}  

Voir aussiSee Also

Répond aux requêtes et propagation des modifications Responding to and Propagating Changes
Propriétés des décorateursProperties of Decorators