2011-02-10 12 views
5

Sto vedendo un comportamento strano quando si tratta di messa a fuoco e navigazione da tastiera. Nell'esempio seguente ho un oggetto ItemsControl semplice che è stato creato in modo che mostri un elenco di CheckBox associati a ItemsSource.Strani comportamenti di messa a fuoco per semplici oggetti WPFControllo

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

Per qualche strana ragione la FocusManager.IsFocusScope = "True" assegnazione provoca fuoco della tastiera per non essere impostato quando il controllo di una casella di controllo tramite un clic del mouse e per la messa a fuoco per saltare fuori della ItemsControl quando un controllo casella viene controllata utilizzando la barra spaziatrice sulla tastiera. Entrambi i sintomi sembrano indicare qualche strano evento di navigazione quando la casella di controllo è selezionata, ma mi viene difficile trovare il fondo.

Questo problema si verifica se imposto alcun elemento padre sull'albero visivo come ambito di attivazione utilizzando questo metodo. Se rimuovo lo FocusManager.IsFocusScope = "True", i problemi scompaiono. Sfortunatamente sto vedendo questo problema in un progetto più ampio in cui non posso semplicemente rimuovere questi ambiti di messa a fuoco senza preoccuparmi di altre conseguenze legate alla messa a fuoco.

Qualcuno potrebbe spiegarmi lo strano comportamento che sto vedendo? È un bug o mi manca qualcosa?

risposta

16

Questo articolo spiega molto bene: http://www.codeproject.com/KB/WPF/EnhancedFocusScope.aspx

per quello che era FocusScope progettato?

Microsoft utilizza FocusScope in WPF su crea un attivo secondario temporaneo. Ogni barra degli strumenti e menu in WPF ha il suo ambito di messa a fuoco personale .

Con questa conoscenza, possiamo chiaramente vedere perché abbiamo questi problemi:

Un pulsante della barra degli strumenti non devono eseguire comandi su se stessa, ma su qualunque avuto concentrarsi prima che la barra degli strumenti è stata cliccato. Per eseguire ciò, i comandi instradati ignorano gli ambiti di messa a fuoco da e utilizzano invece lo stato attivo 'principale' logico . Questo spiega perché i comandi instradati non funzionano all'interno degli ambiti di messa a fuoco .

Perché la casella di testo grande nello screenshot dell'applicazione di prova è ancora visualizza un segno di omissione? Non conosco la risposta a questo - ma perché non dovrebbe? Concesso, la casella di testo non ha lo stato attivo della tastiera (la casella di testo piccola in ha lo scopo di messa a fuoco WPF); ma lo ha ancora il focus logico principale nella finestra attiva di tutti i comandi di routing.

E questa parte riguarda il comportamento che stai vedendo

Perché la mossa fuoco della tastiera per la casella di testo di grandi dimensioni quando si scheda per il CheckBox in WPF concentrarsi portata e premi Space per attivarlo?

Beh, questo è esattamente ciò che ci si aspetta quando si fa clic su una voce di menu o una barra degli strumenti : il fuoco della tastiera dovrebbe ritorno al focus principale. Tutti i controlli derivati ​​da ButtonBase eseguiranno .

+0

+1 per una spiegazione del problema. Ho appena aggiunto l'implementazione del comportamento allegato IsEnhancedFocusScope. –

+4

L'unica cosa che ancora non capisco data questa spiegazione è che se prendo un altro esempio in cui ho impostato una griglia per essere l'ambito di messa a fuoco con un gruppo di bambini che sono pulsanti e caselle di controllo, quando faccio clic su uno di questi caselle di controllo Non vedo la strana perdita di comportamento di messa a fuoco che vedo nel mio ItemsControl. Perché questo sembra influenzare solo le checkbox all'interno di ItemsControl, è qualcosa che ha a che fare con ScrollViewer, ItemsPresenter, ...? – jpierson

8

@Meleak ha spiegato il problema molto bene. Si prega di leggere l'articolo http://www.codeproject.com/KB/WPF/EnhancedFocusScope.aspx per comprendere appieno qual è il problema e come risolverlo. Vorrei solo aggiungere l'attuazione completa del IsEnhancedFocusScope comportamento allegato citato nell'articolo:

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

Nel vostro XAML è sufficiente impostare questa proprietà associata al posto dello standard IsFocusScope proprietà:

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

Funzionerà come ci si aspetta che l'ambito di messa a fuoco funzioni.