标签没有正确聚焦列表框

Label doesn't properly focus a ListBox

为了在 WPF window 中使用快捷键,您必须使用 Label 控件。指定要在其 Target 属性 中聚焦的控件,并在热键前加上 _。这适用于 TextBox 控件,但不适用于 ListBox。焦点不是 "focused" 项,而是列表框控件本身。然后按箭头键会发生不可预测的事情。这样完全不能用。显然这个问题现在 for ten years 存在。

下面是一些演示代码:

<Label Content="_List:" Target="{Binding ElementName=myList}"/>
<ListBox Name="myList" Items="..."/>
<Button Content="OK"/>

您需要先将一些项目放入列表,然后 select 第二个。单击按钮仅将其聚焦。然后按 Alt+L 聚焦列表。

我应该开始搞乱 ListBoxGotFocus 事件来修复焦点吗?或者对此有适当的解决方案吗?我是否错过了一些异国情调 属性 来设置它?

我找到了解决这个问题的方法。它不涉及任何 per-control 定制。根据默认样式,所有必要的事件都会自动附加到所有 ListBox 控件。

我正在处理所有 ListBox 控件的 GotKeyboardFocus 事件,以检查焦点是否真的属于 ListBox 本身之外的其他人。如果一个项目被 selected,它将被聚焦。如果没有并且列表中至少有一个项目,则第一个项目将获得焦点。这也将隐式 select 它(这是错误的)所以这也是固定的。

这不是本机 Windows 行为的精确复制,因为当焦点位于另一个控件上时它不会保留焦点项。 Windows 表单的 ListBox 区分一个或多个 selected 项目和一个重点项目。每个都被正确跟踪和记住。 WPF 的 ListBox 似乎并不关心项目焦点(很明显,或者这整个可悲的解决方案是不必要的)所以必须做出假设。

这在我的应用程序中运行良好,从用户的角度来看不会让我烦恼。在两个方向上通过控件切换都没有问题。

附件属性实现在ListBoxExtensionsclass(ListBoxExtensions.cs):

using System;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;

namespace Unclassified.UI
{
    /// <summary>
    /// Provides extension methods for WPF ListBox controls.
    /// </summary>
    // Type name used in XAML
    [Obfuscation(Exclude = true, ApplyToMembers = false, Feature = "renaming")]
    public static class ListBoxExtensions
    {
        #region ListBox fix focus attached property

        /// <summary>
        /// Identifies the FixFocus XAML attached property.
        /// </summary>
        public static readonly DependencyProperty FixFocusProperty = DependencyProperty.RegisterAttached(
            name: "FixFocus",
            propertyType: typeof(bool),
            ownerType: typeof(ListBoxExtensions),
            defaultMetadata: new PropertyMetadata(OnFixFocusChanged));

        /// <summary>
        /// Gets the value of the FixFocus XAML attached property from the specified DependencyObject.
        /// </summary>
        /// <param name="obj">The object from which to read the property value.</param>
        /// <returns></returns>
        public static bool GetFixFocus(DependencyObject obj)
        {
            return (bool)obj.GetValue(FixFocusProperty);
        }

        /// <summary>
        /// Sets the value of the FixFocus XAML attached property on the specified DependencyObject.
        /// </summary>
        /// <param name="obj">The target object on which to set the FixFocus XAML attached property.</param>
        /// <param name="value">The property value to set.</param>
        public static void SetFixFocus(DependencyObject obj, bool value)
        {
            obj.SetValue(FixFocusProperty, value);
        }

        private static void OnFixFocusChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
        {
            if (obj is ListBox && args != null)
            {
                if ((bool)args.NewValue)
                {
                    ((ListBox)obj).GotKeyboardFocus += ListBox_GotKeyboardFocus;
                }
                else
                {
                    ((ListBox)obj).GotKeyboardFocus -= ListBox_GotKeyboardFocus;
                }
            }
        }

        private static void ListBox_GotKeyboardFocus(object sender, KeyboardFocusChangedEventArgs args)
        {
            var listBox = sender as ListBox;
            if (listBox?.IsKeyboardFocused == true)
            {
                // ListBox has KeyboardFocus, it really should be on an item instead to fix keyboard navigation
                if (listBox.SelectedItem != null)
                {
                    // Focus the selected item
                    (listBox.ItemContainerGenerator.ContainerFromItem(listBox.SelectedItem) as UIElement)?.Focus();
                }
                else if (listBox.Items.Count > 0)
                {
                    // Focus the first item. This implicitly selects it. Clear selection afterwards.
                    (listBox.ItemContainerGenerator.ContainerFromIndex(0) as UIElement)?.Focus();
                    listBox.SelectedItem = null;
                }
            }
        }

        #endregion ListBox fix focus attached property
    }
}

将此添加到引用自 App.xamlResourceDictionary

<ResourceDictionary
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:ui="clr-namespace:Unclassified.UI"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style TargetType="ListBox" BasedOn="{StaticResource {x:Type ListBox}}">
        <Setter Property="ui:ListBoxExtensions.FixFocus" Value="True"/>
    </Style>
</ResourceDictionary>