WPF中文网

什么是Transform转换

在WPF框架中有一个抽象类叫Transform,它定义了实现二维平面中的转换的功能。它包括旋转 (RotateTransform)、缩放 (ScaleTransform)、倾斜 (SkewTransform) 和平移 (TranslateTransform)4个子类。它定义如何将点从一个坐标空间映射或转换到另一个坐标空间。 此映射由转换 Matrix 来描述,它是一个三行三列 Double 值的集合。

这个类可以实现控件的各种转换,比如放置、缩放、倾斜和平移。

首先我们来看一下Transform的定义:

public abstract class Transform : GeneralTransform, IResource
{
    public static Transform Identity { get; }
    public abstract Matrix Value { get; }
    public override GeneralTransform Inverse { get; }

    public static Transform Parse(string source);
    public Transform Clone();
    public Transform CloneCurrentValue();
    public override Rect TransformBounds(Rect rect);
    public override bool TryTransform(Point inPoint, out Point result);

}

Identity表示获取一个Transform 标识转换,Value表示当前的Matrix矩阵变换,Inverse表示获取当前转换的反函数,它的类型是GeneralTransform。

对于任何Transform对象,一旦使用它去变换某个元素后,它的Inverse属性就会有值。它是GeneralTransform类型,也就是一个变换器,用于将元素变换到原始状态,相当于是Transform的反向操作。需要注意的是,Inverse是用于返回到原始状态的,而不上一次转换的状态。

以下内容来自官网的解释

通过操作矩阵值,可以旋转、缩放、倾斜和移动(转换)对象。 例如,如果将第三行第一列的值(OffsetX 值)更改为 100,可使用它来将对象沿 x 轴移动 100 个单位。 如果将第二行第二列中的值更改为 3,可使用它来将对象的当前高度拉伸三倍。 如果同时更改这两个值,将使对象沿 x 轴移动 100 个单位,并将其高度拉伸 3 倍。 由于WPF仅支持仿射转换,因此右边列中的值始终为 0, 0, 1。

由于直接操作矩阵去设置旋转、缩放、倾斜和移动功能比较复杂,所以WPF提供了4个子类来设置相应的功能。例如,借助 ScaleTransform 类,通过设置其 ScaleX 和 ScaleY 属性即可缩放对象,而无需操作转换矩阵。 同样,借助 RotateTransform 类,只需设置其 Angle 属性,即可旋转对象。

下表中展示了2D转换

子类说明效果备注
RotateTransform按指定的 Angle 旋转元素。表示旋转一个对象的角度
ScaleTransform按指定的 ScaleX 和 ScaleY 量缩放元素。表示放大或缩小一个对象
SkewTransform按指定的 AngleX 和 AngleY 量倾斜元素。表示倾斜一个对象
TranslateTransform按指定的 X 和 Y 量移动(转换)元素。表示移动一个对象
TransformGroup将多个 TransformGroup 对象组合成单个 Transform,然后你可以应用它来转换属性。
MatrixTransform建其他 Transform 类不提供的自定义转换。 使用 MatrixTransform 时,可直接操作矩阵。

转换和坐标系

转换对象时,不只是转换对象,还会转换该对象所在的坐标空间。 默认情况下,转换基于目标对象坐标系的原点 (0,0) 居中。 唯一例外是 TranslateTransform;由于其转换效果无论基于何处居中都相同,因此 TranslateTransform 没有可设置的中心属性。

以下示例使用 RotateTransform 围绕默认中心点 (0, 0) 将 Rectangle 元素(一种 FrameworkElement)旋转 45 度。 下图显示了旋转效果。

围绕点 (0,0) 旋转 45 度的矩形元素

<Canvas Width="200" Height="200">
  <Rectangle 
    Canvas.Left="100" Canvas.Top="100"
    Width="50" Height="50" 
    Fill="RoyalBlue" Opacity="1.0">
    <Rectangle.RenderTransform>
      <RotateTransform Angle="45" />
    </Rectangle.RenderTransform>
  </Rectangle>
</Canvas>

默认情况下,元素会围绕其左上角 (0, 0) 旋转。 RotateTransform、ScaleTransform 和 SkewTransform 类提供 CenterX 和 CenterY 属性,以便可以指定应用转换的点。

下面的示例也使用 RotateTransform 将 Rectangle 元素旋转 45 度;但是,这次对 CenterX 和 CenterY 属性进行了设置,使得 RotateTransform 以 (25, 25) 为中心。 下图显示了旋转效果。

围绕点 (25, 25) 旋转 45 度矩形元素

<Canvas Width="200" Height="200">
  <Rectangle 
    Canvas.Left="100" Canvas.Top="100"
    Width="50" Height="50" 
    Fill="RoyalBlue" Opacity="1.0">
    <Rectangle.RenderTransform>
      <RotateTransform Angle="45" CenterX="25" CenterY="25" />
    </Rectangle.RenderTransform>
  </Rectangle>
</Canvas>

FrameworkElement转换

FrameworkElement定义了两个有关转换的属性,分别是LayoutTransform和RenderTransform,前者在布局步骤前应用的转换,后者在布局步骤完成后应用,官方推荐使用RenderTransform,原因是可获得性能优势。另外,因为它们继承自 Animatable 类,所以 Transform 类可以被制作成动画。 若要对 Transform 进行动画处理,请将兼容类型的动画应用到要进行动画处理的属性。关于动画,我们会在下一章进行讲解。

下一节,我们将依次演示WPF的转换功能。

——重庆教主 2023年11月2日

RotateTransform表示旋转一个对象的角度。首先我们来看一下它的定义:

public sealed class RotateTransform : Transform
{

    public static readonly DependencyProperty AngleProperty;
    public static readonly DependencyProperty CenterXProperty;
    public static readonly DependencyProperty CenterYProperty;

    public RotateTransform();
    public RotateTransform(double angle);
    public RotateTransform(double angle, double centerX, double centerY);

    public double Angle { get; set; }
    public double CenterX { get; set; }
    public double CenterY { get; set; }
    public override Matrix Value { get; }

public RotateTransform Clone();
public RotateTransform CloneCurrentValue();
protected override Freezable CreateInstanceCore();
}

Angle属性表示获取或设置顺时针旋转的角度(以度为单位)。默认值是0度。CenterX 和CenterY 表示获取或设置旋转中心点的 x y坐标,Value属性表示当前转换的矩阵。通常我们只需要设置Angle、CenterX 和CenterY即可。

一、RotateTransform示例

前端代码

<Window x:Class="HelloWorld.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:HelloWorld" 
        xmlns:controls="clr-namespace:HelloWorld.Controls"
        xmlns:helper="clr-namespace:HelloWorld.MVVM"
        mc:Ignorable="d" Background="Lavender"   
        MouseMove="Window_MouseMove"
        Title="WPF中文网 - www.wpfsoft.com" Height="350" Width="500">
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Button Grid.Column="0" 
                Width="100" 
                Height="25" 
                Content="RotateTransform" 
                HorizontalAlignment="Center" 
                VerticalAlignment="Center">
            <Button.RenderTransform>
                <RotateTransform Angle="{Binding ElementName=slider,Path=Value}"/>
            </Button.RenderTransform>
        </Button>
        <Button Grid.Column="1" 
                Width="100" 
                Height="25" 
                Content="RotateTransform" 
                HorizontalAlignment="Center" 
                VerticalAlignment="Center">
            <Button.RenderTransform>
                <RotateTransform Angle="{Binding ElementName=slider,Path=Value}" 
                                 CenterX="50" CenterY="12.5"/>
            </Button.RenderTransform>
        </Button>
        
        <Button x:Name="button" 
                Grid.Column="2" 
                Width="100" 
                Height="25" 
                Content="RotateTransform" 
                HorizontalAlignment="Center" 
                VerticalAlignment="Center">
            <Button.RenderTransform>
                <RotateTransform Angle="45"/>
            </Button.RenderTransform>
        </Button>
        <Slider x:Name="slider" 
                Grid.ColumnSpan="3" 
                Margin="30" 
                HorizontalAlignment="Left" 
                VerticalAlignment="Bottom" 
                Value="0" 
                Maximum="720" 
                Width="400" />
    </Grid>
</Window>

在上面的代码中,我们分别用了3个按钮,每个按钮的RenderTransform属性都实例化了一个RotateTransform旋转类型,第一个RotateTransform只设置了Angle度,表示以对象左上角为轴圆心旋转,第二个RotateTransform 同时设置了Angle度、CenterX和CenterY设置为按钮尺寸的一半,表示以对象中间为轴圆心旋转,第三个RotateTransform 的实现比较有趣,我们先是初始化旋转角度为45度,在鼠标移动时,将鼠标的XY坐标相加并实时赋值给Angle属性,所以它看起来是跟随鼠标移动而旋转。

然后我们在C#后端写如下的代码:

private void Window_MouseMove(object sender, MouseEventArgs e)
{
    if (button.RenderTransform is RotateTransform rotateTransform)
    {
        Point point = e.GetPosition(this);
        rotateTransform.Angle = point.X + point.Y;
    }
}

另外,这些Angle值的设置都是通过slider绑定的。最后,它们会呈现如下的样子:

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:089-《RotateTransform旋转转换》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年11月02日

ScaleTransform表示在二维xy坐标系内缩放对象。所以它放大缩小的方向只有两个,分别是X方向和Y方向。另外,每个方向上需要设置一个中心点。

public sealed class ScaleTransform : Transform
{
    public static readonly DependencyProperty ScaleXProperty;
    public static readonly DependencyProperty ScaleYProperty;
    public static readonly DependencyProperty CenterXProperty;
    public static readonly DependencyProperty CenterYProperty;

    public ScaleTransform();
    public ScaleTransform(double scaleX, double scaleY);
    public ScaleTransform(double scaleX, double scaleY, double centerX, double centerY);

    public double ScaleX { get; set; }
    public double ScaleY { get; set; }
    public double CenterX { get; set; }
    public double CenterY { get; set; }
    public override Matrix Value { get; }

    public ScaleTransform Clone();
    public ScaleTransform CloneCurrentValue();
    protected override Freezable CreateInstanceCore();
}ScaleX

ScaleX属性:获取或设置X轴缩放比例。

ScaleY属性:获取或设置Y轴缩放比例。

CenterX属性:获取或设置当前缩放对象的X轴的中心坐标。

CenterY属性:获取或设置当前缩放对象的Y轴的中心坐标。

一、ScaleTransform示例

前端代码

<Window x:Class="HelloWorld.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:HelloWorld" 
        xmlns:controls="clr-namespace:HelloWorld.Controls"
        xmlns:helper="clr-namespace:HelloWorld.MVVM"
        mc:Ignorable="d" Background="Lavender"   
        MouseMove="Window_MouseMove"
        Title="WPF中文网 - www.wpfsoft.com" Height="350" Width="500">
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Button Grid.Column="0" 
                Width="100" 
                Height="25" 
                Content="ScaleTransform" 
                HorizontalAlignment="Center" 
                VerticalAlignment="Center">
            <Button.RenderTransform>
                <ScaleTransform ScaleX="{Binding ElementName=sliderX,Path=Value}"
                                ScaleY="{Binding ElementName=sliderY,Path=Value}"/>
            </Button.RenderTransform>
        </Button>       
        
        <Button x:Name="button" 
                Grid.Column="1" 
                Width="100" 
                Height="25" 
                Content="ScaleTransform" 
                HorizontalAlignment="Center" 
                VerticalAlignment="Center">
            <Button.RenderTransform>
                <ScaleTransform CenterX="50" CenterY="12.5"/>
            </Button.RenderTransform>
        </Button>
        <Slider x:Name="sliderX" 
                Grid.ColumnSpan="3" 
                Margin="40 25 20 15" 
                HorizontalAlignment="Left" 
                VerticalAlignment="Bottom" 
                Value="1" 
                Maximum="10" 
                Width="400" />
        <Slider x:Name="sliderY" 
                Orientation="Vertical"
                Grid.ColumnSpan="3" 
                Margin="20" 
                HorizontalAlignment="Left" 
                VerticalAlignment="Bottom" 
                Value="1" 
                Maximum="10" 
                Height="300" />
    </Grid>
</Window>

在前端代码中,我们实例化了两个Button,并分别设置了ScaleTransform。第一个ScaleTransform 对象 的ScaleX和ScaleY分别绑定了一个slider,方便设置它们的放大缩放值。第二个ScaleTransform 设置了CenterX和CenterY,使其在中心位置放大缩小,最后在C#后端鼠标的移动事件下设置放大缩小值。

private void Window_MouseMove(object sender, MouseEventArgs e)
{
    if (button.RenderTransform is ScaleTransform t)
    {
        Point point = e.GetPosition(this);
        t.ScaleX = (point.X + point.Y) / 200;
        t.ScaleY = (point.X + point.Y) / 200;
    }
}

最后,运行之后的效果如下:

总结:ScaleX和ScaleY的默认值为1,表示不进行放大缩小,大于1时,表示放大,小于1时,表示缩小。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:090-《ScaleTransform缩放》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年11月02日

SkewTransform表示倾斜某个对象,它有两个方向的倾斜角度可以设置,AngleX表示设置x 轴倾斜角度,该角度是从 y 轴逆时针旋转后测量得到,单位为度。AngleY表示设置y 轴倾斜角度,该角度通过测量从 x 轴逆时针旋转得到的角度度数。另外,它也有CenterX和CenterY,表示倾斜转换中心的xy坐标。

public sealed class SkewTransform : Transform
{
   public static readonly DependencyProperty AngleXProperty;
   public static readonly DependencyProperty AngleYProperty;
   public static readonly DependencyProperty CenterXProperty;
   public static readonly DependencyProperty CenterYProperty;

   public SkewTransform();
   public SkewTransform(double angleX, double angleY);
   public SkewTransform(double angleX, double angleY, double centerX, double centerY);

   public double AngleX { get; set; }
   public double AngleY { get; set; }
   public double CenterX { get; set; }
   public double CenterY { get; set; }
   public override Matrix Value { get; }

   public SkewTransform Clone();
   public SkewTransform CloneCurrentValue();

}

一、SkewTransform示例

注意:SkewTransform 可以在XY平面中为一个对象创建 3 维深度的错觉。下面我们通过一个示例,合理的调节X和Y方面的倾斜度,就可以模拟出3维变换的错觉。

前端代码

<Window x:Class="HelloWorld.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:HelloWorld" 
        xmlns:controls="clr-namespace:HelloWorld.Controls"
        xmlns:helper="clr-namespace:HelloWorld.MVVM"
        mc:Ignorable="d" Background="Lavender"   
        MouseMove="Window_MouseMove"
        Title="WPF中文网 - www.wpfsoft.com" Height="350" Width="500">
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Border Grid.Column="0" 
                Width="120" 
                Height="120" 
                Background="LightBlue"
                HorizontalAlignment="Center" 
                VerticalAlignment="Center">
            <Border.RenderTransform>
                <SkewTransform CenterX="0" CenterY="0"
                               AngleX="{Binding ElementName=sliderX,Path=Value}"
                               AngleY="{Binding ElementName=sliderY,Path=Value}"/>
            </Border.RenderTransform>
        </Border>       
        
        <Button x:Name="button" 
                Grid.Column="1" 
                Background="LightBlue"
                Width="120" 
                Height="120" 
                Content="SkewTransform倾斜" 
                HorizontalAlignment="Center" 
                VerticalAlignment="Center">
            <Button.RenderTransform>
                <SkewTransform CenterX="60" CenterY="60"
                               AngleX="{Binding ElementName=sliderX,Path=Value}"
                               AngleY="{Binding ElementName=sliderY,Path=Value}"/>
            </Button.RenderTransform>
        </Button>
        <Slider x:Name="sliderX" 
                Grid.ColumnSpan="3" 
                Margin="40 25 20 15" 
                HorizontalAlignment="Left" 
                VerticalAlignment="Bottom" 
                Value="0" 
                Maximum="180" 
                Width="400" />
        <Slider x:Name="sliderY" 
                Orientation="Vertical"
                Grid.ColumnSpan="3" 
                Margin="20" 
                HorizontalAlignment="Left" 
                VerticalAlignment="Bottom" 
                Value="0" 
                Maximum="180" 
                Height="300" />
    </Grid>
</Window>

接下来F5运行,拖动slider,使用XY两个方向进行倾斜。

注意右边的对象,我们将转换的坐标设置到对象的中心点,这样在拖动X轴slider时,就会呈现出3维变换的效果。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:091-《SkewTransform倾斜》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年11月02日

TranslateTransform只有X和Y两个属性,分别代表X轴和Y轴上的平移距离。

public sealed class TranslateTransform : Transform
{
    public static readonly DependencyProperty XProperty;
    public static readonly DependencyProperty YProperty;

    public TranslateTransform();
    public TranslateTransform(double offsetX, double offsetY);

    public double X { get; set; }
    public double Y { get; set; }
    public override Matrix Value { get; }

    public TranslateTransform Clone();
    public TranslateTransform CloneCurrentValue();

}

接下来,我们以一个例子来说明平移转换的用法。

前端代码

<Window x:Class="HelloWorld.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:HelloWorld" 
        xmlns:controls="clr-namespace:HelloWorld.Controls"
        xmlns:helper="clr-namespace:HelloWorld.MVVM"
        mc:Ignorable="d" Background="Lavender"   
        MouseMove="Window_MouseMove"
        MouseDown="Window_MouseDown"
        MouseUp="Window_MouseUp"
        Title="WPF中文网 - www.wpfsoft.com" Height="350" Width="500">
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition/>
            <ColumnDefinition/>
            <ColumnDefinition/>
        </Grid.ColumnDefinitions>
        <Border Grid.Column="0" 
                Width="120" 
                Height="120" 
                Background="LightBlue"
                HorizontalAlignment="Center" 
                VerticalAlignment="Center">
            <Border.RenderTransform>
                <TranslateTransform 
                               X="{Binding ElementName=sliderX,Path=Value}"
                               Y="{Binding ElementName=sliderY,Path=Value}"/>
            </Border.RenderTransform>
        </Border>

        <Border x:Name="border" 
                Grid.Column="1" 
                Background="LightGreen"
                Width="120" 
                Height="120" 
                HorizontalAlignment="Center" 
                VerticalAlignment="Center">
            <Border.RenderTransform>
                <TranslateTransform/>
            </Border.RenderTransform>
        </Border>
        <Slider x:Name="sliderX" 
                Grid.ColumnSpan="3" 
                Margin="40 25 20 15" 
                HorizontalAlignment="Left" 
                VerticalAlignment="Bottom" 
                Value="0" 
                Maximum="180" 
                Width="400" />
        <Slider x:Name="sliderY" 
                Orientation="Vertical"
                Grid.ColumnSpan="3" 
                Margin="20" 
                HorizontalAlignment="Left" 
                VerticalAlignment="Bottom" 
                Value="0" 
                Maximum="180" 
                Height="300" />
    </Grid>
</Window>

在这个例子中,左边的border的TranslateTransform 对象的值直接绑定到slider,拖动两个slider就可以控制border水平和垂直方向的位置。右边的border则利用鼠标按下、移动和抬起事件,初步实现了用鼠标去移动位置的功能。

后台代码

public partial class MainWindow : Window
{
    public Point DownPoint { get; private set; } = new Point(0, 0);
    public bool IsMouseDown { get; private set; } = false;
    public MainWindow()
    {
        InitializeComponent();
    }

    private void Window_MouseMove(object sender, MouseEventArgs e)
    {
        if (!IsMouseDown) return;

        if (border.RenderTransform is TranslateTransform t)
        {
            Point point = e.GetPosition(this);
            t.X = (point.X - DownPoint.X);
            t.Y = (point.Y - DownPoint.Y);
        }
    }

    private void Window_MouseDown(object sender, MouseButtonEventArgs e)
    {
        IsMouseDown = true;
        DownPoint = e.GetPosition(this);
    }

    private void Window_MouseUp(object sender, MouseButtonEventArgs e)
    {
        IsMouseDown = false;
    }
}

当然,这个例子还不够完善,我们会在后面的图片查看器demo中去完善平移功能。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:092-《TranslateTransform平移》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年11月02日

TransformGroup同样继承于Transform,但是它多了一个TransformCollection类型的Children属性,表示可以将多个Transform组合成一个复合转换。复合转换就是将旋转、平移、缩放、倾斜合并起来形成一个集团转换。由于Children是一个集合,所以可以叠加多个不同的转换。

public sealed class TransformGroup : Transform
{
    public static readonly DependencyProperty ChildrenProperty;

    public TransformGroup();

    public TransformCollection Children { get; set; }
    public override Matrix Value { get; }

    public TransformGroup Clone();
    public TransformGroup CloneCurrentValue();

}

下面我们用一个示例演示TransformGroup的用法,开发一款图片查看器demo,在查看图片的时候,可以移动和缩放图片。移动图片需要用到TranslateTransform平移对象,缩放图片需要用到ScaleTransform缩放对象,将两个对象放到TransformGroup的Children属性中,最后将TransformGroup 作用到Image控件上即可。

下一步就是在鼠标的操作下,根据当前鼠标坐标位置,不断计算平移位置和缩放倍数,并修改TranslateTransform和ScaleTransform的相关属性,即可完成图片查看器的基本功能。

首先准备一张图片,将图片以Resource资源导入到项目中。

然后是XAML前端代码的编写。

<Window x:Class="HelloWorld.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:HelloWorld" 
        xmlns:controls="clr-namespace:HelloWorld.Controls"
        xmlns:helper="clr-namespace:HelloWorld.MVVM"
        mc:Ignorable="d" Background="Black"  
        Title="WPF中文网 - www.wpfsoft.com" Height="350" Width="500">
    <Window.DataContext>
        <local:MainViewModel/>
    </Window.DataContext>
    <Grid>
        <Canvas x:Name="canvas" Background="Transparent"
                MouseWheel="canvas_MouseWheel"
                MouseMove="canvas_MouseMove"
                MouseLeftButtonUp="canvas_MouseLeftButtonUp"
                MouseLeftButtonDown="canvas_MouseLeftButtonDown">
            <Image x:Name="image" Source="/Images/lyf.png"/>
        </Canvas>
        <TextBlock HorizontalAlignment="Left" 
                   VerticalAlignment="Bottom" 
                   Margin="15" Foreground="Red">
            <Run Text="X:"/>
            <Run Text="{Binding X}"/>
            <Run Text="Y:"/>
            <Run Text="{Binding Y}"/>
            <Run Text="Delta:"/>
            <Run Text="{Binding D}"/>
        </TextBlock>
    </Grid>
</Window>

重庆教主友情提示

注意,Canvas一定要设置背景颜色哦!不然捕捉不到鼠标事件。

在上面的代码中,我们实例化了一个Canvas对象用来接收鼠标的操作事件,并在里面实例化了一个Image控件,TextBlock 控件用来显示鼠标的坐标。

后端代码

using HelloWorld.Controls;
using HelloWorld.MVVM;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace HelloWorld
{
    /// <summary>
    /// MainWindow.xaml 的交互逻辑
    /// </summary>
    public partial class MainWindow : Window
    {
        private MainViewModel vm;
        private bool isMouseDown = false;
        private Point mousePoint = new Point(0,0);
        private TranslateTransform translateTransform = new TranslateTransform();
        private ScaleTransform scaleTransform = new ScaleTransform();
        private TransformGroup group = new TransformGroup();
        public MainWindow()
        {
            InitializeComponent();

            Loaded += (s, e) =>
            {
                vm = DataContext as MainViewModel;
                group.Children.Add(scaleTransform);
                group.Children.Add(translateTransform);
                image.RenderTransform = group;
                var scale = Math.Min(
                    canvas.ActualWidth / image.ActualWidth, 
                    canvas.ActualHeight / image.ActualHeight);
                scaleTransform.ScaleX = scale;
                scaleTransform.ScaleY = scale;
                translateTransform.X = (canvas.ActualWidth - image.ActualWidth * scale) / 2;
            };
        }



        private void canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            isMouseDown = true;
            mousePoint = e.GetPosition(canvas);
        }

        private void canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            isMouseDown = false;
        }

        private void canvas_MouseMove(object sender, MouseEventArgs e)
        {
            var position = e.GetPosition(canvas);
            vm.X = position.X;
            vm.Y = position.Y;
            if (isMouseDown)
            {
                translateTransform.X += position.X - mousePoint.X;
                translateTransform.Y += position.Y - mousePoint.Y;
                mousePoint = position;
            }
        }

        private void canvas_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            var delta = e.Delta;
            double scale = delta * 0.001;
            var position = e.GetPosition(canvas);
            vm.X = position.X;
            vm.Y = position.Y;
            vm.D = delta;

            if (scaleTransform.ScaleX + scale < 0.1) return;

            Point inversePoint = group.Inverse.Transform(position);
            scaleTransform.ScaleX += scale;
            scaleTransform.ScaleY += scale;            
            translateTransform.X = -(inversePoint.X * scaleTransform.ScaleX - position.X);
            translateTransform.Y = -(inversePoint.Y * scaleTransform.ScaleY - position.Y);
        }
    }
}

第一步,定义TransformGroup 、ScaleTransform 和TranslateTransform 对象。

第二步,在初始化界面时,计算原始的图片放大倍率和平移位置。

第三步,在鼠标按下时设置标志位,鼠标移动时设置平移参数,鼠标滚动时计算缩放和平移,鼠标抬起时结束操作。

需要注意的是,进行缩放时需要拿到TransformGroup 的反函数并转换当前鼠标坐标,最后缩放结束后再设置平移位置,并再次取反。这样才能实现以鼠标坐标为中心对图片进行缩放。

当前课程源码下载:(注明:本站所有源代码请按标题搜索)

文件名:093-《TransformGroup复合转换》-源代码
链接:https://pan.baidu.com/s/1yu-q4tUtl0poLVgmcMfgBA
提取码:wpff

——重庆教主 2023年11月3日

copyright @重庆教主 WPF中文网 联系站长:(QQ)23611316 (微信)movieclip (QQ群).NET小白课堂:864486030 | 本文由WPF中文网原创发布,谢绝转载 渝ICP备2023009518号-1