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

时间:2012-05-04 07:25:24

标签: wpf listbox selecteditem

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

问题是,如果ListBox在以编程方式设置ListBox时已经具有键盘焦点,而它正确地更新了SelectedItem上的IsSelected属性,那么将键盘焦点设置到它,因此,箭头键相对于列表中先前关注的项目移动,而不是像人们期望的那样移动新选择的项目。

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

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

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

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

XAML文件:

ListBox

代码隐藏:

<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的{​​{1}}与关联视图的SelectedItem属性同步。它与焦点无关,因为这个问题仍然存在。

我们的解决方法是暂时将焦点设置在其他位置,然后设置所选项目,然后将焦点设置回ListBox,但这会产生我们不得不做出{{1}的不良影响知道Current本身,然后根据它是否具有焦点等执行逻辑(即你不想简单地说'聚焦在别处然后回到这里,如果'这里'没有'当你从其他地方窃取它时,你已经有了焦点。)另外,你不能简单地通过声明性绑定来处理它。不用说这很难看。

然后,'丑'船,所以就是那样。

5 个答案:

答案 0 :(得分: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();
}

答案 1 :(得分: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>

答案 2 :(得分: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;

对我而言,这种方法始终有效。

答案 3 :(得分: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 =(你的ListBoxItem);

如果要在ListBox中选择一些项目,则必须使用以下方式:
ListBox.SelectedItems.Add(Your ListBoxItem);

答案 4 :(得分:0)

您只需使用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);


        }

    }