2012-05-04 17 views
31

ListBoxSelectedItemをプログラムで設定し、その項目にフォーカスを当てて、矢印キーがその選択された項目に対して相対的に機能するようにします。シンプルだと思われる。フォーカスが既に設定されているWPF ListBoxのSelectedItemにプログラムでフォーカスを設定するにはどうすればよいですか?

プログラムでSelectedItemを設定するときListBoxはすでにそれが正しくListBoxItemIsSelectedプロパティを更新しない一方で、それがそれにないセットのキーボードフォーカスを行い、これにより、矢印キー、キーボードフォーカスを持っている場合、問題はしかし、ですリスト内の以前にフォーカスされたアイテムに相対的に移動し、新しく選択されたアイテムは期待通りに移動しません。

これは、キーボードを使用しているときに選択がプログラム選択が行われる前の位置にスナップバックするときにジャンプするように見えるため、ユーザーにとって非常に混乱します。

注:前述のように、キーボードフォーカスが既に設定されているListBoxに、SelectedItemプロパティをプログラムで設定した場合のみ、これが発生します。表示されない場合(または終了していても戻ってきた場合)、キーボードのフォーカスがListBoxに戻ると、正しい項目にキーボードのフォーカスが正しく設定されるようになります。

ここに、この問題を示すサンプルコードがあります。これをデモするには、コードを実行し、マウスを使用してリスト内の「セブン」を選択し(ListBoxにフォーカスを当てる)、「テスト」ボタンをクリックします。最後に、キーボードの「Alt」キーをタップして、フォーカス矩形を表示します。あなたはそれがまだ実際には 'セブン'であることがわかります。上向き矢印と下向き矢印を使用すると、それらはその行に相対的であり、ユーザーが期待するように '4'ではありません。

Focusableは、フォーカスのリストボックスを押したときに、リストボックスを奪わないように、ボタン上にfalseに設定されています。私がこれを持っていなかった場合、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を使用することを示唆しているが、そのプロパティは、関連のCurrentプロパティでListBoxSelectedItemを同期させますビュー。この問題は依然として存在するため、フォーカスには関係しません。

私たちの仕事の周りには、一時的にどこかにフォーカスを設定することで、その後、バックListBoxにフォーカスを設定するが、これは私たちがListBox自身の私達のViewModelに認識させることの望ましくない効果があり、その後、選択した項目を設定し、フォーカスを持っているかどうかに応じてロジックを実行します(つまり、「ここでは他の場所にフォーカスしてここに戻ってください」と言いたいとは思わないでしょう。また、宣言的なバインディングによってこれを処理することはできません。言うまでもなく、これは醜いです。

もう一度、「醜い」船は、そうです。

答えて

47

これは数行のコードです。あなたがコードビハインドでそれを望んでいない場合、私はそれが添付された動作でパッケージ化することができると確信しています。まず

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

ContainerFromItemは、コンテナがまだ生成されていない場合はnullを返します。コンテナがまだ生成されていない場合は、仮想化されたリストの場合で、項目はオフスクリーンです。さらに、バインドから値を設定しようとすると、ListBoxへのアクセス権がないため、バインドされません。 (続き...) – MarqueIV

+0

a)コントロールにフォーカスがあって(テストが簡単)、b)変更が来ていない限り、コントロールが盲目的にキーボードフォーカスをListBoxItemに設定しないように、そうでなければ、マルチセレクトモードではフォーカスを別のコントロール(ケースa)から盗んだり、現在のフォーカスをフォーカスしたりすることができます(ケースb)。その行は通常はキーボードのフォーカスを保持する必要がありますが、新しいSelectedItemが設定されていれば、それは奇妙な動作を引き起こします。 – MarqueIV

+0

実際、2番目の考えでは、私は上記のケース「B」の回避策があると思います。あなたは言ったように添付された動作を作成しますが、XAMLで直接設定しないでください。代わりに、ViewModelにプロパティを作成し、その動作をバインドします。そうすれば、聞く人を知る必要はありません。次に、選択した項目をコードの背後から設定する直前に、動作を有効にして項目を選択し、再度動作を無効にします。これは上記の 'B'に対応します。 (もちろん「A」が必要です。)完全ではありませんが、私はこの道を私に導いてくれました。 – MarqueIV

2

添付の動作がありますか?

あなたのXAMLで
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> 
+1

これは私が最初に向かっていたところだったが、問題はドンそれらの項目ですあなたが仮想化されたリストを持っていれば、まだ存在していないので、接続された動作はまだ応答していません。 ScrollIntoViewメソッドをどうにかして最初に行う必要があります。第2に、IsSelectedに厳密に対応すると、マルチセレクトモードでは少し狂ってしまうことがありますが、それは再び望ましい動作と考えられます。仮想化(デフォルトでオン)は本当のキラーです。 – MarqueIV

0

ような何かあなたはこれを試してみました動作しませんでしたか?

<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

私はそれについてあなたに電話します。あなたはそれを試しましたか?あなたのリストボックスに100個のアイテムをロードします。マウスで50番目のアイテムをクリックします。その後、フォーム上にボタンを追加して、プログラムで「SelectedItem」を10番目の項目に設定します。選択は変更されますが、a)選択された項目は、b)フォーカスされていないため、ビューにスクロールしません。 'Alt'キーをタップすると、クリックしたアイテムにキーボードのフォーカスがあることがわかります。 – MarqueIV

+0

ところで...確かに、マウスまたはキーボードを使用して最初に他の行を物理的に選択する必要があります。 – MarqueIV

+0

@MarquelVおそらく私はあなたが望むものを混乱させるでしょう。私はあなたが選択した行をプログラマティックに設定したいと思った。選択された行はフォーカスされた行です。そこに移動するには、ScrollIntoViewを使用する必要があります。そして、はい、私のアプローチは複数の選択のためには機能しません。 – Dummy01

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.SelectedItem =を(あなたのListBoxItem)。

あなたがリストボックスにいくつかの項目を選択したい場合は、この方法を使用する必要があります。
ListBox.SelectedItems.Add(あなたのListBoxItem)。

+0

私は100%確信していませんが、私はあなたが私の質問を誤解しているかもしれないと思います。選択は問題ではありません。焦点はある。あなたのコードは項目を選択しますが、フォーカスを設定しません。これが解決しようとしています。 – MarqueIV

+0

申し訳ありませんだから、もしあなたがあなたのコントロールに焦点を当てれば、あなたのアイテムは焦点を合わせなければならないと思います。 (私の悪い英語には申し訳ありません) –

+0

しかし、コントロールにすでにフォーカスがある場合は、コントロールを再設定することはできません。あなたは何か他のものに集中しなければなりません。 – MarqueIV

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


     } 

    } 
関連する問題