The LostFocus events are a bit different in WPF than in Winforms as you realise. (I will get back with a link to the best explanation I've seen so far later :
To bubble or tunnel basic WPF events[
^] )
I've found that sometimes you get popups hanging because there is not a loss of focus when the mouse leaves the window (or the control) for various reasons.
Instead we have used the LostMouseCapture to trigger the desired behaviours, perhaps there is something you can use from this idea (sorry that it may be a bit confusing because we have a list popup part rather than something that needs to be validated in this control):
In the UserControl xaml add this
LostMouseCapture="thisControl_LostMouseCapture"
and in the code behind we've added this
private void thisControl_LostMouseCapture(object sender, MouseEventArgs e)
{
if (Mouse.Captured != this)
{
if (e.OriginalSource == this)
{
if (Mouse.Captured == null || !IsDescendant(this, Mouse.Captured as DependencyObject))
{
ClosePopup();
return;
}
}
else if (IsDescendant(this, e.OriginalSource as DependencyObject))
{
if (PART_Popup.IsOpen && Mouse.Captured == null && GetCapture() == IntPtr.Zero)
{
Mouse.Capture(this, CaptureMode.SubTree);
e.Handled = true;
return;
}
}
else
{
ClosePopup();
}
}
}
private void ClosePopup(bool killFocus = true)
{
ResetListBoxSelection();
PART_Popup.IsOpen = false;
if(killFocus) Keyboard.Focus(null);
}
internal static bool IsDescendant(DependencyObject reference, DependencyObject node)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(reference); i++)
{
var child = VisualTreeHelper.GetChild(reference, i);
if (child == node) return true;
if (IsDescendant(child, node)) return true;
var popup = child as Popup;
if(popup != null)
{
if (IsDescendant(popup.Child, node)) return true;
}
}
return false;
}
public static Visual GetDescendantByType(Visual element, Type type)
{
if (element == null)
{
return null;
}
if (element.GetType() == type)
{
return element;
}
Visual foundElement = null;
if (element is FrameworkElement)
{
(element as FrameworkElement).ApplyTemplate();
}
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element); i++)
{
Visual visual = VisualTreeHelper.GetChild(element, i) as Visual;
foundElement = GetDescendantByType(visual, type);
if (foundElement != null)
{
break;
}
}
return foundElement;
}
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr GetCapture();
private void PART_EditableTextBox_LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
var node = Keyboard.FocusedElement as DependencyObject;
if (node == null) return;
if (!IsDescendant(this, node)) ClosePopup(false);
}