Rendering standard Windows elements


The DrawFrameControl function allows you to render standard Windows elements in your custom controls. Let's start by simply rendering a selected radio button. Start with our new scratch program and make this very simple change:

class RootWindow : public Window
{
 ...
protected:
 void PaintContent(PAINTSTRUCT *pps);
 ...
};

void RootWindow::PaintContent(PAINTSTRUCT *pps)
{
 RECT rc = { 0, 0, 32, 32 };
 DrawFrameControl(pps->hdc, &rc, DFC_BUTTON,
                  DFCS_BUTTONRADIO | DFCS_CHECKED);
}

When you run the program, you'll see a little radio button in the corner. Woo-hoo.

You might also notice that it's an unthemed radio button. To get a themed radio button, you need to use the theme-drawing functions defined in the uxtheme.h header file. Let's make the following further changes:

class RootWindow : public Window
{
 ...
protected:
 void OpenTheme() { m_htheme = OpenThemeData(m_hwnd, L"Button"); }
 void CloseTheme()
 {
  if (m_htheme) { CloseThemeData(m_htheme); m_htheme = NULL; }
 }
 RootWindow() : m_htheme(NULL) { }
 ~RootWindow() { CloseTheme(); }
 ...
};

LRESULT RootWindow::OnCreate()
{
 OpenTheme();
 return 0;
}

void RootWindow::PaintContent(PAINTSTRUCT *pps)
{
 RECT rc = { 0, 0, 32, 32 };
 if (m_htheme) {
  DrawThemeBackground(m_htheme, pps->hdc,
                      BP_RADIOBUTTON, RBS_CHECKEDNORMAL,
                      &rc, NULL);
 } else {
  DrawFrameControl(pps->hdc, &rc, DFC_BUTTON,
                   DFCS_BUTTONRADIO | DFCS_CHECKED);
 }
}

LRESULT RootWindow::HandleMessage(...)
{
 ...
  case WM_THEMECHANGED:
   CloseTheme();
   OpenTheme();
   break;
 ...
}

This new version attempts to open the "Button" theme for the window when the window is created. If themes are not enabled, then this call will fail. When it comes time to draw, we see whether we have a theme available. If so, then we use the DrawThemeBackground function to draw it; otherwise, we draw it the unthemed way. Of course, we close the theme handle at destruction, and we also refresh the theme handle if the user changes the theme.

If you run this new program with themes enabled, then you will get the pretty themed radio button instead of the old-fashioned unthemed radio button.

Next time, we'll look at the trickier menu bitmaps.

Comments (15)
  1. Universalis says:

    Is there a standard way to get the size of the control you’re drawing? If I’m drawing multiple radio buttons then I need to know how much space to reserve for them so that they don’t overlap or have yawning gaps between them. Do I use GetSystemMetrics, or is there another function?

    Also – your example draws directly onto the window DC, which apparently directly contradicts the DrawFrameControl docs: "If uType is either DFC_MENU or DFC_BUTTON and uState is not DFCS_BUTTONPUSH, the frame control is a black-on-white mask (that is, a black frame control on a white background). In such cases, the application must pass a handle to a bitmap memory device control…".

  2. Christopher says:

    Does anyone have any idea why DrawThemeBackground and the other UxTheme functions fail to properly draw a progress bar control in Windows Vista Beta 1?

    I’ve checked out the theme with a resource editor, and the functions are drawing the contents, but it is not the same as the system progress bars. It looks awful.

    Is the win32 progressbar using something other than UxTheme now? (I don’t have ‘glass’ capabilities). Thanks.

  3. Ivo says:

    Yeah, how to get the standard size for the radio buttons/checkboxes? The system uses some fixed size that doesn’t depend on the dialog’s font size. Is it linked to the DPI settings?

  4. What I’m curious about is how to use the theme drawing functions to properly draw a multiline editfield. Specifically, how do you properly draw the scrollbar?

    The reason I ask is because I’m handling the non-client paint message so that I can make the RichEdit control theme-savvy.

  5. Mihai says:

    Somehow related to other size questions:

    How do I get the size of the text area?

    The client rect included the space for the circle in the radio-button and the one for the checkbox in the check-box button.

    Nice to have to implement some layout manager (controls/dialogs auto-resizing to fit the text).

  6. Andreas Haeber says:

    Here is one place to look for sizing guidelines: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnwue/html/ch14a.asp

    But maybe there is some API to get the information at runtime too?

  7. This topic reminds me of something. Does anyone know how to get the color the Common Controls 6 listview control uses for the background of the selected column, i.e. the column designated in LVM_SETSELECTEDCOLUMN? I hear tell the only way involves GetPixel. :(

  8. Ralph Thomas says:

    There is no standard way to get the dimensions of any arbitrary control. I spent several weeks (fulltime) writing code to extract widget metrics for the Adobe Source Libraries (http://opensource.adobe.com/) Win32 version.

    The source is here:

    http://cvs.sourceforge.net/viewcvs.py/adobe-source/adobe-source/adobe/future/widgets/sources/win/ui_core_implementation.cpp?rev=1.1&view=auto

    The two functions which you are interested in are: calculate_edit_bounds and calculate_best_bounds. These call into a little UxTheme wrapper (which uses UxTheme when it’s available and has some good default values available when it’s not) that is in the same directory in CVS.

    Note that the above code also figures out where the text baseline is inside the control (which you might not need). It does this so that controls in a row are baseline-aligned.

    Ralph

    ralpht@infinite-imagination.com

  9. Juris G. says:

    There is a typo in the link to the scratch program – it has a ‘%22’ character at the end. Should be http://blogs.msdn.com/oldnewthing/archive/2005/04/22/410773.aspx

  10. Drawing the menu checkmark, as an example.

  11. Universalis says:

    To reiterate – the documentation says "use this function to write to a memory DC, not to the screen", and your example writes directly to the screen. Which is right?

  12. oldnewthing says:

    I believe the documentation is in error. The conditions should be "If uType is either DFC_MENU or if uType is DFC_BUTTON and uState is DFCS_RADIOMASK".

    The "must" is a bit too strong. It’s really trying to say, "You’re getting a mask. I hope you know what to do with a mask." I’ll see what I can do to clarify the docs better.

  13. Universalis says:

    Thanks – that would make a lot more sense!

  14. Drawing the menu checkmark, as an example.

  15. rookkey says:

    For those trying to compile Raymond’s example, it may be helpful to know that you need to include some files:

    #include <uxtheme.h>

    #include <tmschema.h>

    Since the WM_THEMECHANGED message has only been available since Windows XP, you may need to explicitly tell the preprocessor which version of Windows to compile under:

    #define _WIN32_WINNT 0x0501

    Also, Raymond’s example assumes you have declared a member variable in the RootWindow class to hold the HTHEME handle:

    class RootWindow : public Window

    {



    private:

    HTHEME m_htheme;

    };

    After making these changes, you’ll see the Windows XP-themed radio button in the upper-left corner of the program’s window.

Comments are closed.