一个简单的位图按钮实现






4.63/5 (15投票s)
2003年7月3日
4分钟阅读

174321

2487
一个小的类,为WinForms提供纯位图按钮功能。
引言
经过一番小小的搜索,似乎没有人为Code Project(或其他任何地方)写过BitmapButton。WiB出色的XP风格按钮控件解决了带图标的XP风格按钮的问题。但是,如果你想完全控制按钮的外观,无论操作系统、清单、主题等,都使用位图呢?这个控件提供了这个能力。请注意,这个类非常基础——我并没有努力使其在窗体设计器中可用。如果有人想更新代码并具备此能力,我非常乐意让他们成为合著者。
使用BitmapButton
指定按钮状态图像
BitmapButton
类使用一个单一的位图,该位图包含一个或多个图像,每个图像代表按钮的五种状态之一。图像必须从左到右连续放置。例如:
代表一个有五种状态的按钮的位图
- up
- down
- 获得焦点
- 鼠标悬停
- disabled
初始化BitmapButton
按钮以通常的方式初始化,并添加一行代码来指定位图文件:
bitmapButton=new BitmapButton(); bitmapButton.Location=new Point(232, 32); bitmapButton.Size=new Size(32, 32); bitmapButton.TabIndex=0; bitmapButton.Text="&Down"; bitmapButton.Image=new Bitmap("downArrow.bmp");
三个重要提示
Text
字段仅用于指定按钮的键盘快捷方式;- 按钮的宽度应与位图中单个图像的宽度相同(所有图像的宽度和高度应相同);
- 按钮的高度应与位图的高度相同。
就这样。(如果你忘记了图像,程序将抛出异常)。
幕后
按钮状态
考虑到键盘和鼠标的双用户界面,按钮状态是一件复杂的事情。例如,用户可能将鼠标悬停在按钮上,但正在使用Tab键在控件之间导航。完整的状态图如下所示:
此图代表了在使用鼠标和键盘时必须考虑的状态转换。这相当复杂!
使用键盘的按钮状态
如果使用键盘,状态图会更简单(为了清晰起见,已移除“禁用”状态):
使用鼠标的按钮状态
如果使用鼠标,状态图中有一些缺失的状态转换(同样,为了清晰起见,已移除“禁用”状态):
事件处理程序
为了管理可能发生的所有事件并正确地在不同状态之间转换,必须定义几个事件处理程序:
Paint+=new PaintEventHandler(BitmapButton_Paint); MouseDown+=new MouseEventHandler(BitmapButton_MouseDown); MouseUp+=new MouseEventHandler(BitmapButton_MouseUp); GotFocus+=new EventHandler(BitmapButton_GotFocus); LostFocus+=new EventHandler(BitmapButton_LostFocus); MouseEnter+=new EventHandler(BitmapButton_MouseEnter); MouseLeave+=new EventHandler(BitmapButton_MouseLeave); KeyDown+=new KeyEventHandler(BitmapButton_KeyDown); KeyUp+=new KeyEventHandler(BitmapButton_KeyUp); EnabledChanged+=new EventHandler(BitmapButton_EnabledChanged);
Paint | 负责以其各种状态绘制按钮。 |
MouseDown | 转换到“已单击”状态。 |
MouseUp | 转换到“获得焦点”状态。 |
GotFocus | 转换到“获得焦点”状态(例如,通过键盘获得焦点)。 |
LostFocus | 转换到“按钮释放”或“鼠标悬停”状态。 |
MouseEnter | 如果处于“按钮释放”状态,则转换为“鼠标悬停”状态。 |
MouseLeave | 如果处于“鼠标悬停”状态,则转换为“按钮释放”状态。 |
KeyDown | 当用户在按钮获得焦点时按下空格键时,转换到“已单击”状态。 |
KeyUp | 当用户在按钮获得焦点时释放空格键时,转换到“获得焦点”状态。 |
EnabledChanged | 当按钮的Enabled状态改变时,转换为“禁用”状态或返回“按钮释放”状态。 |
绘制图像
虽然不需要双缓冲,因为整个状态图像都绘制在按钮表面上,但还是实现了它,以防控件被增强以包含额外的绘图效果。双缓冲通过SetStyle
方法指定,该方法是一个受保护的成员,因为有人决定,由于控件将拥有自己的Paint
处理程序,因此不应在不继承控件的情况下进行设置。不过,Paint
事件处理程序可以在一个完全独立的类中实现!
SetStyle(ControlStyles.UserPaint |
ControlStyles.AllPaintingInWmPaint |
ControlStyles.DoubleBuffer, true);
按钮图像显示在Paint
事件处理程序中。根据状态,它确定所需图像的索引。位图不需要包含所有图像状态,只要提供的图像状态是连续的(也就是说,你不能拥有按钮按下、按钮释放和禁用图像状态,而焦点和鼠标悬停状态为空——如果你想要禁用状态的图像,那么你也必须提供所有其他状态的图像)。private void BitmapButton_Paint(object sender, PaintEventArgs e) { Graphics gr=e.Graphics; int indexWidth=Size.Width*(int)imgState; if (Image.Width > indexWidth) { gr.DrawImage(Image, 0, 0, new Rectangle(new Point(indexWidth, 0), Size), GraphicsUnit.Pixel); } else { gr.DrawImage(Image, 0, 0, new Rectangle(new Point(0, 0), new Size(Size.Width, Size.Height)), GraphicsUnit.Pixel); } }
结论
哇,这可能是我近年来写过的最短的文章之一!位图按钮可以比我在这里实现的复杂得多。例如,如果你的图像没有边框,而你希望控件自己生成3D风格的边框怎么办?还有对不同按钮状态大小和透明度的考虑,例如。希望本文能为读者提供定制位图控件以满足其需求的依据。