Madz
Madz

Reputation: 1279

CCombobox string insertion gives gibberish

I created a custom CCustomCombo by extending CComboBox to implement a DrawItem() function. Here's the code for it.

void CCustomCombo::DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct )
{
    ASSERT( lpDrawItemStruct->CtlType == ODT_COMBOBOX );
    LPCTSTR lpszText = ( LPCTSTR ) lpDrawItemStruct->itemData;
    ASSERT( lpszText != NULL );

    if ( lpDrawItemStruct->itemID == -1 || lpszText == NULL)
        return;

    CDC dc;
    dc.Attach( lpDrawItemStruct->hDC );

    // Save these value to restore them when done drawing. 
    COLORREF crOldTextColor = dc.GetTextColor();
    COLORREF crOldBkColor = dc.GetBkColor();

    // If this item is selected, set the background color  
    // and the text color to appropriate values. Erase 
    // the rect by filling it with the background color. 
    if ( ( lpDrawItemStruct->itemAction & ODA_SELECT ) &&
        ( lpDrawItemStruct->itemState  & ODS_SELECTED ) )
    {
        dc.SetTextColor( ::GetSysColor( COLOR_HIGHLIGHTTEXT ) );
        dc.SetBkColor( ::GetSysColor( COLOR_HIGHLIGHT ) );
        dc.FillSolidRect( &lpDrawItemStruct->rcItem, ::GetSysColor( COLOR_HIGHLIGHT ) );
    }
    else
    {
        dc.FillSolidRect( &lpDrawItemStruct->rcItem, crOldBkColor );
    }

    // Draw the text. 
    dc.DrawText(
        lpszText,
        ( int ) _tcslen( lpszText ),
        &lpDrawItemStruct->rcItem,
        DT_CENTER | DT_SINGLELINE | DT_VCENTER );

    // Reset the background color and the text color back to their 
    // original values. 
    dc.SetTextColor( crOldTextColor );
    dc.SetBkColor( crOldBkColor );

    dc.Detach();
}

creation part -

m_selectionCombo.Create( WS_VSCROLL |
        CBS_DROPDOWNLIST | WS_VISIBLE | WS_TABSTOP| CBS_OWNERDRAWFIXED,
        rect, &m_wndSelectionBar, ID_TEMP_BTN))

Now the problem is with the adding string items to the combobox. When I'm using string objects, it always shows some unicode gibberish.

m_selectionCombo.InsertString(0, "One"); //works

char * one = "one"; 
m_selectionCombo.InsertString(0, one ); //works

CString one = "one"; 
m_selectionCombo.InsertString(0, one ); //shows gibberish

std::string one = "one";
char *cstr = &one[0];
m_wndSelectionBar.m_selectionCombo.InsertString(0, cstr ); //shows gibberish

The same results appear for AddString. The problem is that I have a set of doubles that I have to insert to the combobox. And I have no way of converting them to string without displaying gibberish. I tried half-a-dozen conversion methods and none worked. I'm literally at my wits end!

The funny thing is it worked perfectly before when I used CComboBox and not my CCustomCombo class/CBS_OWNERDRAWFIXED. I tried using CBS_HASSTRINGS, but it displayed nothing, not even the gibberish, so somehow the strings don't even get added in with CBS_HASSTRINGS.

I need the custom Draw method since I plan to highlight some of the dropdown items. I'm using windows 32, VS 2017.

Any help would be highly appreciated. Thanks.

Upvotes: 2

Views: 390

Answers (1)

Barmak Shemirani
Barmak Shemirani

Reputation: 31669

LPCTSTR lpszText = (LPCTSTR)lpDrawItemStruct->itemData;

OwnerDraw function is looking at itemData. itemData is assigned using CComboBox::SetItemData. It is not assigned using InsertString or other text functions.

char * one = "one"; 
m_selectionCombo.InsertString(0, one ); //works

The string and item data are stored in the same memory address when CBS_HASSTRINGS is not set.

See also documentation for CB_SETITEMDATA

If the specified item is in an owner-drawn combo box created without the CBS_HASSTRINGS style, this message replaces the value in the lParam parameter of the CB_ADDSTRING or CB_INSERTSTRING message that added the item to the combo box.

So basically itemData returns a pointer one, and it works fine in this case.

CString one = "one"; 
m_selectionCombo.InsertString(0, one ); //shows gibberish

This time the string is created on stack, it is destroyed after the function exists. itemData points to invalid address.


Solution:

If you are setting the text using InsertString/AddString then make sure CBS_HASSTRINGS is set. And read the strings using GetLBText. Example:

//LPCTSTR lpszText = (LPCTSTR)lpDrawItemStruct->itemData; <- remove this

if(lpDrawItemStruct->itemID >= GetCount())
    return;

CString str;
GetLBText(lpDrawItemStruct->itemID, str);
LPCTSTR lpszText = str;

Otherwise use SetItemData to setup data, and use itemData to read.

Upvotes: 3

Related Questions