如何以编程方式将焦点设置到已获得焦点的 WPF 列表框中的 SelectedItem?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10444518/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 23:24:31  来源:igfitidea点击:

How do you programmatically set focus to the SelectedItem in a WPF ListBox that already has focus?

wpflistboxselecteditem

提问by Mark A. Donohoe

We want to set the SelectedItemof a ListBoxprogrammatically and want that item to then have focus so the arrow keys work relative to that selected item. Seems simple enough.

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

The problem however is if the ListBoxalready has keyboard focus when setting SelectedItemprogrammatically, while it does properly update the IsSelectedproperty on the ListBoxItem, it doesn'tset 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.

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

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.

这对用户来说非常令人困惑,因为它使选择在使用键盘时看起来像跳动,因为它会回到编程选择发生之前的位置。

Note: As I said, this only happens if you programmatically set the SelectedItemproperty on a ListBoxthat 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.

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

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. Finally, tap the 'Alt' key on your keyboard to reveal the focus rect. You will see it's still actually on 'Seven' and if you use the up and down arrows, they are relative to that row, not 'Four' as a user would expect.

这是显示此问题的一些示例代码。要演示此操作,请运行代码,使用鼠标在列表中选择“七”(因此将焦点放在 上ListBox),然后单击“测试”按钮。最后,点击键盘上的“Alt”键以显示焦点矩形。你会看到它实际上仍然在“七”上,如果你使用向上和向下箭头,它们是相对于该行的,而不是用户期望的“四”。

Note that I have Focusableset to falseon the button as not to rob the listbox of focus when pressing it. If I didn't have this, the ListBoxwould lose focus when you click the button, and thus, when focus returned to the ListBox, it would be on the correct item.

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

XAML file:

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>

Code-behind:

代码隐藏:

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

    }

}

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

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

Our work-around is to temporarily set the focus somewhere else, then set the selected item, then set the focus back to the ListBoxbut this has the undesireable effect of us having to make our ViewModelaware of the ListBoxitself, 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.

我们的解决方法是暂时将焦点设置在其他地方,然后设置所选项目,然后将焦点设置回 ,ListBox但这会产生不良影响,我们必须让我们ViewModel意识到ListBox它本身,然后根据是否或不是它有焦点,等等(即你不会想简单地说“焦点在别处然后回到这里,如果“这里”已经没有焦点,因为你会从其他地方窃取它。)另外,你不能简单地通过声明性绑定来处理这个问题。不用说这是丑陋的。

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

再说一次,“丑陋”的船,就是这样。

回答by jeff

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

回答by Dtex

Maybe with an attached behavior? Something like

也许有附加的行为?就像是

public static DependencyProperty FocusWhenSelectedProperty = DependencyProperty.RegisterAttached(
            "FocusWhenSelected", 
            typeof(bool), 
            typeof(FocusWhenSelectedBehavior), 
            new PropertyMetadata(false, new PropertyChangedCallback(OnFocusWhenSelectedChanged)));

private static void OnFocusWhenSelectedChanged(DependencyObject obj, DependencyPropertyChangedEventArgs args)
    {
        var i = (ListBoxItem)obj;
        if ((bool)args.NewValue)
           i.Selected += i_Selected;
        else
           i.Selected -= i_Selected;
    }

static void i_Selected(object sender, RoutedEventArgs e)
{
    ((ListBoxItem)sender).Focus();
}

and in xaml

并在 xaml 中

       <Style TargetType="ListBoxItem">
            <Setter Property="local:FocusWhenSelectedBehavior.FocusWhenSelected" Value="True"/>
        </Style>

回答by Dummy01

In your XAML you tried this and didn't work?

在您的 XAML 中,您尝试过此操作但没有用?

<ListBox IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding Path=YourCollectionView}" SelectedItem="{Binding SelectedItem}"></ListBox>

And the SelectedItemProperty:

SelectedItem财产:

    private YourObject _SelectedItem;
    public YourObject SelectedItem
    {
        get
        {
            return _SelectedItem;
        }
        set
        {
            if (_SelectedItem == value)
                return;

            _SelectedItem = value;

            OnPropertyChanged("SelectedItem");
        }
    }

Now in your code you can do:

现在在您的代码中,您可以执行以下操作:

SelectedItem = theItemYouWant;

To me this approach works always.

对我来说,这种方法总是有效。

回答by Amintabar

First)You must find selected items in listbox with ListBox.Items.IndexOf().
Second)Now add items with ListBox.SelectedItems.Add().

首先)您必须使用 ListBox.Items.IndexOf() 在列表框中找到所选项目。
第二)现在使用 ListBox.SelectedItems.Add() 添加项目。

This is my code :

这是我的代码:

DataRow[] drWidgetItem = dtItemPrice.Select(widgetItemsFilter);
lbxWidgetItem.SelectedItems.Clear(); foreach(DataRow drvItem in
drWidgetItem)
lbxWidgetItem.SelectedItems.Add(lbxWidgetItem.Items[dtItemPrice.Rows.IndexOf(drvItem)]);

If you want to select an item in ListBox you can use this way :
ListBox.SelectedItem = (Your ListBoxItem);

If you want to select some items in ListBox you must use this way :
ListBox.SelectedItems.Add(Your ListBoxItem);

如果你想在 ListBox 中选择一个项目,你可以使用这种方式:
ListBox.SelectedItem = (Your ListBoxItem);

如果你想在 ListBox 中选择一些项目,你必须使用这种方式:
ListBox.SelectedItems.Add(Your ListBoxItem);

回答by Richard Aguirre

You need only use ListBox.SelectedItem and then use ListBox.ScrollIntoView(listBox.SelectedItem)

您只需要使用 ListBox.SelectedItem 然后使用 ListBox.ScrollIntoView(listBox.SelectedItem)

Example code:

示例代码:

        private void textBox2_TextChanged(object sender, TextChangedEventArgs e)
    {

        var comparision = StringComparison.InvariantCultureIgnoreCase;
        string myString = textBox2.Text;
        List<dynamic> index = listBox.Items.SourceCollection.OfType<dynamic>().Where(x=>x.Nombre.StartsWith(myString,comparision)).ToList();
        if (index.Count > 0) { 
        listBox.SelectedItem= index.First();


            listBox.ScrollIntoView(listBox.SelectedItem);


        }

    }