Set focus to a usercontrol when it is made visible

2019-06-26 09:32发布

问题:

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.

回答1:

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.