1
votes

I've just added an Item-Filter-Feature to a CComboBox derived class called ComboBoxFbp in an old MFC application.

BOOL CComboBoxFbp::OnEditChange()
{
    CString csText;

    if (m_wFbpMode & _FbpMode_UserTextFiltersList) {
        GetWindowText(csText);

        // This makes the DropDown "flicker"
        // ShowDropDown(false);

        // Just insert items that match
        FilterItems(csText);

        // Open DropDown (does nothing if already open)
        ShowDropDown(true);
    }

    return FALSE;   // Notification weiterleiten
}

void CComboBoxFbp::FilterItems(CString csFilterText)
{
    CString csCurText;
    int nCurItem;
    DWORD wCurCursor;

    // Text/selection/cursos restore
    GetWindowText(csCurText);
    nCurItem = GetCurSel();
    if (nCurItem != CB_ERR && nCurItem >= 0 && nCurItem < GetCount()) {
        CString csCurItemText;
        GetLBText(nCurItem, csCurItemText);
        if (csCurItemText == csCurText)     csCurText = csCurItemText;
        else                                nCurItem = CB_ERR;
    } else {
        nCurItem = CB_ERR;
    }

    wCurCursor = GetEditSel();

    // Delete all items
    ResetContent();

    csFilterText.MakeLower();

    // Add just the items (from the vector of all possibles) that fit
    for (auto item : m_vItems)
    {
        CString csItemText = item.first;
        csItemText.MakeLower();
        if (!csFilterText.IsEmpty() && csItemText.Find(csFilterText) < 0)
            continue;

        const int i = AddString(item.first);
        SetItemData(i, item.second);
    }

    // Text/selection/cursos restore
    if (nCurItem != CB_ERR)     SelectString(-1, csCurText);
    else                        SetWindowText(csCurText);
    SetEditSel(LOWORD(wCurCursor), HIWORD(wCurCursor));
}

So when the user types, the long list of items in the DropDown gets filtered accordingly. Everything's fine so far.

The size/height of the ListBox/DropDown doesn't change once its open. It does change accordingly when die DropDown opens. Meaning if there are only 2 items the DropDown is only 2 items high.

My issue

When the user enters a text where just one item fits the DropDown is only 1 item in height (this happens with some user workflows, i.e. user manually closes & opens the DropDown).

Now when the user now changes the text so multiple items are fitting the height stays 1 item and it looks weird as even the scrollbar doesn't look correct as it doesn't fit.

What I've tried so far

  1. I cannot use CComboBox::SetMinVisibleItems (or the MSG behind it) as it only works in a Unicode CharacterSet (which I'm not able to change in this old application) and from WinVista onwards (app runs on WinXP).
  2. The only other option is to close and open the DropDown so it gets redrawn correctly with the correct height (see // This makes the DropDown "flicker" in Source Code above).

Now going with option 2 I don't want the user to see the closing and opening ("flicker") of the DropDown after every key he is pressing.

To prevent this I've tried a couple of solutions I've found but none works in my case with a ComboBox-DropDown. Here's a list of methods I've put just before the ShowDropDown(false) and just after the ShowDropDown(true).

  1. EnableWindow(false/true);
  2. (Un)LockWindowUpdate();
  3. SendMessage(WM_SETREDRAW, FALSE/TRUE, 0)

With all three calls I still see the DropDown closing/opening.

Do you guys have other ideas how I can prevent this flicker?

Thanks in advance Soko

1
The feature you tried to re-implement is already available. See Using Autocomplete and particularly How to Enable Autocomplete Manually. With that you only have to manage the list of candidates, and let the system do everything else for you.IInspectable
I've had a look at that, but the Autoappend feature was something I definetly not wanted. I somehow missed the Autosuggest mode though... I'd like to give it a try and would welcome some sample code if you're in the mood...Soko

1 Answers

1
votes

This is an XY question.

It should be easier to use the following approach to adjust the height of the ComboBox

  1. Use GetComboBoxInfo to get the handle of the list control.
  2. Use OnChildNotify or ON_CONTROL_REFLECT and capture CBN_DROPDOWN.
  3. In the handler of the message resize the window as needed Use SetWindowPos and just change the size.