8

I have a UserControl hat contains several elements (ListBoxes, Buttons) and a Popup with ComboBoxes and Buttons.

I tried lost focus on the user contorl, but whenever the focus inside the UserControl changes, the Lost(Keyboard)Focus event fires.

But I don't want to know when a child loses focus to another child of the UserControl, but I want to know, when the keyboard focus moves to an element outside the UserControl and it's Popup.

Is there a way to detect that, without checking the LostFocus of every single element?

netaholic
  • 1,345
  • 10
  • 22
bebo
  • 819
  • 1
  • 9
  • 26
  • Could you please share some code? This way it's easier to correct any mistakes you're making or solve any unexpected problems. Perhaps you should take a look at the [Control.Leave Event](https://msdn.microsoft.com/en-us/library/system.windows.forms.control.leave(v=vs.110).aspx). – Oceans Sep 17 '15 at 09:00
  • Control.Leave is Windows.Forms. – bebo Sep 17 '15 at 09:08
  • "_If you do need to create a new WPF control, the simplest way is to create a class that derives from UserControl (...) deriving from UserControl is a suitable model if you want to build your control by adding existing elements to it, similar to how you build an application. (If you want to use templates with your control, derive from Control instead.)_" - Source: [MSDN UserControl Class](https://msdn.microsoft.com/en-us/library/system.windows.controls.usercontrol(v=vs.110).aspx) – Oceans Sep 17 '15 at 09:25

2 Answers2

6

Does this solution suit you? I've created new event, called LostFocusIgnoreChildren in the UserControl, which fires only if new focused element is not a child of this UserControl.

UserControl1.cs

  public partial class UserControl1 : UserControl
    {
        public UserControl1()
        {
            InitializeComponent();
        }

        public event RoutedEventHandler LostFocusIgnoreChildren;
        private void UserControl_LostFocus(object sender, RoutedEventArgs e)
        {
            var focused_element = FocusManager.GetFocusedElement(Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive));
            var parent = (focused_element as FrameworkElement).TryFindParent<UserControl1>();
            if (parent == null && LostFocusIgnoreChildren != null)
                LostFocusIgnoreChildren(this, e);
        }
    }

VisualTreeHelper extensions

 public static class VisualTreeHelperExt
    {
        public static T TryFindParent<T>(this DependencyObject child)
    where T : DependencyObject
        {
            DependencyObject parentObject = GetParentObject(child);
            if (parentObject == null) return null;
            T parent = parentObject as T;
            if (parent != null)
            {
                return parent;
            }
            else
            {
                return TryFindParent<T>(parentObject);
            }
        }

        public static DependencyObject GetParentObject(this DependencyObject child)
        {
            if (child == null) return null;
            ContentElement contentElement = child as ContentElement;
            if (contentElement != null)
            {
                DependencyObject parent = ContentOperations.GetParent(contentElement);
                if (parent != null) return parent;
                FrameworkContentElement fce = contentElement as FrameworkContentElement;
                return fce != null ? fce.Parent : null;
            }
            FrameworkElement frameworkElement = child as FrameworkElement;
            if (frameworkElement != null)
            {
                DependencyObject parent = frameworkElement.Parent;
                if (parent != null) return parent;
            }
            return VisualTreeHelper.GetParent(child);
        }
    }
netaholic
  • 1,345
  • 10
  • 22
  • 1
    Another way to check if the given control's focus was lost in favor of one of its (recursively) child controls, could be using [this code example](https://stackoverflow.com/a/14875652/997940). – Yoav Feuerstein Jul 31 '17 at 09:19
1

much easier to do this:

private void UserControl_LostFocus(object sender, RoutedEventArgs e)
        {
            if (!this.IsKeyboardFocusWithin)
            {
                _lastFocusedElement = e.OriginalSource as IInputElement;
            }
        }
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 06 '22 at 02:24
  • This is actually a much simpler and better solution. To explain some details: If you ask your UserControl "IsKeyboardFocusWithin", it will essentially know if the UI object itself or its UI items are currently focused. For it's UI objects (also other UserControls) you can make it call its parent's method, so it will also detect focus set onto other UI objects on a parallel level. It's exactly what I was looking for. – Battle Jun 04 '23 at 16:11