Información general sobre el foco

Actualización: noviembre 2007

Hay dos conceptos principales relacionados con el foco en WPF: el foco de teclado y el foco lógico. El foco de teclado se refiere al elemento que recibe las acciones del teclado y el foco lógico se refiere al elemento que tiene el foco dentro de 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 tienen varias zonas donde se puede obtener el foco.

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

La clase Keyboard corresponde principalmente al foco de teclado y la clase FocusManager, al foco lógico, aunque no se trata de una distinción absoluta. Un elemento que tiene el foco de teclado también tiene el foco lógico, pero un elemento que tiene el foco lógico no tiene necesariamente el de teclado. Esto resulta patente cuando se usa la clase Keyboard para establecer el elemento que tiene el foco de teclado, ya que también establece el foco lógico en él.

Este tema contiene las secciones siguientes.

  • Foco de teclado
  • Foco lógico
  • Navegación mediante teclado
  • Navegación con el foco mediante programación
  • Eventos de foco
  • Temas relacionados

Foco de teclado

El foco de teclado se refiere al elemento que está recibiendo actualmente las acciones del teclado. Sólo puede haber un elemento en todo el escritorio que tenga el foco de teclado. En WPF, el elemento que tenga el foco de teclado tendrá la propiedad IsKeyboardFocused establecida en true. La propiedad FocusedElement estática de la clase Keyboard obtiene el elemento que tiene actualmente el foco de teclado.

Para que un elemento obtenga el foco de teclado, las propiedades Focusable y IsVisible de los elementos base deben estar establecidas en true. Algunas clases, como la clase base Panel, tienen la propiedad Focusable establecida en false de manera predeterminada; por consiguiente, debe establecer Focusable en true si desea que un elemento de este tipo pueda obtener el foco de teclado.

El foco de teclado se puede obtener a través de la interacción con el usuario con la interfaz de usuario, por ejemplo, cuando presiona la tecla de tabulación para desplazarse hasta un elemento o hace clic con el mouse en algunos elementos. El foco de teclado también se puede obtener mediante programación utilizando el método Focus de la clase Keyboard. El método Focus intenta proporcionar el foco de teclado al elemento especificado. El elemento devuelto es aquél que tiene el foco de teclado, y que puede ser un elemento diferente al solicitado el objeto que tenía el foco anterior o el que tiene el nuevo bloquea la solicitud.

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

private void OnLoaded(object sender, RoutedEventArgs e)
{
    // Sets keyboard focus on the first Button in the sample.
    Keyboard.Focus(firstButton);
}

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

Al establecer el foco inicial cuando se inicia la aplicación, el elemento que reciba el foco debe estar conectado a PresentationSource y debe tener las propiedades Focusable y IsVisible establecidas en true. El lugar recomendado para establecer el foco inicial en una aplicación es el controlador de eventos Loaded. También se puede utilizar una devolución de llamada Dispatcher llamando a Invoke o a BeginInvoke.

Foco lógico

El foco lógico hace referencia al FocusManager.FocusedElement de un ámbito de foco. Un ámbito de foco es un elemento que realiza un seguimiento de la propiedad FocusedElement dentro de su ámbito. Cuando el foco de teclado salga de un ámbito de foco, el elemento que tenga el foco perderá el foco de teclado, pero conservará el foco lógico. Cuando el foco de teclado regrese al ámbito de foco, el elemento que tenga el foco obtendrá el foco de teclado. Esto permite cambiar el foco de teclado entre varios ámbitos de foco, pero garantiza que el elemento que tiene el foco dentro del ámbito de foco recupere el foco de teclado cuando este regrese al ámbito de foco.

Puede haber varios elementos con el foco lógico en una aplicación, pero sólo puede haber uno con el foco lógico en un ámbito de foco determinado.

Un elemento que tiene el foco de teclado también 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 de marcado de aplicaciones extensible (XAML) estableciendo la propiedad asociada IsFocusScope de FocusManager en true. En código, un elemento se puede convertir en un ámbito de foco llamando a SetIsFocusScope.

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

<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);

GetFocusScope devuelve el ámbito de foco del elemento especificado.

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

GetFocusedElement obtiene el elemento que tiene el foco dentro del ámbito de foco especificado. SetFocusedElement establece el elemento que tiene el 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 que tiene el foco en un ámbito de foco y se obtiene el elemento que tiene el 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);

La clase KeyboardNavigation es responsable de implementar la navegación mediante foco de teclado predeterminada 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, y las teclas FLECHA ARRIBA, FLECHA ABAJO, FLECHA IZQUIERDA y FECHA DERECHA.

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

En el ejemplo siguiente se crea un control Menu con varios objetos MenuItem. La propiedad asociada TabNavigation se establece en Cycle en el control Menu. Cuando se cambie el foco mediante la tecla de tabulación en Menu, el foco se desplazará desde cada elemento y, cuando se alcance el último elemento, volverá al primero.

<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);

Las API adicionales para trabajar con el foco son MoveFocus y PredictFocus.

MoveFocus cambia el foco al elemento siguiente en la aplicación. Se utiliza TraversalRequest para especificar la dirección. El objeto FocusNavigationDirection que se pasa a MoveFocus especifica las distintas direcciones en que se puede mover el foco, como First, Last, Up y Down.

En el ejemplo siguiente se utiliza MoveFocus para cambiar el elemento que tiene el foco. Para obtener el código fuente completo de este ejemplo, vea Ejemplo Manipulate Focus Programmatically.

// 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);
}

PredictFocus devuelve el objeto que recibiría el foco si este se cambiara. En la actualidad, el método PredictFocus únicamente admite los valores Up, Down, Left y Right.

Eventos de foco

Los eventos relacionados con el foco de teclado son PreviewGotKeyboardFocus, GotKeyboardFocus, PreviewLostKeyboardFocus y LostKeyboardFocus. Los eventos se definen como eventos asociados de la clase Keyboard, pero resulta más directo tener acceso a ellos como los eventos enrutados equivalentes de las clases de elementos base. Para obtener más información sobre los eventos, vea Información general sobre eventos enrutados.

El evento GotKeyboardFocus se provoca cuando el elemento obtiene el foco de teclado. El evento LostKeyboardFocus se provoca cuando el elemento pierde el foco de teclado. Si se administra el evento PreviewGotKeyboardFocus o PreviewLostKeyboardFocusEvent y la propiedad Handled se establece en true, el foco no cambiará.

En el ejemplo siguiente se asocian los controladores de eventos de GotKeyboardFocus y LostKeyboardFocus a 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 propiedad Background 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();
    }
}

Cuando TextBox pierde el foco del teclado, la propiedad Background de TextBox se vuelve a establecer en el color 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();
    }
}

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

Vea también

Conceptos

Información general sobre acciones del usuario

Información general sobre elementos base

Referencia

FocusManager

UIElement

ContentElement