I wrote about Silverlight and the mouse(wheel) before and the responses are pretty good. One question that stuck to my brain and has been asked in forums is how to actually have controls like the ListBox respond to the MouseWheel event.
First some code
After decorating a ComboBox like this:
1: <m:MouseSupport MouseWheel="ComboBoxOnMouseWheel">
2: <ComboBox>
3: ...
4: </ComboBox>
5: </m:MouseSupport>
It is pretty easy to add this code:
1: private void ComboBoxOnMouseWheel(object sender, SilverlightMouseSupport.MouseWheelEventArgs e)
2: {
3: ComboBox comboBox = sender as ComboBox;
4: if (comboBox != null)
5: {
6: if (e.Delta > 0 && comboBox.SelectedIndex > 0)
7: {
8: comboBox.SelectedIndex--;
9: }
10: else if (e.Delta < 0 && comboBox.SelectedIndex < comboBox.Items.Count -1)
11: {
12: comboBox.SelectedIndex++;
13: }
14: }
15: }
But as you will notice when testing this code the ComboBox only responds to the wheel when it is collapsed. I tried adding the decorator to the ItemsPanel but that froze the entire Silverlight application. I see no solution for that.
On to the ListBox
To make a control that uses the ScrollViewer to scroll its content you need to access the ScrollViewer that is in the template of the control. For some reason the GetTemplateChild method was made protected so the only way to get it you need to sub class the control and add a little code:
1: public class ListBox : System.Windows.Controls.ListBox
2: {
3: public ScrollViewer ScrollViewer
4: {
5: get
6: {
7: return GetTemplateChild("ScrollViewer") as ScrollViewer;
8: }
9: }
10: }
This ListBox now has a ScrollViewer property that can be used in code like this:
1: private void ListBoxOnMouseWheel(object sender, SilverlightMouseSupport.MouseWheelEventArgs e)
2: {
3: //note: this is a special ListBox, not a standard Silverlight listbox.
4: ListBox listBox = sender as ListBox;
5: double scrollStep = 10;
6: if (listBox != null)
7: {
8: ScrollViewer scrollViewer = listBox.ScrollViewer;
9: if (scrollViewer != null)
10: {
11: if (e.Delta > 0 && scrollViewer.VerticalOffset > 0)
12: {
13: scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - e.Delta * scrollStep);
14: }
15: else if (e.Delta < 0 && scrollViewer.VerticalOffset < scrollViewer.ScrollableHeight)
16: {
17: scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - e.Delta * scrollStep);
18: }
19: }
20: }
21: }
I have tried to encapsulate this behavior into the ListBox class itself but the problem I ran into is that to make it easy to use I need a way of manipulating the template of the ListBox to add the decorator. It is easy to replace an entire template of a control but I wanted to add the decorator to the existing template in runtime. That is not possible as far as I can see.
I updated the download with this code so you can test and try for yourself.
Concluding
It looks like a hack. And it is. To get this working we need the Silverlight Base Control developers to help us out on this.