web-dev-qa-db-ja.com

既にフォーカスがあるWPF ListBoxのSelectedItemにプログラムでフォーカスを設定するにはどうすればよいですか?

SelectedItemListBoxをプログラムで設定し、そのアイテムにフォーカスを持たせて、矢印キーがその選択したアイテムに対して機能するようにします。簡単そうに思えます。

ただし、問題は、ListBoxをプログラムで設定するときにSelectedItemがすでにキーボードフォーカスを持っている場合、IsSelectedListBoxItemプロパティを適切に更新する場合です。 does n'tキーボードフォーカスをそれに設定します。したがって、矢印キーは、リスト内の以前にフォーカスされたアイテムに対して移動します。

キーボードを使用すると、プログラムによる選択が行われる前の位置に戻るため、キーボードを使用すると選択範囲が飛び回るように見えるため、これはユーザーを混乱させます。

注:先ほど言ったように、これは既にキーボードフォーカスを持っているSelectedItemListBoxプロパティをプログラムで設定した場合にのみ発生します。キーボードフォーカスがListBoxに戻った場合(または、行ったままになった場合は、すぐに戻ってきます)、正しいアイテムのキーボードフォーカスは期待どおりになります。

この問題を示すサンプルコードを次に示します。これをデモするには、コードを実行し、マウスを使用してリストで「7」を選択し(したがって、ListBoxにフォーカスを置きます)、「テスト」ボタンをクリックします。最後に、キーボードの「Alt」キーをタップして、フォーカス四角形を表示します。実際にはまだ「7」にあり、上矢印と下矢印を使用する場合、ユーザーが期待する「4」ではなく、その行に相対的です。

ボタンを押すときにフォーカスのリストボックスを奪わないように、ボタンのFocusablefalseに設定していることに注意してください。これがない場合、ボタンをクリックするとListBoxがフォーカスを失います。したがって、フォーカスがListBoxに戻ると、正しいアイテムになります。

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の使用を提案していますが、そのプロパティはSelectedItemListBoxを関連ビューのCurrentプロパティと同期します。この問題はまだ存在するため、フォーカスとは関係ありません。

回避策は、一時的に別の場所にフォーカスを設定し、選択したアイテムを設定し、ListBoxにフォーカスを戻すことですが、これはViewModelを作成しなければならないという望ましくない効果があります。 ListBox自体を認識してから、フォーカスがあるかどうかなどに応じてロジックを実行します(つまり、「他の場所にフォーカスしてから、ここに戻った場合はここに戻ってきます」とは言いたくないでしょう) tを他の場所から盗むので、すでにフォーカスがあります。)さらに、宣言型バインディングを介してこれを単純に処理することはできません。言うまでもなく、これはいです。

それからまた、「ugい」船、それがあります。

33
MarqueIV

数行のコードです。コードビハインドで使用したくない場合は、添付の動作でパッケージ化できると確信しています。

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

おそらく動作が添付されていますか?何かのようなもの

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
Dtex

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;

私にとって、このアプローチは常に機能します。

0
Dummy01

最初) ListBox.Items.IndexOf()でリストボックス内の選択されたアイテムを見つける必要があります。
Second) 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.SelectedItem =(Your ListBoxItem);

ListBoxでいくつかの項目を選択する場合は、この方法を使用する必要があります:
ListBox.SelectedItems.Add(Your ListBoxItem);

0
Amintabar

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


        }

    }
0
Richard Aguirre