如何以编程方式将焦点设置到已获得焦点的 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
How do you programmatically set focus to the SelectedItem in a WPF ListBox that already has focus?
提问by Mark A. Donohoe
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.
我们希望以编程方式设置SelectedItem
aListBox
并希望该项目具有焦点,以便箭头键相对于该选定项目起作用。看起来很简单。
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'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 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.
注意:正如我所说,这仅在您以编程方式将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 Focusable
set to false
on the button as not to rob the listbox of focus when pressing it. If I didn't have this, the ListBox
would 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 SelectedItem
of the ListBox
with the Current
property of the associated view. It isn't related to focus as this problem still exists.
注意:有些人建议使用IsSynchronizedWithCurrentItem
,但该属性将SelectedItem
的ListBox
与Current
关联视图的属性同步。它与焦点无关,因为这个问题仍然存在。
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.
我们的解决方法是暂时将焦点设置在其他地方,然后设置所选项目,然后将焦点设置回 ,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 SelectedItem
Property:
和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);
}
}