Custom Controls and a little about graphics
or "3y3 h4t3 7h3 M$ l00k"

Yo yo... it's me... yer local llamah, writing another essay! I'm sorry that you have to suffer from this but corny made me do it ;) Anyways... today we'll be talking (actually I'll be talking, but saying we makes me feel someone is actually listening) about custom controls. There isn't much to say... but oh well, I owe it to corny :) I hope I'll write the second part of this essay, talking about totally customizing the so called "M$" look, but then again, maybe not ;)

Okay, enough bullshit, lets go on!

Intro

What are custom controls?

"Applications can create custom controls to perform tasks not supported by predefined controls. Windows provides the following ways to create custom controls:

From the Microsoft Win32 API reference

Well, for a change, Microsoft didn't make any mistakes in this text =)

Custom controls are basically controls that don't exist in the standard/common controls, so you create your
own control to handle that task.

In this essay I'll demonstrate the last two approaches:

The level of the code presented here is pretty easy... You shouldn't have any difficulities understanding it.

Method one: Subclassing

Subclassing is basically changing the window procedure of a certain window, after its creation, allowing the subclasser to process the subclassed window's messages, to handle the subclassed window's messages, and to decide whether or not to allow the original window procedure to do the default processing.

It might sound hard, but the actual subclassing only takes one line:

void SubclassControl( HWND parent )
{
    wpOldProc = ( WNDPROC ) SetWindowLong( GetDlgItem( parent, IDC_SUBCLASS ), GWL_WNDPROC,
                                    ( LONG ) SubclassedCBProc );
}

If you're a C newbie you might need some explanation, and since I gotta fill at least one page, here it is:

wpOldProc is a WNDPROC variable, obviously.

What that line does is call SetWindowLong to change the window procedure (GWL_WNDPROC) of the target control (whos handle we get using GetDlgItem) to point to our own procedure (SubclassCBProc).

The return value from that function is a pointer to the start address of the old window procedure for that control.

Now, what we want is only change the look. You probably think that means handling WM_PAINT only.
That's logical... well, you're wrong since the normal check box (which we will subclass) responds to clicks too, and that means the Microsoft check box window procedure probably contains this code:

// Unimportant stuff

case WM_LBUTTONUP:
        Bloat();
        ChangeState();
        InvalidateRect();

// More unimportant stuff

The underlined part is the important part.

Since after a change in the control's state the window must repaint to reflect that change, if we don't handle WM_LBUTTONUP, Windows itself will handle it and overwrite our beatiful custom control with the ugly default look. Darn.

Oh well, that was a big piece of text for a small part of code... I love doing that.

Anyway, here's the code for WM_LBUTTONUP:

case WM_LBUTTONUP:
        BOOL state;
        // Toggle the check box state
        state = SendMessage( hwnd, BM_GETCHECK, 0, 0 ) == BST_CHECKED ? BST_UNCHECKED : BST_CHECKED;
        SendMessage( hwnd, BM_SETCHECK, ( WPARAM ) state, 0 );
        InvalidateRect( hwnd, NULL, FALSE );
        break;

This code does two things, toggles the check box' state, and orders it to repaint.
The toggling is done by getting the current state of the check box, and change it to the other state.

* For you cavemen - the "blah = blah == blah ? blah : blah" is a short version of the if / then statement *

Then comes the standard call InvalidateRect() which repaints the whole control (notice the NULL), and that's it for the state control code.

The paint code is pretty simple too. We get the window's DC, create a compatible memory DC, then BitBlt the bitmap from the memory DC to the window DC (the bitmap drawn depends on the state).

Method two - A new control

We create the custom control like any other window, with one exception - it is created as a child window, with no title bar, system menu or frame.

The window procedure is almost identical to the previous one, but in this one we maintain a static boolean variable that indicates the state of the control.

This also affects how we get the state: since this is a new control, there's no option of using the controls built-in capabilites of keeping and retrieving the state of the control, and we must use our own message, defined as WM_USER+100, to get the state of the control.

Woah, that was a short one =)

A little extra: A totally new control, with no bitmaps used!

Sounds leet eh? :)

Looks leet too...

This control is the same as the previous, only it's a totally different control, and we paint it ourselves, no external bitmaps are used. There's nothing much to explain, since as I've said, it's the same code all over again.

The only bit that might need explaining is creating the "3d-ish" look:

That look actually very simple to create - make a bright "lighten" bevel on top left, and a dark bevel on bottom right, and you got yourself a button. Do the opposite, and you got a... thingie..

The lines are drawn using multiple MoveToEx & LineTo to draw the lines, and CreatePen to create the.. uhh.
pens. There's a better way to do this using a polyline with multiple points, but hey, I'm too lazy and tired
to do that =)

After the border is drawn, we now first erase the whole rectangle which part of it we're about to fill, then
paint the "filling" using the Rectangle() function (yeah yeah, I could've used FillRect, but who gives a damn
anyway).

We also handle WM_LBUTTONDOWN/UP and WM_MOUSEMOVE to simulate dragging:

I also added another check to see if the left button is down, so that users that decide to have fun with my proggie (e.g. clicking, dragging out, releasing, moving in) will FAIL!! muahahah (damnit, I'm tired =)

And that's the whole story... see? That shit was easy :P

Conclusion

Custom controls are easy, and you can change the look of your program with just a little piece of code, which
is no doubt much faster and smaller than Microsoft's ;)

Download some Source code, here

Stuff

Here ya go corny, I wrote yer essay, I bet you're feeling sad looking at it =)
Anyways, I'm gonna kick yer ass in the charts!

Lub ya all, too tired to even write greets or comments or something :)
By the way, my personal opinion: if you "hate the M$ look" that much, switch to linux, dumbass :)

Seeya next essay... or not :)

Signing off,
Zzzzzzzzzzz
Fresh.