MFC CListView 如何在用户键入字母时防止搜索和自动选择?

MFC CListView how to prevent search & auto-selection when user types letters?

TL;DR

在 C++ MFC CListView 中,当 CListView 具有键盘焦点时,如何防止基于用户键入的字母自动 selection?

情况:

MFC 应用程序使用 CListView 显示包含字母数字字符串的行集合。如果我 select 列表使其具有键盘焦点,然后键入一个字母,则列表 selection 会跳转到第一列值以该字母开头的第一项。

ie:如果我键入,比方说,'r',列表 list selection 会跳转到以字母 'r' 开头的第一项。如果我随后键入 'b',则列表 selection 会跳转到以字母 b 开头的第一项。等等。

当我使用 VS2019 新建项目向导创建新应用程序时,此行为在 CListView 中是自动发生的。

问题一:

如何在不干扰 up/down 箭头键导航的情况下防止这种自动 selection 发生?

在与此抗争的过程中,我发现直接使用 class WC_LISTVIEW 的 WIN32 Window 也有这种行为,所以我认为必须有一个打开或关闭此功能的样式?

如果它是一种样式,那么我可以在 OnInitialUpdate() 的 ModifyStyle() 调用中更改它,或者甚至只是使用它的 HWND 向控件发送消息?

所以...问题2:

我可以关闭它而不进入 CListView 的消息处理循环中的杂草吗?

评论

我不是在寻找 CListView 可以执行的高级多字母子字符串匹配搜索

我希望 CListView 在我键入字母或数字时停止更改 selection,并且我希望它继续更改 selection 以响应 [=101] 的正常行为=] 箭头、page-up/page-down 和 home/end 键。所以,如果你愿意,请关闭搜索。

代码

我可以 post 更多代码,但这将是 VS2019 New Project -> C++ -> MFC-> Explorer Style 向导的输出。

我所做的唯一相关更改是将派生的 CListView class 重命名为 MyCFileListView,并填充列表。

填充列表:

static void AddData(CListCtrl &ctrl, int row, int col, const wchar_t *str) {
    LVITEM lv;
    lv.iItem = row;
    lv.iSubItem = col;
    lv.pszText = (LPTSTR) str;
    lv.mask = LVIF_TEXT;
    if(col == 0)
        ctrl.InsertItem(&lv);
    else
        ctrl.SetItem(&lv);  
}

void MyCFileListView::OnInitialUpdate() {
    CListView::OnInitialUpdate();
    ModifyStyle(LVS_TYPEMASK, LVS_REPORT);
    
    CListCtrl &the_list = GetListCtrl();
    the_list.InsertColumn(0, L"File");
    the_list.SetColumnWidth(0, 80);
    the_list.InsertColumn(1, L"Size");
    the_list.SetColumnWidth(1, 80);
    the_list.InsertColumn(2, L"Modified");
    the_list.SetColumnWidth(2, 80);
    the_list.InsertColumn(3, L"Type");
    the_list.SetColumnWidth(3, 80);
    the_list.InsertColumn(4, L"Description");
    the_list.SetColumnWidth(4, 80);

    UINT ii = 0;
    for (const FileInfo &fi: program_state.file_list) {
        AddData(the_list, ii, 0, fi.filename.c_str());
        AddData(the_list, ii, 1, fi.filesize.c_str());
        AddData(the_list, ii, 2, L"NYI");
        AddData(the_list, ii, 3, fi.filetype.c_str());
        AddData(the_list, ii, 4, L"");
        ii++;
    }
}

其中 FileInfo 只是一个容器(应该是一个结构):

class FileInfo {
public:

    FileInfo() : n_filesize(0), epoch_ms(0), is_dir(false) {};

    virtual ~FileInfo() {
    }

    std::wstring filename;
    std::wstring ext;
    std::wstring fqfilename;
    std::wstring filesize;
    size_t       n_filesize;
    __int64      epoch_ms;
    std::wstring mod_date;
    std::wstring filetype;
    std::wstring description;
    std::wstring properties;
    bool is_dir;
};

和 program_state.file_list 是这样声明的:

class ProgramState {
public:
   // ...
   std::vector<FileInfo> file_list;
   // ...
};

我浏览过 MS 的 CListViews 页面,WIN32 ListViews AKA WC_LISTVIEW,它们的样式和扩展样式,但找不到任何关于此行为的信息:

...以及其他太多无法一一列举。

似乎这种行为非常好,以至于没有人愿意禁用它:-)

在您的 class 中,您应该处理 LVN_KEYDOWN 列表通知消息。

BEGIN_MESSAGE_MAP(MyCFileListView, CView)
...
  ON_NOTIFY(LVN_KEYDOWN, IDC_MY_LIST, &MyCFileListView::OnListKeyDown)
...
END_MESSAGE_MAP()

在处理程序方法中,您可以过滤任何您想要的键:

void MyCFileListView::OnListKeyDown(NMHDR* pNMHDR, LRESULT* pResult)
{
   const auto pKey = reinterpret_cast<NMLVKEYDOWN*>(pNMHDR);

   if (pKey->wVKey != VK_UP || pKey->wVKey != VK_DOWN)
   {
       ...
   }
...
}

希望你明白了。