PanZoomImage的像素选择

时间:2012-02-20 17:40:57

标签: wpf wpf-controls

我是WPF的新手,正在尝试编写可点击的缩放平移图像控件。我已经有一个似乎有效的缩放平移图像:

<Border Name="border" ClipToBounds="True">
    <Canvas>
        <Image Name ="image"> 
               Source="{Binding Path=Source}"
               MouseLeftButtonDown="image_MouseLeftButtonDown"
               MouseLeftButtonUp="image_MouseLeftButtonUp"
               MouseMove="image_MouseMove"
               MouseWheel="image_MouseWheel">
        </Image>
    </Canvas>
</Border>

对于鼠标和滚轮事件,我使用了这篇文章:http://www.codeproject.com/Articles/168176/Zooming-and-panning-in-WPF-with-fixed-focus

我通过继承ZoomPanImage并为LeftMouseUp添加事件来编写可点击控件。

public class ClickableImage : PanZoomImage
{
    public event Action<Point> Click;

    //...
    protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
    {
        base.OnMouseLeftButtonUp(e);
         // ... all sorts of checks to distinguish click from mouse move
        if (Click != null)
        {
           Click(ControlToImage(mouseUpCoordinates));
        }
    }

    protected Point ControlToImage(Point controlPixel)
    {
        //this is where i am stuck...       
    }
}

我的问题是,在给定控制坐标的情况下,我似乎无法计算出正确的图像坐标。我需要考虑到图像可以缩放和平移,窗口本身可以调整大小。

我尝试使用渲染变换。当我缩放并平移图像时,我更新了变换。当我尝试将控制坐标转换为图像坐标时,我使用逆变换:

Point imagePixel = image.RenderTransform.Inverse.Transform(controlPixel);

但是这没用。其中一个问题是Transform以Identity开头,而实际上图像被均匀拉伸到控件的大小。

谢谢, 迪娜

1 个答案:

答案 0 :(得分:1)

以下是我如何解决它。正如Clemens建议的那样,我将图像拉伸模式设置为无。

<Image Name="image" RenderOptions.BitmapScalingMode="NearestNeighbor" Stretch="None" 
                   Source="{Binding Path=Source}"
                   MouseLeftButtonDown="image_MouseLeftButtonDown"
                   MouseLeftButtonUp="image_MouseLeftButtonUp"
                   MouseMove="image_MouseMove"
                   MouseWheel="image_MouseWheel"
                   Loaded="image_Loaded">
                    <Image.ContextMenu>
                        <ContextMenu>
                            <MenuItem Header="Fit to window" Click="FitToWindow_MenuItem_Click"></MenuItem>
                        </ContextMenu>
                    </Image.ContextMenu>
                </Image>

这意味着当图像加载到窗口中时,您只能看到它的一部分 - 具体取决于窗口大小。这很糟糕,但重要的是变换是身份,现在您可以手动设置它,使图像完全显示在窗口中。

private void FitViewToWindow()
{
    if (Source == null)
        throw new InvalidOperationException("Source not set");

    BitmapSource bitmapSource = Source as BitmapSource;
    if (bitmapSource == null)
        throw new InvalidOperationException("Unsupported Image Source Type");

    if (border.ActualWidth <= 0 || border.ActualHeight <= 0)
       return;

    double scaleX = border.ActualWidth / bitmapSource.PixelWidth;
    double scaleY = border.ActualHeight / bitmapSource.PixelHeight;
    double scale = Math.Min(scaleX, scaleY);

    Matrix m = Matrix.Identity;
    m.ScaleAtPrepend(scale, scale, 0, 0);

    double centerX = (border.ActualWidth - bitmapSource.PixelWidth * scale) / 2;
    double centerY = (border.ActualHeight - bitmapSource.PixelHeight * scale) / 2;
    m.Translate(centerX, centerY);

    image.RenderTransform = new MatrixTransform(m);
}

应在加载图像和更改图像源时调用此函数。至于调整窗口大小 - 只要跟踪变换,就可以正确转换坐标系。例如,这是我为窗口调整大小做的事情:

protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
{
    base.OnRenderSizeChanged(sizeInfo);

    //center the image in the new size

    if (sizeInfo.PreviousSize.Width <= 0 || sizeInfo.PreviousSize.Height <= 0)
        return;

    Matrix m = image.RenderTransform.Value;

    double offsetX = (sizeInfo.NewSize.Width - sizeInfo.PreviousSize.Width) / 2;
    double offsetY = (sizeInfo.NewSize.Height - sizeInfo.PreviousSize.Height) / 2;

    m.Translate(offsetX, offsetY);
    image.RenderTransform = new MatrixTransform(m);
}