标签没有正确地聚焦ListBox

时间:2016-01-17 14:43:03

标签: wpf listbox focus

要在WPF窗口中使用加速键,必须使用Label控件。指定控件以将焦点放在其Target属性中,并在热键前加_前缀。这适用于TextBox控件,但不适用于ListBox。而不是专注于"聚焦" item,列表框控件本身是重点。按箭头键然后做不可预测的事情。像这样完全无法使用。显然这个问题现在存在for ten years

以下是一些演示代码:

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

您需要先在列表中放入一些项目,然后选择第二项。单击按钮仅对焦。然后按 Alt + L 以聚焦列表。

我是否应该开始讨论ListBox的{​​{1}}事件以确定焦点?或者有适当的解决方案吗?我是否想念一些充满异国情调的房产才能使它成为现实?

1 个答案:

答案 0 :(得分:0)

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

我正在处理所有GotKeyboardFocus控件的ListBox事件,以检查焦点是否真正属于除ListBox之外的其他人。如果选择了某个项目,它将被聚焦。如果没有,并且列表中至少有一个项目,则第一个项目将被聚焦。这也会隐含地选择它(这是错误的),所以这也是固定的。

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

这在我的应用程序中运行良好,不会从用户的角度来惹恼我。在两个方向上切换控件都可以顺利运行。

附加属性在ListBoxExtensions类(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
    }
}

将此添加到ResourceDictionary引用的App.xaml

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