Search code examples
wpfmvvmuser-controlscommandfocus

Set focus to a usercontrol when it is made visible


I am showing a MessageBox and want the user to be able to copy the contents of the message using CTRL+C. The problem is that I can't seem to set focus to the dialog.

The MessageBox is implemented in MVVM. To show it I just make a usercontrol visible (centre screen) and disable the main view.

The copy command is implemented using a Prism DelegateCommand:

<UserControl.InputBindings>
    <KeyBinding Key="C" Modifiers="Control" Command="{Binding CopyCommand}"/>
</UserControl.InputBindings>

If I tab onto on of the message box button the CopyCommand fires. However I cannot get it to work initially when the dialog is shown.

How do I get the usercontrol to accept focus or to attach the KeyBinding to the whole of the usercontrol?

Note: I need an MVVM solution as don't want any code in the code behind file.


Solution

  • In situations when using MVVM pattern and need to interact with the user interface, I always try to implement this solution through an attached behavior. Attached behavior is very powerful and convenient solution that fully satisfies the MVVM pattern, which can also be used in the Blend (with a pre-defined interface).

    In this case, I created an attached behavior VisibleFocusBehavior, which set a IsVisibleChanged event handler, wherein the focus is set in the case of the visibility of the element.

    To avoid the appearance of dotted box, when the control gets focus, I set FocusVisualStyle="{x:Null} for UserControl.

    VisibleFocusBehavior

    public class VisibleFocusBehavior
    {
        #region IsFocusEnabled Dependency Property
    
        public static readonly DependencyProperty IsFocusEnabledProperty;
    
        public static void SetIsFocusEnabled(DependencyObject DepObject, bool value)
        {
            DepObject.SetValue(IsFocusEnabledProperty, value);
        }
    
        public static bool GetIsFocusEnabled(DependencyObject DepObject)
        {
            return (bool)DepObject.GetValue(IsFocusEnabledProperty);
        }
    
        #endregion
    
        #region BringToFrontBehavior Constructor
    
        static VisibleFocusBehavior()
        {
            IsFocusEnabledProperty = DependencyProperty.RegisterAttached("IsFocusEnabled",
                                                                typeof(bool),
                                                                typeof(VisibleFocusBehavior),
                                                                new UIPropertyMetadata(false, IsFocusTurn));
        }
    
        #endregion
    
        #region IsFocusTurn
    
        private static void IsFocusTurn(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            UIElement element = sender as UIElement;
    
            if (e.NewValue is bool && ((bool)e.NewValue) == true)
            {
                if (element != null)
                {
                    element.IsVisibleChanged += new DependencyPropertyChangedEventHandler(ElementIsVisibleChanged);
                }
            }
        }
    
        #endregion
    
        #region ElementIsVisibleChanged Handler
    
        private static void ElementIsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e) 
        {
            UIElement visibilityElement = sender as UIElement;
    
            if (visibilityElement.IsVisible == true) 
            {
                visibilityElement.Focus();
            }
        }
    
        #endregion
    }
    

    Example of using

    <UserControl x:Class="UserControlFocusHelp.TestUserControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:UserControlFocusHelp"             
             mc:Ignorable="d" 
             d:DesignHeight="300" 
             d:DesignWidth="300"
             xmlns:AttachedBehaviors="clr-namespace:UserControlFocusHelp.AttachedBehaviors"
             AttachedBehaviors:VisibleFocusBehavior.IsFocusEnabled="True"
             FocusVisualStyle="{x:Null}">
    
    <UserControl.InputBindings>
        <KeyBinding Key="C" 
                    Modifiers="Control"
                    Command="{Binding CopyCommand}" />
    </UserControl.InputBindings>
    

    Test window

    XAML

    <Grid>
        <local:TestUserControl x:Name="TestUserControl"
                               Width="300"
                               Height="300"
                               Focusable="True"
                               Visibility="Collapsed" />
    
        <Button Width="100"
                Height="30" 
                Content="Visible" 
                HorizontalAlignment="Left"
                Click="Button_Click" />
    </Grid>
    

    Code-behind

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        TestUserControl.Visibility = Visibility.Visible;
    }
    

    Full example is available at this link.