WebBrowser control on transparent WPF window

Many customers are surprised and disappointed to discover that a WebBrowser control (or any native window, really) hosted in a WPF window with AllowsTransparency=True does not work—problems ranging from broken repainting to the child window/control not appearing at all. This is due to the fundamental “airspace” issue. In particular, composition involving different rendering technologies supports transparency only with entire top-level windows, not child windows.

If you must show a WebBrowser control or host any child window (HWND) “in” a WPF window with transparency, here is one workaround technique. The idea is to wrap the native window in a top-level non-WPF one and “float” that window over the area of the main WPF window where the control is supposed to appear, responding to layout and window resizing changes. With appropriate window “styling” and event handling, this can create a pretty good illusion of an actual hosted child window. Of course, you can expect some integration roughness with such a hacky technique, but it may work well enough for you.

Transparent web browser control screenshot

So, how is this trick even possible, if child window composition does not work with transparency? (The hosted control/native window is still a child window.) Well, it does work when the “system-managed” transparency mode is used and all windows in the hierarchy use GDI for drawing. Dwayne has a great overview of the two transparency modes supported by Windows and how WPF relates to them. We cannot use a WPF window to host the native control, or we are back to the original problem. The easiest way to create a GDI window with the system-managed transparency/composition is via WinForms. This is what the attached prototype project does. To make that “overlay” window (wrapped by a System.Windows.Forms.Form object) appear as much as possible as a child window of the main WPF window, it has ShowInTaskbar=False and FormBorderStyle=FormBorderStyle.None. Additionally, it has the main window set as its owner. This takes care of minimize+restore behavior and automatically destroys the owned window with the main one.

To define where in the main window the hosted control should appear, we designate a “placement target” element, which could be a border, panel, or some other FrameworkElement. The “overlay” window has to be moved and/or resized in response to the owner window moving or the placement target element moving in layout or getting resized. Since in typical situations the location of the placement target will remain fixed relative to the window borders, responding only to its SizeChanged event is a good optimization. There is no LocationChanged event, and I don’t know if there is any more efficient way to get a notification like this than to handle every LayoutUpdated event (which looks like a per-instance event, but it’s really per layout pass, which can affect many elements across the tree).

If you need the WebBrowser control in particular and you want to avoid the dependence on WinForms, you could use a WPF window as the “overlay” one and WPF’s System.Windows.Controls.WebBrowser, but you have to give up on the browser control being transparent. The attached prototype project illustrates this alternative. (The button at the top of the initial window opens the WPF-based version.)