Click here to Skip to main content
15,889,909 members
Please Sign up or sign in to vote.
3.67/5 (2 votes)
See more:
Is it possible to get the aero style combo drop down list even after making it owner drawn? I found a few sites that suggested using the comctl 6 version by loading the UTHEME DLL. As an alternate, it can also be done by sub classing and processing the WM_PAINT message. But there is no guarantee that it may work with later versions of windows.
I am confused as to which could be a better way of handling this problem. Please help!!

[Update]

Hi guys, first of all thanks a lot for your time and interest in solving the problem. I thought I will improve my question.
I have a UI design which has many droplist style CBs. I had to make one of them owner drawn since i needed some GDI drawing(for seperators and grayng out). Now i observed that the look of the CB which was made owner drawn has changed(White background looking similar to simple CB). After some searching i ended up reading this article:
http://microsoft.public.win32.programmer.ui.narkive.com/fHHS7f60/drop-down-list-with-cbs-dropdownlist-cbs-ownerdrawvariable-in-vista[^]
So i started by sub classing the CB proc and painted the combo box by loading the APIs from UXTHEME DLL. Now i think its very messy. I need to handle every event (WM_OUSEMOVE, WM_MOUSEHOVER....) and also events sent when the app is minimized or resized etc. Or else the CB turns back to simple style CB.
I tried to draw at the WM_DRAWITEM message. But it does not work.
Am i missing something very basic here or is there a simpler way to do this?
Posted
Updated 10-Nov-13 0:39am
v2
Comments
enhzflep 10-Nov-13 7:16am    
Have you seen this sample? How to Create an Owner-Drawn Combo Box

From a quick look, and by modifying old code I had that custom-draws a list-box, it seems like the only thing that's different in my program from a default combo box is

(1) the height of each item
(2) the fact that there's an icon on the left of each row.

(The sample appears not to use a manifest, so retains the Win98 style - a manifest gives it shiny, rounded goodness - just for the sake of clarity)

It's entirely possible I've missed something as usual :grin:, but I reckon you'll get some utility from the article I linked to.
Re[d]sign 10-Nov-13 12:44pm    
Hi enhzflep,
That was the first link which i referred to. Manifests as far as I understand in this case is telling the compiler which version of comctl to use. right?? I have manually loaded the lib instead of manifesting it just to make sure that my application wont stop working in case that DLL is missing or corrupted!!
enhzflep 10-Nov-13 13:04pm    
Gday Re[d]sign,
Ah, okay - I missed that link of yours. Manifests do a whole heap more than that. In fact, I'm not sure that you even can have several different versions of the commonControls dll on the same pc without naming them explictly or placing them in different folders. It was my impression that windows always carried (only) the newest one.

Not quite sure I (okay, entirely positive that I dont!) understand what situations could present the fear that you describe with a missing/broken dll. It's a part of the system and as such is covered by Windows File Projection Basically, if windows detects that it's missing/busted, then it replaces it silently from a cached copy.

I've never heard of the situation you fear occuring, though there are many more hostile environments than I've spent time in.

Now that I think of it, if you don't specify a manifest, some of the controls - I forget which, are still painted with visual styles. If you specify a manifest, then they all are.

In either case, the example I followed rendered as I would expect - just the same as a default control, though with 48px high items that include an icon. If you were really paranoid, you _could_ include a copy of the dll with your program - though my understanding is that this is not legal and within the user-terms. If windows finds a dll in the exe's directory, it will load it. If not it will keep searching in a specific manner until it looks and finds it in the system directory.

Including the file would allow windows to load it itself, without resorting to LoadLibrary.

Including a manifest causes visual theming to be automatic. None of this open the current theme and do everything by hand, in the dark.

Anyway, I'll post a solution with the code I used. Just supply some 48x48px bitmaps.
Just gimme 5 mins or so.
enhzflep 10-Nov-13 13:25pm    
Note: I meant to mention - since this is a mis-match of two different tutes, with some modification in between, you may wish to take a look at the way the selected item is indicated. The tute shows the whole row painted in blue when selected, my example only draws a blue square around the rather small text.

As promised. Built with gcc 4.7.2 under Windows7. Hah! the code's two years old next week - oh what I've learned since then. :laugh:

main.cpp
C++
#define WIN32_LEAN_AND_MEAN
#define UNICODE 1

#include <windows.h>
#include <commctrl.h>

#include "resource.h"

HINSTANCE hInst;

#define XBITMAP 48
#define YBITMAP 48

#define BUFFER MAX_PATH

HBITMAP hbmpPencil, hbmpCrayon, hbmpMarker, hbmpPen, hbmpFork;
HBITMAP hbmpPicture, hbmpOld;

void AddItem(HWND hwnd, PTSTR pstr, HBITMAP hbmp)
{
    int lbItem;

    lbItem = SendMessage(hwnd, LB_ADDSTRING, 0, (LPARAM)pstr);
    SendMessage(hwnd, LB_SETITEMDATA, (WPARAM)lbItem, (LPARAM)hbmp);
}


void AddComboItem(HWND hwnd, PTSTR pstr, HBITMAP hbmp)
{
    int lbItem;
    lbItem = SendMessage(hwnd, CB_ADDSTRING, 0, (LPARAM)pstr);
    SendMessage(hwnd, CB_SETITEMDATA, (WPARAM)lbItem, (LPARAM)hbmp);
}





INT_PTR CALLBACK DlgDrawProc(HWND hDlg, UINT message,
        UINT wParam, LONG lParam)
{
    HWND hListBox;
    PMEASUREITEMSTRUCT pmis;
    PDRAWITEMSTRUCT pdis;
    HDC hdcMem;
       HBITMAP hbmp;
    TCHAR achBuffer[BUFFER];
    size_t cch;
    int yPos;
    int lbItem;
    TEXTMETRIC tm;
    RECT rcBitmap;
    HRESULT hr;

    switch (message)
    {
         case WM_INITDIALOG:

            // Load the bitmaps. g_hInst is the global HINSTANCE handle.
            hbmpPencil = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_PENCIL));
            hbmpCrayon = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_CRAYON));
            hbmpMarker = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_MARKER));
            hbmpPen = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_PEN));
            hbmpFork = LoadBitmap(hInst, MAKEINTRESOURCE(IDB_FORK));

            // Retrieve the list box handle.
            //hListBox = GetDlgItem(hDlg, IDC_LIST_STUFF);

            // Initialize the list box text and associate a bitmap
            // with each list box item.
           /*
            AddItem(hListBox, L"pencil", hbmpPencil);
            AddItem(hListBox, L"crayon", hbmpCrayon);
            AddItem(hListBox, L"marker", hbmpMarker);
            AddItem(hListBox, L"pen",    hbmpPen);
            AddItem(hListBox, L"fork",   hbmpFork);
            SetFocus(hListBox);
            SendMessage(hListBox, LB_SETCURSEL, 0, 0);
*/

            HWND hCombo;
            hCombo = GetDlgItem(hDlg, IDC_COMBO1);
            AddComboItem(hCombo, L"pencil", hbmpPencil);
            AddComboItem(hCombo, L"crayon", hbmpCrayon);
            AddComboItem(hCombo, L"marker", hbmpMarker);
            AddComboItem(hCombo, L"pen",    hbmpPen);
            AddComboItem(hCombo, L"fork",   hbmpFork);

            return TRUE;

        case WM_MEASUREITEM:

            pmis = (PMEASUREITEMSTRUCT) lParam;

            // Set the height of the list box items.
            pmis->itemHeight = YBITMAP;

            return TRUE;

        case WM_DRAWITEM:

            pdis = (PDRAWITEMSTRUCT) lParam;

            // If there are no list box items, skip this message.
            if (pdis->itemID == -1)
            {
                break;
            }

            // Draw the bitmap and text for the list box item. Draw a
            // rectangle around the bitmap if it is selected.
            switch (pdis->itemAction)
            {
                case ODA_SELECT:
                case ODA_DRAWENTIRE:

                    // Draw the bitmap associated with the item.
                    //
                    // Get the item bitmap.
                    hbmpPicture = (HBITMAP)SendMessage(pdis->hwndItem,
                        CB_GETITEMDATA, pdis->itemID, 0);

                    // Create a compatible device context.
                    hdcMem = CreateCompatibleDC(pdis->hDC);

                    // Select the item bitmap into the compatible device
                    // context and save the old bitmap.
                    hbmpOld = (HBITMAP) SelectObject(hdcMem, hbmpPicture);

                    // Copy the bitmap into the compatible device context.
                    BitBlt(pdis->hDC,
                        pdis->rcItem.left, pdis->rcItem.top,
                        pdis->rcItem.right - pdis->rcItem.left,
                        pdis->rcItem.bottom - pdis->rcItem.top,
                        hdcMem, 0, 0, SRCCOPY);

                    // Draw the string associated with the item.
                    //
                    // Get the item string from the list box.
                    SendMessage(pdis->hwndItem, CB_GETLBTEXT,
                        pdis->itemID, (LPARAM)achBuffer);

                    // Get the metrics for the current font.
                    GetTextMetrics(pdis->hDC, &tm);

                    // Calculate the vertical position for the item string
                    // so that the string will be vertically centered in the
                    // item rectangle.
                    yPos = (pdis->rcItem.bottom + pdis->rcItem.top -
                        tm.tmHeight) / 2;

                    // Get the character length of the item string.
//                    hr = StringCchLength(achBuffer, BUFFER, &cch);
                    //if (FAILED(hr))
                    {
                        cch = wcslen(achBuffer);
                        // TODO: Handle error.
                    }

                    // Draw the string in the item rectangle, leaving a six
                    // pixel gap between the item bitmap and the string.
                    TextOut(pdis->hDC, XBITMAP + 6, yPos, achBuffer, cch);

                    // Clean up.
                    SelectObject(hdcMem, hbmpOld);
                    DeleteDC(hdcMem);

                    // Is the item selected?
                    if (pdis->itemState & ODS_SELECTED)
                    {
                        // Set RECT coordinates to surround only the
                        // bitmap.
                        rcBitmap.left = pdis->rcItem.left;
                        rcBitmap.top = pdis->rcItem.top;
                        rcBitmap.right = pdis->rcItem.left + XBITMAP;
                        rcBitmap.bottom = pdis->rcItem.top + YBITMAP;

                        // Draw a rectangle around bitmap to indicate
                        // the selection.
                        //DrawFocusRect(pdis->hDC, &rcBitmap);
                        DrawFocusRect(pdis->hDC, &rcBitmap);
                    }
                    break;

                case ODA_FOCUS:

                    // Do not process focus changes. The focus caret
                    // (outline rectangle) indicates the selection.
                    // The IDOK button indicates the final
                    // selection.
                    break;
            }
            return TRUE;

        case WM_COMMAND:

            switch (LOWORD(wParam))
            {
                case IDC_BTN_TEST:
                    // Get the selected item's text.
                    lbItem = SendMessage(GetDlgItem(hDlg, IDC_COMBO1),
                        CB_GETCURSEL, 0, 0);

                    // Get the selected item's bitmap.
                    hbmp = (HBITMAP) SendMessage(GetDlgItem(hDlg, IDC_COMBO1),
                         CB_GETITEMDATA, lbItem, 0);


                    // trivial test to see that we can compare the ITEMDATA of an item
                    // to some arbitrary  thing. In this case, I'm checking to see if the image is
                    // a particular one.
                    if (hbmp != hbmpFork)
                    {
                        MessageBox(hDlg, L"Try again!", L"Oops", MB_OK);
                        return FALSE;
                    }
                    else
                    {
                        MessageBox(hDlg, L"You're right!", L"Congratulations.", MB_OK);

                      // Fall through.
                    }

                case IDCANCEL:

                    // Destroy the dialog box.

                    EndDialog(hDlg, TRUE);
                    return TRUE;

                default:

                    return FALSE;
            }

        case WM_DESTROY:

            // Free the bitmap resources.
            DeleteObject(hbmpPencil);
            DeleteObject(hbmpCrayon);
            DeleteObject(hbmpMarker);
            DeleteObject(hbmpPen);
            DeleteObject(hbmpFork);

            return TRUE;

        default:
            return FALSE;

    }
    return FALSE;
}



int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
    hInst = hInstance;
    InitCommonControls();

    // The user interface is a modal dialog box
    return DialogBox(hInstance, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)DlgDrawProc);
}



resource.rc
C++
// Generated by ResEdit 1.5.11
// Copyright (C) 2006-2012
// http://www.resedit.net

#include <windows.h>
#include <commctrl.h>
#include <richedit.h>
#include "resource.h"




//
// Bitmap resources
//
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDB_CRAYON         BITMAP         "bitmap2.bmp"


LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDB_FORK           BITMAP         "bitmap5.bmp"


LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDB_MARKER         BITMAP         "bitmap3.bmp"


LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDB_PEN            BITMAP         "bitmap4.bmp"


LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
IDB_PENCIL         BITMAP         "bitmap1.bmp"



//
// Dialog resources
//
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
DLG_MAIN DIALOGEX 6, 5, 194, 84
STYLE DS_3DLOOK | DS_CENTER | DS_SETFONT | WS_CAPTION | WS_VISIBLE | WS_GROUP | WS_THICKFRAME | WS_SYSMENU
CAPTION "Code::Blocks Template Dialog App"
FONT 8, "Tahoma", 0, 0, 1
{
    PUSHBUTTON      "&Test", IDC_BTN_TEST, 67, 62, 46, 15
    PUSHBUTTON      "&Quit", IDC_BTN_QUIT, 141, 62, 46, 15
    COMBOBOX        IDC_COMBO1, 7, 7, 180, 30, CBS_DROPDOWNLIST | CBS_HASSTRINGS | CBS_OWNERDRAWFIXED
}



//
// Manifest resources
//
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
1                  RT_MANIFEST    ".\\manifest.xml"



resource.h
C++
#ifndef IDC_STATIC
#define IDC_STATIC (-1)
#endif

#define DLG_MAIN                                101
#define IDB_PENCIL                              103
#define IDB_CRAYON                              105
#define IDB_MARKER                              107
#define IDB_PEN                                 109
#define IDB_FORK                                111
#define IDC_COMBO1                              1000
#define IDC_BTN_TEST                            1001
#define IDC_BTN_QUIT                            1002


manifest.xml
XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <dependency>
    <dependentAssembly>
      <assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="*" publicKeyToken="6595b64144ccf1df" language="*"/>
    </dependentAssembly>
  </dependency>
  <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
    <security>
      <requestedPrivileges>
        <requestedExecutionLevel
          level="asInvoker"
          uiAccess="false"/>
      </requestedPrivileges>
    </security>
  </trustInfo>
</assembly>
 
Share this answer
 
v2
Hi FOLKS,

Finally got the problem solved!! It was quite interesting. As enhzflep said I tried using the manifest. It does get the aero look (border and buttons) but I did not get what I wanted which is a owner drawn combo box which looks and behaves as a combo box drop down list. So what I had to do was:

1) Load the UXTHEME DLL from system 32
Note: This works only with WinXP and later.
2) Get func ptr s for the modules you will need.
3) Under WM_PAINT message paint the background for the look of drop list style. Please refer to Parts and states(msdn)
4) Call InvalidateRect from - WM_MOUSEMOVE, ODA_FOCUS(from WM_DRAWITEM) and ODA_SELECT(after selection)
Ended with some flickering problems. You will need to monitor the states and call InvalidateRect as minimally as possible.

In case if a more detailed answer with code snippets is needed please let me know.
And again thanks a lot guys!!!
 
Share this answer
 
Comments
[no name] 12-Jun-19 5:33am    
Please share the code snippets for this.
Of course. As soon as you owner-draw something, the style is on you. Basically, owner-drawing of something can easily create inconsistencies in style. In such cases, you many want to think to making the whole application to look according your own styles and ignoring the system style setting. This is a matter of graphical industrial design and design decisions.

"But there is no guarantee that it may work with later version of window"? I don't think this is a real factor. Eventually, Windows itself should die, I would like to see that. The descendant system is supposed to inherit .NET legacy, but not Win32 legacy, which is already pretty ill. Before it happens, new versions of Windows based on Win32 will most likely guarantee the compatibility of WM_PAINT functionality. Note that owner drawing is not really coupled with WM_PAINT.

—SA
 
Share this answer
 
I'm surprised that making the combo box either of the owner draws styles is messing up the theme. USER should still be drawing most of the control and your code is just responsible for drawing the items. While I haven't used an owner draw CB in yonks I've got an owner draw listbox that still looks the business on Windows 7.

So... When you create this combo box are you using either of the window styles CBS_OWNERDRAWFIXED or CBS_OWNERDRAWVARIABLE? If you're not then you might be trying to be too clever with the drawing and drawing somewhere you shouldn't. As soon as you start trying to do things with WM_PAINT messages in a stock control you're at the point that writing your own control is usually easier.

So my advice is to make sure that you're only responding to or reflecting WM_DRAWITEM and WM_MEASUREITEM messages to do the drawing. If you're doing make sure you really need to and that you can't get by just using those two messages.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900