user566
user566

Reputation: 97

Display formatted text on selecting item in the Combobox

I have a combobox in that I want to display different string on selecting an item in Combo.

My combo box is a dropdown combobox.

For eg: I have following in my combobox.


Alex - Manager

Rain - Project Lead

Shiney - Engineer

Meera - Senior Engineer


OnSelecting an item in combobox I want to diaply only name i.e. Alex.

I tried below code

struct details{
    CString name;
    CString des;
};

BOOL CComboTestDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    details d1;
    d1.name = _T("alex");
    d1.des =_T("manager");
    m_vec.push_back(d1);

    details d2;
    d2.name = _T("Rain");
    d2.des =_T("Engineer");
    m_vec.push_back(d2);


    // TODO: Add extra initialization here
    for(int i=0;i<m_vec.size();i++)
    {
        m_ctrlCombo.AddString(m_vec[i].name+m_vec[i].des);
        m_ctrlCombo.SetItemData(i,(DWORD_PTR)&m_vec[i]);
    }
    m_ctrlCombo.SelectString(-1,m_vec[0].name);
    m_ctrlCombo.SetWindowText(m_vec[0].name);

    return TRUE;  // return TRUE  unless you set the focus to a control
}

void CComboTestDlg::OnCbnSelchangeCombo1()
{
    int nItem = m_ctrlCombo.GetCurSel();
    details* det = (details*)m_ctrlCombo.GetItemData(nItem);
    PostMessage(SETCOMBOTEXT,IDC_COMBO1,(LPARAM)(LPCTSTR)det->name);
}

BOOL CComboTestDlg::PreTranslateMessage(MSG* pMsg) 
{
    MSG msg1=*pMsg;//I am loosing the value after  checking ..so storing temp.
    MSG msg;
    CopyMemory(&msg, pMsg, sizeof(MSG));
    HWND hWndParent = ::GetParent(msg.hwnd);
    while (hWndParent && hWndParent != this->m_hWnd)
    {
        msg.hwnd = hWndParent;
        hWndParent = ::GetParent(hWndParent);
    }

    if (pMsg->message==SETCOMBOTEXT && (pMsg->wParam == IDC_COMBO1))
        SetDlgItemText(IDC_COMBO1, (LPCTSTR)pMsg->lParam);

    if(pMsg->message==WM_KEYDOWN)
    {
        if(pMsg->wParam==VK_RETURN && msg.hwnd ==m_ctrlCombo.m_hWnd )
        {
           OnCbnSelchangeCombo1();
        }
    } 
    return CDialog::PreTranslateMessage(pMsg);
}

I am able to achieve my requirement OnComboSelChange() and Arrow Keys event but on pressing enter key after using arrow keys in combo box, it is not showing formatted text in combo box.

Upvotes: 1

Views: 1339

Answers (1)

zett42
zett42

Reputation: 27766

I think the most reliable and easy to implement solution is to subclass the edit control of the combobox. Intercept the WM_SETTEXT message and change the text as you like before forwarding it to the rest of the chain (finally the original window proc).

Install the sub class proc in OnInitDialog():

COMBOBOXINFO cbi{ sizeof(cbi) };
if( m_ctrlCombo.GetComboBoxInfo( &cbi ) )
{
    SetWindowSubclass( cbi.hwndItem, ComboEditSubClassProc, 0, 0 );
}

ComboEditSubClassProc() could look like this:

LRESULT CALLBACK ComboEditSubClassProc( HWND hWnd, UINT uMsg, WPARAM wParam,
    LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData )
{
    switch( uMsg )
    {
        case WM_SETTEXT:
        {
            CString text = reinterpret_cast<LPCTSTR>( lParam );
            // Extract the name (everything before "-").
            CString name = text.SpanExcluding( _T("-") );
            name.TrimRight();
            // Forward the modified text to any other sub class procs, aswell
            // as the original window proc at the end of the chain.
            return DefSubclassProc( hWnd, uMsg, 0, reinterpret_cast<LPARAM>( name.GetString() ) );
        }
        case WM_NCDESTROY:
        {
            // We must remove our subclass before the subclassed window gets destroyed.
            // This message is our last chance to do that.
            RemoveWindowSubclass( hWnd, ComboEditSubClassProc, uIdSubclass );
            break;
        }
    }

    return DefSubclassProc( hWnd, uMsg, wParam, lParam );
}

Notes:

Contrary to my original solution of processing CBN_SELCHANGE, the current solution also works correctly if the combobox drop-down list is closed by pressing Return or is dismissed.

I think it is in general more reliable because we don't have to rely on the order of the notifications. The combobox has to finally call WM_SETTEXT to change the content of the edit control so this message will always be received.

There will also be no flickering as in the original solution where the text was first changed by the combobox and then modified by our code only after the fact.

Upvotes: 6

Related Questions