如何根据缩放级别在WPF画布上设置平移限制? [英] How to set panning limits on WPF Canvas based on zoom level?

查看:42
本文介绍了如何根据缩放级别在WPF画布上设置平移限制?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在此处找到的控件上设置缩放和平移限制:


实际行为:对象/红色正方形边缘脱离当前视图(仍然有限制,但设置不正确).

代码说明

所有操作都在 此文件> ,重要的是:

  • 平移限制: MinTranslateX,MaxTranslateX;MinTranslateY,MaxTranslateY
  • 当前平移: TranslateX,TranslateY
  • 当前缩放: Zoom
  • 缩放量已更改: deltaZoom (局部变量)
  • Zoom_PropertyChanged 方法
  • LimitZoomingAndPanning 方法

我尝试过的

LimitZoomingAndPanning 方法中,我设置了适用于 Zoom == 1 ( deltaZoom == 1 )的转换/平移限制,但为其他 Zoom 值给出了错误的限制:

  MinTranslateX = box.BottomLeft.X * deltaZoom;MinTranslateY = box.BottomLeft.Y * deltaZoom;MaxTranslateX = ActualWidth-box.Size.Width * deltaZoom;MaxTranslateY = ActualHeight-box.Size.Height * deltaZoom; 

box 变量实际上是画布内对象的边界框. ActualWidth ActualHeight 是在其上渲染对象的画布的大小.

从逻辑上讲,所有转换/平移限制都应取决于 deltaZoom .

也许我遗漏了一些东西?

解决方案

我最初尝试使用 ZoomAndPanControl 做同样的事情,但是无法实现我想要的效果,所以我最终写了我的自己的控件来提供受约束的缩放和平移控件.

我将此控件打包在 nuget 上,但是可以在我的要点和我的

并将主题添加到 app.xaml :

 < Application.Resources>< ResourceDictionary Source ="pack://application:,,,//Han.Wpf.ViewportControl; component/Themes/Generic.xaml"/></Application.Resources> 

我知道您没有使用MatrixTransform,而是将控件约束到父对象的边界,其计算方式如下:

 私有无效OnMouseWheel(对象发送者,MouseWheelEventArgs e){如果(IsEnabled){var scale = e.Delta>0?ZoomSpeed:1/ZoomSpeed;var position = e.GetPosition(_content);var x = Constrain(scale,MinZoom/_matrix.M11,MaxZoom/_matrix.M11);var y = Constrain(scale,MinZoom/_matrix.M22,MaxZoom/_matrix.M22);_matrix.ScaleAtPrepend(x,y,position.X,position.Y);ZoomX = _matrix.M11;ZoomY = _matrix.M22;Invalidate();}}私有无效OnMouseMove(对象发送者,MouseEventArgs e){如果(IsEnabled& _capture){var position = e.GetPosition(this);var point = new Point{X =位置X-_origin.X,Y =位置Y-_origin.Y};var delta =点;_origin =位置;_matrix.Translate(delta.X,delta.Y);Invalidate();}} 

在Invalidate调用中 Constrain()

 私人double约束(double值,double min,double max){如果(min> max){最小值=最大值;}如果(值< =分钟){返回最小值;}如果(值> =最大值){返回最大值}返回值;}私有void Constrain(){var x = Constrain(_matrix.OffsetX,_content.ActualWidth-_content.ActualWidth * _matrix.M11,0);var y = Constrain(_matrix.OffsetY,_content.ActualHeight-_content.ActualHeight * _matrix.M22,0);_matrix = new Matrix(_matrix.M11,0d,0d,_matrix.M22,x,y);} 

I'm trying to set zooming and panning limits on a control I found here: https://wpfextensions.codeplex.com

I managed to set zooming limits, but now I'm having trouble setting the panning limits so that you can't pan the object inside the canvas, outside the view.

I succeeded in setting the limits, but only when the zoom level is 1 (Zoom == 1, so no zoom), but the moment you increase the zoom (by rotating the mouse wheel) things start to go wrong: the limits are set, but they are not set correctly.

In order to set them correctly I have to take into consideration the deltaZoom (the amount zoom changed compared to the previous zoom value).

Small demo project

I have created a simple, standalone project where I can reproduce the issue: https://github.com/igorpopovio/CanvasZoomPan

The project shows a desktop window with the ZoomControl (canvas with ScaleTransform, TranslateTransform and a bunch of dependency properties to make it easier to work with the transforms). The ZoomControl contains a red square and the window contains the ZoomControl and a debug list of properties so I can see live how they change based on left click drag and mouse wheel zoom.

Expected vs actual behaviour

Expected behaviour: object/red square edge cannot get out of the current view.


Actual behaviour: object/red square edge gets out of the current view (still has limits, but aren't correctly set).

Code explanations

All the action happens in this file and the important bits are:

  • the panning limits: MinTranslateX, MaxTranslateX; MinTranslateY, MaxTranslateY
  • the current panning: TranslateX, TranslateY
  • the current zoom: Zoom
  • the amount zoom changed: deltaZoom (local variable)
  • the Zoom_PropertyChanged method
  • the LimitZoomingAndPanning method

What I tried

In the LimitZoomingAndPanning method I set the translation/panning limits which are working for Zoom == 1 (deltaZoom == 1), but are giving incorrect limits for any other Zoom values:

MinTranslateX = box.BottomLeft.X * deltaZoom;
MinTranslateY = box.BottomLeft.Y * deltaZoom;

MaxTranslateX = ActualWidth - box.Size.Width * deltaZoom;
MaxTranslateY = ActualHeight - box.Size.Height * deltaZoom;

The box variable is actually the bounding box of the object inside the canvas. ActualWidth and ActualHeight are the size of the canvas on which the object is rendered.

Logically, all the translation/panning limits should depend on deltaZoom.

Maybe I'm missing something?

解决方案

I originally tried doing the same thing with the ZoomAndPanControl but wasn't able to achieve what I wanted so I ended up writing my own control to provide a constrained zoom and pan control.

I have packaged this control up on nuget but it can be found on my gist and on my github with a demo project loading an image into the viewport control.

PM > Install-Package Han.Wpf.ViewportControl

Usage:

<controls:Viewport MinZoom="1" MaxZoom="50" ZoomSpeed="1.1">

    <Grid width="1200" height="1200">

        <Button />

    </Grid>

</controls:Viewport

and add the theme to the app.xaml:

<Application.Resources>

    <ResourceDictionary Source="pack://application:,,,/Han.Wpf.ViewportControl;component/Themes/Generic.xaml" />

</Application.Resources>

I know your not using the MatrixTransform but to constrain the control to the bounds of the parent is calculated like below:

    private void OnMouseWheel(object sender, MouseWheelEventArgs e)
    {
        if (IsEnabled)
        {
            var scale = e.Delta > 0 ? ZoomSpeed : 1 / ZoomSpeed;
            var position = e.GetPosition(_content);

            var x = Constrain(scale, MinZoom / _matrix.M11, MaxZoom / _matrix.M11);
            var y = Constrain(scale, MinZoom / _matrix.M22, MaxZoom / _matrix.M22);

            _matrix.ScaleAtPrepend(x, y, position.X, position.Y);

            ZoomX = _matrix.M11;
            ZoomY = _matrix.M22;

            Invalidate();
        }
    }

    private void OnMouseMove(object sender, MouseEventArgs e)
    {
        if (IsEnabled && _capture)
        {
            var position = e.GetPosition(this);

            var point = new Point
            {
                X = position.X - _origin.X,
                Y = position.Y - _origin.Y
            };

            var delta = point;
            _origin = position;

            _matrix.Translate(delta.X, delta.Y);

            Invalidate();
        }
    }

In the Invalidate call Constrain()

    private double Constrain(double value, double min, double max)
    {
        if (min > max)
        {
            min = max;
        }

        if (value <= min)
        {
            return min;
        }

        if (value >= max)
        {
            return max;
        }

        return value;
    }

    private void Constrain()
    {
        var x = Constrain(_matrix.OffsetX, _content.ActualWidth - _content.ActualWidth * _matrix.M11, 0);
        var y = Constrain(_matrix.OffsetY, _content.ActualHeight - _content.ActualHeight * _matrix.M22, 0);

        _matrix = new Matrix(_matrix.M11, 0d, 0d, _matrix.M22, x, y);
    }

这篇关于如何根据缩放级别在WPF画布上设置平移限制?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆