为窗体和按钮创建位图区域






4.97/5 (65投票s)
2004年2月9日
2分钟阅读

335668

12004
本文档描述了如何为 WinForms 和按钮创建位图区域。
引言
本文展示了如何为窗体和按钮创建位图区域。我认为已经有几篇文章讨论了 MFC 中的相关内容,而本文使用了类似的技术,即区域 (Regions)。除了在窗体上使用此技术外,它还允许您将其应用于按钮,并实现悬停效果(即,当鼠标悬停在按钮上时更改另一个图像和/或区域),而无需创建自定义控件。
主要功能概述
以下是用于创建位图区域的两个主要函数的代码。它们位于 BitmapRegion.cs 中。
// Create and apply the given bitmap region on the supplied control
public static void CreateControlRegion(Control control, Bitmap bitmap)
{
// Return if control and bitmap are null
if(control == null || bitmap == null)
return;
// Set our control's size to be the same as the bitmap
control.Width = bitmap.Width;
control.Height = bitmap.Height;
// Check if we are dealing with Form here
if(control is System.Windows.Forms.Form)
{
// Cast to a Form object
Form form = (Form)control;
// Set our form's size to be a little larger that the bitmap just
// in case the form's border style is not set to none in the first
// place
form.Width += 15;
form.Height += 35;
// No border
form.FormBorderStyle = FormBorderStyle.None;
// Set bitmap as the background image
form.BackgroundImage = bitmap;
// Calculate the graphics path based on the bitmap supplied
GraphicsPath graphicsPath = CalculateControlGraphicsPath(bitmap);
// Apply new region
form.Region = new Region(graphicsPath);
}
// Check if we are dealing with Button here
else if(control is System.Windows.Forms.Button)
{
// Cast to a button object
Button button = (Button)control;
// Do not show button text
button.Text = "";
// Change cursor to hand when over button
button.Cursor = Cursors.Hand;
// Set background image of button
button.BackgroundImage = bitmap;
// Calculate the graphics path based on the bitmap supplied
GraphicsPath graphicsPath = CalculateControlGraphicsPath(bitmap);
// Apply new region
button.Region = new Region(graphicsPath);
}
}
// Calculate the graphics path that representing the figure in the bitmap
// excluding the transparent color which is the top left pixel.
private static GraphicsPath CalculateControlGraphicsPath(Bitmap bitmap)
{
// Create GraphicsPath for our bitmap calculation
GraphicsPath graphicsPath = new GraphicsPath();
// Use the top left pixel as our transparent color
Color colorTransparent = bitmap.GetPixel(0, 0);
// This is to store the column value where an opaque pixel is first found.
// This value will determine where we start scanning for trailing
// opaque pixels.
int colOpaquePixel = 0;
// Go through all rows (Y axis)
for(int row = 0; row < bitmap.Height; row ++)
{
// Reset value
colOpaquePixel = 0;
// Go through all columns (X axis)
for(int col = 0; col < bitmap.Width; col ++)
{
// If this is an opaque pixel, mark it and search
// for anymore trailing behind
if(bitmap.GetPixel(col, row) != colorTransparent)
{
// Opaque pixel found, mark current position
colOpaquePixel = col;
// Create another variable to set the current pixel position
int colNext = col;
// Starting from current found opaque pixel, search for
// anymore opaque pixels trailing behind, until a transparent
// pixel is found or minimum width is reached
for(colNext=colOpaquePixel; colNext<bitmap.Width; colNext++)
if(bitmap.GetPixel(colNext, row) == colorTransparent)
break;
// Form a rectangle for line of opaque pixels found and
// add it to our graphics path
graphicsPath.AddRectangle(new Rectangle(colOpaquePixel,
row, colNext - colOpaquePixel, 1));
// No need to scan the line of opaque pixels just found
col = colNext;
}
}
}
// Return calculated graphics path
return graphicsPath;
}
为窗体创建位图区域
要为窗体创建位图区域,您只需要这两行代码。请注意,您无需显式地将窗体的边框更改为无边框,因为系统会自动为您完成此操作。
public class Form1 : System.Windows.Forms.Form
{
// Load your bitmap for the form
private Bitmap bmpFrmBack = new Bitmap(typeof(Form1), "back.bmp");
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
// Make our bitmap region for the form
BitmapRegion.CreateControlRegion(this, bmpFrmBack);
}
}
为按钮创建位图区域
为按钮创建位图区域与为窗体创建位图区域相同。此外,您无需对按钮的样式进行任何更改。
public class Form1 : System.Windows.Forms.Form
{
// Load your bitmap for the form
private Bitmap bmpFrmBack = new Bitmap(typeof(Form1), "back.bmp");
// Load your bitmap for the button
private Bitmap bmpBob = new Bitmap(typeof(Form1), "bob.bmp");
public Form1()
{
//
// Required for Windows Form Designer support
//
InitializeComponent();
// Make our bitmap region for the form
BitmapRegion.CreateControlRegion(this, bmpFrmBack);
// Make our bitmap regions for the buttons
BitmapRegion.CreateControlRegion(button1, bmpBob);
}
}
现在,如果您想在鼠标悬停在按钮上时更改按钮的位图,我很高兴地告诉您,您无需为此创建自定义按钮控件。您可以简单地处理如下所示的 MouseLeave
和 MouseEnter
事件。
private void button1_MouseEnter(object sender, System.EventArgs e)
{
// Make bitmap region for button
BitmapRegion.CreateControlRegion(button1, bmpBobSay);
}
private void button1_MouseLeave(object sender, System.EventArgs e)
{
// Make bitmap region for button
BitmapRegion.CreateControlRegion(button1, bmpBob);
}
拖动窗体
由于窗体现在没有标题栏,我们需要使其能够让用户在窗体的任何位置单击并拖动它。MFC 版本使用的解决方案是处理 WM_LBUTTONDOWN
并执行 PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, MAKELPARAM(point.x,point.y))
,以欺骗 Windows 认为我们单击了标题栏。但是,我无法让这在 Windows Forms 中工作,因此以下代码是替代解决方案。
private void Form1_MouseMove(object sender,
System.Windows.Forms.MouseEventArgs e)
{
// Check if dragging of the form has occurred
if(e.Button == MouseButtons.Left)
{
// If this is the first mouse move event for left click dragging
// of the form, store the current point clicked so that we can use
// it to calculate the form's new location in subsequent mouse move
// events due to left click dragging of the form
if(isFirst == true)
{
// Store previous left click position
prevLeftClick = new Point(e.X, e.Y);
// Subsequent mouse move events will not be treated as first time,
// until the left mouse click is released or other mouse click
// occur
isFirst = false;
}
// On subsequent mouse move events with left mouse click down.
// (i.e. During dragging of form)
else
{
// This flag here is to do alternate processing for the form
// dragging because it causes serious flicking when u allow
// every such events to change the form's location.
// You can try commenting this out to see what i mean
if(toBlock == false)
this.Location = new Point(this.Location.X + e.X -
prevLeftClick.X, this.Location.Y + e.Y - prevLeftClick.Y);
// Store new previous left click position
prevLeftClick = new Point(e.X, e.Y);
// Allow or deny next mouse move dragging event
toBlock = !toBlock;
}
}
// This is a new mouse move event so reset flag
else
isFirst = true;
}
需要注意的问题
当按钮获得焦点时,绘制在按钮上的焦点矩形存在问题。我无法禁用此焦点矩形。因此,我将我的位图略微放大,以便焦点矩形最终绘制在按钮区域之外。请查看下图。