Sign Up

Sign Up to our social questions and Answers Engine to ask questions, answer people’s questions, and connect with other people.

Have an account? Sign In

Have an account? Sign In Now

Sign In

Login to our social questions & Answers Engine to ask questions answer people’s questions & connect with other people.

Sign Up Here

Forgot Password?

Don't have account, Sign Up Here

Forgot Password

Lost your password? Please enter your email address. You will receive a link and will create a new password via email.

Have an account? Sign In Now

You must login to ask a question.

Forgot Password?

Need An Account, Sign Up Here

Please briefly explain why you feel this question should be reported.

Please briefly explain why you feel this answer should be reported.

Please briefly explain why you feel this user should be reported.

Sign InSign Up

The Archive Base

The Archive Base Logo The Archive Base Logo

The Archive Base Navigation

  • SEARCH
  • Home
  • About Us
  • Blog
  • Contact Us
Search
Ask A Question

Mobile menu

Close
Ask a Question
  • Home
  • Add group
  • Groups page
  • Feed
  • User Profile
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Buy Points
  • Users
  • Help
  • Buy Theme
  • SEARCH
Home/ Questions/Q 6859517
In Process

The Archive Base Latest Questions

Editorial Team
  • 0
Editorial Team
Asked: May 27, 20262026-05-27T02:17:35+00:00 2026-05-27T02:17:35+00:00

I am trying to make a radio button control with a transparent background using

  • 0

I am trying to make a radio button control with a transparent background using only Win32 when themes are enabled. The reason for doing this is to allow a radio button to be placed over an image and have the image show (rather than the grey default control background).

What happens out of the box is that the control will have the grey default control background and the standard method of changing this by handling either WM_CTLCOLORSTATIC or WM_CTLCOLORBTN as shown below does not work:

case WM_CTLCOLORSTATIC:
    hdcStatic = (HDC)wParam;

    SetTextColor(hdcStatic, RGB(0,0,0)); 
    SetBkMode(hdcStatic,TRANSPARENT);

    return (LRESULT)GetStockObject(NULL_BRUSH);
    break;  

My research so far indicates that Owner Draw is the only way to achieve this. I’ve managed to get most of the way with an Owner Draw radio button – with the code below I have a radio button and a transparent background (the background is set in WM_CTLCOLORBTN). However, the edges of the radio check are cut off using this method – I can get them back by uncommenting the call to the function DrawThemeParentBackgroundEx but this breaks the transparency.

void DrawRadioControl(HWND hwnd, HTHEME hTheme, HDC dc, bool checked, RECT rcItem)
{
    if (hTheme)
    {
      static const int cb_size = 13;

      RECT bgRect, textRect;
      HFONT font = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0);
      WCHAR *text = L"Experiment";

      DWORD state = ((checked) ? RBS_CHECKEDNORMAL : RBS_UNCHECKEDNORMAL) | ((bMouseOverButton) ? RBS_HOT : 0); 

      GetClientRect(hwnd, &bgRect);
      GetThemeBackgroundContentRect(hTheme, dc, BP_RADIOBUTTON, state, &bgRect, &textRect);

      DWORD dtFlags = DT_VCENTER | DT_SINGLELINE;

      if (dtFlags & DT_SINGLELINE) /* Center the checkbox / radio button to the text. */
         bgRect.top = bgRect.top + (textRect.bottom - textRect.top - cb_size) / 2;

      /* adjust for the check/radio marker */
      bgRect.bottom = bgRect.top + cb_size;
      bgRect.right = bgRect.left + cb_size;
      textRect.left = bgRect.right + 6;

      //Uncommenting this line will fix the button corners but breaks transparency
      //DrawThemeParentBackgroundEx(hwnd, dc, DTPB_USECTLCOLORSTATIC, NULL);

      DrawThemeBackground(hTheme, dc, BP_RADIOBUTTON, state, &bgRect, NULL);
      if (text)
      {
          DrawThemeText(hTheme, dc, BP_RADIOBUTTON, state, text, lstrlenW(text), dtFlags, 0, &textRect);

      }

   }
   else
   {
       // Code for rendering the radio when themes are not present
   }

}

The method above is called from WM_DRAWITEM as shown below:

case WM_DRAWITEM:
{
    LPDRAWITEMSTRUCT pDIS = (LPDRAWITEMSTRUCT)lParam;
    hTheme = OpenThemeData(hDlg, L"BUTTON");    

    HDC dc = pDIS->hDC;

    wchar_t sCaption[100];
    GetWindowText(GetDlgItem(hDlg, pDIS->CtlID), sCaption, 100);
    std::wstring staticText(sCaption);

    DrawRadioControl(pDIS->hwndItem, hTheme, dc, radio_group.IsButtonChecked(pDIS->CtlID), pDIS->rcItem, staticText);                               

    SetBkMode(dc, TRANSPARENT);
    SetTextColor(hdcStatic, RGB(0,0,0));                                
    return TRUE;

}                           

So my question is two parts I suppose:

  1. Have I missed some other way to achieve my desired result?
  2. Is it possible to fix the clipped button corners issue with my code and still have a transparent background
  • 1 1 Answer
  • 0 Views
  • 0 Followers
  • 0
Share
  • Facebook
  • Report

Leave an answer
Cancel reply

You must login to add an answer.

Forgot Password?

Need An Account, Sign Up Here

1 Answer

  • Voted
  • Oldest
  • Recent
  • Random
  1. Editorial Team
    Editorial Team
    2026-05-27T02:17:36+00:00Added an answer on May 27, 2026 at 2:17 am

    After looking at this on and off for nearly three months I’ve finally found a solution that I’m pleased with. What I eventually found was that the radio button edges were for some reason not being drawn by the routine within WM_DRAWITEM but that if I invalidated the radio button control’s parent in a rectangle around the control, they appeared.

    Since I could not find a single good example of this I’m providing the full code (in my own solution I have encapsulated my owner drawn controls into their own class, so you will need to provide some details such as whether the button is checked or not)

    This is the creation of the radiobutton (adding it to the parent window) also setting GWL_UserData and subclassing the radiobutton:

    HWND hWndControl = CreateWindow( _T("BUTTON"), caption, WS_CHILD | WS_VISIBLE | BS_OWNERDRAW, 
        xPos, yPos, width, height, parentHwnd, (HMENU) id, NULL, NULL);
    
    // Using SetWindowLong and GWL_USERDATA I pass in the this reference, allowing my 
    // window proc toknow about the control state such as if it is selected
    SetWindowLong( hWndControl, GWL_USERDATA, (LONG)this);
    
    // And subclass the control - the WndProc is shown later
    SetWindowSubclass(hWndControl, OwnerDrawControl::WndProc, 0, 0);
    

    Since it is owner draw we need to handle the WM_DRAWITEM message in the parent window proc.

    case WM_DRAWITEM:      
    {      
        LPDRAWITEMSTRUCT pDIS = (LPDRAWITEMSTRUCT)lParam;      
        hTheme = OpenThemeData(hDlg, L"BUTTON");          
    
        HDC dc = pDIS->hDC;      
    
        wchar_t sCaption[100];      
        GetWindowText(GetDlgItem(hDlg, pDIS->CtlID), sCaption, 100);      
        std::wstring staticText(sCaption);      
    
        // Controller here passes to a class that holds a map of all controls 
        // which then passes on to the correct instance of my owner draw class
        // which has the drawing code I show below
        controller->DrawControl(pDIS->hwndItem, hTheme, dc, pDIS->rcItem, 
            staticText, pDIS->CtlID, pDIS->itemState, pDIS->itemAction);    
    
        SetBkMode(dc, TRANSPARENT);      
        SetTextColor(hdcStatic, RGB(0,0,0));     
    
        CloseThemeData(hTheme);                                 
        return TRUE;      
    
    }    
    

    Here is the DrawControl method – it has access to class level variables to allow state to be managed since with owner draw this is not handled automatically.

    void OwnerDrawControl::DrawControl(HWND hwnd, HTHEME hTheme, HDC dc, bool checked, RECT rcItem, std::wstring caption, int ctrlId, UINT item_state, UINT item_action)
    {   
        // Check if we need to draw themed data    
        if (hTheme)
        {   
            HWND parent = GetParent(hwnd);      
    
            static const int cb_size = 13;                      
    
            RECT bgRect, textRect;
            HFONT font = (HFONT)SendMessageW(hwnd, WM_GETFONT, 0, 0);
    
            DWORD state;
    
            // This method handles both radio buttons and checkboxes - the enums here
            // are part of my own code, not Windows enums.
            // We also have hot tracking - this is shown in the window subclass later
            if (Type() == RADIO_BUTTON) 
                state = ((checked) ? RBS_CHECKEDNORMAL : RBS_UNCHECKEDNORMAL) | ((is_hot_) ? RBS_HOT : 0);      
            else if (Type() == CHECK_BOX)
                state = ((checked) ? CBS_CHECKEDNORMAL : CBS_UNCHECKEDNORMAL) | ((is_hot_) ? RBS_HOT : 0);      
    
            GetClientRect(hwnd, &bgRect);
    
            // the theme type is either BP_RADIOBUTTON or BP_CHECKBOX where these are Windows enums
            DWORD theme_type = ThemeType(); 
    
            GetThemeBackgroundContentRect(hTheme, dc, theme_type, state, &bgRect, &textRect);
    
            DWORD dtFlags = DT_VCENTER | DT_SINGLELINE;
    
            if (dtFlags & DT_SINGLELINE) /* Center the checkbox / radio button to the text. */
                bgRect.top = bgRect.top + (textRect.bottom - textRect.top - cb_size) / 2;
    
            /* adjust for the check/radio marker */
            // The +3 and +6 are a slight fudge to allow the focus rectangle to show correctly
            bgRect.bottom = bgRect.top + cb_size;
            bgRect.left += 3;
            bgRect.right = bgRect.left + cb_size;       
    
            textRect.left = bgRect.right + 6;       
    
            DrawThemeBackground(hTheme, dc, theme_type, state, &bgRect, NULL);          
            DrawThemeText(hTheme, dc, theme_type, state, caption.c_str(), lstrlenW(caption.c_str()), dtFlags, 0, &textRect);                    
    
            // Draw Focus Rectangle - I still don't really like this, it draw on the parent
            // mainly to work around the way DrawFocus toggles the focus rect on and off.
            // That coupled with some of my other drawing meant this was the only way I found
            // to get a reliable focus effect.
            BOOL bODAEntire = (item_action & ODA_DRAWENTIRE);
            BOOL bIsFocused  = (item_state & ODS_FOCUS);        
            BOOL bDrawFocusRect = !(item_state & ODS_NOFOCUSRECT);
    
            if (bIsFocused && bDrawFocusRect)
            {
                if ((!bODAEntire))
                {               
                    HDC pdc = GetDC(parent);
                    RECT prc = GetMappedRectanglePos(hwnd, parent);
                    DrawFocus(pdc, prc);                
                }
            }   
    
        }
          // This handles drawing when we don't have themes
        else
        {
              TEXTMETRIC tm;
              GetTextMetrics(dc, &tm);      
    
              RECT rect = { rcItem.left , 
                  rcItem.top , 
                  rcItem.left + tm.tmHeight - 1, 
                  rcItem.top + tm.tmHeight - 1};    
    
              DWORD state = ((checked) ? DFCS_CHECKED : 0 ); 
    
              if (Type() == RADIO_BUTTON) 
                  DrawFrameControl(dc, &rect, DFC_BUTTON, DFCS_BUTTONRADIO | state);
              else if (Type() == CHECK_BOX)
                  DrawFrameControl(dc, &rect, DFC_BUTTON, DFCS_BUTTONCHECK | state);
    
              RECT textRect = rcItem;
              textRect.left = rcItem.left + 19;
    
              SetTextColor(dc, ::GetSysColor(COLOR_BTNTEXT));
              SetBkColor(dc, ::GetSysColor(COLOR_BTNFACE));
              DrawText(dc, caption.c_str(), -1, &textRect, DT_WORDBREAK | DT_TOP);
        }           
    }
    

    Next is the window proc that is used to subclass the radio button control – this
    is called with all windows messages and handles several before then passing unhandled
    ones on to the default proc.

    LRESULT OwnerDrawControl::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam,
                                   LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
    {
        // Get the button parent window
        HWND parent = GetParent(hWnd);  
    
        // The page controller and the OwnerDrawControl hold some information we need to draw
        // correctly, such as if the control is already set hot.
        st_mini::IPageController * controller = GetWinLong<st_mini::IPageController *> (parent);
    
        // Get the control
        OwnerDrawControl *ctrl = (OwnerDrawControl*)GetWindowLong(hWnd, GWL_USERDATA);
    
        switch (uMsg)
        {       
            case WM_LBUTTONDOWN:
            if (controller)
            {
                int ctrlId = GetDlgCtrlID(hWnd);
    
                // OnCommand is where the logic for things like selecting a radiobutton
                // and deselecting the rest of the group lives.
                // We also call our Invalidate method there, which redraws the radio when
                // it is selected. The Invalidate method will be shown last.
                controller->OnCommand(parent, ctrlId, 0);       
    
                return (0);
            }
            break;
            case WM_LBUTTONDBLCLK:
                // We just treat doubleclicks as clicks
                PostMessage(hWnd, WM_LBUTTONDOWN, wParam, lParam);
                break;
            case WM_MOUSEMOVE:
            {
                if (controller)                 
                {
                    // This is our hot tracking allowing us to paint the control
                    // correctly when the mouse is over it - it sets flags that get
                    // used by the above DrawControl method
                    if(!ctrl->IsHot())
                    {
                        ctrl->SetHot(true);
                        // We invalidate to repaint
                        ctrl->InvalidateControl();
    
                        // Track the mouse event - without this the mouse leave message is not sent
                        TRACKMOUSEEVENT tme;
                        tme.cbSize = sizeof(TRACKMOUSEEVENT);
                        tme.dwFlags = TME_LEAVE;
                        tme.hwndTrack = hWnd;
    
                        TrackMouseEvent(&tme);
                    }
                }    
                return (0);
            }
            break;
        case WM_MOUSELEAVE:
        {
            if (controller)
            {
                // Turn off the hot display on the radio
                if(ctrl->IsHot())
                {
                    ctrl->SetHot(false);        
                    ctrl->InvalidateControl();
                }
            }
    
            return (0);
        }
        case WM_SETFOCUS:
        {
            ctrl->InvalidateControl();
        }
        case WM_KILLFOCUS:
        {
            RECT rcItem;
            GetClientRect(hWnd, &rcItem);
            HDC dc = GetDC(parent);
            RECT prc = GetMappedRectanglePos(hWnd, parent);
            DrawFocus(dc, prc);
    
            return (0);
        }
        case WM_ERASEBKGND:
            return 1;
        }
        // Any messages we don't process must be passed onto the original window function
        return DefSubclassProc(hWnd, uMsg, wParam, lParam); 
    
    }
    

    Finally the last little piece of the puzzle is that you need to invalidate the control (redraw it) at the right times. I eventually found that invalidating the parent allowed the drawing to work 100% correctly. This was causing flicker until I realised that I could get away by only invalidating a rectangle as big as the radio check, rather than as big as the whole control including text as I had been.

    void InvalidateControl()
    {
        // GetMappedRectanglePos is my own helper that uses MapWindowPoints 
        // to take a child control and map it to its parent
        RECT rc = GetMappedRectanglePos(ctrl_, parent_);
    
        // This was my first go, that caused flicker
        // InvalidateRect(parent_, &rc_, FALSE);    
    
        // Now I invalidate a smaller rectangle
        rc.right = rc.left + 13;
        InvalidateRect(parent_, &rc, FALSE);                
    }
    

    A lot of code and effort for something that should be simple – drawing a themed radio button over a background image. Hopefully the answer will save someone else some pain!

    * One big caveat with this is it only works 100% correctly for owner controls that are over a background (such as a fill rectangle or an image). That is ok though, since it is only needed when drawing the radio control over a background.

    • 0
    • Reply
    • Share
      Share
      • Share on Facebook
      • Share on Twitter
      • Share on LinkedIn
      • Share on WhatsApp
      • Report

Sidebar

Related Questions

I am trying to make a radio button list of 4 items. Each item
I've been trying to make a ul controlled by radio buttons. When a button
I trying to make a simple image gallery, where I have several radio button
Trying to make a make generic select control that I can dynamically add elements
I'm trying to make radio buttons that when you have them active, it displays
I'm trying to make a radio group specifying a bunch of options, and an
I am trying to implement a grid with radio button selector. When the radio
I'm trying to open Shadowbox from within a radio button onclick event on a
I'm using Ruby Qt bindings. I'm trying to make a square widget (checker board),
I've been banging my head against a wall trying make this work for a

Explore

  • Home
  • Add group
  • Groups page
  • Communities
  • Questions
    • New Questions
    • Trending Questions
    • Must read Questions
    • Hot Questions
  • Polls
  • Tags
  • Badges
  • Users
  • Help
  • SEARCH

Footer

© 2021 The Archive Base. All Rights Reserved
With Love by The Archive Base

Insert/edit link

Enter the destination URL

Or link to existing content

    No search term specified. Showing recent items. Search or use up and down arrow keys to select an item.