Problem When Using a WinFormsHost in a WPF Popup Control

A colleague of mine recently sent this puzzle my way. Code reuse suggested he use a WinForms TreeView control inside of a WPF popup. The general UI principle here was similar to that of using a ComboBox, but instead of a list, a tree view would be shown in the popup.

The simplest XAML to reproduce the problem is as follows:

<Popup x:Class=”AWOLTree.WinFormsTreePopup”
  PopupAnimation=”Scroll” Placement=”MousePoint” StaysOpen=”False” AllowsTransparency=”False”>
      <wf:TreeView x:Name=”winFormsPopupTreeView”/>

What ends up happening is that the tree gets rendered, but any attempt to interact with the tree results in the popup closing. </setup>

So it was time to dig into the mystery. Why was the popup closing (or the most sensible hypothesis…why did the popup think it needed to close?) My first choice here would have been to use the new VS 2008 .Net source code debugging. However, I have installed .Net 3.5 SP1, and unfortunately the related symbols have not yet been posted (damn my early adoption…)

Instead, it was time to go back to Plan B – Reflector, which is now being published by Red Gate after many years of stellar stewardship by Lutz Roeder.

The MSDN documentation for the Popup control states:

When you set this property [StaysOpen] to false, the Popup content window has mouse capture. The Popup loses mouse capture and the window closes when a mouse event occurs outside the Popup window.

So, odds are that the issue has something to do with Mouse Capture. Sure enough, in the sequence of events, OnLostMouseCapture is one of the last (obvious) overrides called before OnClosed. So I popped open the default implementation of OnLostMouseCapture and found the following:

 private static void OnLostMouseCapture(object sender, MouseEventArgs e)
  Popup popup = sender as Popup
  if (!popup.StaysOpen
    PopupRoot reference = popup._popupRoot.Value
    if (Mouse.Captured != reference)
      if (e.OriginalSource == reference)
        if ((Mouse.Captured == null) || !MenuBase.IsDescendant(reference, Mouse.Captured as DependencyObject))
          popup.IsOpen = false;
(remainder omitted for clarity)

I put in a handler to check teh value of Mouse.Captured, and found that when a WinForms control inside of the popup was selected, it returned null, which falls directly into a call to popup.IsOpen = false, effectively closing the popup. Clicking on a WPF control inside of the same popup results in a valid reference being returned, explaining the difference in behavior.

Now what?

I see a few options. First, the code in question (which I was familiar with) could have been refactored to act on either a WPF or WinForms tree control, maintaining most of the desired reuse. Alternatively, there may be a way to work within the WPF framework to handle this special case, but I prefer to wait until I have .Net source debugging available again before looking at this option.