2

我找不到在 WPF ListView 中以编程方式设置焦点项目的方法。我只能找到 Selected Item | 的变体 项目 | 索引 | 值,但 'Focused' 项目与 'selected' 项目没有直接关系 - 可以不选择焦点项目(例如,当使用 Ctrl+Click 取消选择当前项目时)。

简而言之-我想从下面提供的代码中获得以下行为(它用虚拟的 8 个项目填充虚拟列表视图,并在按下 X 时尝试从末尾开始关注第二个项目):

想要的行为:

  • 使用鼠标 - 选择第二个项目
  • 按 X - 这会从最后开始关注第二个项目
  • 按键盘上的“向下”数组 - 这应该将当前选择移动到最后一项

实际发生了什么:

  • 使用鼠标 - 选择第二个项目
  • 按 X - 这会从最后选择第二个项目,但焦点仍然在从开始的第二个项目上
  • 按键盘上的“向下”数组 - 这应该将当前选择移动到最后一项,但选择的是第三项。

注意:普通的 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 回答中的链接,我获得了更多有用的信息,并为我的案例找到了可行的解决方案。

一些背景资料:

  • 与 ListView 控件的其他实现不同(在 WinForms 或 Win32 中),WPF 的 ListView 版本没有像 FocusedItem 这样的东西。似乎 MS 决定使用 UIElement 的通用接口将项目集中在列表视图中,每个视觉 ListViewItem 都是其后裔。这导致我的用例在 WPF 中变得更加复杂。

  • WPF listview 中的可视项目可以通过获取,ListView.ItemContainerGenerator.ContainerFromIndex(index) as ListViewItem并且该项目具有Focus()需要功能的方法。但是,在虚拟化列表视图中,这不适用于当前可见区域之外的项目 - 它们尚未创建并且方法返回null:(

  • 这就是为什么首先需要使要聚焦的项目可见。这可以通过ScrollViewer在 ListView 中使用它的ScrollToVerticalOffset()方法来完成(您需要知道要滚动到的项目的索引 - 可以使用 来完成ListView.Items.IndexOf())。

  • 接下来,不幸的是,listview 项目不会在程序化滚动完成后立即创建——这就是为什么实际聚焦可以在稍后创建新可见项目时完成的原因。我为此找到了合适的事件 - 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 回答