canvas 内的 ScaleTransform:以鼠标指针为中心导致问题

ScaleTransform within a canvas: Centering around mouse pointer causing issues

我在 canvas (InnerCanvas) 中有一个 canvas (OuterCanvas),我正在按如下方式处理放大和缩小:

private ScaleTransform ScaleTransform = new ScaleTransform();

private void OuterCanvas_MouseWheel(Object sender, MouseWheelEventArgs e)
{
    if (e.Delta >= 1)
    {
        ScaleTransform.ScaleX += 0.03;
        ScaleTransform.ScaleY += 0.03;
    }
    else
    {
        ScaleTransform.ScaleX -= 0.03;
        ScaleTransform.ScaleY -= 0.03;
    }
    ScaleTransform.CenterX = ActualWidth / 2;
    ScaleTransform.CenterY = ActualHeight / 2;
    InnerCanvas.RenderTransform = TransformGroup;
}

在 canvas 的中心放大和缩小效果很好。我想围绕鼠标指针进行放大和缩小,因此按如下方式更改 CenterXCenterY 行:

ScaleTransform.CenterX = e.GetPosition(this).X;
ScaleTransform.CenterY = e.GetPosition(this).Y;

这再次令人满意,但是如果我尝试在两个缩放事件(鼠标滚轮事件)期间移动鼠标,整个 canvas 会随着比例因子的增加或减少而急剧跳跃。

Demonstration of problem

这与将 CenterXCenterY 属性设置为 ActualWidth/2ActualHeight/2 并更改 window 大小时的问题相同.

如何正确处理 CenterXCenterY 属性才能避免此问题?

这里有一个或多或少完整的解决方案,包括平移内部的可能性 Canvas:

<Canvas x:Name="outerCanvas"
        MouseWheel="OnMouseWheel"
        MouseMove="OnMouseMove"
        MouseLeftButtonDown="OnMouseLeftButtonDown"
        MouseLeftButtonUp="OnMouseLeftButtonUp">
    <Canvas x:Name="innerCanvas"
            Width="400" Height="400" Background="AliceBlue">
        <Canvas.RenderTransform>
            <MatrixTransform />
        </Canvas.RenderTransform>
    </Canvas>
</Canvas>

使用这些事件处理程序:

private double zoomScaleFactor = 1.1;
private Point? mousePos;

private void OnMouseWheel(object sender, MouseWheelEventArgs e)
{
    var pos = e.GetPosition(outerCanvas);
    var scale = e.Delta > 0 ? zoomScaleFactor : 1 / zoomScaleFactor;
    var transform = (MatrixTransform)innerCanvas.RenderTransform;
    var matrix = transform.Matrix;
    matrix.ScaleAt(scale, scale, pos.X, pos.Y);
    transform.Matrix = matrix;
}

private void OnMouseMove(object sender, MouseEventArgs e)
{
    if (mousePos.HasValue)
    {
        var pos = e.GetPosition(outerCanvas);
        var delta = pos - mousePos.Value;
        var transform = (MatrixTransform)innerCanvas.RenderTransform;
        var matrix = transform.Matrix;
        matrix.Translate(delta.X, delta.Y);
        transform.Matrix = matrix;
        mousePos = pos;
    }
}

private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    if (outerCanvas.CaptureMouse())
    {
        mousePos = e.GetPosition(outerCanvas);
    }
}

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