Как я могу получить правильный DPI моего экрана в WPF?
Это может звучать как двойной вопрос, но я обещаю, что это не так. Я уже посмотрел ответы на другие вопросы, например, по следующей ссылке:
Как я могу получить DPI в WPF?
Проблема в том, что они не возвращают правильный DPI.
Я точно знаю, что мой монитор (Dell U3415W) имеет DPI 109 точек на дюйм. Разрешение 3440x1440.
В WPF я попытался использовать следующие методы для получения DPI моего экрана:
//Method 1
var dpi_scale = VisualTreeHelper.GetDpi(this);
double dpiX = dpi_scale.PixelsPerInchX;
double dpiY = dpi_scale.PixelsPerInchY;
//Method 2
using (System.Drawing.Graphics g = System.Drawing.Graphics.FromHwnd(IntPtr.Zero))
{
double dpiX = g.DpiX;
double dpiY = g.DpiY;
}
//Method 3
PresentationSource source = PresentationSource.FromVisual(this);
double dpiX, dpiY;
if (source != null)
{
dpiX = 96.0 * source.CompositionTarget.TransformToDevice.M11;
dpiY = 96.0 * source.CompositionTarget.TransformToDevice.M22;
}
Все три из вышеперечисленных методов возвращают 192 в качестве моего DPI (масштабный коэффициент, возвращаемый в методах #1 и #3, равен 2).
Я пишу код, в котором мне нужно надежно отображать расстояния (в физических единицах, например, сантиметрах) между некоторыми объектами на экране, и этот код будет не просто запущен на моем экране, поэтому я не могу просто жестко кодировать "109" в Это.
В связи с этим я озадачен тем, что кажется экземпляром WPF, использующим реальные пиксели вместо независимых от устройства пикселей.
У меня есть следующий XAML, объявляющий окно с простой сеткой и прямоугольником внутри сетки:
<Window x:Class="MyTestWindow.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
Height="768"
Width="1024">
<Grid x:Name="MainObjectLocationGrid">
<Rectangle x:Name="MainObjectLocationRectangle" HorizontalAlignment="Left" VerticalAlignment="Top" />
</Grid>
</Window>
В коде позади у меня есть код, который устанавливает некоторые свойства этого прямоугольника:
MainObjectLocationRectangle.Width = 189.5625;
MainObjectLocationRectangle.Height = 146.4;
MainObjectLocationRectangle.Fill = new SolidColorBrush(Colors.White);
MainObjectLocationRectangle.Stroke = new SolidColorBrush(Colors.Transparent);
MainObjectLocationRectangle.StrokeThickness = 0;
MainObjectLocationRectangle.Margin = new Thickness(0, 0, 0, 0);
Когда мой прямоугольник появляется на экране, он имеет размер 4,4 см х 3,4 см. WPF говорит, что 1 независимый от устройства пиксель равен 1/96 дюйма, поэтому я бы предположил, что 96 dip составляет 1 дюйм (что составляет 2,54 см). Поэтому наклон 189,5625 должен составлять 1,9746 дюйма (или 5,01 см). Тем не менее, похоже, что он не использует dip в этом случае. Если мы вставим фактическое разрешение моего монитора (109 точек на дюйм) в уравнение, мы получим фактический размер отображаемого прямоугольника:
189,5625 пикселей / 109 точек на дюйм = 1,7391 дюйма (или 4,4 см)
Тем не менее, в документации WPF говорится, что свойства Width и Height используют независимые от устройства пиксели:
Итак, сделаем вывод:
(1) Почему все принятые методы запроса DPI не возвращают мне правильный DPI?
(2) Почему кажется, что когда я устанавливаю размер прямоугольника, он интерпретирует этот размер в единицах пикселей, а не в независимых от устройства пикселях?
Спасибо за любую помощь!!!
2 ответа
Это старый вопрос, но вы можете получить физические характеристики монитора из Windows.Devices.Display.DisplayMonitor.
Подготовьтесь к использованию WinRT, затем вызовите метод Windows.Devices.Enumeration.DeviceInformation.FindAllAsync .
using Windows.Devices.Display;
using Windows.Devices.Enumeration;
public async Task CheckMonitors()
{
const string deviceInstanceIdKey = "System.Devices.DeviceInstanceId";
DeviceInformationCollection devices = await DeviceInformation.FindAllAsync(DisplayMonitor.GetDeviceSelector(), new[] { deviceInstanceIdKey });
foreach (DeviceInformation? device in devices)
{
DisplayMonitor? monitor = await DisplayMonitor.FromInterfaceIdAsync(device.Id);
if (monitor is null)
continue;
Debug.WriteLine($"DeviceInstanceId: {device.Properties[deviceInstanceIdKey]}");
Debug.WriteLine($"DisplayName: {monitor.DisplayName}");
Debug.WriteLine($"PhysicalSizeInInches: {monitor.PhysicalSizeInInches}");
Debug.WriteLine($"RawDpiX: {monitor.RawDpiX}");
Debug.WriteLine($"RawDpiY: {monitor.RawDpiY}");
Debug.WriteLine($"NativeResolutionInRawPixels: {monitor.NativeResolutionInRawPixels.Width},{monitor.NativeResolutionInRawPixels.Height}");
}
}
В случае Dell U2415 этот код выдает следующее:
DisplayName: DELL U2415
PhysicalSizeInInches: 20.393702,12.755906
RawDpiX: 94.14671
RawDpiY: 94.074066
NativeResolutionInRawPixels: 1920,1200
Сделайте расчет по своему усмотрению.
Хотя Windows знает разрешение монитора, на самом деле она не знает его физического размера. И, таким образом, у него нет средств для определения фактического физического DPI. Windows просто предполагает, что все экраны имеют разрешение 96 dpi. Если пользователь выбирает коэффициент масштабирования в настройках дисплея, Windows настраивает системное разрешение на дюйм на основе этого.
Для вашего приложения лучше всего иметь страницу калибровки, где оно отображает линию на экране и просит пользователя измерить ее с помощью линейки и ввести измерение. Это даст вам достаточно информации, чтобы выяснить фактическое значение dpi на экране. И технически вам нужно сделать это как по горизонтали, так и по вертикали, поскольку DPI в каждом направлении теоретически могут быть разными. (Но я думаю, что на практике они почти всегда одинаковы.)