wpfmvvmwpf-controlswpf-4.0

How to properly handle focus


Lets say I have next layout:

Window
  UserControl
    UserControl
      UserControl
        Button
        GridControl
          GridCell

And lets say that GridCell currently has Keyboard focus. If a user clicks on button. a message is displayed to user to confirm action. No matter of what choice the user selected (Yes or No), a focus should return to a CurrentCell on GridControl. By default, after a user selected some option, a focus would return to Window (reported by Snoop). I would assume that a Button that was clicked would retain focus, but apparently not.

Handling of button Command is done in ViewModel (MVVM).

How do I return keyboard focus to a current cell in grid?


Solution

  • You can probably pretty safely work around this problem by setting FocusManager.IsFocusScope="true" on your button or if there are multiple buttons the parent element they are in (eg StackPanel or whatever).

    There are a few potential issues with this if you are using RoutedCommands. Basically RoutedCommands don't always work the way you expect them to inside of focus scopes. It sounds like you are binding directly to a command on the View Model though so that shouldn't be a problem. If you would like to read more about the RoutedCommand issue check this code project article out.

    Here is what my sample code to verify this works looks like for your reference.

    XAML:

    <Window x:Class="WpfApplication1.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
            Title="MainWindow" Height="350" Width="525">
        <Grid Margin="25">
            <Grid.RowDefinitions>
                <RowDefinition Height="Auto" />
                <RowDefinition />
            </Grid.RowDefinitions>
    
            <!--You could also have the FocusManager.IsFocusScope set on the Border instead-->
            <Border Margin="0,0,0,15">
                <Button FocusManager.IsFocusScope="True" Click="ButtonBase_OnClick">Click me!</Button>
            </Border>
    
            <TextBox Grid.Row="1" x:Name="MessageTextBox"></TextBox>
        </Grid>
    </Window>
    

    C#:

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
    
        void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            MessageBox.Show("Clicked, message: " + MessageTextBox.Text);
        }
    }