将已知大小转换为设备像素
如果你的可视元素已经连接到 PresentationSource 上(例如,它是窗口中可见的一部分),则可以通过以下方式找到变换:
var source = PresentationSource.FromVisual(element)
Matrix transformToDevice = source.CompositionTarget.TransformToDevice
如果没有,使用HwndSource创建一个临时的hWnd:
Matrix transformToDevice
using(var source = new HwndSource(new HwndSourceParameters()))
transformToDevice = source.CompositionTarget.TransformToDevice
请注意,使用HwndSource构造比使用IntPtr.Zero的hWnd效率要低,但我认为它更可靠,因为HwndSource创建的hWnd将附加到与实际新创建的Window相同的显示设备上。这样,如果不同的显示设备有不同的DPI值,您可以确保获得正确的DPI值。
一旦您获得了转换,您就可以将任何大小从WPF大小转换为像素大小:
var pixelSize = (Size)transformToDevice.Transform((Vector)wpfSize);
将像素大小转换为整数
如果你想将像素大小转换为整数,你可以简单地执行以下操作:
int pixelWidth = (int)pixelSize.Width;
int pixelHeight = (int)pixelSize.Height;
但更健壮的解决方案是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 的期望大小,你需要确保它已经被测量过。在某些情况下,它可能已经被测量过了,这是因为:
- 你已经测量过它;
- 你测量过它的祖先元素;或者
- 它是 PresentationSource 的一部分(例如,在可见窗口中),并且你正在执行低于 DispatcherPriority.Render 的操作,因此你知道自动进行了测量。
如果你的可视元素还没有被测量过,你应该调用相应控件或其祖先元素的 Measure 方法,并传递可用的尺寸(或 new Size(double.PositiveInfinity, double.PositiveInfinity)
如果你想将其大小设为内容大小):
element.Measure(availableSize);
测量完成后,所需的只是使用矩阵来转换DesiredSize大小:
var pixelSize = (Size)transformToDevice.Transform((Vector)element.DesiredSize);
将所有内容放在一起
下面是一个简单的方法,展示了如何获取一个元素的像素大小:
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方法。这提供了一种便捷的方法来完成所有操作,但是它有几个缺点:
- 可能会出现元素的父级传递了一个更小的可用尺寸
- 如果实际的DesiredSize为零,则效率低下(需要反复重新测量)
- 它可能掩盖错误,导致应用程序由于意外的时间问题而失败(例如,在DispatchPriority.Render或以上级别的代码中调用)
因为这些原因,我倾向于在GetElementPixelSize中省略Measure调用,让客户端自己处理。