WPF下實現圖片的放大縮小移動
在windows 7里面有自帶的圖片查看器,這個軟件可以打開一張圖片然后以鼠標在圖片中的焦點為原點來進行縮放,並且放大后可以隨意拖動。下面我們在WPF中實現這個功能。
在實現這個功能之前先說下使用的主要函數和步驟:
ScaleTransform:進行縮放的函數。
TranslateTransform:進行平移的函數。
TransformGroup.Inverse:縮放圖片后,把縮放后圖片上的坐標轉換為在原始圖片的坐標,從而使圖片正常縮放。
- 步驟如下:
- 定義一個TranslateTransform實例來修改圖片顯示的起始位置。
-
定義一個ScaleTransform實例來縮放圖片的大小,你可以通過設置CenterX和CenterY的值來指定圖片縮放的原點。
這里使用TransformGroup.Inverse來轉換放縮原點
- 將兩個Transform放到一個TransformGroup里面,這樣Image控件就可以在顯示的時候綜合使用兩個Transform的效果了。
- 將TransformGroup放到當前窗體的Resource里面,這樣窗體里面所有的Image控件都可以引用到這個實例。
- 在鼠標移動事件里面修改TranslateTransform對應的值。
-
在鼠標滾輪事件里面修改ScaleTransform的ScaleX和ScaleY的值來縮放圖片
- XAML代碼:
<Grid x:Name="IMG" Width="500" Height="300" Background="Black"> <Grid.Resources> <TransformGroup x:Key="Imageview"> <ScaleTransform/> <TranslateTransform/> </TransformGroup> </Grid.Resources>
// 創建TransformGroup 集合,設立關鍵Key,並加入縮放和移動時所需函數。
<ScrollViewer HorizontalScrollBarVisibility="Disabled" VerticalScrollBarVisibility="Disabled" Background="Wheat" Cursor="SizeAll" Margin="5" Focusable="False" x:Name="BackFrame"> <ContentControl MouseLeftButtonDown="IMG1_MouseLeftButtonDown" MouseLeftButtonUp="IMG1_MouseLeftButtonUp" MouseMove="IMG1_MouseMove" MouseWheel="IMG1_MouseWheel" > <Image Name="IMG1" Source="/Image1;component/S.jpg" RenderTransform="{StaticResource Imageview}" Stretch="Uniform" RenderOptions.BitmapScalingMode="NearestNeighbor" ></Image> </ContentControl> </ScrollViewer> </Grid>
//創建ScrollViewer容器來放置在ContentControl 中添加鼠標事件和圖片,並把圖片////RenderTransform的變換信息設置為靜態資源 Imageview。////RenderOptions.BitmapScalingMode="NearestNeighbor"使圖片的變幻過程得到優化。防止出現移動和放縮圖片模糊的情況。
- C#代碼:
private bool mouseDown; private Point mouseXY; private void IMG1_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { var img = sender as ContentControl; if (img == null) { return; } img.CaptureMouse(); mouseDown = true; mouseXY = e.GetPosition(img); }
鼠標按下時的事件,啟用捕獲鼠標位置並把坐標賦值給mouseXY.
private void IMG1_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { var img = sender as ContentControl; if (img == null) { return; } img.ReleaseMouseCapture(); mouseDown = false; }
鼠標松開時的事件,停止捕獲鼠標位置。
private void IMG1_MouseMove(object sender, MouseEventArgs e) { var img = sender as ContentControl; if (img == null) { return; } if (mouseDown) { Domousemove(img, e); } }
鼠標移動時的事件,當鼠標按下並移動時發生
Domousemove(img, e);函數
private void Domousemove(ContentControl img, MouseEventArgs e) { if (e.LeftButton != MouseButtonState.Pressed) { return; } var group = IMG.FindResource("Imageview") as TransformGroup; var transform = group.Children[1] as TranslateTransform; var position = e.GetPosition(img); transform.X-= mouseXY.X- position.X; transform.Y -= mouseXY.Y - position.Y; mouseXY = position; }
group.Children中的第二個是移動的函數
它根據X.Y的值來移動。並把當前鼠標位置賦值給mouseXY.
private void IMG1_MouseWheel(object sender, MouseWheelEventArgs e) { var img = sender as ContentControl; if (img == null) { return; } var point = e.GetPosition(img); var group = IMG.FindResource("Imageview") as TransformGroup; var delta = e.Delta * 0.001; DowheelZoom(group, point, delta); }
鼠標滑輪事件,得到坐標,放縮函數和滑輪指數,由於滑輪值變化較大所以*0.001.
private void DowheelZoom(TransformGroup group, Point point, double delta) { var pointToContent = group.Inverse.Transform(point); var transform = group.Children[0] as ScaleTransform; if (transform.ScaleX + delta < 0.1) return; transform.ScaleX += delta; transform.ScaleY += delta; var transform1 = group.Children[1] as TranslateTransform; transform1.X = -1 * ((pointToContent.X * transform.ScaleX) - point.X); transform1.Y = -1 * ((pointToContent.Y * transform.ScaleY) - point.Y); }
Group.Children中的第一個是放縮函數。
如果ScaleX+滑輪指數小於0.1時就返回。
var pointToContent = group.Inverse.Transform(point);
獲取此變換的逆變換的值
使圖片放縮后,放縮原點也隨之變化。
vartransform1 = 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 DowheelZoom(TransformGroup group, Point point ,double delta) { var transform = group.Children[0] as ScaleTransform; if (transform.ScaleX + delta < 0.1) return; transform.CenterX= point1.X-1; transform.CenterY= point1.Y-1; transform.ScaleX += delta; transform.ScaleY += delta; }
如果這樣寫也可以時圖片放大縮小,並且在自己設定的點放大縮小,但是等圖片放大到一定程度時,放大后,把鼠標移到這個點,此時的放縮原點隨着鼠標到了這個點處。鼠標獲取的坐標是相對與圖片來說的。所以當在這個點處進行縮放時,圖片會一下切換到以這一點為中心進行縮放。
為了避免這種跳躍式的變換。就要把鼠標的坐標變換成原圖片即沒有放縮過的圖片上的坐標。這樣放縮就不會出現跳躍了。
這種變換坐標看起來麻煩,其實WPF已經給我們提供了函數
TransformGroup.Inverse可以把轉換后圖片上的坐標轉換為在原始圖片的坐標。
var pointToContent =group.Inverse.Transform(point);
vartransform = group.Children[0] as ScaleTransform;
if(transform.ScaleX + delta < 1) return;
transform.CenterX =pointToContent.X;
transform.CenterY =pointToContent.Y;
這樣就很大程度上減少了圖片的跳躍。但還是有小幅度的差距。
所以運用算法可以來彌補中間的差距。具體參看第一次加入的DowheelZoom
最終圖片的放大縮小效果圖