0

I have a really big wpf application with many nested controls using caliburn micro and MVVM. One control needs a check a condition before(while) the user leaves. If the check fails the focus is transfered back to the said control.

Using Finding ALL child controls WPF I solved the problem with child control also firing the LostFocus event. And I used the Dispatcher to put the focus back to the control.

my code looks like this:

public void LostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    var parent = ParentFinder.TryFindParent<ProfileFunctionView>(e.NewFocus as FrameworkElement);
    //only fire for outside controls, not child controls
    if (parent == null)
    {
        if (!Apply())
        {
            var restoreFocus = (System.Threading.ThreadStart)delegate { SyntaxEditor.Focus(); };
            Application.Current.Dispatcher.BeginInvoke(restoreFocus);
            //stuff should happen here
        }

    }
}

The problem is that, if I click on a tab control somewhere above my control f.e.. The focus is set back correctly, but than the tab changes. I want to prevent the controls that are clicked outside of my control to react if the condition fails.

Is this possible? Is my approach correct?

Please excuse my confusing title, this problem is really hard to formulate or google. Any help is appreciated.

b.holz
  • 237
  • 3
  • 17

1 Answers1

0

So, the first thing I would do is use the FocusManager to create a focus scope. Here is the MSDN Documentation.

https://learn.microsoft.com/en-us/dotnet/api/system.windows.input.focusmanager?view=netcore-3.1

Setting a focus scope is essentially a container of elements within the scope. So, for your example I would do something like this.

ProfileFunctionView () 
{
    // This will make child controls use this as their FocusScope.
    FocusManager.SetIsFocusScope ( this ); 
}

Next, now that we have created a FocusScope it is easy to check if the element that has focus is in scope or not.

public void LostKeyboardFocus( object sender, KeyboardFocusChangedEventArgs e )
{
    if ( sender is UIElement element ) {
       var scope = FocusManager.GetFocusScope ( element );
       if ( scope is Window ) {
           // This is the default focus scope you have actually lost focus.
           RestoreFocus();
       }
       else if ( scope is ProfileFunctionView view ) 
       {
           // Haven't lost focus do nothing
           return;
       }
       else 
       {
          // Handle case where focus does not belong to either.
       }
    }
}

Another method and probably the more appropriate method would be to set the parent elements Focusable property to False. This will prevent the lost focus events from firing in the first place.

Edit: Adding information based on your comment.

If the events are unwanted Disable all controls out side of FocusScope.

ProfileFunctionView () 
{
    // This will make child controls use this as their FocusScope.
    FocusManager.SetIsFocusScope ( this ); 

    // Better to do this during the Loaded event.
    FocusManagerUtility.DisableUIOutsideOfScope ( scope: this );
}

public static class FocusScopeUtility 
{
    public static void DisableUIOutsideScope ( scope ) 
    {
        // Psuedo function to get the top level UIElement in the tree.
        var container = GetOwningWindowOrPage ( scope );

        // Psuedo function to get traversal of tree
        var traversal = GetVisualTreeTraversalAsEnumerable ();
        foreach ( var node in traversal ) 
        {
            var element = node as UIElement;
            if ( FocusScopeUtility.IsInScope ( scope, element ) 
            {
                // This line disables the control from receiving input.
                element.Enabled = false;
            }
        }    
    }
}
Dan Barrett
  • 225
  • 1
  • 6
  • Thanks for your answer, Focusscope will surely come in handy in the future. But the focus is working correctly. The problem are the other events that are executed Like button click, windows closing or tabchange. These events need to be blocked if the condition fail but need to fire otherwise. – b.holz May 05 '20 at 05:23