当前位置: 首页 > news >正文

WPF 获取鼠标相对于控件的坐标信息,控制控件锚点放缩

使用MouseMove事件+e.GetPosition方法可以获取鼠标相对于控件的坐标信息,或者使用TranslatePoint间接计算。

Canvas是Panel的容器。

eLastMousePosition和relativeToPanel 是相同的

锚点放缩靠Mtatrix实现:

终点在于理解红色框框部分,我还没吃透,可以参考一下:

在RenderTransform上叠加一个ScaleAt-腾讯云开发者社区-腾讯云

理解了希望可以在评论区给出解释。谢谢。

下面是完整代码:

xaml:

<Windowx:Class="PanelZoomDemo.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"Title="Panel缩放演示 - 实时坐标"Width="900"Height="700"><TabControl><TabItem Header="aa"><Grid><Grid.ColumnDefinitions><ColumnDefinition Width="300" /><ColumnDefinition Width="10" /><ColumnDefinition Width="*" /></Grid.ColumnDefinitions><Grid.RowDefinitions><RowDefinition Height="Auto" /><RowDefinition Height="*" /><RowDefinition Height="Auto" /></Grid.RowDefinitions><!--  标题栏  --><StackPanelGrid.Row="0"Grid.ColumnSpan="3"Margin="10"><TextBlockFontSize="16"FontWeight="Bold"Text="Panel缩放演示 - 实时坐标跟踪" /><TextBlock Margin="0,5" Text="左键点击:放大 | 右键点击:缩小 | 移动鼠标实时显示坐标" /><TextBlockx:Name="ScaleInfoText"FontWeight="Bold"Foreground="Blue"Text="当前缩放: 1.00x" /></StackPanel><!--  左侧:坐标信息面板  --><BorderGrid.Row="1"Grid.Column="0"Margin="5"Background="#F5F5F5"BorderBrush="Gray"BorderThickness="1"><ScrollViewer VerticalScrollBarVisibility="Auto"><StackPanel Margin="15"><TextBlockMargin="0,0,0,10"FontSize="16"FontWeight="Bold"Foreground="DarkBlue"Text="实时坐标信息" /><GroupBox Margin="0,0,0,10" Header="鼠标状态"><StackPanel Margin="5"><TextBlockx:Name="MouseOverText"Margin="0,2"FontWeight="Bold"Foreground="Red"Text="位置: 在Panel外" /><TextBlockx:Name="MouseCoordText"Margin="0,5,0,2"Text="屏幕坐标: (0, 0)" /><TextBlockx:Name="RelativeCoordText"Margin="0,2"Text="相对Panel: (0, 0)" /><TextBlockx:Name="eRelativeCoordText"Margin="0,2"Text="e.Position Panel: (0, 0)" /></StackPanel></GroupBox><GroupBox Margin="0,0,0,10" Header="点击信息"><StackPanel Margin="5"><TextBlockx:Name="LastClickText"Margin="0,2"Text="最后点击: (0, 0)" /><TextBlockx:Name="ClickCountText"Margin="0,2"Text="点击次数: 0" /></StackPanel></GroupBox><GroupBox Margin="0,0,0,10" Header="变换信息"><StackPanel Margin="5"><TextBlockx:Name="ScaleText"Margin="0,2"Text="缩放比例: 1.00x" /><TextBlockx:Name="TransformText"Margin="0,2"Text="变换矩阵: Identity" /><TextBlockx:Name="PanelPositionText"Margin="0,2"Text="Panel位置: (100, 100)" /></StackPanel></GroupBox><GroupBox Header="操作提示"><StackPanel Margin="5"><TextBlock Margin="0,2" Text="• 左键点击:放大1.2倍" /><TextBlock Margin="0,2" Text="• 右键点击:缩小0.8倍" /><TextBlock Margin="0,2" Text="• 移动鼠标:实时跟踪坐标" /><TextBlock Margin="0,2" Text="• 标记点不会阻挡点击" /></StackPanel></GroupBox></StackPanel></ScrollViewer></Border><!--  右侧:Canvas绘图区域  --><Canvasx:Name="MainCanvas"Grid.Row="1"Grid.Column="2"Margin="5"Background="LightGray"MouseMove="OnCanvasMouseMove"><!--  测试Panel  --><Borderx:Name="TestPanel"Canvas.Left="100"Canvas.Top="100"Width="200"Height="150"Background="OrangeRed"BorderBrush="DarkRed"BorderThickness="3"CornerRadius="10"MouseDown="OnPanelMouseDown"MouseEnter="OnPanelMouseEnter"MouseLeave="OnPanelMouseLeave"MouseMove="OnPanelMouseMove"><Border.RenderTransform><MatrixTransform x:Name="PanelTransform" /></Border.RenderTransform><!--  Panel内部内容  --><StackPanel HorizontalAlignment="Center" VerticalAlignment="Center"><TextBlockHorizontalAlignment="Center"FontSize="16"FontWeight="Bold"Foreground="White"Text="点击我缩放" /><TextBlockMargin="0,5"HorizontalAlignment="Center"FontSize="12"Foreground="White"Text="左键放大 · 右键缩小" /><TextBlockx:Name="PanelCoordText"Margin="0,5"HorizontalAlignment="Center"FontSize="11"Foreground="White"Text="坐标: (0, 0)" /></StackPanel></Border><!--  鼠标位置指示器  --><Canvas x:Name="MouseIndicatorCanvas" /><!--  点击标记  --><Canvas x:Name="MarkerCanvas" /></Canvas><!--  底部日志  --><TextBoxx:Name="LogTextBox"Grid.Row="2"Grid.ColumnSpan="3"Height="120"Margin="10"Background="Black"FontFamily="Consolas"FontSize="10"Foreground="White"IsReadOnly="True"VerticalScrollBarVisibility="Auto" /></Grid></TabItem><TabItem Header="bb"><RectangleCanvas.Left="100"Canvas.Top="100"Width="50"Height="50"Fill="#CCCCCCFF"Stroke="Blue"StrokeThickness="2"><Rectangle.RenderTransform><TransformGroup><!--  第一次缩放:以中心点缩放2倍  --><ScaleTransform CenterX="50" CenterY="50" ScaleX="2" ScaleY="2" /><!--  第二次缩放:以右下角缩放1.5倍  --><ScaleTransform CenterX="25" CenterY="25" ScaleX="1.5" ScaleY="1.5" /><!--  可以继续添加更多变换  --></TransformGroup></Rectangle.RenderTransform></Rectangle></TabItem></TabControl></Window>

cs:
 

using System;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;namespace PanelZoomDemo
{public partial class MainWindow : Window{private StringBuilder logBuilder = new StringBuilder();private double currentScale = 1.0;private Point lastClickPosition = new Point(0, 0);private DispatcherTimer mouseMoveTimer;private Point lastMousePosition = new Point(0, 0);private Point eLastMousePosition = new Point(0, 0);private bool isMouseOverPanel = false;private int clickCount = 0;public MainWindow(){InitializeComponent();InitializeMouseTracking();UpdateScaleInfo();AddLog("Demo启动 - 移动鼠标实时显示坐标,左键放大,右键缩小");AddLog("==================================================");}private void InitializeMouseTracking(){// 使用定时器限制鼠标移动事件的频率mouseMoveTimer = new DispatcherTimer();mouseMoveTimer.Interval = TimeSpan.FromMilliseconds(50); // 20fpsmouseMoveTimer.Tick += OnMouseMoveTimerTick;mouseMoveTimer.Start();}private void OnCanvasMouseMove(object sender, MouseEventArgs e){// 记录鼠标位置,由定时器处理实际更新lastMousePosition = e.GetPosition(MainCanvas);}private void OnPanelMouseMove(object sender, MouseEventArgs e){// Panel内的鼠标移动也记录位置lastMousePosition = e.GetPosition(MainCanvas);eLastMousePosition = e.GetPosition(TestPanel);}private void OnPanelMouseEnter(object sender, MouseEventArgs e){isMouseOverPanel = true;UpdateMouseStatusDisplay();}private void OnPanelMouseLeave(object sender, MouseEventArgs e){isMouseOverPanel = false;UpdateMouseStatusDisplay();}private void OnMouseMoveTimerTick(object sender, EventArgs e){UpdateMousePosition();}private void UpdateMousePosition(){try{eRelativeCoordText.Text = $"e坐标: ({eLastMousePosition.X:F1}, {eLastMousePosition.Y:F1})"; // 获取鼠标相对于Panel的位置Point mousePosition = lastMousePosition;Point relativeToPanel = MainCanvas.TranslatePoint(mousePosition, TestPanel);// 检查鼠标是否在Panel的变换后边界内bool isInsideTransformedPanel = IsPointInTransformedPanel(mousePosition);// 更新坐标显示MouseCoordText.Text = $"屏幕坐标: ({mousePosition.X:F1}, {mousePosition.Y:F1})";RelativeCoordText.Text = $"相对Panel: ({relativeToPanel.X:F1}, {relativeToPanel.Y:F1})";LastClickText.Text = $"最后点击: ({lastClickPosition.X:F1}, {lastClickPosition.Y:F1})";ClickCountText.Text = $"点击次数: {clickCount}";ScaleText.Text = $"缩放比例: {currentScale:F2}x";Matrix matrix = PanelTransform.Matrix;TransformText.Text = $"变换矩阵: [{matrix.M11:F2} {matrix.M12:F2}]";PanelPositionText.Text = $"Panel位置: ({Canvas.GetLeft(TestPanel):F0}, {Canvas.GetTop(TestPanel):F0})";// 更新Panel内部的坐标显示PanelCoordText.Text = $"坐标: ({relativeToPanel.X:F0}, {relativeToPanel.Y:F0})";// 更新鼠标指示器UpdateMouseIndicator(mousePosition, isInsideTransformedPanel);}catch (Exception ex){AddLog($"坐标更新错误: {ex.Message}");}}private bool IsPointInTransformedPanel(Point mousePosition){// 获取Panel的变换后边界Rect panelBounds = new Rect(Canvas.GetLeft(TestPanel),Canvas.GetTop(TestPanel),TestPanel.Width,TestPanel.Height);// 应用变换到边界Matrix matrix = PanelTransform.Matrix;Point[] corners = new Point[]{new Point(panelBounds.Left, panelBounds.Top),new Point(panelBounds.Right, panelBounds.Top),new Point(panelBounds.Right, panelBounds.Bottom),new Point(panelBounds.Left, panelBounds.Bottom)};for (int i = 0; i < corners.Length; i++){corners[i] = matrix.Transform(corners[i]);}// 简单的边界框检测(对于旋转等复杂变换可能需要更精确的检测)double minX = Math.Min(Math.Min(corners[0].X, corners[1].X), Math.Min(corners[2].X, corners[3].X));double maxX = Math.Max(Math.Max(corners[0].X, corners[1].X), Math.Max(corners[2].X, corners[3].X));double minY = Math.Min(Math.Min(corners[0].Y, corners[1].Y), Math.Min(corners[2].Y, corners[3].Y));double maxY = Math.Max(Math.Max(corners[0].Y, corners[1].Y), Math.Max(corners[2].Y, corners[3].Y));Rect transformedBounds = new Rect(minX, minY, maxX - minX, maxY - minY);return transformedBounds.Contains(mousePosition);}private void UpdateMouseStatusDisplay(){if (isMouseOverPanel){MouseOverText.Text = "位置: 在Panel内";MouseOverText.Foreground = Brushes.Green;}else{MouseOverText.Text = "位置: 在Panel外";MouseOverText.Foreground = Brushes.Red;}}private void UpdateMouseIndicator(Point mousePosition, bool isOverPanel){// 清除之前的指示器MouseIndicatorCanvas.Children.Clear();if (isOverPanel){// 在Panel内显示绿色指示器Ellipse indicator = new Ellipse{Width = 8,Height = 8,Fill = Brushes.LimeGreen,Stroke = Brushes.DarkGreen,StrokeThickness = 1,IsHitTestVisible = false};Canvas.SetLeft(indicator, mousePosition.X - 4);Canvas.SetTop(indicator, mousePosition.Y - 4);MouseIndicatorCanvas.Children.Add(indicator);// 添加十字线Line horizontalLine = new Line{X1 = mousePosition.X - 15,Y1 = mousePosition.Y,X2 = mousePosition.X + 15,Y2 = mousePosition.Y,Stroke = Brushes.LimeGreen,StrokeThickness = 1,IsHitTestVisible = false};Line verticalLine = new Line{X1 = mousePosition.X,Y1 = mousePosition.Y - 15,X2 = mousePosition.X,Y2 = mousePosition.Y + 15,Stroke = Brushes.LimeGreen,StrokeThickness = 1,IsHitTestVisible = false};MouseIndicatorCanvas.Children.Add(horizontalLine);MouseIndicatorCanvas.Children.Add(verticalLine);}else{// 在Panel外显示红色指示器Ellipse indicator = new Ellipse{Width = 6,Height = 6,Fill = Brushes.Red,Stroke = Brushes.DarkRed,StrokeThickness = 1,IsHitTestVisible = false};Canvas.SetLeft(indicator, mousePosition.X - 3);Canvas.SetTop(indicator, mousePosition.Y - 3);MouseIndicatorCanvas.Children.Add(indicator);}}private void OnPanelMouseDown(object sender, MouseButtonEventArgs e){// 获取点击位置相对于Panel的坐标Point clickPosition = e.GetPosition(TestPanel);lastClickPosition = clickPosition;clickCount++;// 确定缩放因子:左键放大,右键缩小double scaleFactor = e.ChangedButton == MouseButton.Left ? 1.2 : 0.8;// 执行缩放ZoomAtPoint(scaleFactor, clickPosition);//ZoomAtPoint(scaleFactor, positionNew);// 添加点击标记AddClickMarker(clickPosition);// 记录日志string action = e.ChangedButton == MouseButton.Left ? "放大" : "缩小";AddLog($"[{action}] 位置:({clickPosition.X:F1},{clickPosition.Y:F1}) 缩放:{scaleFactor:F1}x 总缩放:{currentScale:F2}x");}private void ZoomAtPoint(double scaleFactor, Point centerPoint){// 获取当前变换矩阵Matrix matrix = PanelTransform.Matrix;try{////法1,不符合锚点放缩://Matrix inverseMatrix = matrix;// 计算在正确坐标系中的缩放中心//inverseMatrix.Invert();//Point correctCenter = inverseMatrix.Transform(centerPoint);////应用缩放变换//matrix.ScaleAt(scaleFactor, scaleFactor, correctCenter.X, correctCenter.Y);//法2:var positionNew = matrix.Transform(centerPoint);AddLog($"positionNew 位置:({positionNew.X:F1},{positionNew.Y:F1}) ");matrix.ScaleAt(scaleFactor, scaleFactor, positionNew.X, positionNew.Y);}catch (InvalidOperationException){// 如果矩阵不可逆,使用原始坐标matrix.ScaleAt(scaleFactor, scaleFactor, centerPoint.X, centerPoint.Y);AddLog("警告: 使用原始坐标进行缩放");}// 更新变换PanelTransform.Matrix = matrix;// 更新当前缩放比例currentScale *= scaleFactor;UpdateScaleInfo();}private void AddClickMarker(Point clickPosition){// 清除之前的标记MarkerCanvas.Children.Clear();// 计算标记在Canvas中的实际位置double panelLeft = Canvas.GetLeft(TestPanel);double panelTop = Canvas.GetTop(TestPanel);// 应用当前变换到点击位置Matrix matrix = PanelTransform.Matrix;Point transformedPoint = matrix.Transform(clickPosition);Point canvasPosition = new Point(panelLeft + transformedPoint.X, panelTop + transformedPoint.Y);// 创建标记 - 不拦截鼠标事件Ellipse marker = new Ellipse{Width = 12,Height = 12,Fill = Brushes.Yellow,Stroke = Brushes.Red,StrokeThickness = 2,IsHitTestVisible = false,ToolTip = $"点击位置\n原始:({clickPosition.X:F1},{clickPosition.Y:F1})\n变换后:({transformedPoint.X:F1},{transformedPoint.Y:F1})"};Canvas.SetLeft(marker, canvasPosition.X - 6);Canvas.SetTop(marker, canvasPosition.Y - 6);MarkerCanvas.Children.Add(marker);}private void UpdateScaleInfo(){ScaleInfoText.Text = $"当前缩放: {currentScale:F2}x";}private void AddLog(string message){if (logBuilder.Length > 1500){logBuilder.Clear();logBuilder.AppendLine("日志已清空...");}logBuilder.AppendLine(message);LogTextBox.Text = logBuilder.ToString();LogTextBox.ScrollToEnd();}protected override void OnClosed(EventArgs e){mouseMoveTimer?.Stop();base.OnClosed(e);}}
}

http://www.dtcms.com/a/596440.html

相关文章:

  • 面试题—linux
  • 福田皇岗社区做网站网页制作怎么收费
  • 236. Java 集合 - 使用索引访问 List 元素
  • 天河网站建设哪家强华为企业邮箱
  • 基于Java的电影管理系统的设计与实现
  • C#初级面试题5、拆箱和装箱
  • 如何判断企业是否需要 IAM ?数字化转型中的权限治理自测
  • CSP-J教程——第一阶段——第六课:程序流程控制 - 循环结构(一)for循环
  • 【C++基础与提高】第二章:C++数据类型系统——构建程序的基础砖石
  • 【每天一个知识点】CAG:Context-Augmented Generation
  • 《Linux系统编程之系统导论》【冯诺依曼体系结构 + 操作系统基本概述】
  • 第23天python内容
  • 5.2、Python-字符串的编码和解码
  • 美容院网站源码网站流量下降的原因
  • FreeRTOS 学习:(十七)“外部中断”和“内核中断”的差异,引入 FreeRTOS 中断管理
  • CodeBuddy AI IDE :Skills 模式
  • 大模型学习计划(按周规划)
  • 绥中网站建设分类信息网网站500
  • k8s 部署MySQL 数据持久化
  • Quantinuum 发布新型量子计算机“Helios“
  • ubuntu/kali安装k8s
  • 开源 Objective-C IOS 应用开发(二)Xcode安装
  • 【论文阅读】Gradient Guidance for Diffusion Models:An Optimization Perspective
  • WebSocket调试工具(html),用于调试WebSocket链接是否畅通
  • Springboot3.X+security6.5+jdk21
  • 中文企业网站设计欣赏宁波网站建设方案联系方式
  • Vue2 入门到实战(day2):计算属性、监视属性、样式绑定与条件渲染(附代码案例)
  • C语言位运算深度应用:嵌入式硬件寄存器控制与低功耗优化实践
  • 深圳建立网站营销用动易建设网站教程
  • 京东后端架构技术,Pipline 设计 解决复杂查询逻辑