Reputation:
TL;DR
In a C++ MFC CListView, how to prevent automagic selection based on letters the user types when the CListView has keyboard focus?
Situation:
MFC application using a CListView to show a collection of rows containing alphanumeric strings. If I select the list so it has keyboard focus and then type a letter, the list selection jumps to the first item which first column value begins with that letter.
ie: if I type, say, 'r' the list list selection jumps to the first item which starts with letter 'r'. If I then type, say, 'b' then the list selection jumps to the first item which begins with letter b. And so on.
This behavior is automagic in a CListView when I make a new application using VS2019 New Project wizard.
Question 1:
How can I prevent this automagic selection from happening, while not interfering with up/down arrow key navigation?
In the process of beating my head against this I found that a straight up WIN32 Window with class WC_LISTVIEW behaves this way as well, so I think there must be a style that turns this on or off?
If it is a style then I can change it in the ModifyStyle() call in OnInitialUpdate(), or even just send a message to the control using it's HWND?
So... Question 2:
Can I turn this off without getting into the weeds in the message handling loop for the CListView?
Comment
I am NOT looking for the advanced multi-letter substring matching search that CListView can do.
I want the CListView to stop changing the selection when I type letters or numbers and I want it to continue the normal behaviour of changing the selection in response to the up/down arrow, page-up/page-down, and home/end keys. So, turn off the search, if you will.
Code
I could post more code, but it would be the output of the VS2019 New Project -> C++ -> MFC-> Explorer Style wizard.
The only relevant change I've made was renaming the CListView derived class to MyCFileListView, and populating the list.
Populate the list:
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++;
}
}
Where FileInfo is just a container (should be a struct):
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;
};
and program_state.file_list is declared thus:
class ProgramState {
public:
// ...
std::vector<FileInfo> file_list;
// ...
};
I've been through MS' pages on CListViews, WIN32 ListViews AKA WC_LISTVIEW, their styles & extended styles and cannot find anything on this behaviour:
https://learn.microsoft.com/en-us/cpp/mfc/reference/clistview-class?view=msvc-160
https://learn.microsoft.com/en-us/windows/win32/controls/list-view-controls-overview
https://learn.microsoft.com/en-us/windows/win32/controls/extended-list-view-styles
https://learn.microsoft.com/en-us/windows/win32/msi/listview-control
...and others too numerous to list.
It is almost as if this behavior is so good that nobody would ever want to disable it :-)
Upvotes: 1
Views: 160
Reputation: 789
In your class you should handle the LVN_KEYDOWN
list notify message.
BEGIN_MESSAGE_MAP(MyCFileListView, CView)
...
ON_NOTIFY(LVN_KEYDOWN, IDC_MY_LIST, &MyCFileListView::OnListKeyDown)
...
END_MESSAGE_MAP()
In the handler method you can then filter whatever keys you'd like:
void MyCFileListView::OnListKeyDown(NMHDR* pNMHDR, LRESULT* pResult)
{
const auto pKey = reinterpret_cast<NMLVKEYDOWN*>(pNMHDR);
if (pKey->wVKey != VK_UP || pKey->wVKey != VK_DOWN)
{
...
}
...
}
I hope you got the idea.
Upvotes: 1