Преобразование известного размера в пиксели устройства
Если ваш визуальный элемент уже подключен к PresentationSource (например, он является частью окна, видимого на экране),преобразование находится следующим образом:
var source = PresentationSource.FromVisual(element);
Matrix transformToDevice = source.CompositionTarget.TransformToDevice;
Если нет, используйте HwndSource для создания временного hWnd:
Matrix transformToDevice;
using(var source = new HwndSource(new HwndSourceParameters()))
transformToDevice = source.TransformToDevice;
Обратите внимание, что это менее эффективно, чем создание с использованием hWnd из IntPtr.Zero.но я считаю это более надежным, потому что hWnd, созданный HwndSource, будет подключен к тому же устройству отображения, что и фактическое вновь созданное окно.Таким образом, если разные устройства отображения имеют разные 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.PositivieInfinity, 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);
}
Обратите внимание, что в этом коде я вызываю Measure только в том случае, если DesiredSize отсутствует. Это обеспечивает удобный метод для выполнения всего, но имеет несколько недостатков.iencies:
- Возможно, родительский элемент был бы передан в меньшем доступном размере
- Неэффективно, если фактический DesiredSize равен нулю (он повторно измеряется повторно)
- Это может маскировать ошибки таким образом, что приложение завершается сбоем из-за непредвиденного времени (например,код вызывается на уровне или выше DispatchPriority.Render)
По этим причинам я был бы склонен опустить вызов Measure в GetElementPixelSize и просто позволить клиенту сделать это.