在WPF里面实现以鼠标位置为中心缩放移动图片

简介: 原文:在WPF里面实现以鼠标位置为中心缩放移动图片在以前的文章使用WPF Resource以及Transform等技术实现鼠标控制图片缩放和移动的效果里面,介绍了如何在WPF里面移动和放大缩小图片,程序也支持使用滚轮的方式缩放图片。
原文: 在WPF里面实现以鼠标位置为中心缩放移动图片

在以前的文章使用WPF Resource以及Transform等技术实现鼠标控制图片缩放和移动的效果里面,介绍了如何在WPF里面移动和放大缩小图片,程序也支持使用滚轮的方式缩放图片。然而前面文章里介绍的缩放功能只能以图片中心为原点来实现,但是这种功能往往并不是客户想要的,我们看图片的时候,往往都喜欢以鼠标放在图片的焦点为原点进行图片的缩放。

咋看起来,实现这个功能也不是很难, ScaleTransform类里面定义了CenterXCenterY两个属性就是用来设置缩放的原点坐标的。将这两个属性分别赋予鼠标的X, Y坐标值,就可以实现对原始图片,以鼠标位置为原点缩放图片了。但是,请注意,我说的原始图片是指没有移动之前的图片,如果图片缩放并且移动了,再次缩放的时候,就是另外一个故事了。

画个图说明一下吧,比如下图里面右下方方块是一个WPF程序里面的一个图片,大小是40 x 40,里面的黑点是预备缩放的原点,假设黑点的坐标是(10, 10),在运行程序的时候,用户首先将方块移动到左边的位置,当然原点(黑点)也移动了,假如这个时候图片移动了50个像素。

接着用户在移动后的位置上,将图片缩放,比如说放大了2倍,这个操作也会移动原点(黑点)在最终图片的位置。因为放大图片,实际上就是将原始图片的各个像素移动到新的位置(红点),这个时候,新的原点(红点)的坐标应该是(20, 20),相邻两个像素的空间使用插值的方法填充。这个时候,

ScaleTransform.ScaleX = 2;

ScaleTransform.ScaleY = 2;

 

这个时候,用户打算放大图片当中的另外一个区域,再放大一倍(即放大到原图的3倍),下图里是蓝点,假设坐标是(50, 50),因为无论图片缩放与否,用户只会以他在实际图片看到的内容来判断新的缩放焦点:

如果我们直接盲目地将ScaleTransform的各个属性设置为类似下面的值的话:

ScaleTransform.ScaleX = 3;

ScaleTransform.ScaleY = 3;

ScaleTransform.CenterX = 50;

ScaleTransform.CenterY = 50;

 

就发生问题了, 因为ScaleX = 3表示新图是原图的3倍,然而我们的原点却是在2倍图片上设置的原图的大小只有40 x 40。解决方案当然是将蓝点的位置转换回在原始图片的位置,注意原始图片应该是下图右下方的图片,而不是左边的用户最初已经移动了图片。

看起来转换起来有点麻烦,不过WPF提供了一个 函数TransformGroup.Inverse,可以把转换后图片上的坐标转换会在原始图片的坐标。当然啦,如果你熟悉图形学和线性代数的话,实际上,图片的缩放和移动就是将原始图片乘上一个矩阵,而TransformGroup.Inverse函数就是执行矩阵求逆操作。

下面就是关键代码:

XAML代码:

 

<Grid.Resources>

    <TransformGroup x:Key="ImageCompareResources">

        <ScaleTransform />

        <TranslateTransform/>

    </TransformGroup>

</Grid.Resources>               

 

<ScrollViewer HorizontalScrollBarVisibility="Disabled"

              VerticalScrollBarVisibility="Disabled" Grid.Row="0" Grid.Column="0" x:Name="MasterScrollViewer" Margin="5" Background="WhiteSmoke">

    <ContentControl x:Name="TestContentControl1"

       MouseLeftButtonDown="MasterImage_MouseLeftButtonDown"

       MouseLeftButtonUp="MasterImage_MouseLeftButtonUp"

       MouseMove="MasterImage_MouseMove"

       MouseWheel="MasterImage_MouseWheel">

        <Image RenderOptions.BitmapScalingMode="NearestNeighbor"                                          

               x:Name="MasterImage" Source="{Binding Path=MasterImagePath}" Stretch="Uniform"

               RenderTransform="{StaticResource ImageCompareResources}"/>

    </ContentControl>

</ScrollViewer>

C#代码:

        private void MasterImage_MouseWheel(object sender, MouseWheelEventArgs e)

        {

            ContentControl image = sender as ContentControl;

            if (image == null)

            {

                return;

            }

           

            TransformGroup group = ImageComparePanel.FindResource("ImageCompareResources") as TransformGroup;

            Debug.Assert(group != null, "Can't find transform group from image compare panel resource");

            Point point = e.GetPosition(image);

            double scale = e.Delta * 0.001;

            ZoomImage(group, point, scale);

        }

        private static void ZoomImage(TransformGroup group, Point point, double scale)

        {

            Debug.Assert(group != null, "Oops, ImageCompareResources is removed from current control's resouce");

 

            Point pointToContent = group.Inverse.Transform(point);

            ScaleTransform transform = group.Children[0] as ScaleTransform;

            if (transform.ScaleX + scale < 1)

            {

                return;

            }

 

            transform.ScaleX += scale;

            transform.ScaleY += scale;

            TranslateTransform transform1 = group.Children[1] as TranslateTransform;

            transform1.X = -1 * ((pointToContent.X * transform.ScaleX) - point.X);

            transform1.Y = -1 * ((pointToContent.Y * transform.ScaleY) - point.Y);

        }                  

        private void MasterImage_MouseMove(object sender, MouseEventArgs e)

        {

            ContentControl image = sender as ContentControl;

            if (image == null)

            {

                return;

            }

 

            if (this.isMouseLeftButtonDown && e.LeftButton == MouseButtonState.Pressed)

            {

                this.DoImageMove(image, e.GetPosition(image));

            }

        }

 

        private void DoImageMove(ContentControl image, Point position)

        {

            TransformGroup group = ImageComparePanel.FindResource("ImageCompareResources") as TransformGroup;

            Debug.Assert(group != null, "Can't find transform group from image compare panel resource");

            TranslateTransform transform = group.Children[1] as TranslateTransform;

            transform.X += position.X - this.previousMousePoint.X;

            transform.Y += position.Y - this.previousMousePoint.Y;

            this.previousMousePoint = position;

        }

目录
相关文章
C#WPF 图片在显示时没有问题,但在运行时图片显示不出来的解决
选中项目,点击右上角的显示全部文件按钮,会将默认隐藏的文件显示出来,选中所需图片,右键,添加到项目,然后选择图片查看属性,生成操作选择resource。完毕。本人目前的解决方案。
1296 41
C#WPF 图片在显示时没有问题,但在运行时图片显示不出来的解决
|
前端开发 C# Windows
WPF鼠标、键盘、拖拽事件、用行为封装事件
本文主要介绍了WPF中常用的鼠标事件、键盘事件以及注意事项,同时使用一个案例讲解了拓展事件。除此之外,本文还讲述如何用行为(Behavior)来封装事件。
722 0
|
IDE C# 开发工具
WPF钟表效果实现
中WPF中的RotateTransform实现UI元素的旋转,并模拟钟表的秒针、分针和时针。
1425 0
WPF钟表效果实现
|
IDE 编译器 C#
WPF实现强大的动态公式计算
数据库可以定义表不同列之间的计算公式,进行自动公式计算,但如何实现行上的动态公式计算呢?行由于可以动态扩展,在某些应用场景下将能很好的解决实际问题。本文就探讨一下如何在WPF中实现一种基于行字段的动态公式计算。
1353 0
WPF实现强大的动态公式计算
|
网络协议 C# 移动开发
C# WPF上位机实现和下位机TCP通讯
C# WPF上位机实现和下位机TCP通讯下位机使用北京大华程控电源DH1766-1,上位机使用WPF。实现了电压电流实时采集,曲线显示。上午在公司调试成功,手头没有程控电源,使用TCP服务端模拟。昨天写的TCP服务端正好排上用场。
2778 0
|
C# Windows
WPF中获取鼠标相对于屏幕的位置
原文:WPF中获取鼠标相对于屏幕的位置              WPF中获取鼠标相对于屏幕的位置                                   周银辉WPF编程时,我们经常使用Mouse.GetPosition(IInputElement relativeTo)或MouseEventArgs.GetPosition(IInputElement relativeTo)来获取鼠标相对于某一界面元素的相对位置,这自然也联想到另外的一个问题:如何获取鼠标相对于屏幕的位置。
1530 0
|
C# UED 自然语言处理
在WPF中实现图片一边下载一边显示
原文 在WPF中实现图片一边下载一边显示 当我们上网查看一个较大的图片时,浏览器能一边下载一边显示,这样用户体验是比较好的,但在WPF程序中,当我们通过如下方式显示一幅图片时:     img.Source = new BitmapImage(new Uri("http://localhost:8000/www/test.jpg")); 只能等到图片下载完成时才能显示出来,当图片较大时需要等待很久,即使在旁边放个进度条给人的感觉仍然不好。
1228 0
|
C#
WPF实现三星手机充电界面
原文:WPF实现三星手机充电界面 GitHub地址:https://github.com/ptddqr/wpf-samsung-phone-s5-charging-ui/tree/master 先上效果图 这个效果来自于三星S5的充电界面,版权归三星所有,这里仅仅是技术实现.
1955 0
|
C# vr&ar
WPF实现物理效果 拉一个小球
原文:WPF实现物理效果 拉一个小球 一直以来都对物理效果有神秘感,完全不知道怎么实现的.直到看到了周银辉在老早前写的一篇博客:http://www.cnblogs.com/zhouyinhui/archive/2007/06/23/793724.
1156 0
|
C#
WPF实现Twitter按钮效果
原文:WPF实现Twitter按钮效果 最近上网看到这个CSS3实现的Twitter按钮,感觉很漂亮,于是想用WPF来实现下. 实现这个效果,参考了CSS3 原文地址:http://www.html5tricks.
1395 0