Page view counter

Silverlight Tips of the Day - Blog by Mike Snow

Game Programming with Silverlight

Silverlight Tip of the Day #65 – Adding a Mouse Wheel Event Listener to your Controls

In Tip of the Day #23 I showed you how to capture the mouse wheel event. In this tip we will take it one step further by implementing a IMouseWheelObserver interface that your Silverlight elements and controls can inherit from. This way, anytime the mouse wheel is used over your control, your control will be notified.

The following code is the interface declaration for IMouseWheelObserver:

public interface IMouseWheelObserver
{
    void OnMouseWheel(MouseWheelArgs args);
    event MouseEventHandler MouseEnter;
    event MouseEventHandler MouseLeave;
}

The OnMouseWheel() event passes the following EventArgs when the event is fired for your control. These args:

  1. Track the Shift, Ctrl and Alt key combinations.
  2. Track the delta in change made by the mouse wheel scoll.
public class MouseWheelArgs : EventArgs
{
    private readonly double
        _Delta;
 
    private readonly bool
        _ShiftKey,
        _CtrlKey,
        _AltKey;
 
 
    public double Delta
    {
        get { return this._Delta; }
    }
 
    public bool ShiftKey
    {
        get { return this._ShiftKey; }
    }
 
    public bool CtrlKey
    {
        get { return this._CtrlKey; }
    }
 
    public bool AltKey
    {
        get { return this._AltKey; }
    }
 
    public MouseWheelArgs(double delta, bool shiftKey, bool ctrlKey, bool altKey)
    {
        this._Delta = delta;
        this._ShiftKey = shiftKey;
        this._CtrlKey = ctrlKey;
        this._AltKey = altKey;
    }
}

Next, let’s take a look at the implementation of the WheelMouseListener class. In this class we:

  1. Call the HTMLPage methods to attach to the mouse scroll events on create.
  2. Call the HTMLPage methods to detach the mouse scroll events when the class is destroyed.
  3. Declare a element stack. We keep on the top of a stack the element that the mouse is currently over. This way we know which element to call when the mouse scroll event has occured.
public class WheelMouseListener
{
    private Stack<IMouseWheelObserver> _ElementStack;
 
    private WheelMouseListener()
    {
        this._ElementStack = new Stack<IMouseWheelObserver>();
 
        HtmlPage.Window.AttachEvent("DOMMouseScroll", OnMouseWheel);
        HtmlPage.Window.AttachEvent("onmousewheel", OnMouseWheel);
        HtmlPage.Document.AttachEvent("onmousewheel", OnMouseWheel);
 
        Application.Current.Exit += new EventHandler(OnApplicationExit);
    }
 
    /// <summary>
    /// Detaches from the browser-generated scroll events.
    /// </summary>
    private void Dispose()
    {
        HtmlPage.Window.DetachEvent("DOMMouseScroll", OnMouseWheel);
        HtmlPage.Window.DetachEvent("onmousewheel", OnMouseWheel);
        HtmlPage.Document.DetachEvent("onmousewheel", OnMouseWheel);
    }
 
    public void AddObserver(IMouseWheelObserver element)
    {
        element.MouseEnter += new MouseEventHandler(OnElementMouseEnter);
        element.MouseLeave += new MouseEventHandler(OnElementMouseLeave);
    }
 
    private void OnMouseWheel(object sender, HtmlEventArgs args)
    {
        double delta = 0;
        ScriptObject e = args.EventObject;
 
        if (e.GetProperty("detail") != null)
        {
            // Mozilla and Safari
            delta = ((double)e.GetProperty("detail"));
        }
        else if (e.GetProperty("wheelDelta") != null)
        {
            // IE and Opera
            delta = ((double)e.GetProperty("wheelDelta"));
        }
 
        delta = Math.Sign(delta);
 
        if (this._ElementStack.Count > 0)
            this._ElementStack.Peek().OnMouseWheel(new MouseWheelArgs(delta, args.ShiftKey, args.CtrlKey, args.AltKey));
    }
 
    private void OnElementMouseLeave(object sender, MouseEventArgs e)
    {
        this._ElementStack.Pop();
    }
 
    private void OnElementMouseEnter(object sender, MouseEventArgs e)
    {
        this._ElementStack.Push((IMouseWheelObserver)sender);
    }
 
    private void OnApplicationExit(object sender, EventArgs e)
    {
        this.Dispose();
    }
 
    private static WheelMouseListener _Instance = null;
 
    public static WheelMouseListener Instance
    {
        get
        {
            if (_Instance == null)
            {
                _Instance = new WheelMouseListener();
            }
 
            return _Instance;
        }
    }
}

There are three modifications you need to make to your controls:

  1. Inherit from the IMouseWheelObserver interface
  2. Enlist in the event listener by calling WheelMouseListener.Instance.AddObserver(this); from your controls class constructor.
  3. Add the method OnMouseWheel() to your control which will be called when the event is fired.

Example:

public partial class Toolbar : UserControl, IMouseWheelObserver
{
    public Toolbar()
    {
        InitializeComponent();
 
        WheelMouseListener.Instance.AddObserver(this);
    }
 
    public void OnMouseWheel(MouseWheelArgs args)
    {
        // Process the event here...
    }
}

Finally, I would like to thank Laith Alasad (Laith.Alasad@hyro.com) for this idea and the code contribution above to my original Mouse Wheel code!

Thank you,
--Mike Snow

 Subscribe in a reader

Comments

Microsoft Weblogs said:

In Tip of the Day #23 I showed you how to capture the mouse wheel event. In this tip we will take it

# October 22, 2008 2:45 PM

2008 October 23 - Links for today « My (almost) Daily Links said:

Pingback from  2008 October 23 - Links for today &laquo; My (almost) Daily Links

# October 23, 2008 5:08 AM

Dew Drop - October 23, 2008 | Alvin Ashcraft's Morning Dew said:

Pingback from  Dew Drop - October 23, 2008 | Alvin Ashcraft's Morning Dew

# October 23, 2008 8:23 AM

Mouse Wheel Events at Blog von J??rgen Ebner said:

Pingback from  Mouse Wheel Events at Blog von J??rgen Ebner

# October 23, 2008 4:52 PM

Laith28 said:

This works great, but fails in fullscreen mode. Any ideas? Anyone?

# October 23, 2008 7:09 PM

Silverlight news for October 24, 2008 said:

Pingback from  Silverlight news for October 24, 2008

# October 24, 2008 1:35 AM

Community Blogs said:

In this issue: Cote, Ivan Dragoev, Hannah Watkins, Fons Sonnemans, Bart Czernicki, Jeff Prosise, Dave

# October 24, 2008 9:38 AM

Joseph G. said:

In full screen mode, events are disabled in silverlight. ( Security reasons )

# October 26, 2008 9:13 AM

Visual Web Developer Team Blog said:

Silverlight Tip of the Day #66 Title: How to copy XAML for Silverlight from Expression Designer Silverlight

# November 3, 2008 1:40 PM

Silverlight Tips of the Day - Blog by Mike Snow said:

The purpose of this post is to create an outline summary all the blogs from my Silverlight tips of the

# January 2, 2009 5:56 PM

Silverlight Tips of the Day - Blog by Mike Snow said:

The purpose of this post is to create an outline summary all the blogs from my Silverlight tips of the

# January 2, 2009 5:56 PM

o UAU nosso de cada dia said:

essa lista eu copiei desse blog bárbaro (acompanhe por RSS você também): uma lista de dicas super úteis

# January 3, 2009 6:25 AM

SharpGIS said:

This approach doesn't work very well with Chrome. With all the event listeners hooked up, Chrome will fire the mouse wheel event twice.

# April 14, 2009 1:54 PM

GearWorld said:

Now All I need is how it works.  

I tried this in the

public void OnMouseWheel(GearObjects.MouseWheelArgs args)

{

   svMainMenu.ScrollToVerticalOffset(-args.Delta * 100);

}

To be able to move something but it moves once and that's all.  Everything is in place and at least something move with the mouse wheel but not correcly

Thank you

# June 24, 2009 6:29 PM

GearWorld said:

I also tried this :

svMainMenu.ScrollToVerticalOffset(-args.Delta * svMainMenu.ScrollableHeight / spMainMenu.Children.Count);

Since I have a Stack panel inside a ScrollViewer

# June 24, 2009 6:37 PM

GearWorld said:

Ok finally this did the work :

svMainMenu.ScrollToVerticalOffset(-args.Delta * (svMainMenu.ScrollableHeight / spMainMenu.Children.Count) + svMainMenu.VerticalOffset);

# June 24, 2009 6:46 PM