【问题标题】:How to correctly convert System.Drawing.Point to System.Windows.Point?如何正确将 System.Drawing.Point 转换为 System.Windows.Point?
【发布时间】:2014-09-15 19:10:04
【问题描述】:

首先,到目前为止我还没有找到任何答案。这个网站上的文章没有回答我的问题: Converter of System.Drawing.Point' to '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-to-X 和 Y-to-Y 复制。一定还有其他一些因素。任何想法,我们是否需要在转换中考虑其他显示因素? 请运行附加程序并单击网格上的不同点,您会发现奇怪的行为。

背景:

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

更多背景:

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

我们所做的是首先通过以下方式在 WinForm 中获得点击点: System.Drawing.Point pt = this.PointToClient(e.MousePos);

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

然后通过递归地爬上可视树并记录路径中每个控件的名称属性或文本属性来获得该控件的可视树路径: 控制 parent = child.Parent;

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

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

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

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

  1. 启动程序。

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

  3. 单击 3 个按钮之一,弹出窗口给出可视路径。

  4. 对于 Grid 中的两个按钮,第一个给出 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;   
    

    } }

【问题讨论】:

  • 举个小例子。
  • 世界上所有的文字都无法说服我们new System.Windows.Point(dp.X, dp.Y) 行不通。您需要 create a Minimal, Complete, and Verifiable example 证明您的问题。
  • 我试图从复杂的代码中提取一个简单的示例,但是示例代码有问题:HelpEventArgs.MousePos.X 总是为0。在原始代码中不会发生这种情况。我在这里使用示例代码提出了另一个问题:stackoverflow.com/questions/25874319/…
  • 即使是来自 MSDN 的示例也有同样的问题。 msdn.microsoft.com/en-us/library/…
  • 使用 Cursor.Postion 解决了“HelpEventArgs.MousePos.X 始终为 0”的问题。现在我可以在这里发布我的代码来解决这个问题。

标签: c# wpf


【解决方案1】:

我终于找到了解决方案(通过阅读有关 WPF 屏幕坐标和更多测试的更多信息)。

根本原因是“new System.Windows.Point(dp.X, dp.Y)”在不同的分辨率、多显示器的计算机等上无法可靠运行。需要使用转换来获得鼠标相对于当前屏幕的位置,而不是由所有显示器组成的整个查看区域。

另一个主题问题的答案帮助了我: How do I get the current mouse screen coordinates in 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);

然后它就可以正常工作了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-06
    • 1970-01-01
    • 2021-10-21
    • 2020-08-28
    • 2012-04-30
    • 1970-01-01
    • 2018-07-19
    • 2021-11-21
    相关资源
    最近更新 更多