您如何以编程方式将焦点设置到已具有焦点的 WPF ListBox 中的 SelectedItem? [英] How do you programmatically set focus to the SelectedItem in a WPF ListBox that already has focus?

查看:31
本文介绍了您如何以编程方式将焦点设置到已具有焦点的 WPF ListBox 中的 SelectedItem?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们希望以编程方式设置 ListBoxSelectedItem 并希望该项目具有焦点,以便箭头键相对于该选定项目起作用.看起来很简单.

We want to set the SelectedItem of a ListBox programmatically and want that item to then have focus so the arrow keys work relative to that selected item. Seems simple enough.

但问题是,如果 ListBox 在以编程方式设置 SelectedItem 时已经具有键盘焦点,而它确实正确更新了 IsSelected 上的属性ListBoxItem,它为其设置键盘焦点,因此,箭头键相对于列表中先前聚焦的项目而不是新选择的项目移动正如人们所期望的那样.

The problem however is if the ListBox already has keyboard focus when setting SelectedItem programmatically, while it does properly update the IsSelected property on the ListBoxItem, it doesn't set keyboard focus to it, and thus, the arrow keys move relative to the previously-focused item in the list and not the newly-selected item as one would expect.

这会让用户感到非常困惑,因为它使选择在使用键盘时看起来会跳来跳去,因为它会快速回到编程选择发生之前的位置.

This is very confusing to the user as it makes the selection appear to jump around when using the keyboard as it snaps back to where it was before the programmatic selection took place.

注意:正如我所说,这只有在您以编程方式在已经拥有键盘焦点的 ListBox 上设置 SelectedItem 属性时才会发生.如果没有(或者如果有但你离开了,然后马上回来),当键盘焦点返回到 ListBox 时,正确的项目现在将按预期获得键盘焦点.

Note: As I said, this only happens if you programmatically set the SelectedItem property on a ListBox that already has keyboard focus itself. If it doesn't (or if it does but you leave, then come right back), when the keyboard focus returns to the ListBox, the correct item will now have the keyboard focus as expected.

这里有一些示例代码显示了这个问题.为了演示这个,运行代码,使用鼠标选择列表中的Seven"(因此将焦点放在 ListBox 上),然后单击Test"按钮以编程方式选择第四项.最后,点击键盘上的Alt"键以显示焦点矩形.您会看到它仍然在七"上,而不是您所期望的四",因此,如果您使用向上和向下箭头,它们是相对行七",而不是四",进一步混淆用户,因为他们在视觉上看到的内容与实际关注的内容不同步.

Here's some sample code showing this problem. To demo this, run the code, use the mouse to select 'Seven' in the list (thus putting the focus on the ListBox), then click the 'Test' button to programmatically select the fourth item. Finally, tap the 'Alt' key on your keyboard to reveal the focus rect. You will see it's still on 'Seven', not 'Four' as you may expect, and because of that, if you use the up and down arrows, they are relative row 'Seven', not 'Four' as well, further confusing the user since what they are visually seeing and what is actually focused are not in sync.

重要提示:请注意,我将按钮上的 Focusable 设置为 false.如果我没有,当您单击它时,它会获得焦点,而 ListBox 会失去它,从而掩盖问题,因为再次,当 ListBox 重新获得焦点时,它会正确聚焦选定的 ListBoxItem.问题是当 ListBox 已经 具有焦点并且您以编程方式选择 ListBoxItem 时.

Important: Note that I have Focusable set to false on the button. If I didn't, when you clicked it, it would gain focus and the ListBox would lose it, masking the issue because again, when a ListBox regains focus, it properly focuses the selected ListBoxItem. The issue is when a ListBox already has focus and you programmatically select a ListBoxItem.

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>

代码隐藏:

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

    }

}

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

Note: Some have suggested to use IsSynchronizedWithCurrentItem, but that property synchronizes the SelectedItem of the ListBox with the Current property of the associated view. It isn't related to focus as this problem still exists.

我们的解决方法是暂时将焦点设置在其他地方,然后设置选定的项目,然后将焦点设置回 ListBox 但这会产生不希望的效果,我们必须使 ViewModel 知道 ListBox 本身,然后根据它是否具有焦点等执行逻辑(即你不想简单地说'Focus else then come回到这里,如果这里"还没有焦点,因为你会从其他地方窃取它.)另外,你不能简单地通过声明性绑定来处理这个问题.不用说这很丑.

Our work-around is to temporarily set the focus somewhere else, then set the selected item, then set the focus back to the ListBox but this has the undesireable effect of us having to make our ViewModel aware of the ListBox itself, then perform logic depending on whether or not it has the focus, etc. (i.e. you wouldn't want to simply say 'Focus elsewhere then come back here, if 'here' didn't have the focus already as you'd steal it from somewhere else.) Plus, you can't simply handle this through declarative bindings. Needless to say this is ugly.

再说一次,丑陋"的船,就是这样.

Then again, 'ugly' ships, so there's that.

推荐答案

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

It's a couple lines of code. If you didn't want it in code-behind, I sure it could be packaged in a attached behaviour.

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

这篇关于您如何以编程方式将焦点设置到已具有焦点的 WPF ListBox 中的 SelectedItem?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆