Información general sobre el foco

En WPF, hay dos conceptos principales relacionados con el foco: el foco de teclado y el foco lógico. El foco de teclado hace referencia al elemento que recibe la entrada del teclado, y el foco lógico hace referencia al elemento que tiene el foco en un ámbito de foco. Estos conceptos se describen con detalle en esta información general. Entender la diferencia entre estos conceptos es importante para crear aplicaciones complejas que tengan varias regiones donde se pueda obtener el foco.

Las clases principales que participan en la administración del foco son la Keyboard clase, la FocusManager clase y las clases de elementos base, como UIElement y ContentElement . Para obtener más información sobre los elementos base, consulte Información general sobre los elementos base.

La Keyboard clase está preocupada principalmente por el foco de teclado y FocusManager está relacionado principalmente con el foco lógico, pero no es una distinción absoluta. Un elemento que tiene el foco de teclado también tendrá el foco lógico, pero un elemento que tiene el foco lógico no tendrá necesariamente el foco de teclado. Esto es evidente cuando se usa la Keyboard clase para establecer el elemento que tiene el foco de teclado; también establece el foco lógico en el elemento.

Foco de teclado

El foco de teclado hace referencia al elemento que recibe actualmente la entrada del teclado. Puede haber un único elemento en todo el escritorio que tenga el foco de teclado. En WPF , el elemento que tiene el foco de teclado tendrá IsKeyboardFocused establecido en true . La propiedad estática FocusedElement de la Keyboard clase obtiene el elemento que tiene actualmente el foco de teclado.

Para que un elemento obtenga el foco de teclado, las Focusable propiedades y IsVisible de los elementos base deben establecerse en true . Algunas clases, como la Panel clase base, tienen Focusable establecido en de false forma predeterminada; por lo tanto, debe establecer Focusable en true si desea que dicho elemento pueda obtener el foco de teclado.

El foco de teclado puede obtenerse a través de la interacción del usuario con la UI, como desplazarse mediante la tecla TAB hasta un elemento o hacer clic con el mouse en determinados elementos. El foco de teclado también se puede obtener mediante programación con el Focus método en la Keyboard clase. El Focus método intenta proporcionar el foco de teclado del elemento especificado. El elemento devuelto es el elemento que tiene el foco de teclado, que puede ser un elemento diferente al solicitado si el objeto que tenía el foco antes o el que tiene el foco ahora bloquean la solicitud.

En el ejemplo siguiente se usa el Focus método para establecer el foco de teclado en un Button .

private void OnLoaded(object sender, RoutedEventArgs e)
{
    // Sets keyboard focus on the first Button in the sample.
    Keyboard.Focus(firstButton);
}
Private Sub OnLoaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
    ' Sets keyboard focus on the first Button in the sample.
    Keyboard.Focus(firstButton)
End Sub

La IsKeyboardFocused propiedad de las clases de elementos base obtiene un valor que indica si el elemento tiene el foco de teclado. La IsKeyboardFocusWithin propiedad de las clases de elementos base obtiene un valor que indica si el elemento o cualquiera de sus elementos visuales secundarios tiene el foco de teclado.

Al establecer el foco inicial en el inicio de la aplicación, el elemento para recibir el foco debe estar en el árbol visual de la ventana inicial cargada por la aplicación y el elemento debe tener Focusable y IsVisible establecido en true . El lugar recomendado para establecer el foco inicial es el Loaded controlador de eventos. DispatcherTambién se puede usar una devolución de llamada mediante una llamada a Invoke o BeginInvoke .

Foco lógico

El foco lógico hace referencia a FocusManager.FocusedElement en un ámbito de foco. Un ámbito de foco es un elemento que realiza un seguimiento de FocusedElement dentro de su ámbito. Cuando el foco de teclado sale de un ámbito de foco, el elemento enfocado pierde el foco de teclado, pero conserva el foco lógico. Cuando el foco de teclado vuelve al ámbito de foco, el elemento enfocado recibe el foco de teclado. Esto permite cambiar el foco de teclado entre varios ámbitos de foco, pero garantiza que el elemento enfocado dentro del ámbito de foco vuelva a obtener el foco de teclado cuando el foco vuelva al ámbito de foco.

Puede haber varios elementos que tengan el foco lógico en una aplicación, pero solo puede haber uno con el foco lógico en un ámbito de foco concreto.

Un elemento que tiene el foco de teclado tiene el foco lógico para el ámbito de foco al que pertenece.

Un elemento se puede convertir en un ámbito de foco en Lenguaje XAML (Extensible Application Markup Language) estableciendo la FocusManager propiedad adjunta IsFocusScope en true . En el código, un elemento se puede convertir en un ámbito de foco llamando a SetIsFocusScope .

En el ejemplo siguiente se convierte un StackPanel en un ámbito de foco estableciendo la IsFocusScope propiedad adjunta.

<StackPanel Name="focusScope1" 
            FocusManager.IsFocusScope="True"
            Height="200" Width="200">
  <Button Name="button1" Height="50" Width="50"/>
  <Button Name="button2" Height="50" Width="50"/>
</StackPanel>
StackPanel focuseScope2 = new StackPanel();
FocusManager.SetIsFocusScope(focuseScope2, true);
Dim focuseScope2 As New StackPanel()
FocusManager.SetIsFocusScope(focuseScope2, True)

GetFocusScope Devuelve el ámbito del foco del elemento especificado.

Las clases en WPF que son ámbitos de foco de forma predeterminada son Window , MenuItem , ToolBar y ContextMenu .

GetFocusedElement Obtiene el elemento enfocado para el ámbito de foco especificado. SetFocusedElement establece el elemento con foco en el ámbito de foco especificado. SetFocusedElement se utiliza normalmente para establecer el elemento que tiene el foco inicial.

En el ejemplo siguiente se establece el elemento con foco en un ámbito de foco y se obtiene el elemento con foco de un ámbito de foco.

// Sets the focused element in focusScope1
// focusScope1 is a StackPanel.
FocusManager.SetFocusedElement(focusScope1, button2);

// Gets the focused element for focusScope 1
IInputElement focusedElement = FocusManager.GetFocusedElement(focusScope1);
' Sets the focused element in focusScope1
' focusScope1 is a StackPanel.
FocusManager.SetFocusedElement(focusScope1, button2)

' Gets the focused element for focusScope 1
Dim focusedElement As IInputElement = FocusManager.GetFocusedElement(focusScope1)

Navegación mediante teclado

La KeyboardNavigation clase es responsable de implementar la navegación predeterminada del foco de teclado cuando se presiona una de las teclas de navegación. Las teclas de navegación son: TAB, MAYÚS+TAB, CTRL+TAB, CTRL+MAYÚS+TAB, flecha arriba, flecha abajo, flecha izquierda y flecha derecha.

El comportamiento de navegación de un contenedor de navegación se puede cambiar estableciendo las propiedades adjuntas KeyboardNavigation TabNavigation , ControlTabNavigation y DirectionalNavigation . Estas propiedades son del tipo KeyboardNavigationMode y los valores posibles son Continue , Local , Contained , Cycle , Once y None . El valor predeterminado es Continue , lo que significa que el elemento no es un contenedor de navegación.

En el ejemplo siguiente se crea un Menu objeto con un número de MenuItem objetos. La TabNavigation propiedad adjunta se establece en Cycle en Menu . Cuando se cambia el foco mediante la tecla TAB dentro de Menu , el foco se desplaza de cada elemento y, cuando se alcanza el último elemento, el foco volverá al primer elemento.

<Menu KeyboardNavigation.TabNavigation="Cycle">
  <MenuItem Header="Menu Item 1" />
  <MenuItem Header="Menu Item 2" />
  <MenuItem Header="Menu Item 3" />
  <MenuItem Header="Menu Item 4" />
</Menu>
Menu navigationMenu = new Menu();
MenuItem item1 = new MenuItem();
MenuItem item2 = new MenuItem();
MenuItem item3 = new MenuItem();
MenuItem item4 = new MenuItem();

navigationMenu.Items.Add(item1);
navigationMenu.Items.Add(item2);
navigationMenu.Items.Add(item3);
navigationMenu.Items.Add(item4);

KeyboardNavigation.SetTabNavigation(navigationMenu,
    KeyboardNavigationMode.Cycle);
Dim navigationMenu As New Menu()
Dim item1 As New MenuItem()
Dim item2 As New MenuItem()
Dim item3 As New MenuItem()
Dim item4 As New MenuItem()

navigationMenu.Items.Add(item1)
navigationMenu.Items.Add(item2)
navigationMenu.Items.Add(item3)
navigationMenu.Items.Add(item4)

KeyboardNavigation.SetTabNavigation(navigationMenu, KeyboardNavigationMode.Cycle)

La API adicional para trabajar con el foco es MoveFocus y PredictFocus .

MoveFocus cambia el foco al siguiente elemento de la aplicación. TraversalRequestSe utiliza para especificar la dirección. El FocusNavigationDirection que se pasa a MoveFocus especifica el foco de direcciones diferentes que se puede desplazar, como First , Last Up y Down .

En el ejemplo siguiente MoveFocus se utiliza para cambiar el elemento que tiene el foco.

// Creating a FocusNavigationDirection object and setting it to a
// local field that contains the direction selected.
FocusNavigationDirection focusDirection = _focusMoveValue;

// MoveFocus takes a TraveralReqest as its argument.
TraversalRequest request = new TraversalRequest(focusDirection);

// Gets the element with keyboard focus.
UIElement elementWithFocus = Keyboard.FocusedElement as UIElement;

// Change keyboard focus.
if (elementWithFocus != null)
{
    elementWithFocus.MoveFocus(request);
}
' Creating a FocusNavigationDirection object and setting it to a
' local field that contains the direction selected.
Dim focusDirection As FocusNavigationDirection = _focusMoveValue

' MoveFocus takes a TraveralReqest as its argument.
Dim request As New TraversalRequest(focusDirection)

' Gets the element with keyboard focus.
Dim elementWithFocus As UIElement = TryCast(Keyboard.FocusedElement, UIElement)

' Change keyboard focus.
If elementWithFocus IsNot Nothing Then
    elementWithFocus.MoveFocus(request)
End If

PredictFocus Devuelve el objeto que recibiría el foco si se cambiara el foco. Actualmente, solo Up Down Left Right se admiten,, y PredictFocus .

Eventos de foco

Los eventos relacionados con el foco de teclado son PreviewGotKeyboardFocus , GotKeyboardFocus y PreviewLostKeyboardFocus LostKeyboardFocus . Los eventos se definen como eventos adjuntos en la Keyboard clase, pero se puede acceder a ellos más fácilmente como eventos enrutados equivalentes en las clases de elementos base. Para obtener más información sobre los eventos, consulte Información general sobre eventos enrutados.

GotKeyboardFocus se genera cuando el elemento obtiene el foco de teclado. LostKeyboardFocus se genera cuando el elemento pierde el foco de teclado. Si el PreviewGotKeyboardFocus evento o el PreviewLostKeyboardFocusEvent evento se administran y Handled se establece en true , el foco no cambiará.

En el ejemplo siguiente se adjuntan los GotKeyboardFocus LostKeyboardFocus controladores de eventos y a un TextBox .

<Border BorderBrush="Black" BorderThickness="1"
        Width="200" Height="100" Margin="5">
  <StackPanel>
    <Label HorizontalAlignment="Center" Content="Type Text In This TextBox" />
    <TextBox Width="175"
             Height="50" 
             Margin="5"
             TextWrapping="Wrap"
             HorizontalAlignment="Center"
             VerticalScrollBarVisibility="Auto"
             GotKeyboardFocus="TextBoxGotKeyboardFocus"
             LostKeyboardFocus="TextBoxLostKeyboardFocus"
             KeyDown="SourceTextKeyDown"/>
  </StackPanel>
</Border>

Cuando TextBox obtiene el foco de teclado, la Background propiedad de TextBox se cambia a LightBlue .

private void TextBoxGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    TextBox source = e.Source as TextBox;

    if (source != null)
    {
        // Change the TextBox color when it obtains focus.
        source.Background = Brushes.LightBlue;

        // Clear the TextBox.
        source.Clear();
    }
}
Private Sub TextBoxGotKeyboardFocus(ByVal sender As Object, ByVal e As KeyboardFocusChangedEventArgs)
    Dim source As TextBox = TryCast(e.Source, TextBox)

    If source IsNot Nothing Then
        ' Change the TextBox color when it obtains focus.
        source.Background = Brushes.LightBlue

        ' Clear the TextBox.
        source.Clear()
    End If
End Sub

Cuando TextBox pierde el foco de teclado, la Background propiedad de TextBox se vuelve a cambiar a blanco.

private void TextBoxLostKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e)
{
    TextBox source = e.Source as TextBox;

    if (source != null)
    {
        // Change the TextBox color when it loses focus.
        source.Background = Brushes.White;

        // Set the  hit counter back to zero and updates the display.
        this.ResetCounter();
    }
}
Private Sub TextBoxLostKeyboardFocus(ByVal sender As Object, ByVal e As KeyboardFocusChangedEventArgs)
    Dim source As TextBox = TryCast(e.Source, TextBox)

    If source IsNot Nothing Then
        ' Change the TextBox color when it loses focus.
        source.Background = Brushes.White

        ' Set the  hit counter back to zero and updates the display.
        Me.ResetCounter()
    End If
End Sub

Los eventos relacionados con el foco lógico son GotFocus y LostFocus . Estos eventos se definen en FocusManager como eventos adjuntos, pero FocusManager no expone contenedores de eventos CLR. UIElement y ContentElement exponen estos eventos de forma más cómoda.

Consulte también