2

I'm running a .NET desktop app in Per-Monitor v2 DPI Aware mode. Painting WinForms parts of the UI correctly requires that all painters know the current DPI value. High-level painters can get it from the HWND, that's a working solution. However, lower level painters only have a GDI+ System.Drawing.Graphics object.

I'm struggling with making System.Drawing.Graphics tell the correct DPI. Now I'm seeing the SYSTEM-level DPI on it regardless of the current DPI of its HWND. Its DpiX/DpiY properties are read-only, so they have to be affected indirectly.

Graphics has two basic ways of how it can be created for a control, either with its HWND, or an HDC over it (which might be coming from ::GetDC or ::BeginPaint or even WM_PAINT args). Either way it only has the SYSTEM DPI value, and not the per-monitor aware one. It would probably work to make a Bitmap, configure it appropriately and paint into that first, then blit onto the control, but that's not too neat nor optimal.

So: I'm seeing a good per-monitor-aware DPI for an HWND of a WinForms control, I want to see the same nice value on the Graphics object DpiX/DpiY properties over that control, is there a way?

hypersw
  • 475
  • 3
  • 9
  • 1
    Take a look at this answer https://stackoverflow.com/a/55556061/10128127 . There is mentioned the solution how to get the actual DPI scale factor – Andrew Patynko May 03 '19 at 19:52
  • 2
    Thanks, but that answer is much outdated (it gets DPI for `GetDC(NULL)`, which won't be correct for an app running on multiple monitors or especially in Mixed DPI Hosting Behavior mode). Anyway I have ways to get DPI of a certain `HWND`, but I want a `Graphics` object correctly initialized with that DPI and telling it out to all the parties. If some painter only has a `Graphics` and no `HWND` then there's no good way to get the DPI. And wrong DPI values on `Graphics` would be misleading. – hypersw May 05 '19 at 00:53
  • 1
    Did you ever find a good way of accomplishing this? – Anders Forsgren Apr 26 '22 at 14:25

1 Answers1

0

There should now way. To show a form on two monitors, you have to create 2 instances. To load a graphic/image into both forms you need only one instance of this graphic. A graphic is independent of your monitors and internal it doesn't need it (only a collection of pixel).

Mario
  • 278
  • 2
  • 15
  • 2
    Sorry, I cannot see how this text is any relevant to the question. – hypersw May 03 '19 at 18:33
  • Sorry for my answer! I completely misunderstood your question first. I take a deeper look into v2 DPI Aware, interesting stuff. But I found only one way to get correct DPI values: the new `Control.DeviceDpi` Property. If you reflect this code, you find a DpiHelper class who uses `GetDeviceCaps` and DC with unsafe calls. Could be helpful if you can't call the control properties directly. – Mario May 04 '19 at 12:07
  • 1
    Yep I can get the DPI, I cannot make it get into `Graphics` `DpiX` and `DpiY` properties so that whoever reads that from it while painting would not be using wrong values. Sooner or later somebody uses those properties and gets the UI distorted. Which you won't notice under normal conditions (because you'd have the same DPI all around), only when you remote desktop into the box or attach a monitor with another pixel density. – hypersw May 05 '19 at 00:57
  • If you liked reading about PMv2, you might want to go deeper and read about `DPI_HOSTING_BEHAVIOR_MIXED`. That's where it gets really weird. Especially the part where you can plug SYSTEM into PMv2 but if you try it the other way around then pieces of your UI fall off. – hypersw May 05 '19 at 00:59