Ruslan Garipov
Ruslan Garipov

Reputation: 548

How to render child window with Direct2D in native desktop Windows application?

I have a desktop application where all windows (HWND) render itself with Direct2D 1.1. My question is how to do it more correctly?

Should each window has its own Direct2D device context derived from one Direct2D device? In this case, I cannot render transparent content on a child window without additional tricks (I have to change target on parent window’s context, render parent window to Direct2D bitmap and then draw this bitmap on child’s target).

May be it is better to have one Direct2D device context where all windows render itself? I believe DirectComposition works in a similar way. Unfortunately, I cannot use it because I target Windows 7.

Upvotes: 3

Views: 2245

Answers (1)

Rick Brewster
Rick Brewster

Reputation: 3504

You're asking a question whose answer will be very application specific. I recommend avoiding the whole problem of trying to get HWNDs to render with transparency amongst each other, especially if you're throwing Direct2D into the mix. There is just too much pain in that direction. Every version of Windows that you support will have different bugs that you'll be constantly bumping into and grasping at workarounds for.

Case in point: For the v4.0 release of Paint.NET, I converted all text rendering to DirectWrite, and almost all UI controls to use Direct2D. The image thumbnail control at the top of the window (the MDI selector) is using Direct2D for rendering but it also has to compose on top of what's behind it. And it has to play nice with glass on Win7 (it looks great though!). The code for this is awful, tricky, barely maintainable, and it seems to bump into a different rendering bug on every release of Windows: 7, 7 SP1, 8, 8.1, and 10 all behave slightly differently! It's really annoying to test, too; it's the only reason I have to set up and maintain VMs for every version of Windows that I support (other than the installer and updater). Windows 7 worked fine, then 7 SP1 added a bug which required some tuning to how I filled the alpha channel. Windows 8 has flickering when you resize the window unless I do a certain hack, but 8.1 works fine. 10 then has its own flickering bug if software rendering is used. Remote Desktop breaks things in its own way. Then you also have to worry about High Contrast, and whether DWM is enabled/disabled if you're supporting Windows 7. They all behave differently and it's really really painful.

Anyway. What you seem to really need is a UI system like WPF or XAML which doesn't use anything other than a top-level HWND container. At that point you're custom rendering everything and doing your own hit-testing and input routing (and accessibility and all sorts of other things), so it's not a small task.

Regarding the "how to do it more correctly" question and cardinality of device and device context: Have you thought about just using ID2D1Factory::CreateHWNDRenderTarget or ID2D1Factory::CreateDCRenderTarget ? They return ID2D1RenderTarget but you can call QueryInterface to cast them to ID2D1DeviceContext (this fact is missing from the docs but is also clearly intentional). This should simplify working with Direct2D and HWNDs quite a bit. This is what I do in Paint.NET: I still use an HWND for each control, but each control is using its own HWND or DC render target. If you're willing to poke around with Reflector or ILSpy, checkout Direct2DControl and Direct2DControlHandler in the Paint.NET DLLs.

Also, be careful about using more than 1 hardware accelerated HWND render target. You don't want to get into a weird area where every Direct2D-based UI control is waiting on VSync. Using D2D1_PRESENT_OPTIONS_IMMEDIATELY when creating the HWND render target should help. DWM already handles VSync, so you should be fine to tell Direct2D to ignore it unless you're doing some rather specific stuff with animations and timers.

Upvotes: 11

Related Questions