Cool Controls Library (v1.1)

Introduction

I started to write CoolControls in October last year. The inspiration for me was Corel 8.0 and its great, well-designed UI, especially the dialog controls. If you have already seen Corel then you exactly know what I mean. If no, download the demo project and take a brief look at it now! One picture is often worth more than thousands of words, isn't it? Although the idea is borrowed from Corel, I wrote the entire code myself and I think that my implementation is faster and more accurate. Initially, I wrote support only for drop-down combo-boxes and edit controls and that early version required subclassing of each control individually. In fact this took me only two days, but I wasn't satisfied with that solution. So, I hit upon an idea to make a hook which could subclass all controls automatically. I wrote the code quickly because I've already had some experience with Windows hooks. It was working quite fine but I still had support only for basic controls, nothing more. Well, I realized that I've got to handle a variety of controls and its styles. It seemed to be a horrible work, but I didn't get scared and I was writing support for the rest of the controls. At last, I had to test the code under Windows 95/98 and NT including different system metrics and color sets. It took me a month to complete the code, pretty long time, but I hope that the code is good and doesn't contain too many bugs. That's a whole story.

What's new in version 1.1?

  • Fixed bug with LVS_EX_HEADERDRAGDROP list controls (thanks to Vlad Bychkoff for pointing this out)
  • UNICODE support added
  • WH_CALLWNDPROCRET is no longer supported due to some weird problems with that type of hook
  • Added support for multiple UI threads - (thanks for Mike Walter for the code)
  • Class name has been changed to CCoolControlsManager (my own idea)
  • Added support for SysTabControl32

How does it work?

Yeah, this is a very good question but answer isn't easy. Writing the code is usually easier than writing a good documentation for it :-). Nevertheless, I'll try to explain that...

Generally speaking the effect is done by subclassing a control and painting on the non-client area (in most cases) when the control needs to be drawn. The state of the control depends on the keyboard focus and mouse cursor position. The control is drawn with lighter borders (never totally flat) when it has no focus or mouse is outside of the window. Otherwise, the control is drawn in a normal way (without any changes). In more details, the library consists of two parts. First is a one and only, global CControlsManager object. The most important part of this class is a map of all subclassed controls, implemented as CMapPtrToPtr. The ControlsManager also provides a way to add a control manually by calling AddControl() member function.

Second part is a set of classes (not CWnd-derived) which represent each control individually. All classes derive from CCMControl, which holds important control information and is responsible for drawing the control border. CCMControl derives from CCMCore, which is a virtual class that provides a skeleton for all of the rest. Each CCMControl-derived class typically implements own DrawControl() function, which is the main drawing routine. It was necessary to respect all possible control styles and hence it took relatively long time to write this code and check all possible situations in different system configurations.

The first thing we have to do is installing app-wide hook of WH_CALLWNDPROCRET type. Further processing depends on m_bDialogOnly flag. If this flag is set to TRUE, we intercept WM_INITDIALOG and make a call to ControlManager's Install() method, which gets a handle to the dialog as a parameter. Next ,this function iterates through all dialog controls and for each of them the AddControl() member is being called. This approach allows to subclass only controls that are inserted to some dialog. If m_bDialogOnly is set to FALSE, WM_CREATE is intercepted instead of WM_INITDIALOG, so we are able to subclass all controls including those on toolbars an other non-dialog windows.

The AddControl() member gets a handle to control, retrieves its windows class name and next try to classify the control to one of currently supported groups.

Currently supported controls are:

  • Pushbuttons (except those with BS_OWNERDRAW style)
  • Checkboxes
  • Radiobuttons
  • Scrollbar controls
  • Edit boxes
  • List boxes
  • List views
  • Tree views
  • Spin buttons
  • Slider controls
  • Date/time pickers
  • Combo boxes (all styles)
  • Header controls
  • Hotkey controls
  • IPAddress controls
  • Toolbars (without TBSTYLE_FLAT)
  • Month calendars
  • Extended combo boxes
  • Rich edit controls
  • Tab controls

When window class name matches one of supported items, an object of appropriate type is created, the control is subclassed and the object is added to the map. The ControlsManager checks periodically (by setting a timer of 100ms period) whether the mouse cursor is over of any control in the map. If so, state of that control is changed accordingly. In addition we have to intercept some of messages that may cause redrawing of the control, e.g. WM_KILLFOCUS, WM_SETFOCUS, WM_ENABLE etc. and border of control is redrawn after calling the original window procedure. The control is removed from the map when it receives WM_NCDESTROY, the last message that the system sends to a window.

My code is not strongly MFC-based because I've used only CMapPtrToPtr class from MFC. Initially, I tried to use 'map' class from the STL, but the resulting executable was bigger and slower than this which has been built using CMapPtrToPtr.

For further information look at the CoolControlsManager.cpp and .h files

How to use it?

Single-threaded applications:

This module is extremely easy to use; you have to add to your CWinApp-derived class implementation file only two lines of code. First is a typical #include <CoolControlsManager.h> statement, second is a call to ControlsManager's InstallHook() method. The best place for this is the InitInstance() method of your CWinApp-derived class.

...
#include "CoolControlsManager.h"
...

BOOL CCoolControlsApp::InitInstance()
{
// Install the CoolControls
GetCtrlManager().InstallHook();

// Remaining stuff
}

Multithreaded applications:

Steps are the same as for single-threaded case, but you must add a call to InstallHook() for any additional thread you're going to create. You can place this code in InitInstance() of your CWinThread-derived class.

...
#include "CoolControlsManager.h"
...

BOOL CNewThread::InitInstance()
{
// Install the CoolControls for this thread
GetCtrlManager().InstallHook();

// Remaining stuff
}

BOOL CNewThread::ExitInstance()
{
// Uninstall the CoolControls for this thread
GetCtrlManager().UninstallHook();

// Remaining stuff
}

Of course don't forget to add CoolControlsManager.cpp to your project! That's all. The code can be compiled using VC5 as well as VC6 and has been tested under Win98 and WinNT 4.0.

Standard Disclaimer

This files may be redistributed unmodified by any means providing it is not sold for profit without the authors written consent, and providing that this notice and the authors name and all copyright notices remains intact. This code may be used in compiled form in any way you wish with the following conditions:

If the source code is used in any commercial product then a statement along the lines of "Portions Copyright (C) 1999 Bogdan Ledwig" must be included in the startup banner, "About" box or printed documentation. The source code may not be compiled into a standalone library and sold for profit. In any other cases the code is free to whoever wants it anyway!

This software is provided "as is" without express or implied warranty. Use it at you own risk! The author accepts no liability for any damages to your computer or data these products may cause.

Download demo project -62 KB

Download source - 17 KB

Date Last Updated: May 17, 1999



Comments

  • Amazing!

    Posted by Legacy on 11/17/2003 12:00am

    Originally posted by: Victor N

    Looking for this thing for a long time...
    Made since 1999, still usefull in 2003.

    Reply
  • Error, GetWindowText returned empty string in richedit on Win2000

    Posted by Legacy on 04/15/2002 12:00am

    Originally posted by: Paradoxx

    Hello,
    
    

    I have problem.

    If i use coolctrl on Windows 2000 US Sp2 and I want get text from richedit I got empty string.

    Example of my code:

    TCHAR buff[2];
    GetDlgItem(ID_RICH1)->GetWindowText(buff,2);
    if (buff[0] == '\0')
    TRACE("Error -> Empty string\n");

    If richedit window text is more then 2 char GetWindowText return empty string.
    If I use this code all is good:

    CString buff;
    GetDlgItem(ID_RICH1)->GetWindowText(buff);

    but I need previous code becouse MFC framework use it.
    I use VS C++ 6.0 SP5.

    Please, help me.

    Thanks.


    Paradoxx

    Reply
  • How to handle my own drawn button that conflict with yours?

    Posted by Legacy on 12/02/2001 12:00am

    Originally posted by: devilsword

    I derive a my own drawn button from CButton
    When I used GetCtrlManager().InstallHook()
    my own drawn cannot work as it was.How did I
    disable the button function of yours and didn't
    effect other's controls?

    Reply
  • How can I add support for ownerdrawn push buttons?

    Posted by Legacy on 10/23/2001 12:00am

    Originally posted by: Joerg Hoffmann

    Yes, you did really good work.

    The only missing thing is the support for for push buttons with the BS_OWNWERDRAWN style.

    Any suggestions how or where i could add this ?

    THX

    Reply
  • Does it need the virtual destructor for CCMControl?

    Posted by Legacy on 07/30/2001 12:00am

    Originally posted by: Hyung-Wook Kim

    It's really cool job!
    But studying this work, I wonder why there isn't virtual destructor for CCMControl... The classes inherited from CCMControl are deleted in RemoveControl(), but they are the type of CCMControl. So they are destructed as the object of CCMControl type, right? Is it safe for the memory leakage?

    Reply
  • Truly amazing

    Posted by Legacy on 06/05/2001 12:00am

    Originally posted by: Diarrhio

    Great work, man. Your work really made a diff in my app! Thanks so much for your diligence!

    D

    Reply
  • Flat controls

    Posted by Legacy on 05/21/2001 12:00am

    Originally posted by: Iulian Costache

    I want to make an application that has flat controls all the time (like they are before you move the mouse inside one). I want to be flat even when you push a Scrollbar or a Thumb.

    Can you help me?
    Thnx

    Reply
  • Possible Bug in Horizontal Scroll Bar

    Posted by Legacy on 07/05/1999 12:00am

    Originally posted by: Cris Tagle

    Yes, indeed it is an excellent set of codes. Thank you very much for sharing such knowledge. However, it seems to have a minor glitch in displaying horizontal scroll bars. When the control is not active, it displays a combination of both the original and the new control. The thumb doesn't seem to appear in a single location thus producing a duplicate image. I'll try to trace the problem but since this is your work, you maybe able to track it faster than I could. And if you want a snap shot of the said control, please tell me and I'll email you the image.

    Thanks again.

    Reply
  • Error processing Tab Controls

    Posted by Legacy on 05/20/1999 12:00am

    Originally posted by: Simon Brown

    Hi, first an excellent piece of code, with which I am very happy.
    
    

    When the check is made for "SysTabControl32" I suggest a loop which then calls AddControl() for ALL children of the tab control, not just the first child found. The first child is not always the up-down control, which is optional.

    I use a lot of tab controls with Edit / List / Tree controls set to have the tab control as the parent.

    I suggest this code after the check for "SysTabControl32":

    //
    // Add all children of the tab control...
    //
    HWND hChildWnd = GetTopWindow(hWnd);
    while (hChildWnd)
    {
    AddControl(hChildWnd);
    hChildWnd = GetNextWindow(hChildWnd, GW_HWNDNEXT);
    }

    //
    // Instead of just the first control.
    //
    // AddControl(GetTopWindow(hWnd));

    Disclaimer: it works for me!

    Simon

    Reply
  • More than just thanks

    Posted by Legacy on 05/13/1999 12:00am

    Originally posted by: Ian Duff

    As a programmer of more years than I care to remember, I would just like to add my congratulations to the author. He has done an enormous amount of work, and quite generously offers his source code to us in the programming community gratis. Well done Bogdan! and thank you very much for such a beautiful piece of work. I personally think that this is worthy of Code Guru status.

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • On-demand Event Event Date: December 18, 2014 The Internet of Things (IoT) incorporates physical devices into business processes using predictive analytics. While it relies heavily on existing Internet technologies, it differs by including physical devices, specialized protocols, physical analytics, and a unique partner network. To capture the real business value of IoT, the industry must move beyond customized projects to general patterns and platforms. Check out this webcast and join industry experts as …

  • Today's agile organizations pose operations teams with a tremendous challenge: to deploy new releases to production immediately after development and testing is completed. To ensure that applications are deployed successfully, an automatic and transparent process is required. We refer to this process as Zero Touch Deployment™. This white paper reviews two approaches to Zero Touch Deployment--a script-based solution and a release automation platform. The article discusses how each can solve the key …

Most Popular Programming Stories

More for Developers

RSS Feeds