Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

XP Button Style in VB.NET

0.00/5 (No votes)
21 Aug 2003 1  
One approach to acheiving XP-style gradient buttons in VB.NET

Introduction

I was surprised to find that Visual Studio 2003 .NET, the latest and greatest Windows application development tool, doesn't include an easy way to create XP-style buttons. Then I noticed that Office XP mostly uses the old-fashioned 3D buttons, suggesting that implementing the new style is not trivial. The new buttons are mostly found in Windows XP accessories and Internet Explorer 6. As a programmer, my first reaction was to jump in and reinvent the wheel, which reminds me that I need to start waiting for my second reaction before I waste any more time.

This article describes a way to create XP-like buttons by implementing the button's Paint method. It's an (interesting, I hope) exploration of GDI+ graphics. But you don't have to go to all this trouble, nor do you have to accept less than perfect results, to achieve XP buttons. You simply need to link your Windows Form application with the latest version (6.0) of ComCtl32.

The Real Way to Get XP Buttons

The way to get XP-style controls in your VB.NET application is to create a manifest file that accompanies your .exe file. The manifest specifies that your code depends upon ComCtl32.DLL. A manifest file is like a config file--it's XML and it has the same name as your app, but with ".manifest" appended. If your app is MyApp.exe, then you need to create MyApp.exe.manifest in the same directory.

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
processorArchitecture="x86"
name="MyApp"
type="win32"
/>
<description>My Great Solution</description>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="X86"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</assembly>

This information comes from an (apparently) old article (from when XP was called Whistler) called How to be ready for Windows XP's updated visuals, and from poking around the system. The only other magic you need is to set the FlatStyle of your buttons (and other controls that support FlatStyle) to "System" instead of the default "Standard." My only explanation for Office XP and other Microsoft applications not using the XP controls is that they were released before ComCtl 6.0, I suppose.

Background

Having decided that I would have to subclass Button somehow and intercept WinProc messages, I was pleased to discover that creating owner-drawn buttons in VB.NET is a simple matter of handling the button's Paint method.

I expected several things that were not true as it turned out. First, I thought that I would need to inform the button that it would be owner-drawn. Not so--apparently, the existence of a method that "Handles Button1.Paint" sets a private "owner-draw" field in the button to True. Second, I thought that my Paint handler would be called with explicit "state" instructions--inactive, focus, hover, pressed, and so on (like an owner-draw Tab control gets DrawItem events). Instead, the paint method receives the bare minimum--the button object, a Graphics environment and a clip rectangle--but it is called when the button's state changes.

Implementing the button shown here was not particularly difficult, thanks to GDI+ methods that now include gradient brushes and graphics paths. There is considerable room for improvement, and I welcome any and all comments or suggestions. The 2D drawing primitives can be further optimized, perhaps. I tried to use system colors as an attempt to be "theme-aware" but there are probably better ways to do that, too. In general, I didn't sweat the details too much--my pressed button indents more than the standard Windows XP button, for instance.

Using the code

To use this code in your application, paste the code (exclusive of the Form Designer generated code) into your form and add "Handles MyButton.Paint, MyOtherButton.Paint" to the paint method for each button you want to style.

That is, instead of:

    Private Sub PaintXPButton(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.PaintEventArgs) _
    Handles Button1.Paint

To style your btnOK and btnCancel buttons, you'd say:

    Private Sub PaintXPButton(ByVal sender As Object, _
        ByVal e As System.Windows.Forms.PaintEventArgs) _
    Handles btnOK.Paint, btnCancel.Paint

It's important that you change each button's FlatStyle property to Flat (the default is Standard). If you don't, the button won't light up when you mouse over it.

This code illustrates one set of visual behaviors, but hopefully it will inspire others to try more imaginative effects. There are only four states that matter to me--normal, pressed, hot (mouse over), and disabled, but your application may need more. The gradient makes the normal button appear somewhat convex. When the button is pressed, I change it to concave by inverting the gradient and adding a highlight rectangle. This is more exaggerated than the official XP button's behavior.

When the mouse is over the normal button (or the mouse has left the pressed button), I draw an orange rectangle as a nod toward what XP does. It's more of a "glow" effect, really, so I draw extra highlight lines around the rectangle. I think orange was chosen because it shows up against most background colors. Since there is no orange in the system color palette, I assume that XP always uses orange, so I do, too.

Known Bugs

This code ignores focus. The focus rect is never drawn. I don't miss it, but if you want to implement the focus rect, you'll have to figure out how to determine when the button has the focus and when it doesn't. Drawing the rect probably involves a hatched brush, but that's as much as I want to contemplate.

Likewise, the code ignores the button's TextAlign and image properties (Image, ImageAlign). In this code, text is centered on the button using a StringFormat object which calls left and right near and far (apparently in an attempt to be world-aware and to end the colonialist domination of left-to-right languages). I just didn't want to mess with converting TextAlign's TopRight into the StringFormat's Alignment(far) and LineAlignment(near). I wanted only centered text, so it becomes an exercise for the reader. Sorry.

I notice that the code assumes that the button's Form is its parent. If you are styling buttons in other containers (such as a GroupBox or Panel), change any Me. references in the code to the appropriate container.

Points of Interest

Round-cornered rectangles have disappeared, it seems, which is somewhat troublesome when the button you're trying to draw has eased corners. The new GraphicsPath seems to be the answer here--just draw line, arc, line, arc, etc. in the path, and GDI+ lets you fill and draw your roundrect just like Bill Atkinson or Mark Cutter (antediluvian references to the graphics pioneers who gave us roundrects in the first place, or at least the first that I ever saw). Again, I hope that the idea of using a path for your buttons may inspire you to create some wildly imaginative visual behaviors.

And another thing: I'm slightly embarrassed to say that converting the mouse position to button coordinates stumped me for several minutes. I looked for ScreenToClient(), the old VB 6 way of doing it, and I found nothing in the VS .NET help, MSDN, Code Project, or the web in general. When I couldn't find the name of the function that I knew was there, my first thrash was to subtract my form's TopLeft from the mouse position. Of course, that was off by the width of the form's frame and the height of the form's title bar. My face was turning purple at this point, so I don't remember how I found it, but the answer is:

ButtonContainer.PointToClient(mousePos)
'ButtonContainer might be the form (Me), or it might not

Jan Tielens' article, A simple XP/VS.NET style button control, may be of interest, as well, as he describes a method for dimming and brightening buttons--more prods toward imaginative visual behaviors.

History

  • August 6, 2003 - Updated with the correct method for styling XP controls
  • August 1, 2003 - Thrown together quickly in the single-digit hours to solve the "lack" of XP buttons

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here