news 2026/7/11 2:21:21

WPF Matrix结构体方法ScaleAt的坐标系

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
WPF Matrix结构体方法ScaleAt的坐标系

代码:

namespace MatrixTransformTest { /// <summary> /// MainWindow.xaml 的交互逻辑 /// </summary> public partial class MainWindow : Window { private MatrixTransform buttonTransform; public MainWindow() { InitializeComponent(); InitializeButtonTransform(); } private void MyButton_OnClick(object sender, RoutedEventArgs e) { Debug.WriteLine("=== 点击button1 ==="); Debug.WriteLine($"缩放前矩阵: {MatrixToString(buttonTransform.Matrix)}"); Matrix matrix = buttonTransform.Matrix; matrix.ScaleAt(1.1, 1.1, 100, 100); Debug.WriteLine($"缩放后矩阵: {MatrixToString(matrix)}"); Debug.WriteLine($"中心点(100,100)变换后: {matrix.Transform(new Point(100, 100))}"); buttonTransform.Matrix = matrix; } private void InitializeButtonTransform() { // 创建并初始化MatrixTransform buttonTransform = new MatrixTransform(); myButton.RenderTransform = buttonTransform; // 设置初始变换 ResetTransform(); } private void ResetTransform() { // 重置为单位矩阵(无变换) buttonTransform.Matrix = Matrix.Identity; } private void MyButton2_OnClick(object sender, RoutedEventArgs e) { Debug.WriteLine("=== 点击button2 ==="); Debug.WriteLine($"缩放前矩阵: {MatrixToString(buttonTransform.Matrix)}"); Matrix matrix = buttonTransform.Matrix; matrix.ScaleAt(1.1, 1.1, 0, 0); Debug.WriteLine($"缩放后矩阵: {MatrixToString(matrix)}"); buttonTransform.Matrix = matrix; } private string MatrixToString(Matrix m) { return $"[{m.M11:F2},{m.M12:F2},{m.M21:F2},{m.M22:F2},{m.OffsetX:F2},{m.OffsetY:F2}]"; } } }

xaml:

<Window x:Class="MatrixTransformTest.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:local="clr-namespace:MatrixTransformTest" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" Title="MainWindow" Width="800" Height="450" mc:Ignorable="d"> <Grid ShowGridLines="True"> <Grid.RowDefinitions> <RowDefinition Height="200" /> <RowDefinition /> <RowDefinition /> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition Width="200" /> <ColumnDefinition /> <ColumnDefinition /> </Grid.ColumnDefinitions> <Button Name="myButton" Grid.Row="0" Grid.Column="0" Width="100" Height="100" HorizontalAlignment="Right" VerticalAlignment="Bottom" Content="Button1" Click="MyButton_OnClick" /> <Button Name="myButton2" Grid.Row="2" Grid.Column="0" Content="Button2" Click="MyButton2_OnClick"/> </Grid> </Window>

刚启动的界面:

如果一直点击Button1的话,Buttno1的右下角始终与Grid的行列交界重合:

因为Button1的点击事件是以坐标点(100,100)进行缩放,控件的高宽均为100,所以坐标点(100,100)就是Button1的右下角。

同理,如果一直点击Button2的话,Buttno1的左上角位置也不会发生改变:

这两种情况实际都符合设想。

同理,Button2点击事件是以(0,0)点为原点进行缩放,所以先点几下按钮1,然后点击按钮2的时候,按钮1的左上角应该不动,视觉上是向右下方进行延申。

但是测试发现我的观点是错误的,实际情况是:先点几下按钮1,然后点击按钮2的时候按钮1的左上角也一直在动。

比如在上图基础上又点了几下按钮2:

发现按钮1的左上角也一直在偏移,这就说明我对缩放中心的理解是错的。

看一下输出:

先了解一下缩放因子:

图源:深入浅出WPF变换(Transform)之矩阵(Matrix) - 叶落劲秋 - 博客园

问题核心:ScaleAt的中心点坐标系统

关键事实

  • matrix.ScaleAt(scaleX, scaleY, centerX, centerY)中的centerX, centerY相对于当前变换后的坐标系统

  • 不是相对于按钮的局部坐标,也不是绝对的窗口坐标

  • 每次变换后,整个坐标系都在变化

分析现象

初始状态:

  • 按钮在Grid的(0,0)单元格,右下对齐

  • 按钮的局部坐标系:左上角(0,0),右下角(100,100)

  • buttonTransform.Matrix = Matrix.Identity(单位矩阵)

场景1:只点击button2

matrix.ScaleAt(1.1, 1.1, 0, 0);
  1. 第1次点击

    • 当前矩阵是单位矩阵[1,0,0,1,0,0]

    • 以(0,0)为中心放大1.1倍

    • 新矩阵:[1.1,0,0,1.1,0,0]

    • 按钮变大,但位置不变(OffsetX=0, OffsetY=0)

  2. 第2次点击

    • 当前矩阵:[1.1,0,0,1.1,0,0]

    • 还是以(0,0)为中心放大

    • 实际效果:以当前坐标系的原点(0,0)放大

    • 因为OffsetX和OffsetY一直是0,所以按钮位置不变

场景2:先点击button1,再点击button2

// button1: matrix.ScaleAt(1.1, 1.1, 100, 100); // button2: matrix.ScaleAt(1.1, 1.1, 0, 0);
  1. 第1次点击button1

    • 以(100,100)为中心放大1.1倍

    • 矩阵从[1,0,0,1,0,0]变为[1.1,0,0,1.1,-10,-10]

    • 为什么Offset变成(-10,-10)?

      • 公式:OffsetX = centerX * (1 - scaleX)

      • 100 * (1 - 1.1) = 100 * (-0.1) = -10

    • 按钮向左上方移动了10像素

  2. 第2次点击button2

    • 当前矩阵:[1.1,0,0,1.1,-10,-10]

    • 以(0,0)为中心放大1.1倍

    • 新的Offset计算:

      • OffsetX' = -10 * 1.1 + 0 * (1 - 1.1) = -11

      • 按钮继续向左移动

    • 每次点击都向左上移动更多

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/8 19:48:31

18、Outlook Express使用指南:邮件收发、管理与即时通讯全攻略

Outlook Express使用指南:邮件收发、管理与即时通讯全攻略 在当今数字化的时代,电子邮件和即时通讯已经成为人们日常沟通中不可或缺的工具。Outlook Express作为一款经典的邮件客户端,为用户提供了便捷的邮件收发、联系人管理以及即时通讯等功能。本文将详细介绍Outlook Ex…

作者头像 李华
网站建设 2026/7/10 22:10:01

Excalidraw支持Latex公式吗?数学表达实测结果

Excalidraw 支持 LaTeX 公式吗&#xff1f;数学表达实测结果 在技术团队的日常协作中&#xff0c;我们常常面临一个尴尬场景&#xff1a;想在白板上写个简单的贝叶斯公式 $P(A|B) \frac{P(B|A)P(A)}{P(B)}$&#xff0c;却发现工具只支持纯文本或贴图。这种割裂感不仅打断思路&…

作者头像 李华
网站建设 2026/7/8 10:55:49

吃透指针通用用法:回调函数与 qsort 的使用和模拟

&#x1f3e0;个人主页&#xff1a;黎雁 &#x1f3ac;作者简介&#xff1a;C/C/JAVA后端开发学习者 ❄️个人专栏&#xff1a;C语言、数据结构&#xff08;C语言&#xff09;、EasyX、游戏、规划 ✨ 从来绝巘须孤往&#xff0c;万里同尘即玉京 文章目录前景回顾&#xff1a;前…

作者头像 李华
网站建设 2026/7/10 4:47:38

Excalidraw在新能源电站设计中的可视化应用

Excalidraw在新能源电站设计中的可视化应用 在光伏、风电等新能源项目快速落地的今天&#xff0c;一个普遍存在的痛点正日益凸显&#xff1a;从技术构想到可执行方案之间的“表达鸿沟”。工程师们常常面临这样的场景——会议桌上&#xff0c;电气专业刚讲完逆变器选型逻辑&…

作者头像 李华
网站建设 2026/7/10 2:59:02

Excalidraw开源社区活跃度分析:为何持续走红?

Excalidraw开源社区活跃度分析&#xff1a;为何持续走红&#xff1f; 在远程办公成为常态的今天&#xff0c;团队沟通早已不再局限于文字和语音。一张随手勾勒的草图&#xff0c;往往比千言万语更能精准传达想法——尤其是在技术讨论中&#xff0c;一个简单的架构框图可能就决…

作者头像 李华
网站建设 2026/7/10 8:17:20

Excalidraw绘图支持嵌入实时数据卡片,动态刷新

Excalidraw绘图支持嵌入实时数据卡片&#xff0c;动态刷新 在今天的远程协作环境中&#xff0c;一张静态的架构图往往刚画完就已经“过时”了。服务状态变了、负载升高了、CI/CD 流水线又失败了一次——但白板上的图形却纹丝不动。这种信息滞后不仅影响决策效率&#xff0c;更可…

作者头像 李华