如何以已编程方式将焦点设置为已具有焦点的WPF ListBox中的SelectedItem?

Mar*_*eIV 34 wpf listbox selecteditem

我们要设定SelectedItemListBox程序,并希望该项目则有焦点,使方向键的作用相对于选定的项目.看起来很简单.

然而问题是,如果在以编程方式ListBox设置时已经具有键盘焦点SelectedItem,而它正确地更新了该IsSelected属性ListBoxItem,则它不会将键盘焦点设置到它,因此,箭头键相对于之前关注的项目移动列表而不是人们所期望的新选择的项目.

这对用户来说非常混乱,因为它使选择似乎在使用键盘时跳转,因为它快速回到编程选择发生之前的位置.

注意:正如我所说,只有SelectedItem在以编程方式在ListBox已经具有键盘焦点的属性上设置属性时才会发生这种情况.如果它没有(或者如果它没有,你会离开,然后再回来),当键盘焦点返回时ListBox,正确的项目现在将按预期进行键盘焦点.

这是一些显示此问题的示例代码.要进行演示,请运行代码,使用鼠标在列表中选择"Seven"(从而将焦点放在其上ListBox),然后单击"Test"按钮.最后,点击键盘上的"Alt"键以显示焦点rect.您将看到它仍然实际上在'Seven'上,如果您使用向上和向下箭头,它们是相对于该行,而不是用户期望的'四'.

请注意,我已将按钮Focusable设置为false在按下按钮时不抢夺焦点列表框.如果我没有这个,ListBox当你点击按钮时会失去焦点,因此,当焦点返回到ListBox时,它将在正确的项目上.

XAML文件:

<Window x:Class="Test.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="525" Height="350" WindowStartupLocation="CenterScreen"
    Title="MainWindow" x:Name="Root">

    <DockPanel>

        <Button Content="Test"
            DockPanel.Dock="Bottom"
            HorizontalAlignment="Left"
            Focusable="False"
            Click="Button_Click" />

        <ListBox x:Name="MainListBox" />

    </DockPanel>

</Window>
Run Code Online (Sandbox Code Playgroud)

代码隐藏:

using System.Collections.ObjectModel;
using System.Windows;

namespace Test
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            MainListBox.ItemsSource = new string[]{
                "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight"
            };

        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            MainListBox.SelectedItem = MainListBox.Items[3];
        }

    }

}
Run Code Online (Sandbox Code Playgroud)

注意:有人建议使用IsSynchronizedWithCurrentItem,但该属性将SelectedItemListBoxCurrent相关视图的属性同步.它与焦点无关,因为这个问题仍然存在.

我们的解决方法是暂时将焦点设置在其他位置,然后设置所选项目,然后将焦点设置回到ListBox但是这具有我们必须让我们ViewModel意识到ListBox自身的不可取的效果,然后根据是否或者执行逻辑不是它有焦点等等(也就是说你不想简单地说'专注于其他地方,然后回到这里,如果'这里'没有焦点,因为你从其他地方偷了它.)另外,你不能简单地通过声明性绑定来处理这个问题.不用说这很难看.

然后,'丑'船,所以就是这样.

jef*_*eff 50

这是几行代码.如果您不想在代码隐藏中使用它,我相信它可以打包在附加行为中.

private void Button_Click(object sender, RoutedEventArgs e)
{
    MainListBox.SelectedItem = MainListBox.Items[3];
    MainListBox.UpdateLayout(); // Pre-generates item containers 

    var listBoxItem = (ListBoxItem) MainListBox
        .ItemContainerGenerator
        .ContainerFromItem(MainListBox.SelectedItem);

    listBoxItem.Focus();
}
Run Code Online (Sandbox Code Playgroud)

  • 我在调用`ContainerFromItem()`之前编辑了`UpdateLayout()`,它应该预先生成项容器并避免意外的null结果. (4认同)
  • 如果尚未为其生成容器,ContainerFromItem将返回Null,这是虚拟化列表的情况,并且该项目在屏幕外.另外,如果您尝试从绑定中设置值,则会因为您无权访问ListBox而崩溃,您也不应该这样做.(下面继续......) (2认同)