65.9K
CodeProject 正在变化。 阅读更多。
Home

WPF 中的固定焦点缩放和平移

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.83/5 (16投票s)

2011年3月13日

CPOL

2分钟阅读

viewsIcon

70976

downloadIcon

3891

如何在 WPF 中实现固定焦点的缩放和平移。

Zoomed_Out.PNG

引言

我找到过不少关于缩放到固定点的文章。我也尝试过很多解决方案,有些有效,有些无效。那些有效的方案对我来说过于复杂。那些无效的方案要么无法保持焦点,要么在缩放时会覆盖屏幕的其他部分。另一个常见问题是在平移时。许多解决方案没有记录鼠标的原始位置,因此当你开始平移时,图像会突然跳跃,让你感觉抓住的是左上角,而不是鼠标之前的实际位置。在我的 Windows Forms 解决方案中,最大的问题之一是限制图像大小,以防止它覆盖其他部分;而在 WPF 中,这要容易得多,因为我可以将 "cliptobounds" 设置为 true 来实现相同的效果。

Zoomed_In.PNG

使用代码

经过**大量**的试验和错误,我最终得到了一段相当简单的代码。

几个事件处理程序来捕获一些鼠标事件

WPFWindow.MouseWheel += MainWindow_MouseWheel;
image.MouseLeftButtonDown += image_MouseLeftButtonDown;
image.MouseLeftButtonUp += image_MouseLeftButtonUp;
image.MouseMove += image_MouseMove;

首先,我声明一些全局变量。Start 用于存储鼠标单击时鼠标的位置,而 origin 用于存储图像在移动之前的原始偏移量。

private Point origin;  // Original Offset of image
private Point start;   // Original Position of the mouse

MouseDown 设置这两个变量。

private void image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    if (image.IsMouseCaptured) return;
    image.CaptureMouse();

    start = e.GetPosition(border);
    origin.X = image.RenderTransform.Value.OffsetX;
    origin.Y = image.RenderTransform.Value.OffsetY;
}

MouseMove 处理平移。我相对于在 MouseDown 事件中保存的位置移动图像。我只需将鼠标的原始位置与鼠标的当前位置之间的差值设置为偏移量,并将该值添加到原始偏移量即可。

private void image_MouseMove(object sender, MouseEventArgs e)
{
    if (!image.IsMouseCaptured) return;
    Point p = e.MouseDevice.GetPosition(border);

    Matrix m = image.RenderTransform.Value;
    m.OffsetX = origin.X + (p.X - start.X);
    m.OffsetY = origin.Y + (p.Y - start.Y);

    image.RenderTransform = new MatrixTransform(m);
}

MouseWheel 执行实际的缩放。它使用 ScaleAtPrepend,它不仅执行缩放,还保持焦点。这比 Windows Forms 版本容易得多。

private void MainWindow_MouseWheel(object sender, MouseWheelEventArgs e)
{
    Point p = e.MouseDevice.GetPosition(image);

    Matrix m = image.RenderTransform.Value;
    if (e.Delta > 0)
        m.ScaleAtPrepend(1.1, 1.1, p.X, p.Y);
    else
        m.ScaleAtPrepend(1/1.1, 1/1.1, p.X, p.Y);

    image.RenderTransform = new MatrixTransform(m);
}

关注点

重要的是要注意 XAML 中的 "cliptobounds"。如果它是 true,它将确保图像保持在边框内,并且不会覆盖窗口的其他部分。

<border grid.row="1" name="border" cliptobounds="True">
    <img name="image" opacity="1" 
       source="/WPF%20Image%20Pan%20and%20Zoom;component/Images/test.tif" />
</border>

历史

  • 初始发布。
© . All rights reserved.