2011-02-10 9 views
5

Veo un comportamiento extraño cuando se trata de enfocar y navegar por el teclado. En el siguiente ejemplo, tengo un ItemsControl simple que ha sido modelado para que presente una lista de CheckBoxes vinculados a ItemsSource.Comportamiento de foco extraño para WPF ItemsControl simple

<ItemsControl FocusManager.IsFocusScope="True" 
       ItemsSource="{Binding ElementName=TheWindow, Path=ListOStrings}"> 
    <ItemsControl.ItemTemplate> 
     <DataTemplate> 
      <CheckBox Content="{Binding}" /> 
     </DataTemplate> 
    </ItemsControl.ItemTemplate> 
</ItemsControl> 

Por alguna extraña razón la FocusManager.IsFocusScope = "true" asignación hace que el foco del teclado a dejar de ser fijado al comprobar una casilla de verificación a través de un clic del ratón y de enfoque para saltar fuera de la ItemsControl cuando un cheque la casilla se verifica usando la barra espaciadora en el teclado. Ambos síntomas parecen apuntar a una navegación extraña cuando la casilla de verificación está marcada, pero me está costando llegar al fondo.

Este problema se produce si configuro cualquier elemento primario en el árbol visual como un ámbito de enfoque utilizando este método. Si elimino el FocusManager.IsFocusScope = "True", los problemas desaparecerán. Lamentablemente, estoy viendo este problema en un proyecto más grande en el que no puedo simplemente eliminar estos ámbitos de enfoque sin preocuparme por otras consecuencias relacionadas con el enfoque.

¿Podría alguien explicarme el extraño comportamiento que estoy viendo? ¿Es esto un error o simplemente me falta algo por completo?

Respuesta

16

En este artículo se explica muy bien: http://www.codeproject.com/KB/WPF/EnhancedFocusScope.aspx

para lo que fue diseñado FocusScope?

Microsoft usa FocusScope en WPF a crea un foco secundario temporal. Cada barra de herramientas y menú en WPF tiene su propio alcance de enfoque .

Con este conocimiento, podemos claramente ver por qué tenemos esos problemas:

Un botón de la barra de herramientas no deberían ejecutar comandos sobre sí mismo, sino en lo tenido el enfoque antes de la barra de herramientas era clic. Para lograr esto, los comandos enrutados omiten el enfoque de los ámbitos de enfoque y usan el foco lógico 'principal' en su lugar. Esto explica por qué los comandos enrutados no funcionan dentro de los ámbitos de enfoque .

¿Por qué aparece el cuadro de texto grande en la captura de pantalla de la aplicación de prueba todavía mostrar un símbolo de intercalación? No sé la respuesta a esto, pero ¿por qué no debería? De acuerdo, el cuadro de texto no tiene el foco de teclado (el pequeño cuadro de texto en tiene el alcance de foco de WPF); pero todavía tiene el foco lógico principal en la ventana activa y es el receptor de todos los comandos enrutados.

Y esta parte cubre el comportamiento que se está viendo

¿Por qué el foco del teclado para mover el cuadro de texto grande cuando salta a la casilla de la WPF centrarse alcance y presione la barra espaciadora para alternar?

Bueno, esto es exactamente lo que esperas cuando se hace clic en un elemento de menú o una barra de herramientas : el foco del teclado debe vuelta al foco principal. Todos los controles derivados de ButtonBase harán esto.

+0

+1 para una explicación del problema. Acabo de agregar la implementación del comportamiento adjunto IsEnhancedFocusScope. –

+4

Lo único que todavía no entiendo dada esta explicación es que si tomo otro ejemplo donde configuré una grilla para ser el ámbito de enfoque con un grupo de niños que son botones y casillas de verificación, cuando hago clic en uno de estos casillas de verificación No veo la extraña pérdida de comportamiento de enfoque que veo en mis ItemsControl. ¿Por qué parece que esto solo afecta a las casillas de verificación dentro de ItemsControl? ¿Tiene algo que ver con ScrollViewer, ItemsPresenter, ...? – jpierson

8

@Meleak explicó el problema muy bien. Lea el artículo http://www.codeproject.com/KB/WPF/EnhancedFocusScope.aspx para comprender completamente cuál es el problema y cómo solucionarlo. Me limitaré a añadir la implementación completa de IsEnhancedFocusScope comportamiento adjunto mencionado en el artículo:

public static class FocusExtensions 
{ 
    private static bool SettingKeyboardFocus { get; set; } 

    public static bool GetIsEnhancedFocusScope(DependencyObject element) { 
     return (bool)element.GetValue(IsEnhancedFocusScopeProperty); 
    } 

    public static void SetIsEnhancedFocusScope(DependencyObject element, bool value) { 
     element.SetValue(IsEnhancedFocusScopeProperty, value); 
    } 

    public static readonly DependencyProperty IsEnhancedFocusScopeProperty = 
     DependencyProperty.RegisterAttached(
      "IsEnhancedFocusScope", 
      typeof(bool), 
      typeof(FocusExtensions), 
      new UIPropertyMetadata(false, OnIsEnhancedFocusScopeChanged)); 

    private static void OnIsEnhancedFocusScopeChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs e) { 
     var item = depObj as UIElement; 
     if (item == null) 
      return; 

     if ((bool)e.NewValue) { 
      FocusManager.SetIsFocusScope(item, true); 
      item.GotKeyboardFocus += OnGotKeyboardFocus; 
     } 
     else { 
      FocusManager.SetIsFocusScope(item, false); 
      item.GotKeyboardFocus -= OnGotKeyboardFocus; 
     } 
    } 

    private static void OnGotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs e) { 
     if (SettingKeyboardFocus) { 
      return; 
     } 

     var focusedElement = e.NewFocus as Visual; 

     for (var d = focusedElement; d != null; d = VisualTreeHelper.GetParent(d) as Visual) { 
      if (FocusManager.GetIsFocusScope(d)) { 
       SettingKeyboardFocus = true; 

       try { 
        d.SetValue(FocusManager.FocusedElementProperty, focusedElement); 
       } 
       finally { 
        SettingKeyboardFocus = false; 
       } 

       if (!(bool)d.GetValue(IsEnhancedFocusScopeProperty)) { 
        break; 
       } 
      } 
     } 
    } 
} 

En su XAML sólo tiene que establecer esta propiedad adjunto en lugar de la norma IsFocusScope propiedad:

<ItemsControl my:FocusExtensions.IsEnhancedFocusScope="True" 
       ItemsSource="{Binding ElementName=TheWindow, Path=ListOStrings}"> 
    <ItemsControl.ItemTemplate> 
     <DataTemplate> 
      <CheckBox Content="{Binding}" /> 
     </DataTemplate> 
    </ItemsControl.ItemTemplate> 
</ItemsControl> 

Funcionará como espera que el alcance del enfoque funcione.