在WPF里面實現以鼠標位置為中心縮放移動圖片

作者: donjuan  來源: 博客園  發布時間: 2009-10-30 11:40  閱讀: 3865 次  推薦: 0   原文鏈接   [收藏]  

  在以前的文章使用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("ImageCompareR
esources") as TransformGroup;

            Debug.Assert(group != null, "Can't find transform group from image compare p
anel 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("ImageCompareR
esources") 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;

        }
 

 

 

0
0
 
標簽:WPF
 
 

文章列表

arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()