Tag Archives: mouse events

Dragging shapes with the mouse in WPF

In the last post we saw what happens when the mouse is used to click on a Button. The mouse is, of course, often used  to select a particular point on the interface. A common feature is that of dragging an object from one location to another (often as part of a drag and drop operation, but that’s a bit more advanced so we’ll leave that for a future post). Here we have a look at a simple application in which the mouse can be used to drag two shapes around.

Although most of the action happens in the event handlers, we can start the project as usual in Expression Blend (EB). After the project is created, the first thing we need to do is to change the layout type. To do this right-click on LayoutRoot (the default Grid layout that is provided for you when you create a project) and select “Change Layout Type”. Change the layout to a Canvas. A Canvas layout is one in which all elements must be placed manually, that is, (x,y) coordinates must be given for each component. Although this sort of layout might seem easier than having to cope with the vagaries of a Grid, it lacks any flexibility so that if the window is resized, for example, components will appear badly aligned or, if the window is made smaller, they could become cropped or disappear altogether. However, for images and drawings, the Canvas is usually the best layout to use.

To finish the setup, add an ellipse and a circle to the Canvas. It doesn’t matter where you put them since the aim of this program is to allow them to be moved around. However, it’s a good idea if they don’t overlap in the initial setup. In our example, we placed the square at location (10,10) (that is, 10 units to the left and down from the top-left corner), and the ellipse at (200,100). We also made the square red and the ellipse blue. The initial layout looks like this:

We would like to be able to click the left button of the mouse over one of the shapes and then drag that shape around the Canvas, with the shape remaining in the new position when the button is released. We therefore need to handle 3 events: MouseLeftButtonDown, MouseMove and MouseLeftButtonUp. We will write these event handlers in such a way that the same event handlers can be used for both shapes. In EB, use the Events panel for the ellipse to add handlers for these 3 events. Give them obvious names, like shape_MouseLeftButtonDown, etc. Then select the Rectangle and give the same 3 events the same handlers as those you gave the ellipse.

If you’ve set things up properly, Visual Studio (VS) will open to let you edit the handler code in the C# file that lies behind the XAML for the main window. Here’s the code for the first bit of the class, including MouseLeftButtonDown:

  public partial class MainWindow : Window
    public MainWindow()

    bool captured = false;
    double x_shape, x_canvas, y_shape, y_canvas;
    UIElement source = null;

    private void shape_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
      source = (UIElement)sender;
      captured = true;
      x_shape = Canvas.GetLeft(source);
      x_canvas = e.GetPosition(LayoutRoot).X;
      y_shape = Canvas.GetTop(source);
      y_canvas = e.GetPosition(LayoutRoot).Y;

On lines 8 to 10, we declare a few variables that we’ll use throughout the class, and we’ll explain them as we go along.

First, we want to make the handlers generic so they can be used with both shapes. The source object is declared as a UIElement, which is the base class of all user-interface elements, and is what the Canvas functions expect as arguments. When the left mouse button is pressed, calling the handler that starts on line 12, it will provide the object that triggered the event as the sender argument in the handler. On line 14, we cast this into a UIElement.

On line 15, we capture the mouse, binding it to the shape over which it was clicked. This means that all mouse events will relate to that shape, even if the mouse should be moved outside the Canvas. We’ll see later what difference this makes.

Next, we set a boolean flag captured to true, indicating that the mouse is captured, and that the left button has been pressed.

The remainder of the code, on lines 17 through 20, records the (x,y) coordinates of source (the shape) relative to LayoutRoot (the Canvas) in doubles (x_shape, y_shape). It also records the position of the mouse relative to LayoutRoot, storing this in (x_canvas, y_canvas). Note that the mouse position is obtained by calling GetPosition() from the MouseButtonEventArgs parameter passed to the hander.

We’ve now captured the mouse with the selected shape and recorded the position of the shape and the mouse when the left button was pressed. In the MouseMove handler, we do the moving of the shape.

    private void shape_MouseMove(object sender, MouseEventArgs e)
      if (captured)
        double x = e.GetPosition(LayoutRoot).X;
        double y = e.GetPosition(LayoutRoot).Y;
        x_shape += x - x_canvas;
        Canvas.SetLeft(source, x_shape);
        x_canvas = x;
        y_shape += y - y_canvas;
        Canvas.SetTop(source, y_shape);
        y_canvas = y;

We want the shape to move only if the left mouse button is down, so we check the captured flag on line 3. Next, we obtain the current position of the mouse on lines 5 and 6. Then we calculate what the new coordinates of the shape should be and use the static functions in Canvas to set the new position. We also update x_canvas and y_canvas so they can be used to calculate how much to move the shape the next time the MouseMove handler is called. The MouseMove handler is called only every so often as the mouse is moved, so you won’t get a call every time a coordinate value changes (unless you move the mouse very slowly), but unless you move the mouse very quickly, the motion will appear smooth.

Finally, we need the handler for releasing the mouse button. This is quite simple:

    private void shape_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
      captured = false;

We have to ‘uncapture’ the mouse which we do by calling Capture() with a null argument, and we turn off the captured flag. That’s it for the code.

Finally, we promised we’d explain what difference capturing the mouse would make. This is easy enough to test; we merely comment out the call to Mouse.Capture(source) on line 15 in the MouseLeftButtonDown code above. The program appears to run properly, but note if you drag a shape and let the mouse go outside the Canvas, the shape will stop moving as soon as the mouse leaves the Canvas. If you put the call to Capture(source) back in, you’ll see the shape continue to move even after the mouse has left the Canvas. This latter behaviour is because the shape has sole use of the mouse when it captures it, so mouse move events are still routed back to the handler for that shape. If the shape did not capture the shape, then mouse events are routed to whatever is outside the Canvas when the mouse leaves the Canvas, and the shape stops moving.

Complete project files for this example are here.

WPF event handling: anatomy of a button click

We’ve seen a simple example of how to add an event handler in WPF. However, a glance at the list of events available for any object shows that WPF provides a lot of flexibility in how you handle a user action such as pressing a Button with a mouse click. We’ll have a look at some of those features here.

First, we need to mention that it is possible, and in fact easy, to add pretty well any content to a Button. By default, a Button contains some text as its Content, but that can be replaced by as complex a layout as you wish. As an example, we’ll create a Button whose content is a Grid, and the Grid in turn contains two cells, one of which contains a coloured square, and the other of which contains a TextBlock.

In Expression Blend (EB) we create a project and insert a single Button in the top-level Grid (the one named LayoutRoot by default). Centre the Button in the window. Turn off the Focusable property if you don’t want the Button to flash after it’s clicked. Now, using the Assets button (with a double chevron symbol) in the EB toolbar on the left, find a Grid and add it to the Button as its Content. In the Layout panel (under Properties, on the right) add one row and two columns to the Grid.

In column 0, add a Rectangle, and give it a size of 50 by 50, with a background colour of pure green (RGB = 0, 255, 0). In column 1, add a TextBlock, centre it horizontally and vertically, give it a margin of 5 all round, and set its text to “Press the square”. The result should look like this:

You can run the program at this point, and you should find that the button is pressable, but of course nothing will happen since we haven’t added any event handlers.

We’ve seen that you can add the standard handler for the Click event, which is triggered when the mouse’s left button is pressed and then released over the button. However, WPF actually gives you much finer control over events. Any component on a display, not just controls such as Buttons, can give rise to events. Thus in our example, the Button itself, the Grid it contains, and the Rectangle and TextBlock within the Grid can all give rise to events.

To understand how these events arise and what you can do with them, we need to remember that compound objects like the Button containing graphics are structured as a tree. In the example, the Button is the root of the tree. The Grid is the single child of the Button, and the Rectangle and TextBlock are children of the Grid. If the mouse’s left button is clicked over the Rectangle, say, then events are generated for the Rectangle, the Grid that contains it, and the Button that contains the Grid.

For some events, such as a mouse click, there are two types of events: tunneling and bubbling. Tunneling events are generated by starting at the root of the tree that contains the object over which the mouse was clicked, and then travelling down the tree until the object itself is found. Thus clicking the mouse over the Rectangle generates an event first in the Button, then in the Grid and finally in the Rectangle itself. All tunneling events have names beginning with ‘Preview’. Thus pressing the left mouse button generates tunneling events called PreviewMouseLeftButtonDown.

Once the specific object over which the mouse was clicked is located in the tree, a series of bubbling events is generated. Bubbling events are generated in the opposite order to tunneling events, so they start at the node in the tree corresponding to the object over which the mouse was clicked, and then travel up the tree, ending at the root. Typically, each tunneling event has a matching bubbling event, whose name is the same as that of the tunneling event but without the ‘Preview’ at the start. Thus the bubbling event for pressing the left mouse button is MouseLeftButtonDown.

The easiest way to see these events is to add handlers to all of them, and get each handler to print out a message when it is called. In EB, select each of the Button, Grid, square and TextBlock in turn, and add handlers for PreviewMouseLeftButtonDown and MouseLeftButtonDown. 

One way to see textual output from a program is to make the application a Console application, as we mentioned earlier. A somewhat more professional way is to run the project in debug mode in Visual Studio (VS), and use the System.Diagnostics.Debug class to generate some text. To do this, add the line

using System.Diagnostics;

at the top of the C# code file. Then a typical event handler would look like this (for the tunneling event generated by pressing the left mouse button over the square:

    private void Square_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)

You can write similar handlers for all the other events. When the program is run in VS under Debug mode, look in the Output window at the bottom to see the output from these Debug statements.

Try clicking on various parts of the Button and look at the output. If you click over the green square, you’ll see that 3 tunneling events are generated, for the Button, then the Grid and finally the Rectangle. Following this, you will see 2 bubbling events, first for the Rectangle and then the Grid. The Button itself doesn’t get a bubbling event for mouse down, since WPF combines the mouse down with the mouse up events to generate a Click event, which is the one most commonly handled for a Button.

In practice, the tunneling events aren’t used that much, but they are useful to have in some situations. For example, you may want to change something at the root level in the tree before an event is handled by a lower-down component.

Sometimes, you may want to catch an event at a higher level and prevent any further processing of that event by lower level components. You can do this by setting the event’s Handled flag to ‘true’. In the sample of an event handler above, you’ll notice that the second argument to the handler is an object of class MouseButtonEventArgs. You can use this object to set the Handled flag by inserting this line in the handler:

  e.Handled = true;

Try inserting that line in the Preview handler for the Grid and then run the program again. If you click on the square you’ll see that the Preview handlers for the Button and Grid are generated, but no further event handlers are called.

You will also notice that the first argument to the event handler method is called sender. This is the object that contains the actual component that generated the event. In our example, sender will correspond to the level in the tree at which the event is generated. For example, if we are in the handler for the Button’sPreviewMouseLeftButtonDown event and we click on the square, then sender will be the Button, not the square.

If you want to know the actual component that generated the event no matter where in the tree we are, this is contained in the MouseButtonEventArgs object, in its Source field. We can see both of these objects, sender and source, by adding a bit to the Debug statements in each handler. For example, we can write the handler for the Button’s PreviewMouseLeftButtonDown as follows.

    private void Button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
      Debug.WriteLine("Button_PreviewMouseLeftButtonDown sender: " + sender.ToString()
        + "; Source: " + e.Source.ToString());

Calling the ToString() method of the objects here will return the class name of the object, so you can see which type of object it is. In the code shown, this handler will print out:

Button_PreviewMouseLeftButtonDown sender: System.Windows.Controls.Button; Source: System.Windows.Shapes.Rectangle

As an example of how these features might be used, suppose we wanted to change the colour of the square, but only if the user clicks directly on the square and not any other part of the button. We could put the code for doing this in the bubbling event handler for the square:

    SolidColorBrush RosyBrownBrush = new SolidColorBrush(Colors.RosyBrown);
    SolidColorBrush LimeBrush = new SolidColorBrush(Colors.Lime);
    private void Square_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
      Debug.WriteLine("Square_MouseLeftButtonDown sender: " + sender.ToString()
        + "; Source: " + e.Source.ToString());
      if (!Square.Fill.Equals(RosyBrownBrush))
        Square.Fill = RosyBrownBrush;
        Square.Fill = LimeBrush;
      ButtonText.Text = "Press the square";
      e.Handled = true;

We define a couple of colours from the Colors class (which contains a large number of pre-defined colours with given names). The if statement on line 7 will swap the square’s colour between RosyBrown and Lime. On line 15, we reset the text of the TextBlock (we change it in another handler to be seen shortly). Finally, we set the event to Handled to prevent any further processing.

We want to catch any other mouse click that is inside the Button but not over the square, and print a helpful message for the user reminding him to click on the square. To put this message in the right place, we need to remember the order in which events are generated. Tunneling events are generated from the top down, starting with the Button. Bubbling events are generated from the bottom up, starting with either the Rectangle or the TextBlock.

We might think, therefore, that we can place the message code in the bubbling handler for the Grid, since the Grid lies behind both the Rectangle and TextBlock. So our first attempt might be something like this:

    private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
      Debug.WriteLine("Grid_MouseLeftButtonDown sender: " + sender.ToString()
        + "; Source: " + e.Source.ToString());
      Square.Fill = new SolidColorBrush(Colors.Red);
      ButtonText.Text = "The SQUARE, idiot!";

This sets the square to red as a warning, and changes the TextBlock’s text. We find that if we click directly over the current TextBlock text, this works, but if we click over the empty space within the Button above or below the text, nothing happens.

This is because the Grid cells size themselves to fit round the elements they contain, so the cell containing the TextBlock exists only behind the TextBlock itself and doesn’t extend to the top and bottom of the Button. We could try fiddling with the Grid’s settings to fix this, but in this case there’s an easier way.

Our next thought might be to put the code in the handler for the button’s MouseLeftButtonDown event, but remember that this event doesn’t get triggered for a Button, so again nothing will happen. We can solve the problem by putting the code in the Click event handler for the Button, since this doesn’t get called until the mouse button is released. Thus we get:

    private void Button_Click(object sender, RoutedEventArgs e)
      Debug.WriteLine("Button_Click sender: " + sender.ToString()
        + "; Source: " + e.Source.ToString());
      Square.Fill = new SolidColorBrush(Colors.Red);
      ButtonText.Text = "The SQUARE, idiot!";

This works no matter where outside the square the mouse is clicked. Remember that we stopped the event from further processing in the square’s event handler, so if we click over the square, the Button’s Click handler is never called.

Complete project files for this example are available here.