2

WPF ListView でフォーカスされた項目をプログラムで設定する方法が見つかりません。選択したアイテムのバリエーションしか見つかりません | アイテム | インデックス | 値ですが、「フォーカスされた」アイテムは「選択された」アイテムに直接関連していません - フォーカスされたアイテムは選択されていない可能性があります (たとえば、Ctrl+クリックで現在のアイテムを選択解除する場合)。

手短に言えば、以下のコードから次の動作を取得したいと思います(ダミーのリストビューをダミーの8項目で埋め、Xを押すと最後から2番目の項目にフォーカスしようとします):

望まれる動作:

  • マウスを使用 - 2 番目の項目を選択
  • X を押す - これは最後から 2 番目のアイテムにフォーカスします
  • キーボードの「Down」配列を押します-これにより、現在の選択が最後の項目に移動します

実際に何が起こるか:

  • マウスを使用 - 2 番目の項目を選択
  • X を押す - これにより、最後から 2 番目のアイテムが選択されますが、フォーカスは最初から 2 番目のアイテムに残ります
  • キーボードの「Down」配列を押します。これにより、現在の選択が最後の項目に移動するはずですが、代わりに 3 番目の項目が選択されます。

注: プレーンな Win32 API (もちろん、これは WPF とはまったく異なるものです) には、このための LVM_SETSELECTIONMARK メッセージがあります。WPF でアナログが見つかりませんでした。それは存在しますか?

サンプル XAML:

<Window x:Class="WpfListviewTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="350" Width="525">
  <ListView x:Name="List1" KeyDown="List1_KeyDown">
    <ListView.View>
      <GridView>
        <GridViewColumn Width="140" Header="Column 1" />
        <GridViewColumn Width="140" Header="Column 2" />
        <GridViewColumn Width="140" Header="Column 3" />
      </GridView>
    </ListView.View>
    <sys:DateTime>1/2/3</sys:DateTime>
    <sys:DateTime>4/5/6</sys:DateTime>
    <sys:DateTime>7/8/9</sys:DateTime>
    <sys:DateTime>10/11/12</sys:DateTime>
    <sys:DateTime>1/2/3</sys:DateTime>
    <sys:DateTime>4/5/6</sys:DateTime>
    <sys:DateTime>7/8/9</sys:DateTime>
    <sys:DateTime>10/11/12</sys:DateTime>
  </ListView>
</Window>

サンプル コード ビハインド:

public partial class MainWindow : Window {
    public MainWindow() {
        InitializeComponent();
    }

    private void List1_KeyDown(object sender, KeyEventArgs e) {
        if( e.Key == Key.X ) {
            List1.SelectionMode = SelectionMode.Single;
            List1.SelectedIndex = List1.Items.Count - 2;
        }
    }
}
4

2 に答える 2

2

Mic's answer のリンクのおかげで、より有用な情報が得られ、私の場合の実行可能な解決策が見つかりました。

背景情報:

  • ListView コントロールの他の実装 (WinForms または Win32) とは異なり、WPF のバージョンの ListView には FocusedItem のようなものはありません。MS は、すべての視覚的な ListViewItem の子孫である UIElement の汎用インターフェイスを使用して、リストビューの項目に焦点を当てることにしたようです。これにより、私のユースケースは WPF でより複雑になります。

  • WPFリストビューのビジュアルアイテムは経由で取得できListView.ItemContainerGenerator.ContainerFromIndex(index) as ListViewItem、このアイテムにFocus()は必要な機能を実行するメソッドがあります。ただし、仮想化されたリストビューでは、これは現在表示されている領域外のアイテムに対しては機能しません - それらはまだ作成されておらず、メソッドはnull:(を返します

  • そのため、まず、フォーカスする項目を可視化する必要があります。これはScrollViewer、そのメソッドを使用して ListView 内で実行できScrollToVerticalOffset()ます (スクロールするアイテムのインデックスを知る必要があります - を使用して実行できますListView.Items.IndexOf())。

  • 次に、残念なことに、リストビュー アイテムは、プログラムによるスクロールが行われた直後には作成されません。そのため、実際のフォーカスは、後で新しく表示されるアイテムが作成されたときに行うことができます。これに適したイベントを見つけました - ListView.LayoutUpdated.

  • サイド ノード:

    • 同じものを取得するための「ネイティブパス」と思われる方法がありListView.ScrollIntoView(object item)ますが、リストに異なる位置に同等のアイテムが含まれている場合、確実に機能しません。ListView には のようなものはありませんListView.ScrollIndexIntoView(int index)

    • 上記のスタッフのほとんどは単一の方法VirtualizingStackPanel.BringIndexIntoView()で利用できるようですが、MSはそれを保護することを決定したため、外部からアクセスできません...

これが実用的なソリューションです

サンプル XAML:

<Window x:Class="WpfListviewTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=mscorlib"
        Title="MainWindow" Height="150" Width="525">
  <ListView x:Name="List1" KeyDown="List1_KeyDown" LayoutUpdated="List1_LayoutUpdated">
    <ListView.View>
      <GridView>
        <GridViewColumn Width="140" Header="Column 1" />
        <GridViewColumn Width="140" Header="Column 2" />
        <GridViewColumn Width="140" Header="Column 3" />
      </GridView>
    </ListView.View>
    <sys:DateTime>1/2/3</sys:DateTime>
    <sys:DateTime>4/5/6</sys:DateTime>
    <sys:DateTime>7/8/9</sys:DateTime>
    <sys:DateTime>10/11/12</sys:DateTime>
    <sys:DateTime>1/2/3</sys:DateTime>
    <sys:DateTime>4/5/6</sys:DateTime>
    <sys:DateTime>7/8/9</sys:DateTime>
    <sys:DateTime>10/11/12</sys:DateTime>
  </ListView>
</Window>

サンプル コード ビハインド:

public partial class MainWindow : Window {
    public MainWindow() {
        InitializeComponent();
    }

    private int? _indexToFocus;

    private void List1_KeyDown(object sender, KeyEventArgs e) {
        switch( e.Key ) {
            case Key.S:
                FocusItemByIndex(1); break;
            case Key.X:
                FocusItemByIndex(List1.Items.Count - 2); break;
            case Key.Z:
                List1.ScrollIntoView(List1.Items[List1.Items.Count - 2]); break;
        }
    }

    public void FocusItemByIndex(int index) {
        ScrollViewer sv = FindChild<ScrollViewer>(List1);
        double firstVisible = sv.VerticalOffset;
        double lastVisible = firstVisible + sv.ViewportHeight;

        if( index > lastVisible ) {
            double topVisible = index - sv.ViewportHeight + 1;
            sv.ScrollToVerticalOffset(topVisible);
        }
        else if( index < firstVisible ) {
            sv.ScrollToVerticalOffset(index);
        }

        _indexToFocus = index;
    }

    public static T FindChild<T>(DependencyObject parent, string name = null) 
        where T : DependencyObject 
    {
        if( parent == null )
            return null;

        int cChildren = VisualTreeHelper.GetChildrenCount(parent);
        T result = null;

        for( int i = 0; (result == null) && (i < cChildren); i++ ) {
            DependencyObject child = VisualTreeHelper.GetChild(parent, i);
            T tChild = child as T;

            if( tChild != null ) {
                if( name == null ) {
                    result = (T)child;
                }
                else {
                    FrameworkElement feChild = child as FrameworkElement;

                    if( feChild != null && feChild.Name == name )
                        result = (T)child;
                }
            }

            if( result == null )
                result = FindChild<T>(child, name);
        }

        return result;
    }

    private void List1_LayoutUpdated(object sender, EventArgs e) {
        if( _indexToFocus != null ) {
            ItemContainerGenerator lvItems = List1.ItemContainerGenerator;
            ListViewItem lvitemToFocus = lvItems.ContainerFromIndex(_indexToFocus.Value) as ListViewItem;

            if( lvitemToFocus != null ) {
                lvitemToFocus.Focus();
                _indexToFocus = null;
            }
        }
    }  
}
于 2013-02-24T11:54:48.943 に答える
1

コードビハインドでそれを行い、IsFocusedプロパティを確認/変更する必要があるようです。詳細については、このブログ投稿を参照してください。

また、必要なものを正確に説明しているこの SO 投稿もご覧ください。

于 2013-02-23T21:43:33.860 に答える