Title: Make a PictureBox act like a button in C#
This program uses several images to make a PictureBox behave like a button. These images give you complete control over how the "button" looks as it is pressed and released.
A button should have these behaviors:
- When the mouse is not over the button and the button is not pressed, the button should display the first, up image.
- When the mouse moves over the button but is not pressed, the button should display the second, highlight image.
- When the mouse presses down on it, the button should display the third, down image.
- If the mouse is pressed over the button and then leaves the button, the button should display the up image.
- If the pressed mouse re-enters the button, it should re-display the down image.
- If the user releases a pressed mouse while over the button, the button should raise a Click event.
- If the user releases a pressed mouse while not over the button, the button should not raise any event.
First, at design time, I set the PictureBox control's Image property to the button up image. I also set its BackgroundColor to Transparent.
The button displays the following images, which are stored in application properties:
- ButtonUp - The up image.
- ButtonDown - The down image.
- ButtonMouseOver - The highlight image.
- ButtonMask - A mask that is black for pixels that are non-transparent in the ButtonUp and ButtonDown images.
At design time, I stored the up, down, mouse over, and mask images in application properties called ButtonUp, ButtonDown, ButtonMouseOver, and ButtonMask.
The following code executes when the user presses the mouse down over the PictureBox.
// Keep track of whether the button is pressed.
private bool ClickMeButtonIsPressed = false;
// Display the button down image.
private void picClickMe_MouseDown(object sender, MouseEventArgs e)
{
// See if the mouse is over the masked area.
if (MouseIsOverButton(e.Location))
{
ClickMeButtonIsPressed = true;
picClickMe.Image = Properties.Resources.ButtonDown;
}
}
The ClickMeButtonIsPressed variable keeps track of whether the button is pressed. (Initially it is not pressed.)
When the mouse is pressed down over the button, the MouseDown event handler executes. It calls the MouseIsOverButton method described shortly to see if the mouse is over a non-transparent part of the button. (For example, if the button is a circle, the PictureBox will receive this event even if the user clicks inside the PictureBox control's rectangular area but outside of the circle.)
If the mouse is over the button's non-transparent area, the code sets ClickMeButtonIsPressed to true and displays the down button image.
When the user releases the mouse, the following code executes.
// Display the button up image.
private void picClickMe_MouseUp(object sender, MouseEventArgs e)
{
ClickMeButtonIsPressed = false;
picClickMe.Image = Properties.Resources.ButtonUp;
}
This code simply sets ClickMeButtonIsPressed to false and displays the up image.
The code doesn't actually need to do anything to raise any kind of click event. Instead the program can simply catch the PictureBox control's MouseClick event as shown in the following code.
// The button has been clicked.
private void picClickMe_MouseClick(object sender, MouseEventArgs e)
{
// See if the mouse is over the masked area.
if (MouseIsOverButton(e.Location))
{
MessageBox.Show("Clicked");
}
}
This event occurs when the user presses the mouse down over the control and then releases the mouse while over the control. The only thing the code needs to add is a call to MouseIsOverButton to see whether the mouse is over a non-transparent part of the control.
The following code shows the MouseIsOverButton method.
// Return true if the mouse is over the button's masked area.
private bool MouseIsOverButton(Point location)
{
// Make sure the location is over the image.
if (location.X < 0) return false;
if (location.Y < 0) return false;
if (location.X >= Properties.Resources.ButtonMask.Width) return false;
if (location.Y >= Properties.Resources.ButtonMask.Height) return false;
// See if the mask pixel at this position is black.
Color color =
Properties.Resources.ButtonMask.GetPixel(
location.X, location.Y);
return ((color.A == 255) &&
(color.R == 0) &&
(color.G == 0) &&
(color.B == 0));
}
This method first determines whether the mouse is over the control and, if it is not, it returns false.
If the mouse is over the control, the method examines the image stored in the ButtonMask property. It gets the pixel at the mouse's position and compares it to black. If the pixel is black, then the method returns true to indicate that the mouse is over the button. If the pixel is not black, the method returns false.
The final piece of the program is the following MouseMove event handler.
// If the button is pressed, display the appropriate image.
private void picClickMe_MouseMove(object sender, MouseEventArgs e)
{
// The picture the button should have.
Image desired_picture = Properties.Resources.ButtonUp;
// See if the mouse is over the button's masked area.
if (MouseIsOverButton(e.Location))
{
// The mouse is over the masked area.
// See if the mouse is pressed.
if (ClickMeButtonIsPressed)
desired_picture = Properties.Resources.ButtonDown;
else
desired_picture = Properties.Resources.ButtonMouseOver;
}
else
{
// The mouse is not over the masked area.
// The button should be in the up position.
desired_picture = Properties.Resources.ButtonUp;
}
// See if we need to change the button image.
if (picClickMe.Image != desired_picture)
picClickMe.Image = desired_picture;
}
The variable desired_picture keeps track of the picture that the button should display. Initially, the method assumes the button should display the up image.
If the mouse is over the button, then code checks ClickMeButtonIsPressed to see if the button is currently pressed. If it is, then the program should display the down image.
If the mouse is over the button but ClickMeButtonIsPressed indicates that the mouse is not pressed, then the program should display the highlight image.
If the mouse is not over the button, the program should display the up image.
After deciding which image it should display, the program compares that image to the one currently displayed. If the desired image is not displayed, the code displays it.
That's all there is to this example. It's fairly simple but produces a nice customized effect.
Note that you can create much more elaborate customizations if you use WPF and XAML. In those programs, you can use actual buttons and customize them for an application consistently, although not exactly easily.
Note also that this example only handles button-like behavior when the mouse interacts with the PictureBox control. It doesn't mess with other behaviors of true buttons such as handling focus when the user tabs onto the button.
Download the example to experiment with it and to see additional details.
|