Changing+a+control's+colours

//by Richard Russell, September 2007//

You will normally want dialogue box controls to use their default colour scheme, for reasons of uniformity with other Windows applications, but occasionally you may want to change the foreground and/or background colours for special purposes. There are a number of techniques that can be used to achieve this, for example you can create a coloured Edit Box by using a Rich Edit control or you can create a coloured Static Control by drawing into a suitable bitmap and displaying it in a picture box or you can use a custom graphics control.

This article describes an alternative technique, involving subclassing the dialogue box using the **SUBCLASS.BBC** library (version 1.3 or later). In some ways it is more flexible than the other methods, since it allows you to change the colours of other controls, not just Text Boxes and Static Controls. To illustrate the effect the program listed below creates this rather garish dialogue box:



Firstly we need to install a couple of libraries and define some constants: code format="bb4w" INSTALL @lib$+"WINLIB2" INSTALL @lib$+"SUBCLASS" INSTALL @lib$+"NOWAIT"

BS_DEFPUSHBUTTON = 1 LB_ADDSTRING = &180 WM_CTLCOLORLISTBOX = 308 WM_CTLCOLORBTN = 309 WM_CTLCOLORDLG = 310 WM_CTLCOLORSTATIC = 312 WS_GROUP = &20000 WS_BORDER = &800000 code Note that version 1.3 or later of SUBCLASS.BBC is required.

Next we can create the dialogue box template in the usual way: code format="bb4w" dlg% = FN_newdialog("Dialogue box", 20, 20, 160, 128, 8, 560)

PROC_listbox(dlg%, "", 101, 10, 8, 140, 80, 0) PROC_static(dlg%, "Static box", 102, 10, 82, 140, 20, WS_BORDER) PROC_pushbutton(dlg%, "OK", 1, 12, 108, 56, 14, WS_GROUP + BS_DEFPUSHBUTTON) PROC_pushbutton(dlg%, "Cancel", 2, 92, 108, 56, 14, 0) code So far everything is conventional, but now we sub-class the necessary Windows messages: code format="bb4w" PROC_subclassdlg(dlg%, WM_CTLCOLORLISTBOX, FNctlcolorlistbox) PROC_subclassdlg(dlg%, WM_CTLCOLORSTATIC, FNctlcolorstatic) PROC_subclassdlg(dlg%, WM_CTLCOLORDLG, FNctlcolorbackgnd) PROC_subclassdlg(dlg%, WM_CTLCOLORBTN, FNctlcolorbackgnd) code Note that in this particular case the same function is used for both the CTLCOLORDLG and CTLCOLORBTN messages because in each case all we want to do is change the background colour. If you don't change the background colour of the buttons to match the dialogue box background an ugly border around the buttons will be visible.

Now we can display the dialogue box and initialise any controls that need it: code format="bb4w" PROC_showdialog(dlg%)

SYS "SendDlgItemMessage", !dlg%, 101, LB_ADDSTRING, 0, "Listbox item 0" SYS "SendDlgItemMessage", !dlg%, 101, LB_ADDSTRING, 0, "Listbox item 1" SYS "SendDlgItemMessage", !dlg%, 101, LB_ADDSTRING, 0, "Listbox item 2" SYS "SendDlgItemMessage", !dlg%, 101, LB_ADDSTRING, 0, "Listbox item 3" code Again, this is conventional.

Finally we run our main loop which, for the purposes of the exercise, does nothing useful: code format="bb4w" ON CLOSE PROC_closedialog(dlg%) : QUIT ON ERROR PROC_closedialog(dlg%) : SYS "MessageBox", @hwnd%, REPORT$, 0, 48 : QUIT ON SYS Click% = @wparam% AND &FFFF:RETURN

Click% = 0 REPEAT PROCwait(0) click% = 0 SWAP Click%,click% UNTIL click%=1 OR click%=2 OR !dlg%=0 PROC_closedialog(dlg%) END code Note that while subclassing is in effect you must avoid statements which may take a long time to execute, such as **GET**, **INKEY**, **INPUT** and **WAIT** (WAIT should be avoided even if the delay is short). You should also be careful not to use **SOUND** when the sound queue is already full. You can find suitable replacements for these functions in the [|NOWAIT] library.

We mustn't forget the vital routines that handle the subclassing: code format="bb4w" DEF FNctlcolorbackgnd(M%, W%, L%) PRIVATE B%     IF B%=0 SYS "CreateSolidBrush", &8080FF TO B%      =B%

DEF FNctlcolorlistbox(M%, W%, L%) PRIVATE B%     SYS "SetTextColor", W%, &00FFFF SYS "SetBkColor", W%, &336633 IF B%=0 SYS "CreateSolidBrush", &336633 TO B%     =B%

DEF FNctlcolorstatic(M%, W%, L%) PRIVATE B%     SYS "SetTextColor", W%, &FFFFFF SYS "SetBkColor", W%, &663366 IF B%=0 SYS "CreateSolidBrush", &663366 TO B%     =B% code The colours are here specified in the usual Windows hexadecimal format. For the listbox and the static control both the foreground and background colours are set. You should normally set the text background colour to be the same as the control's background colour.

Note that in the above example **all** controls of a particular type are affected. For example, if the WM_CTLCOLORSTATIC message is processed then all Static Controls will change colour. If you want to change the colour of only a subset of the controls of a given type, or give them different colours, then you can test the control's handle and act accordingly: code format="bb4w" DEF FNctlcolorstatic(M%, W%, L%) CASE L% OF       WHEN hStatic1%: PRIVATE B%         SYS "SetTextColor", W%, &FFFFFF SYS "SetBkColor", W%, &663366 IF B%=0 SYS "CreateSolidBrush", &663366 TO B%     ENDCASE =B% code Here **hStatic1%** is a global variable containing the handle of the particular Static Control whose colour needs to be changed. Because that handle isn't known until after you have shown the dialogue box, you will need to force the static control to redraw itself: code format="bb4w" SYS "GetDlgItem", !dlg%, 102 TO hStatic1% SYS "InvalidateRect", hStatic1%, 0, 0 code Also, remember to initialise **hStatic1%** (e.g. to zero) so you don't get a 'No such variable' error when **FNctlcolorstatic** is called for the first time.