programming4us
programming4us
MULTIMEDIA

Mouse Events in Silverlight

7/25/2010 5:09:07 PM
I_section2_d1e3682.html
Silverlight 2 supports these five mouse events, three of which you have already seen:
MouseEnter

The mouse pointer entering the display area of an object

MouseLeave

The mouse pointer leaving the display area of an object

MouseMove

The mouse pointer moving

MouseLeftButtonDown

The left mouse button being clicked down

MouseLeftButtonUp

The left mouse button being clicked and released

The events themselves are self-explanatory, but we should probably discuss the difference between MouseLeftButtonDown and MouseLeftButtonUp. When a user clicks on an element, first MouseLeftButtonDown occurs, and then MouseLeftButtonUp. So, a mouse click is actually complete only when MouseLeftButtonUp has been fired. In the real world, the distinction makes sense in only one special case: the user hovers the mouse over an element, clicks the button, holds the button, and then moves the mouse away again. When you use MouseLeftButtonUp, it isn't fired over the target object, which is desirable in some scenarios (think buttons) and undesirable in other scenarios (think drag and drop).

NOTE

The mouse event handling mechanism of the vector graphics format SVG, for instance, supports three mouse events: button pressed, button released, and a completed mouse click.

1. Mouse Position

When you capture a mouse event, you want to know where the mouse pointer currently is. The "where" question has previously been answered with "on this object." More specifically is the question "At which position?" This is where the second argument passed to event handler functions, eventArgs, comes into play. It provides access to this very information, by supporting the getPosition() method.

The getPosition() method supports an optional argument, which is any XML element. If this is set, getPosition() retrieves the relative position of the mouse to the given element. Otherwise, you get the absolute coordinates (i.e., if you do not provide an argument or if you provide null).

The return value of a getPosition() call is an object with the two properties, x and y, which of course contain the horizontal and vertical positions of the mouse pointers. As always with the Web, the origin is in the top-left corner.

Example 1 contains the XAML markup to track mouse movements. Note how the property is used to display the location of the mouse pointer. Also, note that the main canvas's Loaded event again executes a method called canvasLoaded().

Example 1. Determining the mouse position, the XAML file (Page.xaml, project MousePosition)

<UserControl x:Class="MousePosition.Page"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<Grid x:Name="LayoutRoot" Background="White">
<Canvas Loaded="canvasLoaded">
<Rectangle Width="200" Height="75" Stroke="Orange" StrokeThickness="15" />
<TextBlock FontFamily="Arial" FontSize="28" Canvas.Left="25" Canvas.Top="25"
Foreground="Black"
Text="X: ?? Y: ??" x:Name="MousePosition" />
</Canvas>
</Grid>
</UserControl>

The task of the C# code is to determine and output the current mouse pointer position whenever the mouse is moving. The associated event name is MouseMove, and an event listener is ideal for this:

private void canvasLoaded(object sender, RoutedEventArgs e)
{
Canvas c = sender as Canvas;
c.MouseMove += new MouseEventHandler(mouseMove);
}

All that's left to do is to write the event listener, so we will use an anonymous function here. The code determines the current mouse position using GetPosition() and writes it into the text box. Refer to Example 2 for the complete C# code, and to Figure 1 to see how this sample looks in the browser.

Example 2. Determining the mouse position, the XAML C# file (Page.xaml.js, project MousePosition)

private void mouseMove(object sender, MouseEventArgs e)
{
double x = e.GetPosition(null).X;
double y = e.GetPosition(null).Y;
MousePosition.Text = String.Format("X: {0} Y: {1}", x, y);
}

Figure 1. The current mouse position is displayed


As mentioned before, you can also remove event listeners. To do this, you "remove" from the event listener the object to which the event listener has been attached. To demonstrate this mechanism, we implement the hover effect again, but this time it can be enabled and disabled by clicking on the text. We start with the previous XAML markup from Example 1. The C# code, however, changes quite a bit. At first, we define two global properties. One will be used to save the attached event handler, and the other one is a Boolean value that tells the script whether we currently want to trace the mouse:

private Boolean traceMouse = false;
private MouseEventHandler handler = null;

Assigning the event handler to the canvas's Loaded event changes a bit, too. We assign a new event handler method called toggle() to it:

private void canvasLoaded(object sender, RoutedEventArgs e)
{
Canvas c = sender as Canvas;
c.MouseLeftButtonDown += new MouseButtonEventHandler(toggle);
}

The toggle() method first needs to check whether the mouse pointer coordinates are traced. If not, tracing must be enabled (since we want to toggle the behavior). We use the same code as before: whenever the mouse pointer moves, the new coordinates are displayed. Notice how the new event handler is saved in the handler property before it is used (which facilitates removing this event handler later on):

private void toggle(object sender, MouseEventArgs e)
{
if (!traceMouse)
{
handler = new MouseEventHandler(mouseMove);
(sender as Canvas).MouseMove += handler;
}

If the mouse has been traced before (traceMouse equals true), it must be deactivated; also, the event listener must be removed. A call to removeEventListener() takes care of that; remember that you have to use the addEventListener() return values as the second argument!

    else
{
(sender as Canvas).MouseMove -= handler;
}

Don't forget to toggle the traceMode variable from true to false or from false to true:

    traceMouse = !traceMouse;
}

Example 3 contains the complete code of the XAML C# file. If you run this example in the browser, you will need to click on the text to start seeing the mouse pointer coordinates. Clicking on the text again stops this.

Example 3. Adding and removing event listeners, the XAML JavaScript code (Page.xaml.js, project MousePositionToggle)

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace MousePositionToggle
{
public partial class Page : UserControl
{
private Boolean traceMouse = false;
private MouseEventHandler handler = null;

public Page()
{
// Required to initialize variables
InitializeComponent();
}

private void canvasLoaded(object sender, RoutedEventArgs e)
{
Canvas c = sender as Canvas;
c.MouseLeftButtonDown += new MouseButtonEventHandler(toggle);
}

private void toggle(object sender, MouseEventArgs e)
{
if (!traceMouse)
{
handler = new MouseEventHandler(mouseMove);
(sender as Canvas).MouseMove += handler;
}
else
{
(sender as Canvas).MouseMove -= handler;
}
traceMouse = !traceMouse;
}

private void mouseMove(object sender, MouseEventArgs e)
{
double x = e.GetPosition(null).X;
double y = e.GetPosition(null).Y;
MousePosition.Text = String.Format("X: {0} Y: {1}", x, y);
}
}
}


2. Drag and Drop

One of the most difficult JavaScript effects to implement is drag and drop. Not only can it be hard to control individual elements on the page, but browser incompatibilities ultimately break the developer's neck. Silverlight does not come with built-in drag-and-drop support, but it is possible to implement this with rather little effort. If you plan it appropriately, the code will come together quickly.

Drag and drop always consists of three phases, which can be directly mapped on Silverlight mouse events:

MouseLeftButtonDown

The user clicks on a draggable element and the application enters drag mode.

MouseMove

The user moves the mouse, while the mouse button remains clicked. The selected object changes position according to the current position of the mouse pointer.

MouseLeftButtonUp

The user releases the mouse button; the application leaves drag mode.

Actually, this is about 50% of the solution. The other 50% comes from a different challenge. Let's assume that the draggable element is a 10 x 10 pixel square. The user clicks somewhere on the square and drags it. Let's further assume that the user releases the square at some position—for instance, at (50,40) so that the x coordinate is 50 pixels and the y coordinate is 40 pixels. Where should the JavaScript code now put the square? At (50,40)? This would place the top-left corner of the square, so the position would be correct only if the user initially dragged the square by clicking exactly on the top-left corner. This is rarely the case, of course.

So, working with the absolute position is not a good solution. Instead, we will work with deltas: how far did the user move the mouse? In the first phase of drag and drop, the C# code records the current position of the mouse pointer. Whenever the mouse is moved, the new position of the mouse pointer is retrieved. Based on these two values, C# can calculate by how many pixels the mouse has been moved—for example, 15 pixels to the right and 20 pixels to the bottom. These delta values can then be applied to the actual object that will be moved: it also must be moved 15 pixels to the right and 20 pixels to the bottom. (In reality, these pixel values are usually much smaller, since the MouseMove event is fired so often.)

This algorithm is the missing half of drag and drop. Writing the code is no big challenge anymore. We start with the XAML code in Example 4. We once again have the surrounding rectangle and a black circle that will serve as the draggable object. Notice how the circle uses attributes to handle the three relevant mouse events.

Example 4. Drag and drop, the XAML file (Page.xaml, project DragDrop)

<UserControl x:Class="DragDrop.Page"
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="400" Height="300">
<Grid x:Name="LayoutRoot" Background="White">
<Canvas>
<Rectangle Width="300" Height="150" Stroke="Orange" StrokeThickness="15" />
<Ellipse Width="50" Height="50" Fill="Black" Canvas.Left="20" Canvas.Top="20"
MouseLeftButtonDown="mouseInit"
MouseLeftButtonUp="mouseRelease"
MouseMove="mouseMove" />
</Canvas>
</Grid>
</UserControl>

We can now start to implement the actual drag and drop. Before we do that, one word of caution. If the draggable object has processed a mouse event, it hands it over to its parent element. And even worse, other elements farther down in the object chain could process and act upon this event as well (this is also called "event tunneling"). Therefore, other elements could work with those events too, which is usually undesirable in this scenario. So, Silverlight provides a CaptureMouse() method. If an object calls this method, all mouse events are directly routed to the object; other objects do not receive them any longer. When the left mouse button is pressed (the MouseLeftButtonDown event), the initial position of the draggable object is retained and the mouse capturing mode is activated. The (global) moving property remembers whether the application is in drag-and-drop mode (true) or not (false):

function mouseInit(sender, eventArgs) {
sender.captureMouse();
lastX = eventArgs.getPosition(null).x;
lastY = eventArgs.getPosition(null).y;
moving = true;
}

If the mouse is moving, the position of the draggable object needs to be updated properly, according to the algorithm we designed at the beginning of this section. Remember, the current mouse position is determined, and then the script code calculates the delta between the current and the last known positions. The position of the draggable object is updated accordingly. Finally, the variables holding the last known coordinates are updated.

If you are updating the circle's position, you have to take care of one special issue: the property names you need to set are represented by the Canvas.Left and Canvas.Top attributes. However, these properties are not directly accessible for C#, since they are so-called dependency properties (they depend on their parent elements). However, there is a workaround. The static Canvas.GetLeft() and Canvas.GetTop() methods calculate the x and y positions of any element. In an analogous fashion, Canvas.SetLeft() and Canvas.SetTop() set these values. Now all you have to remember is that the first argument of the event handler function is the object firing the event—in this case, the element we want to position. Then the rest of the code is easy:

private void mouseMove(object sender, MouseEventArgs e)
{
if (moving) {
Ellipse el = sender as Ellipse;
var x = e.GetPosition(null).X;
var y = e.GetPosition(null).Y;
Canvas.SetLeft(el, Canvas.GetLeft(el) + x - lastX); Canvas.SetTop(el, Canvas.GetTop(el) + y - lastY); lastX = x; lastY = y; } }

NOTE

You now see why we needed the moving variable. Mouse moving events happen all the time, but you want the circle to move only if the user is dragging it!

The final step happens when the user releases the mouse button (the LeftMouseButtonUp event). You can reset moving to false and unlock access to mouse events for other elements on the page by calling the ReleaseMouseCapture() method. We will implement one additional feature here. As you have seen, there is an orange rectangle in the XAML file. This serves as the barrier for the draggable element: the element must not touch or even leave this border.

We need to define a couple of class properties that provide us with the minimum and maximum coordinates where the circle is allowed:

private double minX = 15;
private double maxX = 235;
private double minY = 15;
private double maxY = 85;

We will also save the position of the circle when the user starts a drag-and-drop operation (this code obviously belongs in the mouseInit() method):

startX = Canvas.GetLeft(el);
startY = Canvas.GetTop(el);

Finally, when the user releases the mouse button, the current position is determined and checked against the valid coordinates. If the position is out of bounds, the draggable object is placed at the position it had at the beginning of the drag-and-drop operation:

var x = Canvas.GetLeft(el);
var y = Canvas.GetTop(el);
if (x < minX || x > maxX || y < minY || y > maxY) {
Canvas.SetLeft(el, startX);
Canvas.SetTop(el, startY);
}

Example 5 sums up the complete C# code. Figure 2 shows the example in action: if the user releases the mouse button now, the circle will jump back to its original position.

Example 5. Drag and drop, the XAML C# file (Page.xaml.cs, project DragDrop)

using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;

namespace DragDrop
{
public partial class Page : UserControl
{

double startX, startY, lastX, lastY;
private double minX = 15;
private double maxX = 235;
private double minY = 15;
private double maxY = 85;

private Boolean moving = false;

public Page()
{
// Required to initialize variables
InitializeComponent();
}

private void mouseInit(object sender, MouseButtonEventArgs e)
{
Ellipse el = sender as Ellipse;
el.CaptureMouse();
startX = Canvas.GetLeft(el);
startY = Canvas.GetTop(el);
lastX = e.GetPosition(null).X;
lastY = e.GetPosition(null).Y;
moving = true;
}

private void mouseRelease(object sender, MouseButtonEventArgs e)
{
Ellipse el = sender as Ellipse;
el.ReleaseMouseCapture();
moving = false;
var x = Canvas.GetLeft(el);
var y = Canvas.GetTop(el);
if (x < minX || x > maxX || y < minY || y > maxY) {
Canvas.SetLeft(el, startX);
Canvas.SetTop(el, startY);
}
}

private void mouseMove(object sender, MouseEventArgs e)
{
if (moving) {
Ellipse el = sender as Ellipse;
var x = e.GetPosition(null).X;
var y = e.GetPosition(null).Y;
Canvas.SetLeft(el, Canvas.GetLeft(el) + x - lastX);
Canvas.SetTop(el, Canvas.GetTop(el) + y - lastY);
lastX = x;
lastY = y;
}
}
}
}


Figure 2. Drag and drop with Silverlight


Other  
 
PS4 game trailer XBox One game trailer
WiiU game trailer 3ds game trailer
Top 10 Video Game
-   Minecraft Mods - MAD PACK #10 'NETHER DOOM!' with Vikkstar & Pete (Minecraft Mod - Mad Pack 2)
-   Minecraft Mods - MAD PACK #9 'KING SLIME!' with Vikkstar & Pete (Minecraft Mod - Mad Pack 2)
-   Minecraft Mods - MAD PACK #2 'LAVA LOBBERS!' with Vikkstar & Pete (Minecraft Mod - Mad Pack 2)
-   Minecraft Mods - MAD PACK #3 'OBSIDIAN LONGSWORD!' with Vikkstar & Pete (Minecraft Mod - Mad Pack 2)
-   Total War: Warhammer [PC] Demigryph Trailer
-   Minecraft | MINIONS MOVIE MOD! (Despicable Me, Minions Movie)
-   Minecraft | Crazy Craft 3.0 - Ep 3! "TITANS ATTACK"
-   Minecraft | Crazy Craft 3.0 - Ep 2! "THIEVING FROM THE CRAZIES"
-   Minecraft | MORPH HIDE AND SEEK - Minions Despicable Me Mod
-   Minecraft | Dream Craft - Star Wars Modded Survival Ep 92 "IS JOE DEAD?!"
-   Minecraft | Dream Craft - Star Wars Modded Survival Ep 93 "JEDI STRIKE BACK"
-   Minecraft | Dream Craft - Star Wars Modded Survival Ep 94 "TATOOINE PLANET DESTRUCTION"
-   Minecraft | Dream Craft - Star Wars Modded Survival Ep 95 "TATOOINE CAPTIVES"
-   Hitman [PS4/XOne/PC] Alpha Gameplay Trailer
-   Satellite Reign [PC] Release Date Trailer
Video
programming4us
 
 
programming4us