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

148 Views Asked by At

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:

...and others too numerous to list.

It is almost as if this behavior is so good that nobody would ever want to disable it :-)

1

There are 1 best solutions below

0
On BEST ANSWER

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.