Click here to Skip to main content
15,881,281 members
Articles / Desktop Programming / MFC
Article

XColorPickerXP - an MFC color picker control with themed look

Rate me:
Please Sign up or sign in to vote.
4.13/5 (7 votes)
9 Apr 2008CPOL3 min read 43.9K   1.5K   25   5
XColorPickerXP is a simple drop-in color picker based on CComboBox that pops up color selection grid.

Introduction

For many years I have been using my XColourPicker control, which I based on Chris Maunder's excellent color picker control. Recently my project manager asked me why I wasn't using a control with a modern look. That's when I realized that my control still had the flat Windows 2000 look, even when used in a themed app:

screenshot

To make this look good in a themed app, I knew I had to add support for visual themes, which has always involved a good deal of effort whenever I tried it. Nevertheless, I decided to look into it. What I discovered was something unexpected that completely surprised me.

Comboboxes and Themes

In trying to decide how to proceed, it struck me that the color picker has the appearance of a combobox, rather than a button (my XColourPicker control is derived from CButton). Because of this, my XColourPicker control needed code to position and draw the down arrow, and a lot of other details to make it look like a combobox.

But what if I started from CComboBox? Could I eliminate some of the drawing code? To investigate this possibility, I started by creating a new class derived from CComboBox. I added the virtual DrawItem() function, which the framework calls for ownerdraw controls. Then I made sure the combobox in the demo was marked as ownerdraw. When I tried the demo app, I was amazed. Without using any of the uxtheme functions at all, the combobox was drawn using visual themes, even though it was marked as ownerdraw. Mouse hover also worked, changing the appearance to hot state. And I did not have to do anything to achieve this!

Naturally this made the job much easier. I added code to DrawItem() to draw a color-filled rect, and I was done.

Here is what the demo app for the new control looks like:

screenshot

XColorPickerXP API

The programmatic interface to XColorSpectrumCtrl attributes is very simple: just six functions to get/set color and custom colors.

FunctionDescription
COLORREF GetColor() Retrieves RGB color value
void GetCustomColors(COLORREF * pCustomColors) Retrieves array of 16 custom color RGB values
CString GetCustomColors() Retrieves 16 custom colors as space-separated string of RGB values
CXColorPickerXP& SetColor(COLORREF crColor) Sets color from RGB value
CXColorPickerXP& SetCustomColors(COLORREF * pCustomColors) Sets 16 custom colors from array of RGB values
CXColorPickerXP& SetCustomColors(LPCTSTR lpszCustomColors) Sets 16 custom colors from space-separated string of RGB values

XColorPickerXP DDX Helpers

XColorPickerXP provides two DDX helper functions:
// get/set color value
void AFXAPI DDX_XColorPickerXP(CDataExchange * pDX, int nIDC, COLORREF& crColor);

// get/set custom colors from string of space-separated color values
void AFXAPI DDX_XColorPickerXPCustom(CDataExchange * pDX, int nIDC, CString& strCustomColors);
In the demo app, these functions are used in DoDataExchange() function:
void CXColorPickerXPTestDlg::DoDataExchange(CDataExchange* pDX)
{
    CDialog::DoDataExchange(pDX);
    //{{AFX_DATA_MAP(CXColorPickerXPTestDlg)
    DDX_Control(pDX, IDC_COLOR_SAMPLE, m_stcColorSample);
    DDX_Control(pDX, IDC_BACKGROUND_COLOR, m_ctlBackgroundColor);
    DDX_Control(pDX, IDC_TEXT_COLOR, m_ctlTextColor);
    //}}AFX_DATA_MAP

    DDX_XColorPickerXP(pDX, IDC_TEXT_COLOR, m_rgbText);
    DDX_XColorPickerXP(pDX, IDC_BACKGROUND_COLOR, m_rgbBackground);

    DDX_XColorPickerXPCustom(pDX, IDC_TEXT_COLOR, m_strTextCustomColors);
    DDX_XColorPickerXPCustom(pDX, IDC_BACKGROUND_COLOR, m_strBackgroundCustomColors);
}

How To Use

To integrate XColorPickerXP into your app, you first need to add following files to your project:

  • XColorPickerXP.cpp
  • XColorPickerXP.h
  • XColorPopupXP.cpp
  • XColorPopupXP.h

Then use resource editor to add combobox control to your dialog, and use Class Wizard to attach member variable to that control.

Next, include header file XColorPickerXP.h in the dialog's header file. Then replace the CComboBox definition with CXColorPickerXP. Now you are ready to start using XColorPickerXP.

Demo App

The demo dialog processes color changes in the OnColorChange handler:
LRESULT CXColorPickerXPTestDlg::OnColorChange(WPARAM, LPARAM lParam)
{
    TRACE(_T("in CXColorPickerXPTestDlg::OnColorChange\n"));

    if (lParam == IDC_TEXT_COLOR)
    {
        TRACE(_T("IDC_TEXT_COLOR\n"));
    }
    else if (lParam == IDC_BACKGROUND_COLOR)
    {
        TRACE(_T("IDC_BACKGROUND_COLOR\n"));
    }

    if (m_ctlTextColor.GetColor() == m_ctlBackgroundColor.GetColor())
    {
        CXBalloonMsg::Show(_T("Unreadable Colors"),
                           _T("The text and background colors are identical.\r\n")
                           _T("The text will not be readable."),
                           m_ctlBackgroundColor.m_hWnd, 
                           m_hWnd,
                           AfxGetInstanceHandle(),
                           TTI_ERROR);
    }
    else
    {
        m_rgbText = m_ctlTextColor.GetColor();
        m_rgbBackground = m_ctlBackgroundColor.GetColor();
    }

    m_ctlTextColor.SetColor(m_rgbText);
    m_ctlBackgroundColor.SetColor(m_rgbBackground);
    m_stcColorSample.SetTextColor(m_rgbText);
    m_stcColorSample.SetBackgroundColor(m_rgbBackground);

    return 0;
}

Revision History

Version 1.0 - 2008 April 9

  • Initial public release.

Usage

This software is released into the public domain. You are free to use it in any way you like, except that you may not sell this source code. If you modify it or extend it, please to consider posting new code here for everyone to share. This software is provided "as is" with no expressed or implied warranty. I accept no liability for any damage or loss of business that this software may cause.

License

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


Written By
Software Developer (Senior) Hans Dietrich Software
United States United States
I attended St. Michael's College of the University of Toronto, with the intention of becoming a priest. A friend in the University's Computer Science Department got me interested in programming, and I have been hooked ever since.

Recently, I have moved to Los Angeles where I am doing consulting and development work.

For consulting and custom software development, please see www.hdsoft.org.






Comments and Discussions

 
QuestionQuestion on use Pin
David Crow24-Feb-12 3:43
David Crow24-Feb-12 3:43 
GeneralProblem in my vc2010 [modified] Pin
wiils6-Oct-10 4:24
wiils6-Oct-10 4:24 
GeneralRe: Problem in my vc2010 Pin
Hans Dietrich6-Oct-10 5:09
mentorHans Dietrich6-Oct-10 5:09 
Thanks for the kind words.

niils wrote:
in my version 2010, XColorPopupXP.cpp(line 622), SystemParametersInfo cannot run


This problem is caused by a change to the NONCLIENTMETRICS struct:
typedef struct tagNONCLIENTMETRICSA
{
    UINT    cbSize;
    int     iBorderWidth;
    int     iScrollWidth;
    int     iScrollHeight;
    int     iCaptionWidth;
    int     iCaptionHeight;
    LOGFONTA lfCaptionFont;
    int     iSmCaptionWidth;
    int     iSmCaptionHeight;
    LOGFONTA lfSmCaptionFont;
    int     iMenuWidth;
    int     iMenuHeight;
    LOGFONTA lfMenuFont;
    LOGFONTA lfStatusFont;
    LOGFONTA lfMessageFont;
#if(WINVER >= 0x0600)
    int     iPaddedBorderWidth;
#endif /* WINVER >= 0x0600 */
}   NONCLIENTMETRICSA, *PNONCLIENTMETRICSA, FAR* LPNONCLIENTMETRICSA;

As you can see, the new iPaddedBorderWidth is added when WINVER is 0x0600 (Vista) or greater. This is fine for Vista, but using this on an earlier OS will cause SystemParametersInfo() to fail.

My solution is to do this:
    struct OLD_NONCLIENTMETRICS
    {
        UINT    cbSize;
        int     iBorderWidth;
        int     iScrollWidth;
        int     iScrollHeight;
        int     iCaptionWidth;
        int     iCaptionHeight;
        LOGFONT lfCaptionFont;
        int     iSmCaptionWidth;
        int     iSmCaptionHeight;
        LOGFONT lfSmCaptionFont;
        int     iMenuWidth;
        int     iMenuHeight;
        LOGFONT lfMenuFont;
        LOGFONT lfStatusFont;
        LOGFONT lfMessageFont;
    };

    const UINT cbProperSize = sizeof(OLD_NONCLIENTMETRICS);

    NONCLIENTMETRICS ncm;
    ncm.cbSize = cbProperSize;

#ifdef _DEBUG
    BOOL ok = 
#endif

    ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, cbProperSize, &ncm, 0);
    ASSERT(ok);

    TRACE(_T("message font=<%s>\n"), ncm.lfMessageFont.lfFaceName);
    m_font.CreateFontIndirect(&ncm.lfMessageFont);

You can also play around with WINVER and set it to some value less than 0x600, but this has its own problems.
Best wishes,
Hans


[Hans Dietrich Software]

GeneralPopup is not themed Pin
Damir Valiulin23-Apr-08 16:23
Damir Valiulin23-Apr-08 16:23 
GeneralRe: Popup is not themed Pin
theCPkid4-Nov-08 18:53
theCPkid4-Nov-08 18:53 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.