Click here to Skip to main content
Click here to Skip to main content

Yet Another Vista-Style CommandLink

, 17 Jul 2008 CPOL
Rate this:
Please Sign up or sign in to vote.
An OS-independent CommandLink control.

Introduction

There are a lot of great Vista-style controls written in .NET. So this is yet one more, a command-link button. I am a fan of creating custom controls from scratch rather than depending on the OS drawing libraries. Thus, our CommandLink is drawn entirely with C# code, making it compatible with older versions of Windows.

Goal of the CommandLink

When I set out to write the control, I decided I wanted a CommandLink that had that Vista-style feel to it, but that did replicate default Command-Links exactly. So, I wrote a simple goal list of which elements to include:

  • Two different font-sizes within the same button, i.e., for the header text and the description text.
  • An image/icon off to the left that, unlike Vista’s CommandLink, can be aligned vertically to the top, middle, or bottom.
  • A blended flat-look for default and a gradient look for the mouse-hover.
  • Behave like a button.

Rounded Rectangles

Before drawing the actual control, first, we need a function to draw rounded rectangles. Since the button will need to outline and fill the round rectangle, it is easiest to write a function that returns a GraphicsPath:

private static GraphicsPath RoundedRect(int width, int  height, int radius)
{
      RectangleF  baseRect = new RectangleF(0, 0, width, height);
      float diameter =  radius * 2.0f;
      SizeF sizeF = new SizeF(diameter,  diameter);
      RectangleF arc = new  RectangleF(baseRect.Location, sizeF);
      GraphicsPath path  = new GraphicsPath();

      // top left arc
      path.AddArc(arc,  180, 90);
      // top right arc 
      arc.X =  baseRect.Right - diameter;
      path.AddArc(arc,  270, 90);
      // bottom right  arc 
      arc.Y =  baseRect.Bottom - diameter;
      path.AddArc(arc,  0, 90);
      // bottom left arc
      arc.X =  baseRect.Left;
      path.AddArc(arc,  90, 90);

      path.CloseFigure();
      return path;
}

Drawing Elements

So, let’s break down the visual elements of the CommandLink. The only two complicated states are the Hover and the Down state.

Hover

hover-1.png

The part that makes the button pop-up is a simple white gradient that goes three-fourths of the way down the button’s height. Due to the way the LinearGradientBrush works, sometimes if the gradient drawing area is 1 pixel too tall, the gradient will start over, making an ugly white line appear in the middle of the control. To fix that, we add the following line after the LinearGradientBrush is declared:

WrapMode.TileFlipX

hover-2.png

Next comes the outline. It is a 3 radius rounded rectangle generated with the above function. The color can either be SystemColors.ColorDark, or if you prefer a fixed color, (189, 189, 189) is nice.

hover-3.png

Then, we need an inside outline. This will be 2 radius instead, and is positioned at coordinates (1, 1). The color is a slightly transparent white with an alpha value of 245.

We put it together by drawing them in order, and we get something like this:

hover-all.png

Down

down-1.png

The background this time is solid, and once again, can either be a system color (ControlLight), or (234, 234, 234), if you like fixed colors better.

down-2.png

The outline will be the same as before, except the color will be darker, (167, 167, 167).

down-3.png

Lastly, the inner outline too will only change colors to a dark color (to give a shadow effect).

The final down state:

down-all.png

Highlight

The user should be able to tell when the CommandLink is selected, even if it was done with Tab. To highlight a selected CommandLink, we draw only an inner outline with the color (192, 233, 243), which is a light blue.

Foreground – Image and Text

The foreground elements will be the same for any state of the button. There is actually nothing special to drawing the text and the image. The description text will always be three sizes smaller than the header text. The font can be changed, but the default one is Tahoma. To center the combined sizes of the header and the description text, go with:

SizeF headerLayout = g.MeasureString(headerText, this.Font);
SizeF descriptLayout = g.MeasureString(descriptionText,  descriptFont);

//Merge the two sizes into one big rectangle
Rectangle totalRect = new Rectangle(0, 0, 
                                   (int)Math.Max(headerLayout.Width,  
                                   descriptLayout.Width), 
                                   (int)(headerLayout.Height + 
                                    descriptLayout.Height) - 4);

Also, this is the part of the control that will change if the control is disabled. The text need only change color. The image, however, needs to be converted to grayscale if it hasn’t been done already.

Events to Override

There are a few events that need to be overridden to get the CommandLink to behave like we want:

  • OnPaint – Handles all the drawing methods; depending on the CommandLink’s state, it executes the proper drawing routine.
  • OnClick – Since the user control is not inheriting the Button class, if we want to be able to specify a DialogResult, the behavior needs to manually be handled here.
  • OnKeyPress – If the CommandLink is selected with Tab and the user hits Enter, then do a PerformClick.
  • OnGotFocus/OnLostFocus – Refreshes the control to draw/remove the light-blue highlight.
  • OnMouse[…] – All the OnMouse events simply change a variable to reflect the current state of the CommandLink and makes the control redraw itself.
  • OnEnabledChanged – Sets the correct state and redraws the CommandLink. (Note, unfortunately, this event is not called during design-time, but it works fine at run-time.)

Conclusion and Improvements

At the end, we are left with a control that has the basic functionality of a button but the appearance of a Vista-style CommandLink. The implementation is meant to be compatible for older versions of Windows, so there are optional improvements that can be done. For example, there is no support for gradual fading like Vista controls do, and for now, the image has to be on the left side.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

VCSKicks

United States United States
Visit Visual C# Kicks for more free C#.NET articles, resources, and downloads at
http://www.vcskicks.com

Comments and Discussions

 
GeneralDescription Text PinmemberLateNightCoder12329-Aug-08 21:01 
GeneralRe: Description Text PinmemberVCKicks30-Aug-08 19:57 
GeneralRe: Description Text PinmemberVCKicks12-Oct-08 16:45 
GeneralNice looking Pinmembersotona18-Jul-08 22:48 
GeneralRe: Nice looking Pinmembersotona18-Jul-08 22:58 
GeneralRe: Nice looking PinmemberVCKicks19-Jul-08 11:45 
JokeRe: Nice looking Pinmembersotona21-Jul-08 2:38 
GeneralExcellent! PinmemberIndy200518-Jul-08 12:11 
GeneralI like it! PinmemberAndré Knuth18-Jul-08 5:07 
GeneralRe: I like it! PinmemberVCKicks18-Jul-08 9:01 
Generalawsome!!!! Pinmemberyassir hannoun18-Jul-08 2:55 
Generalcool! Pinmemberstrictly8618-Jul-08 0:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.141223.1 | Last Updated 17 Jul 2008
Article Copyright 2008 by VCSKicks
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid