Deep Zoom Demo now with Mouse Wheel Support

I've updated my Deep Zoom demo to add mouse wheel support for zooming. There is no way to capture mouse wheel events in managed code in Silverlight 2 so I tried a couple of approaches. Firstly I tried to attach a managed event handler to the DOMMouseScroll event on the HTML DOM's window object. I didn't get very far with this - I got the HTML bridge stuff working okay (ie registering a scriptable object via HtmlPage.RegisterScriptableObject() and attributing up my class and methods with ScriptableType and ScriptableMember respectively) but the DOMMouseScroll event never seemed to fire.

I then turned to trusty old JavaScript and, with the help of Jeff Prosise and the documentation managed to get that working. Note that I think there are a couple of errors in the current documentation. In "Calling Managed Code from Client Script" they talk about adding the ScriptableMember attribute but I couldn't see mention of adding the ScriptableType attribute to your class. I needed to do this to get the HTML bridge stuff working (above). Then on the JavaScript side, they have some sample code in the documentation:

 <script type="text/javascript">
var ajaxCtl = null;
function pluginLoaded(sender){
    ajaxCtl  = sender.get_element();
    alert(ajaxCtl.Content.mySLapp.MyToUpper("Test String"));
}
</script>

Where they say sender.get_element() I think that should be sender.GetHost().

On the managed code side, all I did was add the following to my Page constructor:

 HtmlPage.RegisterScriptableObject("slApp", this);

And attribute up the Page class and relevant methods eg

 [ScriptableType()]
public partial class Page : UserControl
{

And I added a Zoom method I could call without passing a zoom origin:

 [ScriptableMember()]
public void Zoom(double ZoomFactor)
{
    Point P = new Point(0.5, 0.5);
    this.msi.ZoomAboutLogicalPoint(ZoomFactor, P.X, P.Y);
}

For the JavaScript, I hooked up the onLoad event on my plug-in and added the following:

 var ajaxCtl = null;
function pluginLoaded(sender) {
        ajaxCtl  = sender.GetHost();
        if (window.addEventListener) {
            window.addEventListener('DOMMouseScroll', OnMouseWheelTurned, false);
        }
        
        window.onmousewheel = document.onmousewheel = OnMouseWheelTurned;
}

function OnMouseWheelTurned(event) {
    var delta = 0;

        if (!event) {
            event = window.event;
        }
 
        if (event.wheelDelta) {
            delta = event.wheelDelta;

            if (window.opera) {
                delta = -delta;
            }
            else if (event.detail) {
                delta = -event.detail;
            }

            if (delta) {

                if (delta > 0) {
                    ajaxCtl.Content.slApp.Zoom(1.1);
                }
                else {
                    ajaxCtl.Content.slApp.Zoom(0.9);
                }
            }
        }

        if (event.preventDefault) {
            event.preventDefault();
        }
        event.returnValue = false;
}

The bulk of which comes pretty much verbatim from Jeff's post. Note the call the the Zoom() managed method (ajaxCtl.Content.slApp.Zoom()) from the JavaScript event handler.

The finished result is here.

Update: I've just noticed that my zooming isn't quite right as I don't take account of an offset zoom origin of the image has been panned. So needs a bit more work but it'll have to wait 'til I'm back in from Mix...

UpdateAgain: No it wont, I simply changed the Zoom() method to:

 [ScriptableMember()]
public void Zoom(double ZoomFactor)
{
    msi.ZoomAboutLogicalPoint(ZoomFactor, 
        this.msi.ViewportOrigin.X, this.msi.ViewportOrigin.Y);
}

ie the ViewPortOrigin is the logical place to use as the zoom origin rather than (0.5,0.5) as I had before.

Technorati Tags: silverlight,deep zoom