如何正确地将System.Drawing.Point转换为System.Windows.Point

本文关键字:Point System 转换 Windows Drawing 正确地 | 更新日期: 2023-09-27 18:22:01

首先,到目前为止我还没有找到任何答案。这个网站上的文章没有回答我的问题:系统转换器。绘图。点';到';System.Windows.Point

在上面的文章中,有人建议这样转换:

    //convert 
    System.Drawing.Point dp = …;
    return new System.Windows.Point(dp.X, dp.Y);

但是转换似乎没有得到正确的结果。我从嵌入WPF可视化的WinForm的单击事件中获得了System.Drawing.Point。当我尝试使用上面的转换和VisualTreeHelper.HitTest()来识别在WPF可视化中单击的控件时,左上角的控件可以正确识别,右下角的控件越多,就越不准确。意味着点转换不是简单的X到X和Y到Y的复制。肯定还涉及其他一些因素。有什么想法吗,我们在转换时需要考虑其他显示因素吗?请运行附件中的程序,点击网格上的不同点,你会发现奇怪的行为。

背景:

我在WinForm中嵌入了一个WPF UI。当单击WinForm上的帮助按钮"?",然后单击WPF UI中的控件时,WinForm会收到通知,我可以在WinForm中获得正确的System.Drawing.Point。然后我想在WPF中点击相应的位置。我计算了System.Drawing.Point与ElementHost的相对值,ElementHost是连接第一个WPF可视化的最后一个WinForm元素,然后尝试使用上述方法在WPF UI中获取System.Windows.Point,但它没有获得与单击位置对应的正确点。

更多背景:

我们有一些作为UserControls的第三方UI库,这些库无法更改。我们使用WinForm来保存它们。但是,当用户单击WinForm上的"?"按钮(然后鼠标光标变为?),然后单击UserControls中的UI控件时,我们希望侵入UserControls并添加帮助窗口。如果第三方用户界面是纯用WinForm编写的,这就可以很好地工作。

我们所做的是,我们首先通过以下方式在WinForm中获得点击点:System.Drawing.Point pt=此。指向客户端(e.MousePos);

然后获得了最前面的控制权。Form.GetChildAtPoint(pt);

然后通过递归地爬上可视化树并记录路径中每个控件的Name属性或Text属性,获得该控件的可视化树路径:控件父级=子级。父母亲

然后,我们使用路径作为字符串来查找预定义的表,以确定标识该控件的关键字。一旦通过关键字识别控件,关键字就可以映射到帮助窗口或其工具提示。

如果一切都是WinForm,则上述过程可以正常工作。

但是,当UserControl包含ElementHost和嵌入的WPF内容时,上述内容就被破坏了。我想只要我能正确地把"System.Drawing.Point"翻译成"System.Windows.Point",它应该也能工作。

以下代码演示了问题:复制过程:

  1. 启动程序。

  2. 点击"?"按钮,光标变为"?"。

  3. 单击3个按钮中的一个,弹出窗口会给出视觉路径。

  4. 对于网格中的两个按钮,第一个按钮提供WPF路径,但第二个按钮没有,因为System.Windows.Point不正确。

    //Please add into project the reference to (right click the project and select "Add Reference"):
    //WindowsBase
    //WindowsFormsIntegration
    //PresentationCore
    //PresentationFramework
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Forms.Integration;
    using System.Windows.Media;
    namespace WindowsFormsApplication1
    {
    

    公共分部类Form1:Form{[DllImport("user32.dll")]静态外部IntPtr GetFocus();

      public Form1()
      {
         InitializeComponent();
         System.Windows.Controls.Button b = new System.Windows.Controls.Button();
         b.Content = "WPF_Button";
         b.Name = "WPF_Button_name";
         Grid.SetColumn(b, 0);
         Grid.SetRow(b, 0);
         System.Windows.Controls.Button b2 = new System.Windows.Controls.Button();
         b2.Content = "WPF_Button2";
         b2.Name = "WPF_Button_name2";
         Grid.SetColumn(b2, 2);
         Grid.SetRow(b2, 2);
         Grid g = new Grid();
         g.Name = "WPF_Grid";
         g.Width = 250;
         g.Height = 100;
         g.HorizontalAlignment = System.Windows.HorizontalAlignment.Stretch;
         g.VerticalAlignment = VerticalAlignment.Stretch;
         g.ShowGridLines = true;
         // Define the Columns
         ColumnDefinition colDef1 = new ColumnDefinition();
         ColumnDefinition colDef2 = new ColumnDefinition();
         ColumnDefinition colDef3 = new ColumnDefinition();
         g.ColumnDefinitions.Add(colDef1);
         g.ColumnDefinitions.Add(colDef2);
         g.ColumnDefinitions.Add(colDef3);
         // Define the Rows
         RowDefinition rowDef1 = new RowDefinition();
         RowDefinition rowDef2 = new RowDefinition();
         RowDefinition rowDef3 = new RowDefinition();
         RowDefinition rowDef4 = new RowDefinition();
         g.RowDefinitions.Add(rowDef1);
         g.RowDefinitions.Add(rowDef2);
         g.RowDefinitions.Add(rowDef3);
         g.RowDefinitions.Add(rowDef4);
    
         g.Children.Add(b);
         g.Children.Add(b2);
         this.elementHost1.Child = g;
      }
      // More usefull version of GetChildAtPoint
      public static System.Windows.Forms.Control FindChildAtPoint(System.Windows.Forms.Control parent, System.Drawing.Point pt, ref string path, out System.Drawing.Point relative_point_inside_child)
      {
         //Find a child
         System.Windows.Forms.Control child = parent.GetChildAtPoint(pt);
         //If no child, this is the control at the mouse cursor
         if (child == null)
         {
            path += parent.Name;
            //Offset our current position to be relative to the child to get the point inside the child.
            relative_point_inside_child = new System.Drawing.Point(pt.X, pt.Y);
            return parent;
         }
         path += parent.Name + "''";
         //If a child, offset our current position to be relative to the child
         System.Drawing.Point childPoint = new System.Drawing.Point(pt.X - child.Location.X, pt.Y - child.Location.Y);
         //Find child of child control at offset position
         return FindChildAtPoint(child, childPoint, ref path, out relative_point_inside_child);
      }
    
      private void Form1_HelpRequested(object sender, HelpEventArgs e)
      {
         if (System.Windows.Forms.Control.MouseButtons == MouseButtons.None)
         {
            //No mouse activity, this function is entered because of F1 key press.
            //Get the focused Control. If no focused Control then launch overall Help-Window.
            //
            IntPtr handle = GetFocus();
            if (handle != IntPtr.Zero)
            {
               System.Windows.Forms.Control ctl = System.Windows.Forms.Control.FromHandle(handle);
               //Got the ctl, do whatever
            }
         }
         else
         { // Help Button ? pressed (After click ?, the mouse cursor changes to ?, 
            // Then user can move the mouse to a UI Control and click it, in this case,
            // we need find the control that is clicked, the control might not be focused
            // in such case.
            // Convert screen coordinates to client coordinates
            System.Drawing.Point p = this.PointToClient(e.MousePos);
            System.Drawing.Point pt = this.PointToClient(Cursor.Position);
            //Look for control user clicked on
            System.Drawing.Point relative_point_inside_child;
            string path = "";
            System.Windows.Forms.Control ctl = FindChildAtPoint(this, pt, ref path, out relative_point_inside_child);
            if (ctl is ElementHost)
            {
               // This is a WPF view embedded in WinForm, get the root Control
               UIElement wpf_root = (ctl as ElementHost).Child;
               if (wpf_root != null)
               {
                  System.Windows.Point pt_WPF = new System.Windows.Point(relative_point_inside_child.X, relative_point_inside_child.Y);
                  HitTestResult htr = VisualTreeHelper.HitTest(wpf_root, pt_WPF);
                  if (htr != null && htr.VisualHit != null)
                  {
                     //IInputElement elem = System.Windows.Input.Mouse.DirectlyOver;
                     string pathWPF = "";
                     if (htr.VisualHit is DependencyObject)
                     {
                        FindPathOfControlWPF(htr.VisualHit, ref pathWPF);
                     }
                     if (pathWPF != "")
                     {
                        path = path + pathWPF;
                     }
                  }
               }
            }
            System.Windows.Forms.MessageBox.Show("Clicked on PATH: " + path);
            e.Handled = true;
         }
      }
    
      //WPF version of More usefull version of FindPathOfControl
      public static System.Windows.DependencyObject FindPathOfControlWPF(System.Windows.DependencyObject child, ref string path)
      {
         //Find a parent
         System.Windows.DependencyObject parent
            = System.Windows.Media.VisualTreeHelper.GetParent(child);
         if (child is System.Windows.Controls.TextBlock)
         {
            path = ":" + (child as System.Windows.Controls.TextBlock).Text + path;
         }
         else if (child is System.Windows.Controls.Label)
         {
            path = ":" + (child as System.Windows.Controls.Label).Content.ToString() + path;
         }
         else if (child is System.Windows.Controls.Primitives.ButtonBase)
         {
            path = ":" + (child as System.Windows.Controls.Primitives.ButtonBase).Content.ToString() + path;
         }
         if (child is System.Windows.FrameworkElement)
         {
            path = (child as System.Windows.FrameworkElement).Name + path;
         }
         //If no parent, this is the top control
         if (parent == null)
         {
            return child;
         }
         path = "''" + path;
         //Find parent of child control
         return FindPathOfControlWPF(parent, ref path);
      }
      /// <summary>
      /// Required designer variable.
      /// </summary>
      private System.ComponentModel.IContainer components = null;
      /// <summary>
      /// Clean up any resources being used.
      /// </summary>
      /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
      protected override void Dispose(bool disposing)
      {
         if (disposing && (components != null))
         {
            components.Dispose();
         }
         base.Dispose(disposing);
      }
      #region Windows Form Designer generated code
      /// <summary>
      /// Required method for Designer support - do not modify
      /// the contents of this method with the code editor.
      /// </summary>
      private void InitializeComponent()
      {
         //this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
         //this.flowLayoutPanel1 = new System.Windows.Forms.Panel();
         this.panel1 = new System.Windows.Forms.Panel();
         this.button1 = new System.Windows.Forms.Button();
         this.panel2 = new System.Windows.Forms.Panel();
         this.elementHost1 = new System.Windows.Forms.Integration.ElementHost();
         //this.flowLayoutPanel1.SuspendLayout();
         this.panel1.SuspendLayout();
         this.panel2.SuspendLayout();
         this.SuspendLayout();
         // 
         // flowLayoutPanel1
         // 
         //this.flowLayoutPanel1.Controls.Add(this.panel1);
         //this.flowLayoutPanel1.Controls.Add(this.panel2);
         //this.flowLayoutPanel1.Location = new System.Drawing.Point(24, 33);
         //this.flowLayoutPanel1.Name = "flowLayoutPanel1";
         //this.flowLayoutPanel1.Size = new System.Drawing.Size(242, 205);
         //this.flowLayoutPanel1.TabIndex = 0;
         //this.flowLayoutPanel1.BackColor = System.Drawing.Color.Blue;
         // 
         // panel1
         // 
         this.panel1.Controls.Add(this.button1);
         this.panel1.Location = new System.Drawing.Point(3, 3);
         this.panel1.Name = "panel1";
         this.panel1.Size = new System.Drawing.Size(90, 134);
         this.panel1.TabIndex = 0;
         this.panel1.BackColor = System.Drawing.Color.Green;
         this.panel1.BringToFront();
         // 
         // button1
         // 
         this.button1.Location = new System.Drawing.Point(16, 39);
         this.button1.Name = "button1";
         this.button1.Size = new System.Drawing.Size(60, 35);
         this.button1.TabIndex = 0;
         this.button1.Text = "button1";
         this.button1.UseVisualStyleBackColor = true;
         this.button1.BackColor = System.Drawing.Color.Yellow;
         this.panel1.BringToFront();
         // 
         // panel2
         // 
         this.panel2.Controls.Add(this.elementHost1);
         this.panel2.Location = new System.Drawing.Point(99, 3);
         this.panel2.Name = "panel2";
         this.panel2.Size = new System.Drawing.Size(300, 300);
         this.panel2.TabIndex = 1;
         this.panel2.BackColor = System.Drawing.Color.Purple;
         this.panel1.BringToFront();
         // 
         // elementHost1
         // 
         this.elementHost1.Dock = System.Windows.Forms.DockStyle.Fill;
         this.elementHost1.Location = new System.Drawing.Point(0, 0);
         this.elementHost1.Name = "elementHost1";
         this.elementHost1.TabIndex = 0;
         this.elementHost1.Text = "elementHost1Text";
         this.elementHost1.Child = null;
         this.panel1.BringToFront();
         // 
         // Form1
         // 
         this.HelpButton = true;
         this.MaximizeBox = false;
         this.MinimizeBox = false;
         this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.Form1_HelpRequested);
         this.Size = new System.Drawing.Size(600, 600);
         this.AutoScaleDimensions = new System.Drawing.SizeF(8F, 16F);
         this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
         this.ClientSize = new System.Drawing.Size(600, 600);
         //this.Controls.Add(this.flowLayoutPanel1);
         this.Controls.Add(this.panel1);
         this.Controls.Add(this.panel2);
         this.Name = "Form1";
         this.Text = "Form1";
         this.BackColor = System.Drawing.Color.Red;
         //this.flowLayoutPanel1.ResumeLayout(false);
         this.panel1.ResumeLayout(false);
         this.panel2.ResumeLayout(false);
         this.ResumeLayout(false);
      }
      #endregion
      //private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
      //private System.Windows.Forms.Panel flowLayoutPanel1;
      private System.Windows.Forms.Panel panel1;
      private System.Windows.Forms.Button button1;
      private System.Windows.Forms.Panel panel2;
      private System.Windows.Forms.Integration.ElementHost elementHost1;   
    

    }}

如何正确地将System.Drawing.Point转换为System.Windows.Point

最后我找到了解决方案(通过阅读更多关于WPF屏幕坐标的内容和更多测试)。

根本原因是"新System.Windows.Point(dp.X,dp.Y)"无法在不同分辨率、具有多个监视器的计算机等上可靠工作。需要使用变换来获取鼠标相对于当前屏幕的位置,而不是由所有监视器组成的整个查看区域。

另一个主题问题的答案帮助了我:如何在WPF中获取当前鼠标屏幕坐标?

如果更改以下两行我的代码:

          System.Windows.Point pt_WPF = new System.Windows.Point(relative_point_inside_child.X, relative_point_inside_child.Y);
          HitTestResult htr = VisualTreeHelper.HitTest(wpf_root, pt_WPF);

收件人:

          System.Windows.Point pt_WPF = new System.Windows.Point(relative_point_inside_child.X, relative_point_inside_child.Y);
           var transform = PresentationSource.FromVisual(wpf_root).CompositionTarget.TransformFromDevice;
          pt_WPF = transform.Transform(pt_WPF);
          HitTestResult htr = VisualTreeHelper.HitTest(wpf_root, pt_WPF);

然后它工作得很好。