I have code that I believe is (technically) correct for getting the DPI of a monitor in Windows 10.
[DllImport("shcore.dll")]
static extern UInt32 GetDpiForMonitor(IntPtr hmonitor,
int dpiType,
out UInt32 dpiX,
out UInt32 dpiY);
[DllImport("user32.dll")]
static extern IntPtr MonitorFromWindow(IntPtr hwnd, UInt32 dwFlags);
...
var whdl = Process.GetCurrentProcess().MainWindowHandle;
var mhdl = MonitorFromWindow(whdl, 0);
GetDpiForMonitor(mhdl, 2, out DpiX, out DpiY); // 255, 256
var ct = PresentationSource.FromVisual(MainImage).CompositionTarget;
var scaleX = ct.TransformToDevice.M11;
var scaleY = ct.TransformToDevice.M22;
var pixelWidth = (int)(GridImage.ActualWidth * DpiX / 96.0);
var pixelHeight = (int)(GridImage.ActualHeight * DpiY / 96.0);
writeableBitmap = new WriteableBitmap(pixelWidth,
pixelHeight,
DpiX,
DpiY,
System.Windows.Media.PixelFormats.Bgr32,
null);
I tested this with a 17" (laptop) 4K panel (3840x2160 with 250% scaling). I used a simple WPF grid control which contains an Image control. The DPI that comes from GetDpiForMonitor
is (X:Y) 255:256. The problem is that this isn't accurate. When I draw a simple grid (lines DPI apart) the squares are not 1", they are 17 (raw) pixels short. The WriteableBitmap
is inside a WPF Image control and that control has Stretch="None"
.
Anyone know why these values are inaccurate or how to get accurate values. Is there a way to get a monitor's resolution and dimensions (so I could calculate the DPI)?
Update
Well this question has been down voted and I still don't have an answer, but I find this little puzzle both interesting and irritating ("interating", "irriresting"). Anyway, here's some additional info sent into the future for anyone that runs across it.
First this is a HP Envy 4K laptop (W2K87UA#ABA) running Windows 10 Pro Build 15063.483. Using the GetDevCaps I queried for the system monitor dimensions
[DllImport("gdi32.dll")]
static extern int GetDeviceCaps(IntPtr hdc, int nIndex);
private const int HORZSIZE = 4;
private const int VERTSIZE = 6;
private const double MM_TO_INCH_CONVERSION_FACTOR = 25.4;
...
var hDC = System.Drawing.Graphics.FromHwnd(whdl).GetHdc();
int horizontalSizeInMilliMeters = GetDeviceCaps(hDC, HORZSIZE);
double horizontalSizeInInches = horizontalSizeInMilliMeters / MM_TO_INCH_CONVERSION_FACTOR;
int vertivalSizeInMilliMeters = GetDeviceCaps(hDC, VERTSIZE);
double verticalSizeInInches = vertivalSizeInMilliMeters / MM_TO_INCH_CONVERSION_FACTOR;
This returns the following:
horizontalSizeInMilliMeters: 382
horizontalSizeInInches: 15.039370078740159
Measured by hand: 15 1/32 " 15.0625" 382.6 mm
vertivalSizeInMilliMeters: 214
verticalSizeInInches: 8.4251968503937018
Measured by hand: 8 15/32" 8.46875 215.1 mm
This all works out to be an X dpi of 255.329842931937 and a Y dpi of 256.373831775701, or approximately (255, 256) which is exactly what the GetDpiForMonitor returns.
Using a WriteableBitmap I draw a grid on the screen using the following code:
color = Colors.White;
int color_data = (color.R << 16)
| (color.G << 8)
| color.B;
dpiy = (int) DpiY;
for (var y = 0; y < height; y += dpiy) {
for (var x = 0; x < width; x++) {
var p = BackBuffer + (y * Stride) + (x * 4);
unsafe {
*((int*)p) = color_data;
}
}
}
dpix = (int) DpiX;
for (var x = 0; x < width; x += dpix) {
for (var y = 0; y < height; y++) {
var p = BackBuffer + (y * Stride) + (x * 4);
unsafe {
*((int*)p) = color_data;
}
}
}
When I measure the grid lines I find that I need to add about 17 pixels both ways to get a 1" unit square. If my "real" DPI were 272 (255 + 17) then the actual width of my screen would be 14.1176470588235" (3840/272).
Out of curiosity I opened Word 2016 set the scale to 100% and measured the Word "ruler" tool. It too is off ~15/16" instead of 1", I have to scale to between 105% and 106% in order to get a "true" inch. So whatever the issue is it also effects MS written apps.
WPF has per-monitor DPI support since .NET Framework 4.6.2. There is more information and an example available at GitHub: http://github.com/Microsoft/WPF-Samples/tree/master/PerMonitorDPI .
You may also want to check out the VisualTreeHelper.GetDpi method.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.