RT,这个功能比较常见,但凡涉及到图片预览的都跑不了,在说自己的实现方式前,介绍一个好用的控件:Extended.Toolkit中的Zoombox,感兴趣的同学可以去搜一下这个控件,它封装了常用的预览功能。
开始撸代码,整理一下思路先:
首先打算用一个Window来展示图片,但是QQ、微信预览图片的时候,任务栏并没有出现图标,难道用了什么黑科技?不管它,将Window的ShowInTaskbar设置为False就可以不显示在任务栏了。
继续,提到移动、旋转、缩放之类的,WPF提供了与之对应的Transform:TranslateTransform、RotateTransform、ScaleTransform,所以我们必然要使用他们仨了。怎么触发呢?那就用到事件了,移动么MouseMove,缩放么MouseWheel,旋转的话就是固定的按钮的点击事件了。另外,缩放的话可以限制一下最大和最小的缩放系数,我这边只设了缩小时如果小于系数0.3就不再缩小了,放大系数没有设置。
说一下其中的坑:
MouseWheel事件参数里会取到鼠标的Delta,这个参数不好直接用来设置缩放系数,那多少合适呢,看到别人的经验之谈,用这个Delta/3000即可。
double delta = (double)e.Delta / 3000;
ScaleTransform scale = group.Children[0] as ScaleTransform;
if (scale.ScaleX < 0.3 && scale.ScaleY < 0.3 && e.Delta < 0)
{
return;
}
scale.CenterX = this.imageControl.ActualWidth / 2;
scale.CenterY = this.imageControl.ActualHeight / 2;
scale.ScaleX += delta;
scale.ScaleY += delta;
MouseMove移动图片的时候,是可以将整个图片移到Window外面去的,这就要我们限制一下图片的移动范围了:
private void TopMove(TranslateTransform translate, Point position, Point imagePos)
{
if (scaleValue > 1.0)
{
if (mouseXY.Y - position.Y > 0.0)//up
{
double wm = winHeight - position.Y;
double im = (this.imageControl.ActualHeight - imagePos.Y) * scaleValue;
if (wm - im <= -5.0)
{
translate.Y -= mouseXY.Y - position.Y;
}
}
else
{
if (position.Y / scaleValue - imagePos.Y <= -5.0)
{
translate.Y -= mouseXY.Y - position.Y;
}
}
if (mouseXY.X - position.X > 0.0)//left
{
double iw = (this.imageControl.ActualWidth - imagePos.X) * scaleValue;
double ww = winWidth - position.X;
if (ww - iw <= -5.0)
{
translate.X -= mouseXY.X - position.X;
}
}
else
{
if (position.X / scaleValue - imagePos.X <= -5.0)
{
translate.X -= mouseXY.X - position.X;
}
}
}
else
{
if (mouseXY.Y - position.Y > 0.0)//up
{
if (position.Y / scaleValue >= imagePos.Y)
{
translate.Y -= mouseXY.Y - position.Y;
}
}
else
{
double im = (this.imageControl.ActualHeight - imagePos.Y) * scaleValue;
double wm = winHeight - position.Y;
if (im <= wm)
{
translate.Y -= mouseXY.Y - position.Y;
}
}
if (mouseXY.X - position.X > 0.0)//left
{
if (position.X / scaleValue >= imagePos.X)
{
translate.X -= mouseXY.X - position.X;
}
}
else
{
double iw = (this.imageControl.ActualWidth - imagePos.X) * scaleValue;
double ww = winWidth - position.X;
if (iw <= ww)
{
translate.X -= mouseXY.X - position.X;
}
}
}
}
稍微解释一下上面的代码,position是鼠标相对于Window的位置,imagePos是鼠标相对于图片的位置,scaleValue是图片当前的缩放比例,当图片是缩小状态(scaleValue<1)时,允许图片在整个Window里拖动,上移时,如果图片上边框触碰到了Window上边框,则不能再上移(另外3个方向原理相同),反之(scaleValue>1),只有当图片超出Window范围时才允许拖动,假设,图片上下超出了Window,则只能上下移动不能左右移动,并且,上移时,如果图片下边框触碰到了Window下边框,则不能再上移(另外3个方向原理相同)。
眼尖的同学可能注意到了上面这个方法名:TopMove,没错,这个算法只适用于图片正着放的时候的位移计算,我们不是有旋转吗,旋转了只有就不能这么算了,原因在于,imagePos始终是相对于图片来的,也就是说,如果图片旋转了90°,那么imagePos的坐标系也要旋转90°,但是我们最终要计算的是相对于Window的坐标系,所以要做相应的转换。
好了,上完整的代码:
<Window x:Class="VcreditChat.Windows.MaxPicture"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit"
WindowStyle="None"
AllowsTransparency="True"
Background="#82000000"
KeyDown="PicWin_KeyDown"
Loaded="PicWin_Loaded"
MouseLeftButtonDown="Window_MouseLeftButtonDown"
WindowStartupLocation="CenterScreen"
Icon="../Resources/Images/ico_48.ico"
ShowInTaskbar="False"
Closed="PicWin_Closed"
x:Name="picWin">
<Grid x:Name="outGrid">
<Grid.Resources>
<TransformGroup x:Key="transGroup">
<ScaleTransform/>
<TranslateTransform/>