Vista Style Command link
Have you obeserved the new taskdialog in vista, (May I am the last one to write about this), but sure looks great, and it is easy to implement,

thre are lots of versions out there but like this the most, so here is the code
using System; using System.ComponentModel; using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; using System.Windows.Forms; namespace CommandLink { [ToolboxBitmap(typeof(Button))] [DefaultEvent("Click")] public partial class CommandLink : Button { public CommandLink() { InitializeComponent(); this.DoubleBuffered = true; //Smooth redrawing this.ImageVerticalAlign = VerticalAlign.Middle; //Set Default } #region Fields--------------------------------- public enum State { Normal, Hover, Pushed, Disabled } public enum VerticalAlign { Top, Middle, Bottom } private State state = State.Normal; private int offset = 0; private string headerText = "Header Text"; private string descriptionText = "Description"; private Bitmap image; private Bitmap grayImage; private Size imageSize = new Size(24, 24); private VerticalAlign imageAlign = VerticalAlign.Top; private VerticalAlign textAlign = VerticalAlign.Middle; private Font descriptFont; #endregion #region Properties---------------------------- /// /// Gets or sets the string associated with the Header text of the control. /// [Category("Command Appearance"), Browsable(true), DefaultValue("Header Text")] public string HeaderText { get { return headerText; } set { headerText = value; this.Refresh(); } } /// /// Gets or sets the string associated with the Description text of the control. /// [Category("Command Appearance"), Browsable(true), DefaultValue("Description")] public string DescriptionText { get { return descriptionText; } set { descriptionText = value; this.Refresh(); } } /// /// Gets or sets the left-hand Bitmap object of the control. /// [Category("Command Appearance"), Browsable(true), DefaultValue(null)] public Bitmap Image { get { return image; } set { //Clean up if (image != null) image.Dispose(); if (grayImage != null) grayImage.Dispose(); image = value; if (image != null) grayImage = GetGrayscale(image); //generate image for disabled state else grayImage = null; this.Refresh(); } } /// /// Gets or sets the target size of the Image object. /// [Category("Command Appearance"), Browsable(true), DefaultValue(typeof(Size), "24,24")] public Size ImageScalingSize { get { return imageSize; } set { imageSize = value; this.Refresh(); } } /// /// Gets or sets the alignment of the Image object along the vertical axis. /// [Category("Command Appearance"), Browsable(true), DefaultValue(VerticalAlign.Top)] public VerticalAlign ImageVerticalAlign { get { return imageAlign; } set { imageAlign = value; this.Refresh(); } } /// /// Gets or sets the alignment of the displayed text along the vertical axis. /// [Category("Command Appearance"), Browsable(true), DefaultValue(VerticalAlign.Middle)] public VerticalAlign TextVerticalAlign { get { return textAlign; } set { textAlign = value; this.Refresh(); } } /// /// Gets or sets the Font object that will be applied to the header and description strings. /// [Category("Command Appearance")] public override Font Font { get { return base.Font; } set { base.Font = value; //Clean up if (descriptFont != null) descriptFont.Dispose(); //Update the description font, which is the same just 3 sizes smaller descriptFont = new Font(this.Font.FontFamily, this.Font.Size - 3); } } /// /// Gets a string representation of the header and description strings. /// [Browsable(false)] public override string Text { get { return this.HeaderText + ": " + this.DescriptionText; } } #endregion #region Events----------------------------------- protected override void OnPaint(PaintEventArgs e) { DrawBackground(e.Graphics); if (this.Focused && state == State.Normal) DrawHighlight(e.Graphics); switch (state) { case State.Normal: DrawNormalState(e.Graphics); break; case State.Hover: DrawHoverState(e.Graphics); break; case State.Pushed: DrawPushedState(e.Graphics); break; case State.Disabled: DrawNormalState(e.Graphics); //DrawForeground takes care of drawing the disabled state break; default: break; } } protected override void OnKeyPress(KeyPressEventArgs e) { if (e.KeyChar == Convert.ToChar(Keys.Enter)) this.PerformClick(); base.OnKeyPress(e); } protected override void OnGotFocus(EventArgs e) { this.Refresh(); base.OnGotFocus(e); } protected override void OnLostFocus(EventArgs e) { this.Refresh(); base.OnLostFocus(e); } protected override void OnMouseEnter(EventArgs e) { if (this.Enabled) state = State.Hover; this.Refresh(); base.OnMouseEnter(e); } protected override void OnMouseLeave(EventArgs e) { if (this.Enabled) state = State.Normal; this.Refresh(); base.OnMouseLeave(e); } protected override void OnMouseDown(MouseEventArgs e) { if (this.Enabled) state = State.Pushed; this.Refresh(); base.OnMouseDown(e); } protected override void OnMouseUp(MouseEventArgs e) { if (this.Enabled) { if (this.RectangleToScreen(this.ClientRectangle).Contains(Cursor.Position)) state = State.Hover; else state = State.Normal; } this.Refresh(); base.OnMouseUp(e); } protected override void OnEnabledChanged(EventArgs e) { if (this.Enabled) state = State.Normal; else state = State.Disabled; this.Refresh(); base.OnEnabledChanged(e); } protected override void Dispose(bool disposing) { if (disposing && components != null) { image.Dispose(); grayImage.Dispose(); descriptFont.Dispose(); components.Dispose(); } base.Dispose(disposing); } #endregion #region Drawing Methods------------------------- //Draws the light-blue rectangle around the button when it is focused (by Tab for example) private void DrawHighlight(Graphics g) { //The outline is drawn inside the button GraphicsPath innerRegion = RoundedRect(this.Width - 3, this.Height - 3, 3); //----Shift the inner region inwards Matrix translate = new Matrix(); translate.Translate(1, 1); innerRegion.Transform(translate); translate.Dispose(); //----- Pen inlinePen = new Pen(Color.FromArgb(192, 233, 243)); //Light-blue g.SmoothingMode = SmoothingMode.AntiAlias; g.DrawPath(inlinePen, innerRegion); //Clean-up inlinePen.Dispose(); innerRegion.Dispose(); } //Draws the button when the mouse is over it private void DrawHoverState(Graphics g) { GraphicsPath outerRegion = RoundedRect(this.Width - 1, this.Height - 1, 3); GraphicsPath innerRegion = RoundedRect(this.Width - 3, this.Height - 3, 2); //----Shift the inner region inwards Matrix translate = new Matrix(); translate.Translate(1, 1); innerRegion.Transform(translate); translate.Dispose(); //----- Rectangle backgroundRect = new Rectangle(1, 1, this.Width - 2, (int)(this.Height * 0.75f) - 2); Pen outlinePen = new Pen(Color.FromArgb(189, 189, 189)); //SystemColors.ControlDark Pen inlinePen = new Pen(Color.FromArgb(245, 255, 255, 255)); //Slightly transparent white //Gradient brush for the background, goes from white to transparent 75% of the way down LinearGradientBrush backBrush = new LinearGradientBrush(new Point(0, 0), new Point(0, backgroundRect.Height), Color.White, Color.Transparent); backBrush.WrapMode = WrapMode.TileFlipX; //keeps the gradient smooth incase of the glitch where there's an extra gradient line g.SmoothingMode = SmoothingMode.AntiAlias; g.FillRectangle(backBrush, backgroundRect); g.DrawPath(inlinePen, innerRegion); g.DrawPath(outlinePen, outerRegion); //Text/Image offset = 0; //Text/Image doesn't move DrawForeground(g); //Clean up outlinePen.Dispose(); inlinePen.Dispose(); outerRegion.Dispose(); innerRegion.Dispose(); } //Draws the button when it's clicked down private void DrawPushedState(Graphics g) { GraphicsPath outerRegion = RoundedRect(this.Width - 1, this.Height - 1, 3); GraphicsPath innerRegion = RoundedRect(this.Width - 3, this.Height - 3, 2); //----Shift the inner region inwards Matrix translate = new Matrix(); translate.Translate(1, 1); innerRegion.Transform(translate); translate.Dispose(); //----- Rectangle backgroundRect = new Rectangle(1, 1, this.Width - 3, this.Height - 3); Pen outlinePen = new Pen(Color.FromArgb(167, 167, 167)); //Outline is darker than normal Pen inlinePen = new Pen(Color.FromArgb(227, 227, 227)); //Darker white SolidBrush backBrush = new SolidBrush(Color.FromArgb(234, 234, 234)); //SystemColors.ControlLight g.SmoothingMode = SmoothingMode.AntiAlias; g.FillRectangle(backBrush, backgroundRect); g.DrawPath(inlinePen, innerRegion); g.DrawPath(outlinePen, outerRegion); //Text/Image offset = 1; //moves image inwards 1 pixel (x and y) to create the illusion that the button was pushed DrawForeground(g); //Clean up outlinePen.Dispose(); inlinePen.Dispose(); outerRegion.Dispose(); innerRegion.Dispose(); } //Draws the button in it's regular state private void DrawNormalState(Graphics g) { //Nothing needs to be drawn but the text and image //Text/Image offset = 0; //Text/Image doesn't move DrawForeground(g); } //Draws Text and Image private void DrawForeground(Graphics g) { //Make sure drawing is of good quality g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit; g.PixelOffsetMode = PixelOffsetMode.HighQuality; //Coordinates------------------------------- int imageLeft = 9; int imageTop = 0; int textLeft = 20; if (image != null) textLeft = imageLeft + imageSize.Width + 5; //adjust the text left coordinate to accomodate the image // //Text Layout------------------------------- string wrappedDescriptText = WordWrap(descriptionText, descriptFont, this.Width - (textLeft + offset) - 5); //Gets the width/height of the text once it's drawn out SizeF headerLayout = g.MeasureString(headerText, this.Font); SizeF descriptLayout = g.MeasureString(wrappedDescriptText, 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); //Align the text rectangle totalRect.X = textLeft; switch (textAlign) { case VerticalAlign.Top: totalRect.Y = 4; break; case VerticalAlign.Middle: totalRect.Y = (this.Height / 2) - (totalRect.Height / 2); break; case VerticalAlign.Bottom: totalRect.Y = this.Height - totalRect.Height; break; default: break; } //--------------------------------------------------- //Align the top of the image--------------------- if (image != null) { switch (imageAlign) { case VerticalAlign.Top: imageTop = 4; break; case VerticalAlign.Middle: imageTop = (imageSize.Height / 2); break; case VerticalAlign.Bottom: imageTop = this.Height - imageSize.Height; break; default: break; } } //----------------------------------------------- //Brushes-------------------------------- // Determine text color depending on whether the control is enabled or not Color textColor = this.ForeColor; if (!this.Enabled) textColor = SystemColors.GrayText; SolidBrush textBrush = new SolidBrush(textColor); //------------------------------------------ g.DrawString(headerText, this.Font, textBrush, totalRect.Left + offset, totalRect.Top + offset); g.DrawString(wrappedDescriptText, descriptFont, textBrush, totalRect.Left + 1 + offset, totalRect.Bottom - (int)descriptLayout.Height + offset); //Note: the + 1 in "totalRect.Left + 1 + offset" compensates for GDI+ inconsistency if (image != null) { if (this.Enabled) g.DrawImage(image, new Rectangle(imageLeft + offset, imageTop + offset, imageSize.Width, imageSize.Height)); else { //make sure there is a gray-image if (grayImage == null) grayImage = GetGrayscale(image); //generate grayscale now g.DrawImage(grayImage, new Rectangle(imageLeft + offset, imageTop + offset, imageSize.Width, imageSize.Height)); } } //Clean-up textBrush.Dispose(); } //Draws the solid background of the control private void DrawBackground(Graphics g) { SolidBrush backBrush = new SolidBrush(this.BackColor); g.FillRectangle(backBrush, this.DisplayRectangle); backBrush.Dispose(); } #endregion #region Helper Methods-------------------------- 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; } private static Bitmap GetGrayscale(Image original) { //Set up the drawing surface Bitmap grayscale = new Bitmap(original.Width, original.Height); Graphics g = Graphics.FromImage(grayscale); //Grayscale Color Matrix ColorMatrix colorMatrix = new ColorMatrix(new float[][] { new float[] {0.3f, 0.3f, 0.3f, 0, 0}, new float[] {0.59f, 0.59f, 0.59f, 0, 0}, new float[] {0.11f, 0.11f, 0.11f, 0, 0}, new float[] {0, 0, 0, 1, 0}, new float[] {0, 0, 0, 0, 1} }); //Create attributes ImageAttributes att = new ImageAttributes(); att.SetColorMatrix(colorMatrix); //Draw the image with the new attributes g.DrawImage(original, new Rectangle(0, 0, original.Width, original.Height), 0, 0, original.Width, original.Height, GraphicsUnit.Pixel, att); //Clean up g.Dispose(); return grayscale; } private static string WordWrap(string originalString, Font font, int targetWidth) { string[] words = originalString.Split(' '); string wrappedString = words[0]; //Add one word at a time, making sure it doesn't go over for (int i = 1; i < words.Length; i++) { //Use TextRenderer since it has a static measure function if (TextRenderer.MeasureText(wrappedString + " " + words[i], font).Width <= targetWidth) wrappedString += " " + words[i]; //next word fits on the same line else wrappedString += Environment.NewLine + words[i]; //start it on the next line } return wrappedString; } public void PerformClick() { this.OnClick(null); } #endregion } }
I will also post for the complete task dialog in my next post.
* I did not write this code, credit to whomever who did this