Странное поведение фокуса для простого WPF ItemsControl - PullRequest
5 голосов
/ 10 февраля 2011

Я вижу странное поведение, когда дело доходит до фокусировки и навигации с помощью клавиатуры.В приведенном ниже примере у меня есть простой ItemsControl, который был спроектирован так, чтобы он представлял список CheckBoxes, связанных с ItemsSource.

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

По какой-то странной причине FocusManager.IsFocusScope = "True"При назначении фокус клавиатуры не устанавливается при установке флажка с помощью щелчка мыши, а фокус выпрыгивает из ItemsControl, если флажок установлен с помощью пробела на клавиатуре.Кажется, что оба симптома указывают на некоторую странную навигацию, происходящую, когда флажок установлен, но мне трудно разобраться в этом.

Эта проблема возникает, если я устанавливаю любой родительский элемент в визуальном дереве каксфокусированность с использованием этого метода.Если я удалю FocusManager.IsFocusScope = "True" , то проблемы исчезнут.К сожалению, я вижу эту проблему в большом проекте, где я не могу просто удалить эти области фокусировки, не беспокоясь о других последствиях, связанных с фокусом.

Может ли кто-нибудь объяснить мне странное поведение, которое я вижу?Это ошибка или я просто что-то упустил?

Ответы [ 2 ]

16 голосов
/ 10 февраля 2011

Эта статья объясняет это очень хорошо: http://www.codeproject.com/KB/WPF/EnhancedFocusScope.aspx

Для чего был разработан FocusScope?

Microsoft использует FocusScope в WPF для создать временный вторичный фокус. Каждая панель инструментов и меню в WPF имеет свои сфера собственного фокуса.

С этим знанием мы можем ясно понять, почему у нас есть такие проблемы:

Кнопка на панели инструментов не должна выполняться Команды на себя, но на что угодно был сосредоточен, прежде чем панель инструментов была щелкнул. Чтобы сделать это, маршрутизируется команды игнорируют фокус от фокуса Области и использовать «основной» логический сосредоточиться вместо этого. Это объясняет почему перенаправленные команды не работают внутри рамки фокусировки.

Почему большое текстовое поле в скриншот тестового приложения показать карету? Я не знаю Ответ на это - но почему бы и нет? Конечно, текстовое поле не имеет фокус клавиатуры (небольшое текстовое поле в это сфокусировано на WPF); но это по-прежнему имеет основной логический фокус в активное окно и является получателем всех перенаправленных команд.

И эта часть описывает поведение, которое вы видите

Почему фокус клавиатуры перемещается в большое текстовое поле при переходе к CheckBox в фокусе WPF и нажмите пробел, чтобы включить его?

Ну, это именно то, что вы ожидаете когда вы нажимаете пункт меню или панель инструментов: фокус клавиатуры должен вернитесь к основному фокусу. Все Элементы управления на основе ButtonBase будут делать это.

8 голосов
/ 10 февраля 2011

@ Милик очень хорошо объяснил проблему.Пожалуйста, прочитайте статью http://www.codeproject.com/KB/WPF/EnhancedFocusScope.aspx, чтобы полностью понять, в чем проблема и как ее решить.Я просто добавлю полную реализацию IsEnhancedFocusScope присоединенного поведения, упомянутого в статье:

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

В вашем XAML вам просто нужно установить это присоединенное свойство вместо стандартного IsFocusScope свойства:

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

Это будет работать так, как вы ожидаете, что сработает фокусировка.

...