Articles   Members Online: 3
-Article/Tip Search
-News Group Search over 21 Million news group articles.
Member Area
-Account Center
-Top 10 NEW!!
-Submit Article/Tip
-Forums Upgraded!!
-My Articles
-Edit Information
-Become a Member
-Why sign up!
-Chat Online!
-Indexes NEW!!
-Build your resume
-Find a job
-Post a job
-Resume Search
-Link to us
Visit Embarcadero
Embarcadero Community
Creating Themed Custom Controls Turn on/off line numbers in source code. Switch to Orginial background IDE or DSP color Comment or reply to this aritlce/tip for discussion. Bookmark this article to my favorite article(s). Print this article
Delphi 5.x
User Rating
No Votes
# Votes
DSP, Administrator
Reference URL:
			Author: Yorai Aminov

Creating Themed Custom Controls


I have to admit: visual styles are starting to annoy me. It's not that I don't like 
them - it's that every piece of visual code I write now has to take them into 

In this short article, I'll discuss some of the implications of supporting visual 
styles when developing custom controls in Delphi. You can read more about visual 
styles in my previous article "Visual Styles in Delphi", which deals with drawing 
system elements.
Borderline Personality 

In terms of visual design, every custom control can contain both standard and 
non-standard elements. Standard elements are the parts that make the control look 
like other visual elements in Windows, such as its border and scroll bars. 
Non-standard elements are what makes the control unique. In most Windows controls, 
this concept is implemented by separating the client and non-client areas of the 

Traditionally, the non-client area of a control is painted by Windows. This works 
well in most cases. A form's non-client area, for example, includes the form's 
border, its caption (and caption buttons), and scroll bars. By letting Windows take 
over this part, windows have a standard look throughout the system. Windows also 
provides some default non-client area handling for other controls. Unfortunately, 
the default processing does not fully support visual styles. The control border has 
to be drawn separately.

In ancient times (that is, before visual styles were inflicted on us), most 
controls had just three border styles: a sunken 3D border, a flat border, or no 
border at all. Many Delphi controls expose these styles by using the BorderStyle 
and Ctl3D properties. These styles were implemented internally by Windows, and were 
controlled by setting various bits in the call to CreateWindow or CreateWindowEx. 
The WS_BORDER style gave a control a flat border, while the WS_EX_CLIENTEDGE 
extended style gave it a sunken border.

Control borders in Windows XP are a little more complicated. Instead of a fixed set 
of styles, XP controls can have any sort of border. Borders can have a variety of 
colors, patterns, shapes, and levels of transparency. Different controls can have 
different border styles. For example, group boxes have round corners, while edit 
boxes have a rectangular border, but use a different color. Because there is no 
single standard for borders, the default non-client area painting code doesn't draw 
any of the new border styles. This means we have to do it ourselves.

Obviously, there's a catch. We want our control to look "right" when using visual 
styles, so we need a standard border. The problem is that there is no single border 
style. One solution offered by Microsoft is to borrow 
elements from other controls. That's the solution I'll use here.

TThemedCustomControl is a simple TCustomControl descendant. It handles the 
WM_NCPAINT message to draw a themed border:

1   procedure TThemedCustomControl.WMNCPaint(var message: TMessage);
2   var
3     R: TRect;
4     Details: TThemedElementDetails;
5     DC: HDC;
6     XEdge, YEdge: Integer;
7   begin
8     inherited;
9     if (ThemeServices.ThemesEnabled) and Ctl3D then
10    begin
11      R := Rect(0, 0, Width, Height);
12      DC := GetWindowDC(Handle);
13      XEdge := GetSystemMetrics(SM_CXEDGE);
14      YEdge := GetSystemMetrics(SM_CYEDGE);
15      ExcludeClipRect(DC, XEdge, YEdge, Width - XEdge, Height - YEdge);
16      try
17        Details := ThemeServices.GetElementDetails(teEditRoot);
18        ThemeServices.DrawParentBackground(Handle, DC, @Details, True);
19        ThemeServices.DrawElement(DC, Details, R);
20      finally
21        ReleaseDC(Handle, DC);
22      end;
23    end;
24  end;

Although the code is fairly simple, certain parts require explanation. Let's start 
at the top.

The procedure starts by invoking the default handler for the message. Although this 
causes the standard border to be drawn even if themes are supported, we need this 
in order to draw other non-client elements - specifically, scroll bars.

Once we've established that a themed border needs to be drawn, we get the control's 
device context. To make sure we only draw over the border area, we call 
ExcludeClipRect so that Windows clips everything else. The calls to 
GetSystemMetrics get the size of the standard border that was already painted. The 
border is usually 2 pixels wide, but it's safer to ask.

To draw our border, we need access to the theme data. This is where we decide what 
our border looks like. I've decided to use the same border as an edit box, but you 
can use any control you want. Simply replace teEditRoot with the appropriate value.

That's it. If you download the control, 
you'll see a little more code. That's just housekeeping code for handling the Ctl3D 

The code was written in Delphi 7, but should work with earlier versions. You will 
the ThemeServices class, though. You can download it from Mike Lischke's Delphi 
Gems site.

Component Download:

Vote: How useful do you find this Article/Tip?
Bad Excellent
1 2 3 4 5 6 7 8 9 10


Share this page
Download from Google

Copyright © Mendozi Enterprises LLC