Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
405 views
in Technique[技术] by (71.8m points)

c# - Zoom on Canvas, centered on mouse position

I know there are several posts on stack and others websites, but it seems I still make something wrong. When I zoom with MouseWheel event, the zoom is always not centered, but the left side of my canvas always stays on the left on my ViewBox, so when I zoom in, I only can see the left of my canvas.

XAML code :

<Grid x:Name="MainGrid">
        <Viewbox x:Name="ViewBoxDessin" Stretch="None" HorizontalAlignment="Center" VerticalAlignment="Center">
            <Canvas  x:Name="monDessin" Background="WhiteSmoke" MouseWheel="monDessin_MouseWheel">
                <Canvas.LayoutTransform>
                    <ScaleTransform x:Name="st" ScaleX="1" ScaleY="-1" CenterX=".5" CenterY=".5" />
                </Canvas.LayoutTransform>
            </Canvas>
        </Viewbox>
        <Viewbox x:Name="ViewBoxDessin2" Stretch="None">
            <Canvas x:Name="monDessin2">
                <Canvas.LayoutTransform>
                    <ScaleTransform x:Name="st2" ScaleX="1" ScaleY="1" CenterX=".5" CenterY=".5" />
                </Canvas.LayoutTransform>
            </Canvas>
        </Viewbox>
    </Grid>

Code behind

public AfficheGraphiquePiece()
        {
            InitializeComponent();
            MakeMyDrawing();
            ViewBoxDessin.Width = System.Windows.SystemParameters.PrimaryScreenWidth;
            ViewBoxDessin.Height = System.Windows.SystemParameters.PrimaryScreenHeight;

            double ech_x = monDessin.Width / System.Windows.SystemParameters.PrimaryScreenWidth;
            double ech_y = monDessin.Height / System.Windows.SystemParameters.PrimaryScreenHeight;
            double ech = Math.Min(ech_x, ech_y);
            this.ech_full = ech;
            st.ScaleX = ech;
            st.ScaleY = -ech;
            st2.ScaleX = ech;
            st2.ScaleY = ech;
        }
        private void monDessin_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            double zoom = e.Delta > 0 ? 1.1 : 0.9;
            if(st.ScaleX<this.ech_full*1.1 && zoom<1)
            {
                st.ScaleX = this.ech_full;
                st.ScaleY = -this.ech_full;
            }
            else
            {
                st.ScaleX *= zoom;
                st.ScaleY *= zoom;
                double coor_x = Mouse.GetPosition(monDessin).X;
                double coor_y = Mouse.GetPosition(monDessin).Y;
                st.CenterX = coor_x;
                st.CenterY = coor_y;

            }

        }

Excuse me, didn't remove some code, and it could make confusion, just replaced it by a function MakeMyDrawing()

Well, after Clemens advise, and help of that link for matrix use, I could do the following :

XAML :

<Grid x:Name="MainGrid">
        <Canvas  x:Name="monDessin" Background="WhiteSmoke" MouseWheel="monDessin_MouseWheel" MouseLeftButtonDown="image_MouseLeftButtonDown" MouseMove="image_MouseMove" MouseLeftButtonUp="image_MouseLeftButtonUp" MouseLeave="image_MouseLeave" >
            <Canvas.RenderTransform >
                <MatrixTransform/>
            </Canvas.RenderTransform>
        </Canvas>
        <Canvas x:Name="monDessin2">
            <Canvas.RenderTransform >
                <MatrixTransform/>
            </Canvas.RenderTransform>
        </Canvas>
    </Grid>

Code behind

public AfficheGraphiquePiece(Repere rep)
        {
            InitializeComponent();
            ClassGraphique monGraphe = new ClassGraphique(monDessin);
            ClassGraphique monGraphe2 = new ClassGraphique(monDessin2);
            MakeMyDrawing();
            double screenWidth = System.Windows.SystemParameters.PrimaryScreenWidth;
            double screenHeight = System.Windows.SystemParameters.PrimaryScreenHeight;

            double ech_x = screenWidth/ monDessin.Width ;
            double ech_y = screenHeight/ monDessin.Height;
            double ech = Math.Min(ech_x, ech_y)*0.9;
            this.ech_full = ech;
            this.echelleNow = this.ech_full;
            MatrixTransform maTrans =(MatrixTransform)monDessin.RenderTransform;
            var mat = maTrans.Matrix;
            mat.ScaleAt(ech, -ech, 0.1* screenWidth, (screenHeight-monDessin.Height*ech)/2-0.1*screenHeight);
            MatrixTransform maTrans2 = (MatrixTransform)monDessin2.RenderTransform;
            var mat2 = maTrans2.Matrix;
            mat2.ScaleAt(ech, ech, 0.1 * screenWidth, screenHeight*ech-((screenHeight - monDessin.Height * ech) / 2 - 0.1 * screenHeight));
            maTrans.Matrix = mat;
            maTrans2.Matrix = mat2;
        }
        private void monDessin_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            try
            {
                var position = e.GetPosition(monDessin);
                MatrixTransform transform = (MatrixTransform)monDessin.RenderTransform;
                MatrixTransform transform2 = (MatrixTransform)monDessin2.RenderTransform;
                var matrix = transform.Matrix;
                var matrix2 = transform2.Matrix;
                var scale = e.Delta >= 0 ? 1.1 : (1.0 / 1.1); 
                this.echelleNow *= scale;
                matrix.ScaleAtPrepend(scale, scale, position.X, position.Y);
                matrix2.ScaleAtPrepend(scale, scale, position.X,monDessin.Height-position.Y);
                monDessin.RenderTransform = new MatrixTransform(matrix);
                monDessin2.RenderTransform = new MatrixTransform(matrix2);
            }
            catch { }
        }
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Here is a very basic example for zooming and panning a Canvas with fixed initial size. The MatrixTransform in the RenderTransform of the inner Canvas provides the necessary transformations, while the outer Canvas handles mouse input and sets an initial scaling.

<Canvas Background="Transparent"
        SizeChanged="ViewportSizeChanged"
        MouseLeftButtonDown="ViewportMouseLeftButtonDown"
        MouseLeftButtonUp="ViewportMouseLeftButtonUp"
        MouseMove="ViewportMouseMove"
        MouseWheel="ViewportMouseWheel">

    <Canvas x:Name="canvas" Width="1000" Height="600">
        <Canvas.RenderTransform>
            <MatrixTransform x:Name="transform"/>
        </Canvas.RenderTransform>

        <Ellipse Fill="Red" Width="100" Height="100" Canvas.Left="100" Canvas.Top="100"/>
        <Ellipse Fill="Green" Width="100" Height="100" Canvas.Right="100" Canvas.Bottom="100"/>
    </Canvas>
</Canvas>

Code behind:

private Point? mousePos;

private void ViewportSizeChanged(object sender, SizeChangedEventArgs e)
{
    ((MatrixTransform)canvas.RenderTransform).Matrix = new Matrix(
        e.NewSize.Width / canvas.ActualWidth,
        0, 0,
        e.NewSize.Height / canvas.ActualHeight,
        0, 0);
}

private void ViewportMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    var viewport = (UIElement)sender;
    viewport.CaptureMouse();
    mousePos = e.GetPosition(viewport);
}

private void ViewportMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    ((UIElement)sender).ReleaseMouseCapture();
    mousePos = null;
}

private void ViewportMouseMove(object sender, MouseEventArgs e)
{
    if (mousePos.HasValue)
    {
        var pos = e.GetPosition((UIElement)sender);
        var matrix = transform.Matrix;
        matrix.Translate(pos.X - mousePos.Value.X, pos.Y - mousePos.Value.Y);
        transform.Matrix = matrix;
        mousePos = pos;
    }
}

private void ViewportMouseWheel(object sender, MouseWheelEventArgs e)
{
    var pos = e.GetPosition((UIElement)sender);
    var matrix = transform.Matrix;
    var scale = e.Delta > 0 ? 1.1 : 1 / 1.1;
    matrix.ScaleAt(scale, scale, pos.X, pos.Y);
    transform.Matrix = matrix;
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...