Contents
- Introduction
- Background
- Where to draw the border
- The implementation in C#
- Filter some Windows messages
- Find out if you should render with XP styles
- Get the device context of the window frame
- Draw the border using uxTheme.dll
- Points of interests
- History
1. Introduction
In this article, I will show you how to apply XP-styles to a RichTextBox
. The only thing I do here is to derive from the RichTextBox
and add support for XP-themed border style. The control is able to run on .NET 1.1 and .NET 2.0.
2. Background
Visual Styles are not fully supported in NET 1.1. There is a bug in the Application.EnableVisualStyles
method, and some controls do not render the XP-styles correctly. Here is a good overview about what is going wrong in NET 1.1: VisualStyles. But now we have NET 2.0 and all these bugs are fixed, and nearly all controls render with XP-styles. The only exception is the RichTextBox
control. So I decided to write this article.
3. Where to draw the border
Normally, in NET, all paintings are done in the OnPaint
or OnPaintBackground
methods of a control. The problem is, inside these methods, you always draw to the client area (ClientRectangle
) of a control. However, the borders are drawn outside this area in the window frame that should be drawn during the WM_NCPAINT
message. For a better understanding, look at this illustration...
When we customize the window frame, we must filter some messages and process them.
WM_NCPAINT
message tells us that the border should be redrawn.
WM_NCCALCSIZE
message tells us that the client area should be calculated.
protected override void WndProc(ref Message m)
{
switch(m.Msg)
{
case NativeMethods.WM_NCPAINT:
WmNcpaint(ref m);
break;
case NativeMethods.WM_NCCALCSIZE:
WmNccalcsize(ref m);
break;
default:
base.WndProc (ref m);
break;
}
}
To find out if we should render an application with visual styles, is not an easy task. There are three things to consider.
- Check if the OS supports XP-styles. Windows XP or above supports theming.
- Check if XP-styles are activated in the current environment.
- Check if XP-styles are enabled for the current application.
After Googling around in the web, I found a method that does the job: IsVisualStylesEnabled Revisited.
To draw in the window frame, we need some Win32-APIs to get the corresponding device context (DC).
GetWindowDC
to get the device context of the window frame.
GetWindowRect
to get the rectangle of the window frame.
ExcludeClipRect
to exclude the client area of the window frame.
ReleaseDC
to release the DC.
Here are the declarations in C#:
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern int ReleaseDC(IntPtr hWnd, IntPtr hDC);
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
[DllImport("gdi32.dll")]
public static extern int ExcludeClipRect(IntPtr hdc,
int nLeftRect, int nTopRect, int nRightRect, int nBottomRect);
Drawing visual styles is provided through the uxTheme.dll. We only need a few functions to draw the border.
OpenThemeData
to get a theme handle.
GetThemeBackgroundContentRect
to retrieve the size of the border.
DrawThemeBackground
draws the border.
CloseThemeData
releases the theme handle.
Here are the declarations in C#.
[DllImport("uxtheme.dll", ExactSpelling=true, CharSet=CharSet.Unicode)]
public static extern IntPtr
OpenThemeData(IntPtr hWnd, String classList);
[DllImport("uxtheme", ExactSpelling=true)]
public extern static Int32 GetThemeBackgroundContentRect(IntPtr hTheme,
IntPtr hdc, int iPartId, int iStateId,
ref RECT pBoundingRect, out RECT pContentRect);
[DllImport("uxtheme", ExactSpelling=true)]
public extern static Int32 DrawThemeBackground(IntPtr hTheme,
IntPtr hdc, int iPartId, int iStateId,
ref RECT pRect, IntPtr pClipRect);
[DllImport("uxtheme.dll", ExactSpelling=true)]
public extern static Int32 CloseThemeData(IntPtr hTheme);
The .NET Framework 2.0 contains new classes for drawing visual styles. You can find them in the System.Windows.Forms.VisualStyles
namespace. Also a method to check, if visual styles are enabled, is available in the new framework. Look for the Application.RenderWithVisualStyles
property. However, I had not made two versions, because I think having one version for both is less to do on modifications and thus the better solution.
- 11-10-2006 - Bug fix: Themed border disappears after
MultiLine
is set to false
.
- 17-07-2006 - Bug fix: Problem when mixing NET Framework versions.
- 07-04-2006 - First release.