0

Please note that I have found that I need to add COMBOBOXEXITEM values much like LVITEM based on this book: Image Lists and ComboBoxEx Controls | Programming Windows with MFC, Second Edition (flylib.com)

enter image description here

FYI, I am getting the above error on the SetImageList call:

// Add color icons to combobox
for (int nCount = 0; nCount < m_colorBarTemplateFiles.GetCount(); nCount++) {
    CBitmap colorTemplateBitmap;
    const CString fname = colorBarTemplateDirectory + "\\" + m_colorBarTemplateImageFileNames[nCount];
    HANDLE colorTemplateImageHandle = LoadImage(0, fname, IMAGE_BITMAP, 16, 16, LR_LOADFROMFILE);
    colorTemplateBitmap.FromHandle((HBITMAP)colorTemplateImageHandle);
    m_colorBarTemplateImages.Add(&colorTemplateBitmap, (COLORREF)0xFFFFFF);
}
SetImageList(&m_colorBarTemplateImages);

In the book I link, the author writes this code (BTW, I have purchased the book which will hopefully come with the CD, but it won't arrive for another 3 weeks):

m_il.Create (IDB_IMAGES, 16, 1, RGB (255, 0, 255));         
SetImageList (&m_il); 

But unfortunately, on the website the code does not have an *.rc file:

So I don’t know how he sets up IDB_IMAGES. As far as I understand, the IDB_IMAGES is a big bitmap that is partitioned out into different icons, but it is not clear to met how to set that up in MFC.

This is afxcmn2.inl Line 334:

_AFXCMN_INLINE CImageList* CComboBoxEx::SetImageList(_In_ CImageList* pImageList)
    { ASSERT(::IsWindow(m_hWnd)); return CImageList::FromHandle((HIMAGELIST) ::SendMessage(m_hWnd, CBEM_SETIMAGELIST, 0, (LPARAM)pImageList->GetSafeHandle())); }

So somehow I am not creating the handle properly. I have also checked out other posts like:

visual c++ - How to add Images to CListCtrl in MFC - Stack Overflow

ccombobox - MFC CComboBoxEx icon update issue - Stack Overflow

CImageList Class | Microsoft Docs

CComboBoxEx Class | Microsoft Docs

Do you have any suggestions? TIA.

UPDATE:

Please note that I just learned that I need to be able to add these colors dynamically at runtime so it turns out I won't be able to work with *.rc files and CBitmaps. Instead, I'll have to research adding a colored region to the ComboBoxEx in OnPaint or OnDraw somehow using using something like this:

    COLORREF itemColor = colorArray[subitem][item];
    CRect rect;
    GetSubItemRect(item, subitem, LVIR_LABEL, rect);
    CDC* pDc = GetDC();
    pDc->FillRect(rect, &CBrush(itemColor));
    ReleaseDC(pDc);

So I'll keep you posted on what I figure out.

user8128167
  • 6,929
  • 6
  • 66
  • 79
  • Create it using the built-in Image Editor (well, it's a very basic one) or another (external) Image Editor and import it into your resources. Of course, it will be assigned a resource ID (`IDB_IMAGES` in the example). This will create an image-list. The bitmap must consist of fixed-width parts. Check the toolbar bitmaps generated by the MFC wizard, to see an example. – Constantine Georgiou Mar 31 '21 at 14:52
  • 1
    The most likely culprit is `ASSERT(::IsWindow(m_hWnd))` from what I see. When are you calling this code? It can't be in the constructor of your combobox or of your dialog. It needs to be called after DoDataExchange() for your combo box...assuming you have a DDX_Control() entry in your DoDataExchange() for your combo box. The preferred method would to be making a call to this from somewher in OnInitDialog(). The combo box must already be created in windows. It's probably asserting because m_hWnd of your combo box is NULL...i.e. the window for the combobox has not been created yet. – Joseph Willcoxson Mar 31 '21 at 15:11
  • Did your read the instructions on the assertion dialog? – IInspectable Mar 31 '21 at 15:11
  • Thanks, I'm pretty new at MFC so I'll take a closer look. – user8128167 Mar 31 '21 at 15:28
  • For the image... Imagine you want 10 32 x 32 icons. Then your image will be 32 high by 320 wide. – Andrew Truckle Mar 31 '21 at 19:31
  • 1
    Apparently you want to create an image-list for your `ComboBoxEx` control. Calling the `Create()` member (the one that takes a bitmap resource ID as a parameter) isn't the only way to initialize an image-list. You can instead call `Create()` with an initial number of images of 0, and then call the `Add()` method, which adds one or multiple images to the image-list. `Add()` in turn takes `CBitmap` objects as parameters, which can be loaded from external image-files or created programmatically. Check the `CImageList` class documentation for details (on MS-Docs). – Constantine Georgiou Apr 01 '21 at 06:06
  • As for the `SetImageList()` assertion fail, you have already located its code, so it wouldn't be hard to copy (and rename it) in your code, then break the calls into more lines to find which one exactly causes the failure. – Constantine Georgiou Apr 01 '21 at 06:12

1 Answers1

0

This is how I ended up adding a color swatch to the combobox dynamically without using *.rc files or *.bmp files. In the parent windows class OnCreate function where a key argument is CBS_OWNERDRAWFIXED which causes the DrawItem function to be called every time the combobox is opened by the user:

int ParentToolbarWindow::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    // ...
    CRect rect;
    int toolbarButtonIndex = 0;
    wndToolBar.GetItemRect(toolbarButtonIndex, &rect);
    CSize colorSwatchSize = pDC->GetTextExtent("1234567");
    rect.left = rect.right;
    rect.right = rect.left + colorSwatchSize.cx + 60;
    if (!wndToolBar.cmbColor.Create(WS_CHILD | WS_VISIBLE | CBS_DROPDOWN | WS_VSCROLL | CBS_HASSTRINGS | CBS_OWNERDRAWFIXED,
        rect, &wndToolBar, IDC_COMBO_XLINE)) {
    // error message...
        return -1;
    }
    // ...
    UpdateCombobox();
    // ...
}

void ParentToolbarWindow::UpdateCombobox()
{
    wndToolBar.cmbColor.ResetContent();
    // Get input value CStringArray "colorSwatchNames" for combobox...

    for (int i = 0; i < colorSwatchNames.GetCount(); i++) {
        if (wndToolBar.cmbColor.AddString(colorSwatchNames[i]) == CB_ERR || wndToolBar.cmbColor.AddString(colorSwatchNames[i]) == CB_ERRSPACE) {
            AfxMessageBox("Error setting up combo");
            break;
        }       
    }
 
   wndToolBar.cmbColor.SetCurSel(0);
}

Here is where I actually draw the color swatch along with the text:

void CColorCombo::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    // ...
    CRect itemRect(lpDrawItemStruct->rcItem);
    CRect blockRect(itemRect);
    // ... setup rect sizes and other setup steps

    // Draw color swatch as columns, each one pixel wide where inputColors is CStringArray
    for (int i = 0; i < inputColors.GetCount(); i++) {
        CString inputColorLine = inputColors[i];
        // read and tokenize inputColors into inputColorLine...
        int red = atoi(inputColorLine[0]);
        int green = atoi(inputColorLine[1]);
        int blue = atoi(inputColorLine[2]);

        COLORREF color = RGB(red, green, blue);
        CPen* pen = new CPen(PS_SOLID, 1, color);
        dc.SelectObject(pen);
        dc.MoveTo(blockRect.left + i, blockRect.top);
        dc.LineTo(blockRect.left + i, blockRect.bottom);
        if (pen != NULL) delete pen;
    }    
}
user8128167
  • 6,929
  • 6
  • 66
  • 79