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,它们的样式和扩展样式,但找不到任何关于此行为的信息:
https://docs.microsoft.com/en-us/cpp/mfc/reference/clistview-class?view=msvc-160
https://docs.microsoft.com/en-us/windows/win32/controls/list-view-controls-overview
https://docs.microsoft.com/en-us/windows/win32/controls/extended-list-view-styles
https://docs.microsoft.com/en-us/windows/win32/msi/listview-control
...以及其他太多无法一一列举。
似乎这种行为非常好,以至于没有人愿意禁用它:-)
在您的 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)
{
...
}
...
}
希望你明白了。
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,它们的样式和扩展样式,但找不到任何关于此行为的信息:
https://docs.microsoft.com/en-us/cpp/mfc/reference/clistview-class?view=msvc-160
https://docs.microsoft.com/en-us/windows/win32/controls/list-view-controls-overview
https://docs.microsoft.com/en-us/windows/win32/controls/extended-list-view-styles
https://docs.microsoft.com/en-us/windows/win32/msi/listview-control
...以及其他太多无法一一列举。
似乎这种行为非常好,以至于没有人愿意禁用它:-)
在您的 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)
{
...
}
...
}
希望你明白了。