2
votes

I'm working on Zooming with Silverlight 5,

My Idea is to zoom according to mouse position on the Canvas, and the ability to Drag that Canvas,

The problem I have is when scale is less than 1, about 0.6 or 0.5, point to the corner of the canvas and wheel up, the canvas will change its position or "jump", any help please?

these two photos describe the status before Then after:

Before ZOOM

After Zoom

I have the following XAML:

<Grid x:Name="LayoutRoot" Background="White">
    <ScrollViewer x:Name="sv" Margin="0,0,0,76" ScrollViewer.VerticalScrollBarVisibility="Disabled" Background="#FFE3E7F1">
        <Canvas x:Name="grd" Height="394" Width="630">
            <Canvas x:Name="cvs" Background="White" MouseWheel="cvs_MouseWheel" MouseLeftButtonDown="cvs_MouseLeftButtonDown" MouseLeftButtonUp="cvs_MouseLeftButtonUp" MouseMove="cvs_MouseMove" Height="391" Canvas.Left="2" Canvas.Top="1" Width="625">
                <Canvas.Effect>
                    <DropShadowEffect ShadowDepth="0"/>
                </Canvas.Effect>
                <Rectangle Height="70" Canvas.Left="155" Canvas.Top="58" Width="79" Fill="#FFFFBFBF"/>
                <Rectangle Height="70" Canvas.Left="544" Canvas.Top="126" Width="79" Fill="#FF8B92FF"/>
            </Canvas>
        </Canvas>
    </ScrollViewer>
</Grid>

and here's the C#:

public partial class MainPage : UserControl
{
    CompositeTransform canvasTransform = new CompositeTransform();
    bool canDragCanvas;
    double mouseRelatedPositionX = 0;
    double mouseRelatedPositionY = 0;
    public MainPage()
    {
        // Required to initialize variables
        InitializeComponent();
    }

    private void cvs_MouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
    {
        var scaleFactor = 0.2*(e.Delta < 0?-1:1);
        var centerX = e.GetPosition(cvs).X;
        var centerY = e.GetPosition(cvs).Y;
        if (centerX > cvs.ActualWidth * canvasTransform.ScaleX || centerX < 0 || centerY > cvs.ActualHeight * canvasTransform.ScaleY || centerY < 0)
        {
            centerX = cvs.ActualWidth/2;
            centerY = cvs.ActualHeight/2;
        }
        canvasTransform.CenterX = centerX;
        canvasTransform.CenterY = centerY;
        canvasTransform.ScaleX += scaleFactor;
        canvasTransform.ScaleY += scaleFactor;
        cvs.RenderTransform = canvasTransform;
    }

    private void cvs_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        canDragCanvas = true;
        mouseRelatedPositionX = e.GetPosition(cvs).X;
        mouseRelatedPositionY = e.GetPosition(cvs).Y;
    }

    private void cvs_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
    {
        canDragCanvas = false;
    }

    private void cvs_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
    {
        if(!canDragCanvas) return;
        var leftValueToAdd = e.GetPosition(cvs).X - mouseRelatedPositionX;
        var topValueToAdd = e.GetPosition(cvs).Y - mouseRelatedPositionY;
        UpdateCanvasPosition(leftValueToAdd*canvasTransform.ScaleX, topValueToAdd*canvasTransform.ScaleX);
    }

    void UpdateCanvasPosition(double leftValueToAdd,double topValueToAdd)
    {
        var leftOffset = canvasTransform.CenterX - canvasTransform.CenterX * canvasTransform.ScaleX;
        var rightOffset = (cvs.ActualWidth - canvasTransform.CenterX) - (cvs.ActualWidth - canvasTransform.CenterX) * canvasTransform.ScaleX;
        var topOffset = canvasTransform.CenterY - canvasTransform.CenterY * canvasTransform.ScaleY;
        var bottomOffset = (cvs.ActualHeight - canvasTransform.CenterY) - (cvs.ActualHeight - canvasTransform.CenterY) * canvasTransform.ScaleY;

        var canvasLeftInBorders = Canvas.GetLeft(cvs)+ leftValueToAdd + leftOffset > 0;
        var canvasRightInBorders = Canvas.GetLeft(cvs) + cvs.ActualWidth * canvasTransform.ScaleX + leftValueToAdd + leftOffset < grd.ActualWidth;
        var canvasTopInBorders = Canvas.GetTop(cvs) + topValueToAdd + topOffset > 0;
        var canvasBottomInBorders = Canvas.GetTop(cvs) + cvs.ActualHeight * canvasTransform.ScaleY + topValueToAdd + topOffset < grd.ActualHeight;

        if (leftValueToAdd > 0)
        {
            if (canvasLeftInBorders)
                leftValueToAdd = 0;
        }
        else if (leftValueToAdd < 0)
            if (canvasRightInBorders)
                leftValueToAdd = 0;

        if (topValueToAdd > 0)
        {
            if (canvasTopInBorders)
                topValueToAdd = 0;
        }
        else if (topValueToAdd < 0)
            if (canvasBottomInBorders)
                topValueToAdd = 0;

        Canvas.SetLeft(cvs, Canvas.GetLeft(cvs) + leftValueToAdd);
        Canvas.SetTop(cvs,Canvas.GetTop(cvs)+topValueToAdd);
    }
}
1
+1 for providing code, XAML and pictures! Wish more questions were that clear. Welcome top SO.Gone Coding

1 Answers

2
votes

Basically you are attaching mouse events to the surface that is zooming and the mouse coordinates get altered too. Silverlight is designed to still be interactive when you rotate, zoom and tilt.

You want to put a transparent layer over the top, that is not zoomed, and attached your mouse methods that that layer.

If you leave that layer turned on you will have to calculate collisions, but you can make it so that the layer only appears on mouse down and goes away on mouse up.