如何将WPF大小转换为物理像素? [英] How do I convert a WPF size to physical pixels?

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

问题描述

将WPF(与分辨率无关)的宽度和高度转换为物理屏幕像素的最佳方法是什么?

What's the best way to convert a WPF (resolution-independent) width and height to physical screen pixels?

我正在WinForms表单中显示WPF内容(通过ElementHost),并尝试确定一些尺寸调整逻辑。当操作系统以默认的96 dpi运行时,我可以正常工作。但是,当操作系统设置为120 dpi或其他分辨率时,它将不起作用,因为就WinForms而言,报告宽度为96的WPF元素实际上将为120像素宽。

I'm showing WPF content in a WinForms Form (via ElementHost) and trying to work out some sizing logic. I've got it working fine when the OS is running at the default 96 dpi. But it won't work when the OS is set to 120 dpi or some other resolution, because then a WPF element that reports its Width as 96 will actually be 120 pixels wide as far as WinForms is concerned.

我在System.Windows.SystemParameters上找不到任何每英寸像素设置。我确定我可以使用WinForms等效项(System.Windows.Forms.SystemInformation),但是有没有更好的方法(请阅读:使用WPF API的方法,而不是使用WinForms API并手动进行数学运算)?将WPF像素转换为实际屏幕像素的最佳方法是什么?

I couldn't find any "pixels per inch" settings on System.Windows.SystemParameters. I'm sure I could use the WinForms equivalent (System.Windows.Forms.SystemInformation), but is there a better way to do this (read: a way using WPF APIs, rather than using WinForms APIs and manually doing the math)? What's the "best way" to convert WPF "pixels" to real screen pixels?

编辑:我还希望在WPF控件显示在屏幕上之前执行此操作。屏幕。看起来Visual.PointToScreen可以为我提供正确的答案,但是我不能使用它,因为该控件尚未成为父项,并且我收到InvalidOperationException此Visual未连接到PresentationSource。

I'm also looking to do this before the WPF control is shown on the screen. It looks like Visual.PointToScreen could be made to give me the right answer, but I can't use it, because the control isn't parented yet and I get InvalidOperationException "This Visual is not connected to a PresentationSource".

推荐答案

将已知大小转换为设备像素

元素已经附加到PresentationSource(例如,它是在屏幕上可见的窗口的一部分),可以通过以下方式找到转换:

If your visual element is already attached to a PresentationSource (for example, it is part of a window that is visible on screen), the transform is found this way:

var source = PresentationSource.FromVisual(element);
Matrix transformToDevice = source.CompositionTarget.TransformToDevice;

如果没有,请使用HwndSource创建一个临时的hWnd:

If not, use HwndSource to create a temporary hWnd:

Matrix transformToDevice;
using(var source = new HwndSource(new HwndSourceParameters()))
  transformToDevice = source.CompositionTarget.TransformToDevice;

请注意,这比使用IntPtr.Zero的hWnd构造效率低,但我认为它更可靠,因为hWnd创建了HwndSource制作的窗口将被附加到与实际新创建的Window相同的显示设备上。这样,如果不同的显示设备具有不同的DPI,则您一定会获得正确的DPI值。

Note that this is less efficient than constructing using a hWnd of IntPtr.Zero but I consider it more reliable because the hWnd created by HwndSource will be attached to the same display device as an actual newly-created Window would. That way, if different display devices have different DPIs you are sure to get the right DPI value.

一旦进行了转换,就可以将任何大小从WPF大小转换为像素大小。 :

Once you have the transform, you can convert any size from a WPF size to a pixel size:

var pixelSize = (Size)transformToDevice.Transform((Vector)wpfSize);

将像素大小转换为整数

如果您要将像素大小转换为整数,只需执行以下操作:

If you want to convert the pixel size to integers, you can simply do:

int pixelWidth = (int)pixelSize.Width;
int pixelHeight = (int)pixelSize.Height;

,但是ElementHost使用的解决方案会更可靠:

but a more robust solution would be the one used by ElementHost:

int pixelWidth = (int)Math.Max(int.MinValue, Math.Min(int.MaxValue, pixelSize.Width));
int pixelHeight = (int)Math.Max(int.MinValue, Math.Min(int.MaxValue, pixelSize.Height));

获取所需的UIElement大小

要获得所需的UIElement大小,您需要确保已对其进行测量。在某些情况下,由于以下原因,它已经被测量了:

To get the desired size of a UIElement you need to make sure it is measured. In some circumstances it will already be measured, either because:


  1. 您已经测量了

  2. 您测量了其中一个祖先,或者

  3. 它是PresentationSource的一部分(例如,它在可见的Window中),并且您正在DispatcherPriority.Render下执行,因此您知道测量已经自动进行。

如果尚未测量视觉元素,则应在控件或其祖先之一上调用Measure,并传入可用大小(或 new Size(double.PositivieInfinity,double.PositiveInfinity)如果要调整内容大小:

If your visual element has not been measured yet, you should call Measure on the control or one of its ancestors as appropriate, passing in the available size (or new Size(double.PositivieInfinity, double.PositiveInfinity) if you want to size to content:

element.Measure(availableSize);

一旦完成测量,所有必要的就是使用矩阵来变换DesiredSize :

Once the measuring is done, all that is necessary is to use the matrix to transform the DesiredSize:

var pixelSize = (Size)transformToDevice.Transform((Vector)element.DesiredSize);

将它们放在一起

以下是显示如何获取元素像素大小的简单方法:

Here is a simple method that shows how to get the pixel size of an element:

public Size GetElementPixelSize(UIElement element)
{
  Matrix transformToDevice;
  var source = PresentationSource.FromVisual(element);
  if(source!=null)
    transformToDevice = source.CompositionTarget.TransformToDevice;
  else
    using(var source = new HwndSource(new HwndSourceParameters()))
      transformToDevice = source.CompositionTarget.TransformToDevice;

  if(element.DesiredSize == new Size())
    element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));

  return (Size)transformToDevice.Transform((Vector)element.DesiredSize);
}

请注意,在此代码中,仅当不存在DesiredSize时才调用Measure。这提供了一种方便的方法来执行所有操作,但有一些缺陷:

Note that in this code I call Measure only if no DesiredSize is present. This provides a convenient method to do everything but has several deficiencies:


  1. 可能是元素的父级传递了一个较小的availableSize

  2. 如果实际DesiredSize为零(重复测量),效率低下。

  3. 它可能会掩盖错误,从而导致应用程序由于意外的计时而失败(例如,在DispatchPriority.Render或更高级别调用的代码)

由于这些原因,我倾向于在GetElementPixelSize中省略Measure调用,而只是让客户来做。

Because of these reasons, I would be inclined to omit the Measure call in GetElementPixelSize and just let the client do it.

这篇关于如何将WPF大小转换为物理像素?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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