如何将WPF英寸单位转换为Winforms像素,反之亦然? [英] How to Convert a WPF inch unit to Winforms pixels and vice versa?

查看:104
本文介绍了如何将WPF英寸单位转换为Winforms像素,反之亦然?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个设计为 WPF ,然后在 WinForms 所有者. 现在,我要移动所有者表单,此刻,我的

I have a window that designed at WPF and I used that on center of a WinForms owner. Now, I want to move the owner form and at the moment my WPF window has also to be moved in the center of the form!

但是我有一个问题,仅当窗口位于屏幕中央的窗体的中央时.否则,其行为方式将与Windows坐标不同. 我只是将表单的位移值添加到窗口位置.

But I have a problem, Only when window is in the center of the form that form in the center of the screen. And otherwise act in a different form than the Windows coordinates. I just add the displacement values of the form to window location.

现在我得出的结论是WinForms在WPF Windows上的像素坐标是不同的!

Now I have come to the conclusion that the coordinates of the pixels on WPF Windows are different by WinForms!

如何转换 WPF 窗口WinForms 基本位置反之亦然?

How to convert WPF window location to WinForms base location and vice versa ?

所有者表格代码为:

public partial class Form1 : Form
{
    private WPF_Window.WPF win;

    public Form1()
    {
        InitializeComponent();

        win = new WPF();
        win.Show();
        CenterToParent(win);
    }

    private void CenterToParent(System.Windows.Window win)
    {
        win.Left = this.Left + (this.Width - win.Width) / 2;
        win.Top = this.Top + (this.Height - win.Height) / 2;
    }

    protected override void OnMove(EventArgs e)
    {
        base.OnMove(e);
        CenterToParent(win);
    }
}

推荐答案

在WPF中获取DPI值的最佳方法

方法1

与您在Windows窗体中进行操作的方式相同. System.Drawing.Graphics对象提供方便的属性来获取水平和垂直DPI.让我们来勾画一个辅助方法:

Best way to get DPI value in WPF

Method 1

It’s the same way you did that in Windows Forms. System.Drawing.Graphics object provides convenient properties to get horizontal and vertical DPI. Let’s sketch up a helper method:

/// <summary>
/// Transforms device independent units (1/96 of an inch)
/// to pixels
/// </summary>
/// <param name="unitX">a device independent unit value X</param>
/// <param name="unitY">a device independent unit value Y</param>
/// <param name="pixelX">returns the X value in pixels</param>
/// <param name="pixelY">returns the Y value in pixels</param>
public void TransformToPixels(double unitX,
                              double unitY,
                              out int pixelX,
                              out int pixelY)
{
    using (Graphics g = Graphics.FromHwnd(IntPtr.Zero))
    {
        pixelX = (int)((g.DpiX / 96) * unitX);
        pixelY = (int)((g.DpiY / 96) * unitY);
    }

    // alternative:
    // using (Graphics g = Graphics.FromHdc(IntPtr.Zero)) { }
}

您可以使用它来变换坐标和大小值.它非常简单,健壮,完全使用托管代码(至少就您(消费者)而言)而言.将IntPtr.Zero作为HWNDHDC参数传递会导致Graphics对象,该对象包装整个屏幕的设备上下文.

You can use it transforms both coordinates as well as size values. It’s pretty simple and robust and completely in managed code (at least as far as you, the consumer, is concerned). Passing IntPtr.Zero as HWND or HDC parameter results in a Graphics object that wraps a device context of the entire screen.

这种方法有一个问题.它依赖于Windows Forms/GDI +基础结构.您将必须添加对System.Drawing程序集的引用.大不了不确定您的身份,但是对我来说,这是一个需要避免的问题.

There is one problem with this approach though. It has a dependency on Windows Forms/GDI+ infrastructure. You are going to have to add a reference to System.Drawing assembly. Big deal? Not sure about you, but for me this is an issue to avoid.

让我们更进一步,以Win API的方式来做. GetDeviceCaps函数可检索指定设备的各种信息,并在分别传递LOGPIXELSXLOGPIXELSY参数时能够检索水平和垂直DPI.

Let’s take it one step deeper and do it the Win API way. GetDeviceCaps function retrieves various information for the specified device and is able to retrieve horizontal and vertical DPI’s when we pass it LOGPIXELSX and LOGPIXELSY parameters respectively.

GetDeviceCaps函数是在gdi32.dll中定义的,可能是System.Drawing.Graphics在后台使用的功能.

GetDeviceCaps function is defined in gdi32.dll and is probably what System.Drawing.Graphics uses under the hood.

让我们看看我们的助手已经变成了:

Let’s have a look at what our helper has become:

[DllImport("gdi32.dll")]
public static extern int GetDeviceCaps(IntPtr hDc, int nIndex);

[DllImport("user32.dll")]
public static extern IntPtr GetDC(IntPtr hWnd);

[DllImport("user32.dll")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDc);

public const int LOGPIXELSX = 88;
public const int LOGPIXELSY = 90;

/// <summary>
/// Transforms device independent units (1/96 of an inch)
/// to pixels
/// </summary>
/// <param name="unitX">a device independent unit value X</param>
/// <param name="unitY">a device independent unit value Y</param>
/// <param name="pixelX">returns the X value in pixels</param>
/// <param name="pixelY">returns the Y value in pixels</param>
public void TransformToPixels(double unitX,
                              double unitY,
                              out int pixelX,
                              out int pixelY)
{
    IntPtr hDc = GetDC(IntPtr.Zero);
    if (hDc != IntPtr.Zero)
    {
        int dpiX = GetDeviceCaps(hDc, LOGPIXELSX);
        int dpiY = GetDeviceCaps(hDc, LOGPIXELSY);

        ReleaseDC(IntPtr.Zero, hDc);

        pixelX = (int)(((double)dpiX / 96) * unitX);
        pixelY = (int)(((double)dpiY / 96) * unitY);
    }
    else
        throw new ArgumentNullException("Failed to get DC.");
}

因此,我们已将对托管GDI +的依赖关系换成了对精美Win API调用的依赖关系.有什么改善吗?我认为是的,只要我们在Windows Win API上运行是最不常见的.它是轻量级的.在其他平台上,我们首先可能不会遇到这个难题.

So we have exchanged a dependency on managed GDI+ for the dependency on fancy Win API calls. Is that an improvement? In my opinion yes, as long as we run on Windows Win API is a least common denominator. It is lightweight. On other platforms we wouldn’t probably have this dilemma in the first place.

也不要被那个ArgumentNullException所迷惑.该解决方案与第一个解决方案一样强大.如果System.Drawing.Graphics也无法获取设备上下文,则会抛出相同的异常.

And don’t get fooled by that ArgumentNullException. This solution is as robust as the first one. System.Drawing.Graphics will throw this same exception if it can’t obtain a device context too.

正式记录在 此处 是注册表中的特殊键:HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontDPI.它存储一个DWORD值,该值正是用户在显示设置对话框中为DPI选择的值(在此处称为字体大小).

As officially documented here there is a special key in the registry: HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\FontDPI. It stores a DWORD value which is exactly what the user chooses for DPI in the display settings dialog (it’s called a font size there).

阅读它很容易,但我不建议这样做.您会看到官方API和用于各种设置的存储之间存在差异.该API是一项公共合同,即使内部逻辑已被完全重写(如果整个平台不烂,不是吗?),它也保持不变.

Reading it is a no-brainer but I wouldn’t recommend it. You see there is a difference between an official API and a storage for various settings. The API is a public contract that stays the same even if the internal logic is totally rewritten (If it doesn’t the whole platform sucks, doesn’t it?).

但是没有人保证内部存储将保持不变.它可能已经使用了几十年,但是描述其重新布置的重要设计文档可能已经在等待批准.你永远不会知道.

But nobody guarantees that the internal storage will remain the same. It may have lasted for a couple of decades but a crucial design document that describes its relocation may already be pending an approval. You never know.

始终坚持使用API​​(无论它是什么,本机,Windows窗体,WPF等).即使基础代码从您知道的位置读取了值.

Always stick to API (whatever it is, native, Windows Forms, WPF, etc). Even if the underlying code reads the value from the location you know.

这是一个非常优雅的WPF方法,我在

This is a pretty elegant WPF approach that I’ve found documented in this blog post. It is based on the functionality provided by System.Windows.Media.CompositionTarget class that ultimately represents the display surface on which the WPF application is drawn. The class provides 2 useful methods:

  • TransformFromDevice
  • TransformToDevice
  • TransformFromDevice
  • TransformToDevice

名称是不言而喻的,在两种情况下,我们都得到一个System.Windows.Media.Matrix对象,该对象包含设备单位(像素)和独立单位之间的映射系数. M11将包含X轴系数和M22 – Y轴系数.

The names are self-explanatory and in both cases we get a System.Windows.Media.Matrix object that contains the mapping coefficients between device units (pixels) and independent units. M11 will contain a coefficient for the X axis and M22 – for the Y axis.

到目前为止,由于我们一直在考虑units-> pixels方向,因此请使用CompositionTarget.TransformToDevice.重新编写我们的助手.调用此方法时,M11和M22将包含我们计算为的值:

As we have been considering a units->pixels direction so far let’s re-write our helper with CompositionTarget.TransformToDevice. When calling this method M11 and M22 will contain values that we calculated as:

  • dpiX/96
  • dpiY/96

因此,在DPI设置为120的机器上,系数为1.25.

So on a machine with DPI set to 120 the coefficients will be 1.25.

这是新的助手:

/// <summary>
/// Transforms device independent units (1/96 of an inch)
/// to pixels
/// </summary>
/// <param name="visual">a visual object</param>
/// <param name="unitX">a device independent unit value X</param>
/// <param name="unitY">a device independent unit value Y</param>
/// <param name="pixelX">returns the X value in pixels</param>
/// <param name="pixelY">returns the Y value in pixels</param>
public void TransformToPixels(Visual visual,
                              double unitX,
                              double unitY,
                              out int pixelX,
                              out int pixelY)
{
    Matrix matrix;
    var source = PresentationSource.FromVisual(visual);
    if (source != null)
    {
        matrix = source.CompositionTarget.TransformToDevice;
    }
    else
    {
        using (var src = new HwndSource(new HwndSourceParameters()))
        {
            matrix = src.CompositionTarget.TransformToDevice;
        }
    }

    pixelX = (int)(matrix.M11 * unitX);
    pixelY = (int)(matrix.M22 * unitY);
}

我必须在方法中添加另一个参数,即Visual.我们需要它作为计算的基础(以前的示例为此使用了整个屏幕的设备上下文).我认为这不是什么大问题,因为在运行WPF应用程序时,您很可能会遇到Visual(否则,为什么需要平移像素坐标?).但是,如果您的视觉效果尚未附加到演示文稿源(即尚未显示),则无法获取演示文稿源(因此,我们将检查NULL并构造一个新的HwndSource ).

I had to add one more parameter to the method, the Visual. We need it as a base for calculations (previous samples used the device context of the entire screen for that). I don’t think it’s a big issue as you are more than likely to have a Visual at hand when running your WPF application (otherwise, why would you need to translate pixel coordinates?). However, if your visual hasn't been attached to a presentation source (that is, it hasn't been shown yet) you can't get the presentation source (thus, we have a check for NULL and construct a new HwndSource).

参考

这篇关于如何将WPF英寸单位转换为Winforms像素,反之亦然?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆