hwnd interop (part 2)
Airspace
Now that we understand what's going on behind the scenes, let's describe some of implications, which I refer to as "airspace". Within a top-level window, I like to think of each hwnd as having its own "airspace" -- each pixel within the window belongs to exactly one hwnd, which constitutes that hwnd's airspace. And informally, I'll talk about the Avalon airspace versus the DirectX airspace versus the Win32 airspace -- although strictly speaking, there will be more than one Avalon airspace if there's more than one Avalon hwnd. I call it airspace because like air traffic control, bad things happen when you try to travel into someone else's airspace.
Let's walk through a couple examples. We'll start with an application that mixes Win32, DirectX, and Avalon, since each technology gets its own separate, non-overlapping set of pixels, everyone is happy:
But suppose you take that same application, and create an animation that flies over those three regions, that violates airspace. Which technology is drawing that animation? No matter what the answer, that technology will violates someone else's airspace, so it can't be built. This is illustrated in the below picture, where the green circle is attempting to move around the window:
Another violation of airspace is when we use transparency/alpha blending between different technologies. In the picture below, the Avalon box is violating Win32 and DirectX airspace -- because pixels are semi-transparent, they would have to be owned jointly by both DirectX and Avalon, which is not possible. So this is another violation of airspace and can't be built:
Previous three examples used rectangular regions, but there's no reason that airspace has to be rectangular. It can be a rectangle with a hole (e.g., Win32's airspace is everything except the Avalon & DirectX airspace):
And airspaces can be completely nonrectangular, indeed any shape describable by a Win32 HRGN (region):
Hwnds inside Avalon
HwndHost lets you put hwnds inside Avalon – think of HwndHost as a special control (technically, FrameworkElement) that hides the hwnd-ness from the rest of Avalon. HwndHost mostly behaves like any other Avalon FrameworkElement, although there are some important differences around output (drawing and graphics) and input (mouse and keyboard) stemming from limitations in what hwnds can do.
Differences in output behavior include:
- HwndHost cannot be rotated, scaled, skewed, or otherwise affected by a Transform.
- HwndHost does not support the Opacity property (aka alpha blending). If content inside the HwndHost does System.Drawing operations that include alpha, that's fine, but the HwndHost itself only supports Opacity = 100% and can only be contained within other elements that are Opacity = 100%.
- HwndHost will appear on top of other Avalon elements in the same top-level window. (But note that menus, ToolTips, and combo box drop-downs are separate top-level windows, and so should work fine with HwndHost.) This is because multiple Avalon elements share a single hwnd, and you can't say to Win32, "please put this hosted hwnd in front of only some parts of the Avalon hwnd."
- HwndHost does not respect the clipping region of its parent UIElement.
Differences in input behavior include:
- While the mouse is over the HwndHost, you won't receive Avalon mouse events, and Avalon's IsMouseOver property will return false.
- While the HwndHost has keyboard focus, you won't receive Avalon keyboard events, and Avalon's IsFocusWithin property will return false.
- When focus is within the HwndHost and changes to another control inside the HwndHost, you won't receive Avalon GotFocus/LostFocus events.
Comments
- Anonymous
July 14, 2005
Thank you, Nick! It is very valuable knowledge drop. - Anonymous
July 18, 2005
So that concludes my series about hwnd interop, which is really a draft of the hwnd interop white... - Anonymous
January 26, 2006
Hi Nick, you wrote: "Avalon supports HRGN just fine in all its variations, we don’t provide any managed API for doing this but you can always use PInvoke and HwndSource and do it yourself." I think you meant we could get the HwndSource of the top-level window and call SetWindowRgn() on it. But I can't because the HwndSource is a private member of Window, so I can't get the HWND.
Could you explain if this is what you meant or were you suggesting a different technique?