38

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

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

这对用户来说非常混乱,因为它使选择在使用键盘时看起来会跳来跳去,因为它会快速回到编程选择发生之前的位置。

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

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

重要提示:请注意,我已在按钮上Focusable设置为。false如果我没有,当您单击它时,它会获得焦点并且ListBox会失去焦点,从而掩盖了问题,因为当 aListBox重新获得焦点时,它会正确聚焦选定的ListBoxItem. 问题是当 aListBox 已经具有焦点并且您以编程方式选择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,但该属性将SelectedItemListBoxCurrent关联视图的属性同步。它与焦点无关,因为这个问题仍然存在。

ListBox我们的解决方法是暂时将焦点设置在其他地方,然后设置所选项目,然后将焦点设置ViewModelListBox不是它有焦点,等等(即你不想简单地说'焦点在别处然后回到这里,如果'这里'没有焦点,因为你会从其他地方偷走它。)另外,您不能简单地通过声明性绑定来处理这个问题。不用说这是丑陋的。

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

4

5 回答 5

57

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

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();
}
于 2012-05-05T15:14:32.433 回答
2

也许有附加行为?就像是

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

并在 xaml

       <Style TargetType="ListBoxItem">
            <Setter Property="local:FocusWhenSelectedBehavior.FocusWhenSelected" Value="True"/>
        </Style>
于 2012-05-04T10:03:21.070 回答
1

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

示例代码:

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


        }

    }
于 2016-10-29T04:44:48.063 回答
0

在您的 XAML 中,您尝试过这个但没有工作?

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

SelectedItem财产:

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

            _SelectedItem = value;

            OnPropertyChanged("SelectedItem");
        }
    }

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

SelectedItem = theItemYouWant;

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

于 2012-05-04T10:33:13.630 回答
0

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

这是我的代码:

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

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

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

于 2013-08-28T09:16:16.100 回答