I move focus to the Popup on its opening:
wcl:FocusHelper.IsFocused="{Binding RelativeSource={RelativeSource Self}, Path=IsOpen}"
FocusHelper class code:
public static class FocusHelper
{
public static readonly DependencyProperty IsFocusedProperty =
DependencyProperty.RegisterAttached("IsFocused", typeof(bool?), typeof(FocusHelper), new FrameworkPropertyMetadata(IsFocusedChanged));
public static bool? GetIsFocused(DependencyObject element)
{
if (element == null)
{
throw new ArgumentNullException("element");
}
return (bool?)element.GetValue(IsFocusedProperty);
}
public static void SetIsFocused(DependencyObject element, bool? value)
{
if (element == null)
throw new ArgumentNullException("element");
element.SetValue(IsFocusedProperty, value);
}
private static void IsFocusedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var fe = (FrameworkElement)d;
if (e.OldValue == null)
{
fe.GotFocus += ElementGotFocus;
fe.LostFocus += ElementLostFocus;
fe.IsVisibleChanged += ElementIsVisibleChanged;
}
if (e.NewValue == null)
{
fe.GotFocus -= ElementGotFocus;
fe.LostFocus -= ElementLostFocus;
fe.IsVisibleChanged -= ElementIsVisibleChanged;
return;
}
if ((bool)e.NewValue)
{
fe.SetFocusWithin();
}
}
private static void ElementIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var fe = (FrameworkElement)sender;
if (fe.IsVisible
&& (bool)(((FrameworkElement) sender).GetValue(IsFocusedProperty))) // Bring focus to just become visible element.
fe.Focus();
}
private static void ElementGotFocus(object sender, RoutedEventArgs e)
{
((FrameworkElement)sender).SetCurrentValue(IsFocusedProperty, true);
}
private static void ElementLostFocus(object sender, RoutedEventArgs e)
{
((FrameworkElement)sender).SetCurrentValue(IsFocusedProperty, false);
}
/// <summary>
/// Tries to set focus to the element or any other element inside this one.
/// Tab index is respected
/// </summary>
/// <param name="element"></param>
/// <returns></returns>
public static bool SetFocusWithin(this DependencyObject element)
{
if (element == null)
throw new ArgumentNullException("element");
var inputElement = element as IInputElement;
if (inputElement == null || !inputElement.Focus())
{
var children = element.GetVisualChildrenSortedByTabIndex().Where(child => !(child is Control) || (bool)child.GetValue(Control.IsTabStopProperty) );
return children.Any(SetFocusWithin);
}
return true;
}
}
ElementTreeHelper class part :
public static IEnumerable<DependencyObject> GetVisualChildren(this DependencyObject parent)
{
if (parent == null)
throw new ArgumentNullException("parent");
var count = VisualTreeHelper.GetChildrenCount(parent);
for (var i = 0; i < count; i++)
yield return VisualTreeHelper.GetChild(parent, i);
}
public static IEnumerable<DependencyObject> GetVisualChildrenSortedByTabIndex(this DependencyObject parent)
{
if (parent == null)
throw new ArgumentNullException("parent");
return parent.GetVisualChildren().OrderBy(KeyboardNavigation.GetTabIndex);
}
The problem is that var count = VisualTreeHelper.GetChildrenCount(parent) == 0 when parent is Popup.
UPDATE
The answer is here
The Popup doesn't host the Child within itself. Instead it creates a PopupRoot (internal class) that is the root visual of a new HWND created to host the popup's content within a separate top level window (or child window in xbap). The Child of the Popup is hosted within an AdornerDecorator within that PopupRoot.