摘自:http://www.cnblogs.com/risen/archive/2008/01/15/1039751.html
public partial class AlertForm : Form
{
/*
*
在类AlertForm定义的下方,
*
我们创建用于显示的字符串和其颜色的变量,
*
再定义几个Rectangle对象的变量用于放置标题、
*
提示内容以及可以拖动窗体的区域和关闭按钮的区域。
*
然后,我们需要保存窗体在浮动时的高度以便计算移动后的新高度,
*
intervalValue变量用来确定窗体显示和隐藏的速度。
*
进行平台调用时我们需要提前定义好常量的值用来传递给函数,
*
WM_NCLBUTTONDOWN和HT_CAPTION常量用于拖动窗体,
* 他们的值都保存在WinUser.h头文件中,
*
所对应的动态链接库名为:user32.dll。
*
我们用到的Win32API为:SendMessage、ReleaseCapture和ShowWindow,
*
通过使用DllImportAttribute可以导入相应的函数并在程序中重新进行定义,如下:
*/
public Bitmap BackgroundBitmap
= null;
private string
titleText;
private string
contentText;
private Color
normalTitleColor = Color.FromArgb(0, 0,
0);
private Font normalTitleFont =
new Font("宋体", 12, FontStyle.Regular,
GraphicsUnit.Pixel);
private Color
normalContentColor = Color.FromArgb(0, 0,
0);
private Font normalContentFont
= new Font("宋体", 12, FontStyle.Regular,
GraphicsUnit.Pixel);
public
Rectangle TitleRectangle;
public
Rectangle TitlebarRectangle;
public Rectangle ContentRectangle;
public Rectangle
CloseBtnRectangle;
private
Rectangle WorkAreaRectangle;
private int SavedTop;
private int
currentTop = 1;
private int
intervalValue = 2;
public
const int WM_NCLBUTTONDOWN = 0x00A1; //消息:左键点击
winuser.h
public const int
HT_CAPTION = 0x0002; //标题栏
[DllImportAttribute("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int
lParam); //发送消息 //winuser.h
中有函数原型定义
[DllImportAttribute("user32.dll")]
public static extern bool ReleaseCapture(); //释放鼠标捕捉
winuser.h
/*
*
SendMessage向消息循环发送标题栏被按下的消息来模拟窗体的拖动,
* ShowWindow用来将特定句柄的窗体显示出来,
*
注意第二个参数nCmdShow,它表示窗体应该怎样显示出来,
* 而我们需要窗体不获得焦点显示出来,
*
SW_SHOWNOACTIVATE可以满足我们要求,
*
继续在WinUser.h文件中搜索找到该常量对应的值为4,
* 于是我们就可以这样调用来显示窗体了:
*/
[DllImportAttribute("user32.dll")]
//winuser.h
private static extern
Boolean ShowWindow(IntPtr hWnd, Int32
nCmdShow);
public int
CurrentState=0; //0=hide 1=uptoshow 2=showing
3=downtohide
private AlertForm()
{
InitializeComponent();
SetBackgroundBitmap(snnu.ccs.Properties.Resources.popup2, Color.FromArgb(255, 0,
255));
}
public static void Show(string
ftitletext, string fcontenttext)
{
AlertForm alert = new
AlertForm();
alert.ShowForm(ftitletext,
fcontenttext);
}
/*
*
当窗体需要升起时将窗体的Top属性值不断减少,
*
而窗体回落时将Top属性值增加并超过屏幕的高度窗体就消失了,
* 虽然原理很简单但仍需精确控制。
*
SetBackgroundBitmap函数首先将窗体背景图像保存到BackgroundBitmap变量中,
*
然后根据该位图图像轮廓和透明色创建Region,BitmapToRegion就用于完成Bitmap到Region的转换,
*
程序再将这个Region付值给窗体的Region属性以完成不规则窗体的创建。
*/
protected void
SetBackgroundBitmap(Image image, Color
transparencyColor)
{
BackgroundBitmap = new
Bitmap(image);
Width =
BackgroundBitmap.Width;
Height =
BackgroundBitmap.Height;
TitleRectangle = new Rectangle(2, 1, 70,
25);
TitlebarRectangle = new Rectangle(1, 1, Width,
25);
ContentRectangle = new Rectangle(1, 25, Width, Height -
25);
CloseBtnRectangle = new Rectangle(Width - 25, 1, 25,
25);
Region = BitmapToRegion(BackgroundBitmap,
transparencyColor);
}
protected Region
BitmapToRegion(Bitmap bitmap, Color
transparencyColor)
{
if
(bitmap ==
null)
throw new ArgumentNullException("Bitmap", "Bitmap cannot be
null!");
int height =
bitmap.Height;
int width =
bitmap.Width;
GraphicsPath path = new
GraphicsPath();
for (int j = 0; j < height;
j++)
for (int i = 0; i < width;
i++)
{
if (bitmap.GetPixel(i, j) ==
transparencyColor)
continue;
int x0 =
i;
while ((i < width) && (bitmap.GetPixel(i, j) !=
transparencyColor))
i++;
path.AddRectangle(new Rectangle(x0, j, i - x0,
1));
}
Region region = new
Region(path);
path.Dispose();
return region;
}
protected void
DrawText(Graphics grfx)
{
if
(titleText != null && titleText.Length !=
0)
{
StringFormat sf = new
StringFormat();
sf.Alignment =
StringAlignment.Near;
sf.LineAlignment =
StringAlignment.Center;
sf.FormatFlags =
StringFormatFlags.NoWrap;
sf.Trimming =
StringTrimming.EllipsisCharacter;
grfx.DrawString(titleText, normalTitleFont, new SolidBrush(normalTitleColor),
TitleRectangle,
sf);
}
if
(contentText != null && contentText.Length !=
0)
{
StringFormat sf = new
StringFormat();
sf.Alignment =
StringAlignment.Center;
sf.LineAlignment =
StringAlignment.Center;
sf.FormatFlags =
StringFormatFlags.MeasureTrailingSpaces;
sf.Trimming = StringTrimming.Word;
grfx.DrawString(contentText, normalContentFont, new
SolidBrush(normalContentColor), ContentRectangle,
sf);
}
}
/*
*
通知窗体背景以及文字的绘制在重载的OnPaintBackground方法中完成,
* 而且利用了双重缓冲区技术来进行绘制操作,代码如下:
*/
protected override void
OnPaintBackground(PaintEventArgs
e)
{
Graphics
grfx =
e.Graphics;
grfx.PageUnit =
GraphicsUnit.Pixel;
Graphics
offScreenGraphics;
Bitmap
offscreenBitmap;
offscreenBitmap = new Bitmap(BackgroundBitmap.Width,
BackgroundBitmap.Height);
offScreenGraphics =
Graphics.FromImage(offscreenBitmap);
if (BackgroundBitmap !=
null)
{
offScreenGraphics.DrawImage(BackgroundBitmap, 0, 0, BackgroundBitmap.Width,
BackgroundBitmap.Height);
}
DrawText(offScreenGraphics);
grfx.DrawImage(offscreenBitmap, 0,
0);
}
/*
*
CurrentState变量表示窗体的状态是显示中、停留中还是隐藏中,
* 两个计时器根据窗体不同状态对窗体的位置进行更改,
*
我们会使用SetBounds来执行该操作:
*
this.SetBounds(WorkAreaRectangle.Width - this.Width, WorkAreaRectangle.Height -
currentTop, this.Width,
this.Height);
*/
protected void ShowForm(string
ftitletext, string fcontenttext)
{
titleText =
ftitletext;
contentText =
fcontenttext;
WorkAreaRectangle =
Screen.GetWorkingArea(WorkAreaRectangle);
this.Top = WorkAreaRectangle.Height +
this.Height;
FormBorderStyle =
FormBorderStyle.None;
WindowState =
FormWindowState.Normal;
this.SetBounds(WorkAreaRectangle.Width - this.Width, WorkAreaRectangle.Height -
currentTop, this.Width,
this.Height);
CurrentState =
1;
timer1.Enabled =
true;
ShowWindow(this.Handle, 4); //#define SW_SHOWNOACTIVATE
4
}
private void
timer1_Tick(object sender, EventArgs
e)
{
if
(CurrentState == 1)
{
this.SetBounds(WorkAreaRectangle.Width - this.Width, WorkAreaRectangle.Height -
currentTop, this.Width,
this.Height);
currentTop = currentTop +
intervalValue;
if (this.Top <= WorkAreaRectangle.Height -
this.Height)
{
timer1.Enabled =
false;
CurrentState =
2;
timer2.Enabled = true;
//显示停留计时
currentTop =
1;
}
}
else if
(CurrentState==3)
{
this.SetBounds(WorkAreaRectangle.Width - this.Width, SavedTop + currentTop,
this.Width,
this.Height);
currentTop = currentTop +
intervalValue;
if (this.Top >=
WorkAreaRectangle.Height)
{
timer1.Enabled =
false;
this.Close();
CurrentState =
0;
currentTop =
1;
}
}
}
/*
上述代码首先返回窗体绘制表面的Graphics并保存在变量grfx中,
*
然后创建一个内存Graphics对象offScreenGraphics和内存位图对象offscreenBitmap,
*
将内存位图对象的引用付值给offScreenGraphics,
*
这样所有对offScreenGraphics的绘制操作也都同时作用于offscreenBitmap,
*
这时就将需要绘制到通知窗体表面的背景图像BackgroundBitmap绘制到内存的Graphics对象上,
*
DrawText函数根据需要显示文字的大小和范围调用Graphics.DrawString将文字显示在窗体的特定区域。
*
最后,调用Graphics.DrawImage将内存中已经绘制完成的图像显示到通知窗体表面。
* 我们还需要捕获窗体的鼠标操作,有三个操作在这里进行,
* 1、处理拖动窗体操作,
*
2、处理通知窗体的关闭操作,
*
3、内容区域的单击操作。
*
三个操作都需要检测鼠标的当前位置与每个Rectangle区域的包含关系,
* 只要单击落在特定区域我们就进行相应的处理,代码如下
*/
private void
TaskbarForm_MouseDown(object sender, MouseEventArgs
e)
{
if
(e.Button ==
MouseButtons.Left)
{
if (TitlebarRectangle.Contains(e.Location))
//单击标题栏时拖动
{
ReleaseCapture();
//释放鼠标捕捉
SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
//发送左键点击的消息至该窗体(标题栏)
}
if (CloseBtnRectangle.Contains(e.Location))
//单击Close按钮关闭
{
this.Close();
currentTop =
1;
}
}
}
private void
timer2_Tick(object sender, EventArgs
e)
{
timer2.Enabled =
false;
CurrentState =
1;
currentTop =
1;
SavedTop =
this.Top;
CurrentState =
3;
timer1.Enabled = true;
}
private void
TaskbarForm_MouseMove(object sender, MouseEventArgs
e)
{
if
(ContentRectangle.Contains(e.Location))
{
Cursor =
Cursors.Hand;
}
else
Cursor = Cursors.Default;
}
}