Using VisualTreeHelper.GetDrawing to get around the Z-Order limitation when hosting Windows Forms controls
Several times I have had needed to put WPF content in front of Windows Forms control hosted in a WPF app...
Unfortunately, this is a limitation in the interop story, WindowsFormsHost elements are always drawn on top, and don't get affected by z-order ...
The first time I did this, I was working with an imaging company, so it was easy as their activex rendered onto a bitmap already.. so we used that as a workaround: create a bitmap, and then 'replace' the control with the bitmap at specific times/triggers..
The next time, I tried to use the same workaround .. but this time, I wrote some 'ugly' code to make a simple winforms control render into a bitmap ... [trust me it was ugly, took me a day+, used interop, ec.] ... Now, I see this post from LLobo telling me there was an API for this ... way to make me feel dumber than usual :( ...
So, I am putting it here so no one else misses it like I did, also as a reference as now I have to email Robert -sorry man - to tell him to replace our old code :) The API is VisualTreeHelper.GetDrawing.. Wrote the simplest test, here is kinda what it looks like [will do for a quick airspace demo :) ]
Explanation of the screen above:
The Left hand side is a WindowsFormsHost control hosting a MonthCalendar Windows Forms controls. . The RHS is a Rectangle witha DrawingBrush of the WindowsFormsHost in the LHS... Across both UIElements, there is supposed to be a red rectangle overlayed [imagine this could be any other element or a flying animation, etc.] ... Notice that on the Left, the Red rectangle is behind the WindowsFormsHost. . on the right, you get the right visual experience. .
You could easily use this strategy to swap the WindowsFormsHost in and out ... don't you think??
The code is trivial, I packaged it inside a Dispatcher callback - I don't think it is absolutely needed, but felt safer, trying to make sure windowsformshost has been drawn] ....
void Window1_Loaded(object sender, RoutedEventArgs e)
{
Matrix m =
PresentationSource.FromVisual(Application.Current.MainWindow).CompositionTarget.TransformToDevice;
double dx = m.M11;
double dy = m.M22;
System.Windows.Forms.MonthCalendar mc = new System.Windows.Forms.MonthCalendar();
mc.Width = (int)(wfHost.Width * dx);
mc.Height = (int)(wfHost.Height * dy);
wfHost.Child = mc;
this.Dispatcher.BeginInvoke (System.Windows.Threading.DispatcherPriority.Background ,
new DispatcherOperationCallback(delegate
{
DrawingGroup dg = VisualTreeHelper.GetDrawing( wfHost );
DrawingBrush db = new DrawingBrush ( dg );
db.TileMode = TileMode.None;
db.Stretch = Stretch.None;
rect.Fill= db ;
return null;
} ), null );
}
Full sample is here.
I tested with several custom windows forms controls seems to work ... That said, I could not get Lester's demo to work with the IE frame ...
I believe this same code will work for Win32 interop (HwndHost) but did not test it, if you try it and it does not work ping me..