Welcome to MSDN Blogs Sign in | Join | Help

News

  • Welcome to my Microsoft weblog. I'm a Lead Software Design Engineer in Test with the XNA Communities Team. Hopefully the posts in this blog will be insightful to into XNA, but also have other fun tidbits about things at Microsoft.
     
    Notice
    This posting is provided "AS IS" with no warranties, and confers no rights. Use of any included script samples are subject to the terms specified at http://www.microsoft.com/ info/cpyright.htm
Showing images on XP-styled buttons.

The other day, I decided to enable visual styles on one of my winform apps and found out that doing so breaks buttons with images. To get around this, I had to write a control which would inherit from the System.Windows.Forms.Button class and then handle the WM_PAINT message and draw the image myself. Below is the code I used.

/*
 * Showing images on XP-styled buttons.
 *
 * Author: Andrew Ma

 * http://blogs.msdn.com/ajma/archive/2005/01/21/358356.aspx
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted.
 *
 * This posting is provided "AS IS" with no warranties, and confers no rights.
 * Use of included code samples are subject to the terms specified at
http://www.microsoft.com/info/cpyright.htm
 */

using System;
using System.Drawing;
using System.Windows.Forms;

namespace ajma
{
 /// <summary>
 /// Represents a Windows button control that will use Visual Styles by default. This button will also draw images if specified.
 /// </summary>
 public class Button : System.Windows.Forms.Button
 {
  /// <summary>
  /// WM_PAINT message
  /// </summary>
  const int WM_PAINT = 0xF;

  /// <summary>
  /// Initializes a new instance of the Button class.
  /// </summary>
  public Button()
  {
   FlatStyle = FlatStyle.System;
  }

  /// <summary>
  /// Overridden. See <see cref="Control.WndProc"/>.
  /// </summary>
  /// <param name="m">The Windows <see cref="Message"/> to process. </param>
  protected override void WndProc(ref Message m)
  {
   // let base.WndProc go first
   base.WndProc (ref m);

   // if paiting (and there is a message), draw image on top of button
   if(m.Msg == WM_PAINT && this.Image != null)
   {
    Graphics g = Graphics.FromHwnd(this.Handle);
    switch(this.ImageAlign)
    {
     case ContentAlignment.BottomCenter:
      g.DrawImage(this.Image, centerX(), bottom());
      break;
     case ContentAlignment.BottomLeft:
      g.DrawImage(this.Image, left(), bottom());
      break;
     case ContentAlignment.BottomRight:
      g.DrawImage(this.Image, right(), bottom());
      break;
     case ContentAlignment.MiddleCenter:
      g.DrawImage(this.Image, centerX(), centerY());
      break;
     case ContentAlignment.MiddleLeft:
      g.DrawImage(this.Image, left(), centerY());
      break;
     case ContentAlignment.MiddleRight:
      g.DrawImage(this.Image, right(), centerY());
      break;
     case ContentAlignment.TopCenter:
      g.DrawImage(this.Image, centerX(), top());
      break;
     case ContentAlignment.TopLeft:
      g.DrawImage(this.Image, left(), top());
      break;
     case ContentAlignment.TopRight:
      g.DrawImage(this.Image, right(), top());
      break;
    }
    g.Dispose();
   }
  }

  #region Returns the X/Y points for the image to be drawn
  private int centerX()
  {
   return (this.Width - this.Image.Width) / 2;
  }

  private int centerY()
  {
   return (this.Height - this.Image.Height) / 2;
  }

  private int top()
  {
   return 4;
  }

  private int left()
  {
   return 4;
  }

  private int right()
  {
   return this.Width - this.Image.Width - left();
  }

  private int bottom()
  {
   return this.Height - this.Image.Width - top();
  }
  #endregion
 }
}

This posting is provided "AS IS" with no warranties, and confers no rights.

Posted: Friday, January 21, 2005 12:33 PM by ajma
Filed under:

Comments

G. Man said:

Thanks, but are you sure it has no bugs? I have found that trying to customize WM_PAINT by first letting the default handler paint and then painting on top of that, causes some graphical glitches. I remember in MFC it always told you do not call the base class paint...

# January 21, 2005 5:43 PM

Andrew said:

So I'm no expert on WM_PAINT and stuff like that. The reason I call the base method is so that it will paint the button first and draw the text. If not, then I would have to draw this myself. The buttonalso varies from theme to theme, so I'd rather let the base method do it. If I run into problems with it, I'll let you know.
One thing I've noticed is that the image will draw on top of the text. In a normal button, the text would draw over the image.
# January 21, 2005 5:59 PM

Dean Harding said:

Those images come from an ImageList, right? And I'll bet your Main method looks like this:

static void Main()
{
Application.EnableVisualStyles();
Application.Run(new MainForm());
}

right? The problem is that the handles for image lists are created when the form is constructor, but visual styles are only enabled once the message loop is created. Try this"

static void Main()
{
Application.EnableVisualStyles();
Application.DoEvents();
Application.Run(new MainForm());
}

And see how it goes...
# January 23, 2005 6:04 PM

Andrew said:

Yup, I do call DoEvents() as well. It still won't draw the images on the buttons.

So the only shortcoming I've found so far is that it flickers a bit when re-drawing.
Oh, I also had to handle when the button is disabled to draw a grayscale version of the button.
# January 23, 2005 6:08 PM

Jan Schreuder on .Net said:

Last week, I needed to add an image to a button. Not a problem of course, unless you need to switch to...
# August 1, 2005 4:58 AM

Jan Schreuder on .Net said:

Last week, I needed to add an image to a button. Not a problem of course, unless you need to switch to...
# August 1, 2005 5:03 AM

Jan Schreuder on .Net said:

Last week, I needed to add an image to a button. Not a problem of course, unless you need to switch to...
# August 1, 2005 5:05 AM

Jan Schreuder on .Net said:

Last week, I needed to add an image to a button. Not a problem of course, unless you need to switch to...
# August 1, 2005 5:06 AM

Jan Schreuder on .Net said:

Last week, I needed to add an image to a button. Not a problem of course, unless you need to switch to...
# August 1, 2005 5:09 AM
New Comments to this post are disabled
Page view tracker