tags:

views:

321

answers:

2

Hello,
I have a Canvas inside a ScrollViewer. I want to have the user to be able to grab the canvas and move it around, with the thumbs on the scrollbars updating appropriately.
My initial implementation calculates the offset on each mouse move, and updates the scrollbars:

 // Calculate the new drag distance
 Point newOffsetPos = e.GetPosition(MapCanvas);
 System.Diagnostics.Debug.WriteLine("   newOffsetPos : " + newOffsetPos.X + " " + newOffsetPos.Y);

 double deltaX = newOffsetPos.X - _offsetPosition.X ;
 double deltaY = newOffsetPos.Y - _offsetPosition.Y ;

 System.Diagnostics.Debug.WriteLine("   delta X / Y : " + deltaX + " " + deltaY);
 System.Diagnostics.Debug.WriteLine("   sv offsets X / Y : " + _scrollViewer.HorizontalOffset + " " + _scrollViewer.VerticalOffset);

 _scrollViewer.ScrollToHorizontalOffset(_scrollViewer.HorizontalOffset - deltaX);
 _scrollViewer.ScrollToVerticalOffset(_scrollViewer.VerticalOffset - deltaY);

 _offsetPosition = newOffsetPos;

While this works, it is not very smooth.
Is there a better way to do this? If Transforms are used, will the scrollbars update automagically when the Canvas is moved?
Thanks for any tips...

A: 

You could try this (or at least take a peek at how it's implemented).

karenc
Thanks. Not very intuitive to use, but maybe I can get some ideas for my implementation.
Number8
Hmmm... looks like it is doing what I am trying to do, update the scrollviewer offsets: TargetScrollViewer.ScrollToHorizontalOffset(TargetScrollViewer.HorizontalOffset + ScrollPixelsPerTick);
Number8
+1  A: 

Actually this sort of problem is really a matter of using the right pattern to track the mouse. I've seen this issue in variety of cases not just in Silverlight.

The best pattern is to trap the original locations of both mouse and subject, then recalculate the new offset from the fixed original values. That way the mouse stays planted solid at a single point on the image being panned. Here is my simple Repro:-

Start with a fresh Silverlight Application in visual studio. Modify MainPage.Xaml thus:-

<UserControl x:Class="SilverlightApplication1.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"&gt;
    <Grid x:Name="LayoutRoot" Background="White">
        <ScrollViewer x:Name="Scroller" HorizontalScrollBarVisibility="Auto">
            <Image x:Name="Map" Source="test.jpg" Width="1600" Height="1200" />
        </ScrollViewer>
    </Grid>
</UserControl>

Add the following code to the MainPage.xaml.cs file:-

    public MainPage()
    {
        InitializeComponent();
        Map.MouseLeftButtonDown += new MouseButtonEventHandler(Map_MouseLeftButtonDown);
    }

    void Map_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        Point mapOrigin = new Point(Scroller.HorizontalOffset, Scroller.VerticalOffset);
        Point mouseOrigin = e.GetPosition(Application.Current.RootVisual);

        MouseEventHandler moveHandler = null;
        MouseButtonEventHandler upHandler = null;

        moveHandler = (s, args) =>
        {
            Point mouseNew = args.GetPosition(Application.Current.RootVisual);
            Scroller.ScrollToHorizontalOffset(mapOrigin.X - (mouseNew.X - mouseOrigin.X));
            Scroller.ScrollToVerticalOffset(mapOrigin.Y - (mouseNew.Y - mouseOrigin.Y));
        };


        upHandler = (s, args) =>
        {
            Scroller.MouseMove -= moveHandler;
            Scroller.MouseLeftButtonUp -= upHandler;
        };

        Scroller.MouseMove += moveHandler;
        Scroller.MouseLeftButtonUp += upHandler;
    }
}

Give it a reasonably large test.jpg (doesn't need to be 1600x1200 Image will scale it).

You'll note that when dragging the mouse remains exactly over a fixed point in the image until you hit a boundary. Move the mouse as fast as you like it always tracks, this is because it doesn't depend on deltas being accurate and up-to-date. The only variable is the current mouse position, the other values remain fixed as they were at mouse down.

AnthonyWJones
Thanks for the reply. I still get the unexpected position info in the moveHandler - moving steadily to the right, note that roughly every other X coord is smaller than the previous:mouseNew: 1296 876 mouseNew: 1284 876 mouseNew: 1297 876 mouseNew: 1298 876 mouseNew: 1286 876 mouseNew: 1299 876 mouseNew: 1300 876 mouseNew: 1301 876 mouseNew: 1287 876Makes for not-smooth scrolling... My scrollviewer control is 1/2 the height and width of the canvas it contains.
Number8
@Number8: Sounds like something else is dodgy on your system, works perfectly for me. Either the `GetPosition` calcs are off at times for some reason (you are using the app `RootVisual` right?) or perhaps you might even have a dodgy mouse or driver. Is the mouse smooth when you move it elsewhere?
AnthonyWJones
Thanks for your help. I was using the wrong element in GetPosition().
Number8