Вставка строки CCombobox дает бред - PullRequest
1 голос
/ 06 ноября 2019

Я создал собственный CCustomCombo, расширив CComboBox для реализации функции DrawItem (). Вот код для него.

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();
}

часть создания -

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

Теперь проблема заключается в добавлении строковых элементов в комбинированный список. Когда я использую строковые объекты, он всегда показывает какую-то юникодную тарабарщину.

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

Те же результаты появляются для AddString. Проблема в том, что у меня есть набор двойников, которые я должен вставить в комбинированный список. И у меня нет никакого способа преобразовать их в строку без отображения тарабарщины. Я попробовал полдюжины методов конвертации, и ни один из них не сработал. Я в буквальном смысле в своем уме!

Самое смешное, что он отлично работал раньше, когда я использовал CComboBox, а не мой класс CCustomCombo / CBS_OWNERDRAWFIXED. Я попытался использовать CBS_HASSTRINGS, но он ничего не отображал, даже бред, поэтому каким-то образом строки даже не добавляются с CBS_HASSTRINGS.

Мне нужен собственный метод Draw, так как я планирую выделить некоторые выпадающие элементы. Я использую Windows 32, VS 2017.

Любая помощь будет принята с благодарностью. Спасибо.

1 Ответ

2 голосов
/ 06 ноября 2019
LPCTSTR lpszText = (LPCTSTR)lpDrawItemStruct->itemData;

Функция OwnerDraw смотрит на itemData. itemData назначается с помощью CComboBox::SetItemData. Он не назначается с помощью InsertString или других текстовых функций.

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

Строка и данные элемента хранятся в одном и том же адресе памяти, когда CBS_HASSTRINGS не задано.

См. Также документацию для CB_SETITEMDATA

Если указанный элемент находится в поле со списком, созданным владельцем, созданным без стиля CBS_HASSTRINGS, это сообщение заменяетзначение в параметре lParam сообщения CB_ADDSTRING или CB_INSERTSTRING, которое добавило элемент в поле со списком.

Таким образом, в основном itemData возвращает указатель one, и в этом случае работает нормально.

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

На этот раз строка создается в стеке, она уничтожается после существования функции. itemData указывает на неверный адрес.


Решение:

Если вы настраиваете текст с помощью InsertString/AddString, убедитесь, что установлено CBS_HASSTRINGS. И читать строки, используя GetLBText. Пример:

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

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

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

В противном случае используйте SetItemData для настройки данных и itemData для чтения.

...