2011-02-10 16 views
5

Widzę dziwne zachowanie, jeśli chodzi o ostrość i nawigację po klawiaturze. W poniższym przykładzie mam proste ItemsControl, który został szablon, tak, że przedstawia listę CheckBoxes związane z ItemsSource.Dziwne zachowanie ostrości dla prostych elementów WPF Sterowanie

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

Z jakiegoś dziwnego powodu FocusManager.IsFocusScope = „True” cesja powoduje skupienie klawiatury, aby nie zostać ustawiony podczas sprawdzania pole wyboru poprzez kliknięcie myszką i skupić się wyskoczyć z ItemsControl gdy czek pole jest sprawdzane za pomocą klawisza spacji na klawiaturze. Oba symptomy zdają się wskazywać na dziwną nawigację, która dzieje się, gdy pole wyboru jest zaznaczone, ale mam trudności z dostaniem się na dół.

Ten problem występuje, jeśli mogę ustawić dowolny element nadrzędny górę drzewa wizualnej jako zakres ostrości przy użyciu tej metody. Jeśli usuniemy FocusManager.IsFocusScope = "True", problemy znikną. Niestety widzę ten problem w większym projekcie, w którym nie mogę po prostu usunąć tych zakresów ostrości, nie martwiąc się o inne konsekwencje związane z ogniskiem.

Czy ktoś mógłby mi wytłumaczyć dziwne zachowanie, które widzę? Czy to błąd, czy po prostu zupełnie czegoś brakuje?

Odpowiedz

16

Ten artykuł wyjaśnia to bardzo dobrze: http://www.codeproject.com/KB/WPF/EnhancedFocusScope.aspx

za to, co zostało FocusScope Zaprojektowany?

Firma Microsoft używa FocusScope w WPF do utworzyć tymczasowy fokus dodatkowy. Każdy pasek narzędzi i menu w WPF ma własny zakres ostrości w postaci .

Dzięki tej wiedzy możemy jasno dlaczego mamy te problemy:

przycisk paska narzędzi nie powinny wykonywać komendy na sobie, ale na cokolwiek miał skupić przed Pasek był kliknięciu. Aby to osiągnąć, polecenia routowane ignorują ogniska z zakresów skupienia i zamiast tego używają "głównego" logicznego ogniska . To wyjaśnia, dlaczego polecenia trasowane nie działają w zakresach ustawiania ostrości w zakresie .

Dlaczego duże pole tekstowe na zrzucie ekranu testu aplikacji nadal jest wyświetlane pod numerem ? Nie znam odpowiedzi - ale dlaczego nie? Zgoda, pole tekstowe nie ma ostrości klawiatury (małe pole tekstowe w ma to pole ostrości WPF); ale nadal ma główne skupienie logiczne w aktywnym oknie i jest odbiornikiem wszystkich poleceń trasowanych.

I ta część obejmuje zachowanie jesteś widząc

Dlaczego klawiatura Fokus do duże pole tekstowe, gdy zakładka do CheckBox w WPF skupić zakres i naciśnij spację, aby ją przełączyć?

Cóż, to jest dokładnie to, czego można oczekiwać po kliknięciu menu lub paska narzędzi : nacisk Klawiatura powinna powrót do głównego centrum uwagi. Wszystkie kontrole pochodne ButtonBase wykonają to za pomocą .

+0

+1 dla wyjaśnienia problemu. Właśnie dodałem implementację dołączonego zachowania IsEnhancedFocusScope. –

+4

Jedyne, czego wciąż nie rozumiem, biorąc pod uwagę to wyjaśnienie, to że jeśli weźmiemy inny przykład, w którym ustawiam siatkę na zakres ostrości z grupą dzieci, które są przyciskami i polami wyboru, kiedy klikam jedną z nich pola wyboru Nie widzę dziwnej utraty zachowania skupienia, które widzę w moich ItemsControl. Dlaczego ma to wpływ tylko na pola wyboru w ItemControl, czy ma to coś wspólnego z ScrollViewer, ItemsPresenter, ...? – jpierson

8

@Meleak wyjaśnił problem bardzo dobrze. Przeczytaj artykuł http://www.codeproject.com/KB/WPF/EnhancedFocusScope.aspx, aby w pełni zrozumieć, czym jest problem i jak go rozwiązać. Dodam jeszcze pełnego wdrożenia IsEnhancedFocusScope załączonym zachowania wymienionego w artykule:

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

W swojej XAML wystarczy ustawić ten załączony właściwość zamiast standardowego IsFocusScope nieruchomości:

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

To będzie działać jak oczekujesz, że zakres ostrości zadziała.