如何根据缩放级别设置WPF Canvas的平移限制?

时间:2017-12-12 11:47:12

标签: wpf math canvas geometry zoom

我试图在我在此处找到的控件上设置缩放和平移限制https://wpfextensions.codeplex.com

我设法设置了缩放限制,但现在我设置了平移限制,因此您无法在视图外部平移画布内的对象。< / p>

我成功设置了限制,但只有当缩放级别为1(Zoom == 1,因此没有缩放),但增加缩放的那一刻(通过旋转鼠标滚轮) )事情开始出错:设置限制,但设置不正确。

为了正确设置它们,我必须考虑deltaZoom(与前一个缩放值相比变焦量的变化)。

小型演示项目

我创建了一个简单的独立项目,我可以重现这个问题: https://github.com/igorpopovio/CanvasZoomPan

项目显示一个桌面窗口,其中包含ZoomControl(带有ScaleTransform的画布,TranslateTransform和一系列依赖项属性,以便更轻松地处理变换)。 ZoomControl包含一个红色方块,窗口包含ZoomControl和一个属性的调试列表,因此我可以看到它们如何根据左击拖动和鼠标滚轮缩放进行更改。

预期与实际行为

预期行为:对象/红色方块边缘无法离开当前视图。

Expected behaviour


实际行为:对象/红色方边缘离开当前视图(仍有限制,但未正确设置)。

Actual behaviour

代码说明

所有动作都发生在this file中,重要的部分是:

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

我尝试了什么

LimitZoomingAndPanning方法中,我设置了适用于Zoom == 1deltaZoom == 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变量实际上是画布内对象的边界框。 ActualWidthActualHeight是渲染对象的画布的大小。

逻辑上,所有翻译/平移限制都应取决于deltaZoom

也许我错过了什么?

1 个答案:

答案 0 :(得分:0)

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

我已经将此控件打包在nuget上,但是可以在我的gist和我的github上找到,该演示项目将图像加载到视口控件中。

PM > Install-Package Han.Wpf.ViewportControl

用法:

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

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

        <Button />

    </Grid>

</controls:Viewport

并将主题添加到app.xaml

<Application.Resources>

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

</Application.Resources>

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

    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();
        }
    }

在无效呼叫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);
    }