Silverlight : Enable Mouse Wheel on Slider with Behaviors
Custom Behaviors to enable MouseWheel support to Silverlight Slider and bit more
You might need to unblock some DLLs in:
- MouseWheelApp\MouseWheelApp\Bin\Debug
- MouseWheelApp\MouseWheelBehaviorLibrary\Bin\Debug
Live App
Kudos to Adefwebserver for hosting my app
Introduction
This article will walk through the creation of 2 simple behaviors
to enable MouseWheel
support for the Slider
which Silverlight lacks as of this moment. This will also be a simple introduction to behaviors
.
The Problem
The Silverlight Slider
control does not have MouseWheel
support. There is an event MouseWheel
that you can listen to, but it will not expose the Delta
value which tells whether the wheel was scrolled up or down in Blend. One way to add mouse support to your Slider
is by using code behind which will not give designers freedom or be as reusable compared to a behavior.
Tried using ChangePropertyAction
, but there is no way to get Mouse Values. This limits designers' ability to do their jobs.
My Solution
I have made 2 different behaviors
that will enable MouseWheel
support and these behaviors
use properties set on the Slider
for some flexibility. The properties are: Maximum
, Minimum
, SmallChange
, and Value
. The purpose is to allow designers to set boundaries, and how much the value should increment when mouse wheel is scrolled.
Beyond the Slider
While this is a behavior
for the Slider
control, this can be used for more. Because the slider
contains properties to set Maximum
, Minimum
, and SmallChange
, this is perfect for binding your properties to and setting up boundaries. This was originally designed for allowing MouseWheel
support to whatever it associates (that has numeric property and boundaries) so it can be used for setting up other controls with MouseWheel
though some bindings are required. It also avoids code behind by the use of behaviors
. For example, a volume control on a video player where you can bind the value of the volume to Slider
's value, and you associate my behavior to the video player so a user can adjust volume by MouseWheel
when mouse is above the player. Or maybe resize an image or window by MouseWheel
and hide the slider
.
Basic Sample
Before I go dig into the code, I would like to give an overview of what it can do so you can decide whether you would like to go on further.
In this app which we will be creating in the course of the article, we have a rectangle
, and 3 sliders
. Each of them has a simple behavior
that is attached to the slider
to enable mouseWheel
support. In addition to this, I have another Behavior
that is attached to the Rectangle
which will respond to mouseWheel
events when the mouse is on the Rectangle
. This behavior
will change one of the Slider
's value.
The Code
Start by creating a new Silverlight application and name it something like MouseWheelApp
, this will be the app that will use the behaviors
. Go ahead and select Host the Silverlight Application in a new website.
Now add a new project and select Silverlight Class Library and name it MouseWheelBehaviorLibrary
.
To the MouseWheelBehaviorLibrary
, add a reference to System.Windows.Interactivity. This DLL is from Microsoft Expression Blend SDK.
In the MouseWheelBehaviorLibrary
project, add a new class and name it EnableSliderMouseWheel
.
EnableSliderMouseWheel.cs
- Add using statement:
using System.Windows.Interactivity;
- Inherit class from:
public class EnableSliderMouseWheel : Behavior<Slider>
The class is inheriting from the
Behavior
and setting the generic to aSlider
control. By setting the type to aSlider
, it will restrict what thisbehavior
can attach to. For thisbehavior
, I would like to make a simple one that just enablesMouseWheel
support on attach, or for the designer drop on to it. - Now paste the following code:
protected override void OnAttached() { base.OnAttached(); //attach event handler on mouse wheel event AssociatedObject.MouseWheel += new MouseWheelEventHandler(SliderMouseScroll_MouseWheel); }
This code runs when the
behavior
is "attached" to the control.AssociatedObject
represents what it is attached to, in our case aSlider
control. We are adding an event handler to theMouseWheel
Event. (Code will error at this point, I will show theSliderMouseScroll_MouseWheel
method later.) Basically what is happening is once thebehavior
attaches to theslider
, it is adding the event handler to manipulate theslider
values. - Paste the following code again:
protected override void OnDetaching() { base.OnDetaching(); //remove event handler on mouse wheel event AssociatedObject.MouseWheel -= new MouseWheelEventHandler(SliderMouseScroll_MouseWheel); }
This is the opposite of the code above, when this is detached it will remove the event handler that it attached.
- Now paste the meat of the behavior, the method that gets fired when Mouse is scrolled.
void SliderMouseScroll_MouseWheel(object sender, MouseWheelEventArgs e) { //Delta = how much the wheel was scrolled. + for up - for down //check to see if Delta is positive. if (e.Delta > 0) { //using small change of Target(slider) property. //this allows how much value can change from the designer // //alternatively you can actually use the delta value instead //of forcing small change increments. AssociatedObject.Value = AssociatedObject.Value + AssociatedObject.SmallChange; } else //same as above just backwards { //using small change of Target(slider) property. //this allows how much value can change from the designer // //alternatively you can actually use the delta value instead //of forcing small change increments. AssociatedObject.Value = AssociatedObject.Value - AssociatedObject.SmallChange; } }
First I check for the Delta
value, this tells how much the mouse
was scrolled and direction. If it's positive it was scrolled up, and negative if it was scrolled down. I only use the Delta
to check the direction. Next I get the value of the slider
and add or subtract the slider
's SmallChange
value. By using the SmallChange
, I allow designers to set how much it should change.
I do not have any checks to see if it will go over or less than the boundaries, this is because it already has these checks built in.
Our First Behavior is complete. Now we will start making the example shown above.
App to Use the Behavior
The next step is to set up an app that will consume this behavior.
- Add reference to the DLL from the previous step or
MouseWheelBehaviorLibrary
. Just go to the bin directory of theMouseWheelBehaviorLibrary
project and selectMouseWheelBehaviorLibrary
.dll
. - Open the MainPage.xaml from the
MouseWheelApp
inBlend
. (right click and select Open in Expression Blend)Make sure we have the
Behavior
. - Re-create a UI similar to this (Rectangle, 3 sliders, textblock as labels). Make sure the values of
rectangle's
value are not set toAuto
, which will not allow binding in the next step. The sliders and labels is in a grid. - Bind the
Slider
Values to theRectangle
Properties. Make sure that itstwo-way
binding. Next Set theMaximum
,Minimum
andSmallChange
to what suits your project. NoteOpacity
Maximum
should be 1. - Attach the
Behavior
we have created to theSliders
and run. Now when you scroll your mouse on top of theSlider
, it will change the value. (Just drag and drop thebehavior
on to theSliders
.)
The More Flexible Behavior
The next one takes it a step further. This one will be able to attach to any control and will respond to MouseWheel
when the mouse is on the attached control. As explained earlier, this can be used to add MouseWheel
support to other controls. (Because it's almost identical, I will breeze through some details explained in the previous one.)
- Like the previous
behavior
, we will need a new class in our library. Add a new class called EnableMouseWheel.cs. Addusing
:using System.Windows.Interactivity;
- Inherit from
TargetedTriggerAction<slider><T>
, useSlider
forT
. Also Implement theInvoke
method or copy the method.public class EnableMouseWheel : TargetedTriggerAction<Slider> { //this method is required for the base class protected override void Invoke(object parameter) { } }
The
Invoke
method here we will do nothing with. This type ofbehavior
will attach to anything, and thegeneric
<T> is where you can specify a target class. TheTarget
is used for accessing another control. For ours, we will set it toSlider
so whenMouseWheel
was scrolled, theBehavior
will change the value of theTarget
'Slider
'. - Copy the following code:
protected override void OnAttached() { base.OnAttached(); //add event handler to mouse wheel event ((FrameworkElement)AssociatedObject).MouseWheel += new MouseWheelEventHandler(SliderMouseScroll_MouseWheel); } protected override void OnDetaching() { base.OnDetaching(); //remove the event handler ((FrameworkElement)AssociatedObject).MouseWheel -= new MouseWheelEventHandler(SliderMouseScroll_MouseWheel); } //this method runs on Mouse Wheel event void SliderMouseScroll_MouseWheel(object sender, MouseWheelEventArgs e) { //Delta = how much the wheel was scrolled. + for up - for down //check to see if Delta is positive. if (e.Delta > 0) { //using small change of Target(slider) property. //this allows how much value can change from the designer // //alternatively you can actually use the delta value instead //of forcing small change increments. Target.Value = Target.Value + Target.SmallChange; } else //same as above just backwards { //using small change of Target(slider) property. //this allows how much value can change from the designer // //alternatively you can actually use the delta value instead //of forcing small change increments. Target.Value = Target.Value - Target.SmallChange; } }
This is basically the same code, but with a few changes. When we attach, we will cast it to a
FrameworkElement
which is the base for all controls. We need to cast it because it doesn't know what type it really is. Next is in the event handler where we are using aTarget
instead ofAssociatedObject
, this is because we want to edit theSlider
's value which is theTarget
instead of what we attached to (AssociatedObject
). - This is an optional step. If we look at the screenshot when we checked to see if we had the
Behavior
, you might have noticed there was no explanation of what thebehavior
is. Here we will add that:[System.ComponentModel.Description("Target : Trigger. Responds to mousewheel event of attached object when focused. Manipulates slider values based on slider properties.")] public class EnableMouseWheel : TargetedTriggerAction<Slider> {
Add the
attribute
. Build and go back to the Mainpage.xaml inBlend.
Using the Behavior
- Check to see that we have the Behavior and the description:
- Attach the
Behavior
to theRectangle
and in the properties of theBehavior
, set theTarget
to one of thesliders
. - Now Run and when you scroll on the Rectangle, it should change the slider. Now you are done.
Full Code
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
namespace MouseWheelBehaviorLibrary
{
[System.ComponentModel.Description
("Target : Trigger. Responds to mousewheel event of attached object
when focused. Manipulates slider values based on slider properties.")]
public class EnableMouseWheel : TargetedTriggerAction<Slider>
{
//this method is required for the base class
protected override void Invoke(object parameter)
{
}
protected override void OnAttached()
{
base.OnAttached();
//add event handler to mouse wheel event
((FrameworkElement)AssociatedObject).MouseWheel +=
new MouseWheelEventHandler(SliderMouseScroll_MouseWheel);
}
protected override void OnDetaching()
{
base.OnDetaching();
//remove the event handler
((FrameworkElement)AssociatedObject).MouseWheel -=
new MouseWheelEventHandler(SliderMouseScroll_MouseWheel);
}
//this method runs on Mouse Wheel event
void SliderMouseScroll_MouseWheel(object sender, MouseWheelEventArgs e)
{
//Delta = how much the wheel was scrolled. + for up - for down
//check to see if Delta is positive.
if (e.Delta > 0)
{
//using small change of Target(slider) property.
//this allows how much value can change from the designer
//
//alternatively you can actually use the delta value instead
//of forcing small change increments.
Target.Value = Target.Value + Target.SmallChange;
}
else //same as above just backwards
{
//using small change of Target(slider) property.
//this allows how much value can change from the designer
//
//alternatively you can actually use the delta value instead
//of forcing small change increments.
Target.Value = Target.Value - Target.SmallChange;
}
}
}
}
/////
/////
/////
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interactivity;
namespace MouseWheelBehaviorLibrary
{
public class EnableSliderMouseWheel : Behavior<Slider>
{
protected override void OnAttached()
{
base.OnAttached();
//attach event handler on mouse wheel event
AssociatedObject.MouseWheel += new MouseWheelEventHandler
(SliderMouseScroll_MouseWheel);
}
protected override void OnDetaching()
{
base.OnDetaching();
//remove event handler on mouse wheel event
AssociatedObject.MouseWheel -= new MouseWheelEventHandler
(SliderMouseScroll_MouseWheel);
}
void SliderMouseScroll_MouseWheel(object sender, MouseWheelEventArgs e)
{
//Delta = how much the wheel was scrolled. + for up - for down
//check to see if Delta is positive.
if (e.Delta > 0)
{
//using small change of Target(slider) property.
//this allows how much value can change from the designer
//
//alternatively you can actually use the delta value instead
//of forcing small change increments.
AssociatedObject.Value = AssociatedObject.Value +
AssociatedObject.SmallChange;
}
else //same as above just backwards
{
//using small change of Target(slider) property.
//this allows how much value can change from the designer
//
//alternatively you can actually use the delta value instead
//of forcing small change increments.
AssociatedObject.Value = AssociatedObject.Value -
AssociatedObject.SmallChange;
}
}
}
}
Notes
There are some compatibilty issues I have found with the EnableMouseWheel
Behavior
which are primarily around controls that already have some MouseWheel
support. Combobox
seems to work only if selected index is -1
and things like the listbox
seem to work once it has reached the end of the list.
For more information on Behaviors, please see Silverlight Show.
Thanks for reading and I hope this was somewhat useful for you. This is my first article, so I would appreciate any feedback on the article or the code. This is my first article on CodeProject.
History
- 7th July, 2010: Initial post