1

TL;博士

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

情况:

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

即:如果我键入“r”,则列表列表选择会跳转到以字母“r”开头的第一个项目。如果我然后键入“b”,则列表选择会跳转到以字母 b 开头的第一个项目。等等。

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

问题一:

如何防止这种自动选择发生,同时不干扰向上/向下箭头键导航?

在对此进行打击的过程中,我发现带有 WC_LISTVIEW 类的直接 WIN32 窗口的行为也是如此,所以我认为必须有一种样式可以打开或关闭它?

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

所以......问题2:

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

评论

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

我希望 CListView 在我键入字母或数字时停止更改选择,并且我希望它继续更改选择以响应向上/向下箭头、向上翻页/向下翻页和主页/结束键的正常行为. 因此,如果您愿意,请关闭搜索。

代码

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

我所做的唯一相关更改是将 CListView 派生类重命名为 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、它们的样式和扩展样式上的页面,但找不到关于此行为的任何内容:

...以及其他太多,无法列出。

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

4

1 回答 1

2

在您的班级中,您应该处理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)
   {
       ...
   }
...
}

我希望你明白了。

于 2021-09-13T02:56:54.803 回答