Tuesday, October 14, 2008

SilverLight 2.0 mouse wheel scrolling work-around

I'm new to the Silverlight experience and the potential is clear (the WPF similarity is a winning concept). I've encountered the scrolling issue and i can't say i get it. I want to share a straight forward and naive solution I've scribbled.
Accepting the Mouse wheel events
The first step is to register to the HtmlPage.Window events for all browser types.
HtmlPage.Window.AttachEvent("DOMMouseScroll", OnMouseScroll); // firefox
HtmlPage.Window.AttachEvent("onmousewheel", OnMouseScroll);
HtmlPage.Document.AttachEvent("onmousewheel", OnMouseScroll); // ie
Create the mouse scroll event handler
This method is invoked when the events registered above. It expects a m_scroller (ScrollViewer instance) to be set correctly (we will set it in the next part) and invokes the ScrollToVerticalOffset method.
/// Mouse scroll handler
private void OnMouseScroll(object sender, HtmlEventArgs args)
{
double delta = 0;
ScriptObject e = args.EventObject; // safari & firefox
if (e.GetProperty("detail") != null)
{
delta = ((double)e.GetProperty("detail"))* -100;
}
else if (e.GetProperty("wheelDelta") != null) // ie && Opera
{
delta = ((double)e.GetProperty("wheelDelta"));
}
//if scroller is recognized update it
if (m_scroller != null)
{
m_scroller.ScrollToVerticalOffset(m_scroller.VerticalOffset + delta * -1 * 0.1);
} }
Register a control to enable mouse wheel scrolling
We need to register to the only the GotFocus event of the Page.xaml control to enable the scrolling. The Register method encapsulates the action.

/// Register the top most control (page.xaml) only to activate scrolling
public void Register(FrameworkElement element)
{
if (element != null)
{
element.GotFocus += new RoutedEventHandler(OnGotFocus);
} }

Impliment OnGotFocus event handler
In this part we will manage the m_scroller instance when needed in a recursive manner. This way we will allways effect the relevant hosting ScrollViewer of the focused control.

/// GotFocus event handler
private void OnGotFocus(object sender, RoutedEventArgs e)
{
lock (this)
{
m_scroller = CheckParent((FrameworkElement)e.OriginalSource);
}}

/// Recursive method to update the correct scrollviewer (if exists)
private ScrollViewer CheckParent(FrameworkElement element)
{
ScrollViewer _result = element as ScrollViewer;
if (element != null && _result ==null)
{
FrameworkElement _temp = element.Parent as FrameworkElement;
_result= CheckParent(_temp);
}
return _result;
}

Create MouseWheelController
We will create a simple singletone controller to manage the state of the current ScrollViewer.
/// MouseWheelControler class
public class MouseWheelControler
{
private ScrollViewer m_scroller;
public static MouseWheelControler Instance { get; protected set; }
static MouseWheelControler()
{
Instance = new MouseWheelControler();
}
///
/// register to the html events for all browsers
///

public MouseWheelControler()
{
HtmlPage.Window.AttachEvent("DOMMouseScroll", OnMouseScroll); // firefox
HtmlPage.Window.AttachEvent("onmousewheel", OnMouseScroll);
HtmlPage.Document.AttachEvent("onmousewheel", OnMouseScroll); // ie
}...
Connecting the pieces
All we need to do at this part is register the relevant page (Page.xaml) to our MouseWheelController.
///Page.xaml constructor
public Page()
{
InitializeComponent();
MouseWheelControler.Instance.Register(this);
}


Hope it makes sence
Gilad

1 comment:

Zain said...

hie, i used the same code,, but i am still unable to get scrollviewer work... :(
any help...