SharpGIS

#GIS from a .NET developer's perspective

Add MouseWheel scrolling to Silverlight's DataGrid

In my previous post, I wrote about how you could add MouseWheel scrolling to the ScrollViewer using a simple behavior. I wanted to have the same MouseWheel scroll on the DataGrid, and thought that it could be done the exact same way.

However, it turns out that the DataGrid is not using a ScrollViewer, but instead used a Virtualized panel that is re-rendered every time a ScrollBar is scrolled. Unfortunately the DataGrid only reacts to the Scroll event of the ScrollBar, and not it’s Value property (not sure why they did it like this), so it’s not just a matter of finding the ScrollBar and increase the value. If I did that, the ScrollBar would move, but the DataGrid would not update. In fact the only way to raise the Scroll event was to click the ScrollBar, but I wanted to do this in code so I can’t really “click” something.

Fortunately Silverlight has a UIAutomation framework, that allows you to simulate user interaction. This is great for running automated tests against your controls, where you want to simulate a user clicking and typing inside your application. This got me thinking that the way to raise the Scroll event was simply to use the UIAutomation framework to virtually ‘click’ the ScrollBar. So in other words, each time you scroll down, the code ‘clicks’ the down button in the ScrollBar, and vice versa. While this simulated click approach works well, there’s an even simpler way, since the DataGrid’s Automation allows for calling scroll, which is what I’ll be using here.

The first step is to get a reference to the AutomationPeer of the DataGrid:

AutomationPeer automationPeer = FrameworkElementAutomationPeer.FromElement(this.AssociatedObject);

Since the automation peer for the DataGrid implements the IScrollProvider pattern, we can simply request that pattern and call Scroll() to scroll the DataGrid:

IScrollProvider scrollProvider = automationPeer.GetPattern(PatternInterface.Scroll) as IScrollProvider;
ScrollProvider.Scroll(ScrollAmount.NoAmount, ScrollAmount.SmallIncrement);

The rest is trivial for the DataGrid MouseWheel behavior. On the MouseWheel event of the DataGrid we call the Scroll method with either a SmallIncrement or SmallDecrement, and the DataGrid will scroll one row. It’s not as smooth as the ScrollViewer implementation in the previous blogpost, but at least it allows us to use the MouseWheel to scroll the rows. Since most scrollable controls in Silverlight already has UIAutomation and IScrollProvider support, this behavior will also work on other scrollable controls, and not only the DataGrid.

So here’s what it looks like in XAML when you included a reference to the behavior assembly:

<data:DataGrid x:Name="datagrid" Height="100" >
	<i:Interaction.Behaviors>
		<behaviors:MouseScrollBehavior/>
	</i:Interaction.Behaviors>
</data:DataGrid>

I’ve updated the source code for my Behavior library, and also updated the sample application to include the DataGrid.

Comments (12) -

  • David

    9/23/2009 8:14:13 AM |

    Perfect!  Works great and helped a lot!

  • Toolshed

    10/13/2009 2:25:02 PM |

    Is there any way to include this interaction behavior in a style for the data grid?

  • Morten

    10/13/2009 5:12:32 PM |

    Toolshed: Didn't you read the last two lines in my blogpost? The sample app already demonstrates this behavior in action on the datagrid.

  • Toolshed

    10/14/2009 7:57:30 AM |

    Yes, I see where you are using the behavior with a specific instance of a DataGrid. I want to include the behavior in a style so I don't have to define this behavior for every instance of DataGrid.

    <Style x:Key="DataGridStyle" TargetType="dataLaughingataGrid">
        <!-- Put the behavior somewhere in here -->
    </Style>

    Can this be done?

  • Morten

    10/14/2009 9:44:16 AM |

    Toolshed: You can't set a behavior in a style (mostly because its an attached behavior type that doesn't have a public constructor for the BehaviorCollection). You could create your own custom attached property that you set in a style, that in turn adds the behavior to the behavior collection.

  • Britney Spears

    10/19/2009 1:00:07 AM |

    Hi Morten,

    That is quite a nice solution for supporting the mouse wheel. Thanks for sharing your code!

    Yours,
    Britney

  • Brian

    10/28/2009 6:23:22 AM |

    I am creating datagrids pragmatically... how would I attach your behavior class in code?

  • Bob

    10/28/2009 7:52:24 AM |


    System.Windows.Interactivity.Interaction.GetBehaviors(MyDataGrid).Add(new MouseWheelBehavior());

  • Manish

    10/29/2009 1:14:24 AM |

    Hi Morten,
    I need a help.iam new to silverlight.I have a grid in my form and has few rows and columns of textboxes.
    If the column exceeds the width of the datagrid the scrollbar automatically comes,But when i use tab control in the grid it does jump to the next Column of the grid but the scrollbar does not sizes according to the position of the column..

    So could u pls help me out
    Thanks

  • Toolshed

    11/18/2009 9:08:49 AM |

    I'm getting an exception when using this attached behavior while grouping is enabled on the datagrid. It's so bad, I've had to remove the behavior. If I get the time, I'll try and debug to see what the issue is.

    A first chance exception of type 'System.ArgumentOutOfRangeException' occurred in mscorlib.dll
       at System.ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument argument, ExceptionResource resource)
       at System.Collections.Generic.List`1.Insert(Int32 index, T item)
       at System.Windows.Controls.DataGridDisplayData.LoadScrollingSlot(Int32 slot, UIElement element, Boolean updateSlotInformation)
       at System.Windows.Controls.DataGrid.InsertDisplayedElement(Int32 slot, UIElement element, Boolean wasNewlyAdded, Boolean updateSlotInformation)
       at System.Windows.Controls.DataGrid.InsertDisplayedElement(Int32 slot, Boolean updateSlotInformation)
       at System.Windows.Controls.DataGrid.GetExactSlotElementHeight(Int32 slot)
       at System.Windows.Controls.DataGrid.UpdateDisplayedRows(Int32 newFirstDisplayedSlot, Double displayHeight)
       at System.Windows.Controls.DataGrid.ScrollSlotIntoView(Int32 slot, Boolean scrolledHorizontally)
       at System.Windows.Controls.DataGrid.ProcessVerticalScroll(ScrollEventType scrollEventType)
       at System.Windows.Automation.Peers.DataGridAutomationPeer.System.Windows.Automation.Provider.IScrollProvider.Scroll(ScrollAmount horizontalAmount, ScrollAmount verticalAmount)

  • Felix De Light

    12/8/2009 12:27:19 AM |

    it's not work to me....
    can you explain it....so confused...

  • Morten

    12/8/2009 5:40:18 PM |

    Felix De Light: I thought I did in the blogpost Smile

    Could you be more specific what your problem is? Do you get any errors? (if so what?). Do you have all the correct assembly references added?
    Maybe you should start with first getting the built-in behaviors that come with Blend to work. I'm sure that when you get that far, you will know how to use this behavior as well.

Comments are closed