想必大部分网友都使用过QQ、MSN等聊天程序,它们的界面都相当华丽,尤其是当网友上线以及消息提示时会有一个浮动的窗体从屏幕的右下方缓慢升起,既美观又人性化。本文主要讲解用C#来实现任务栏通知窗口。

简介

QQ和MSN的任务栏通知窗口很人性化,它可以在不丢失主窗体焦点的前提下显示一个具备皮肤Skin的通知窗体,当它显示一段时间后会自动消失,所以用户根本不用干预它。

这样的通知窗体和一般的具备标题栏、系统图标和按钮的窗体没有太大的区别,窗体表面其实就是画上去的一张位图而已,而窗体的浮动则会复杂一点,我们会用到.Net框架的双重缓冲区绘图技术(参见作者编译文章“Windows窗体的.Net框架绘图技术”)来保证移动窗体时所显示的内容平滑且不闪烁,以及使用P/Invoke平台调用进行对Win32API

函数的调用来完成不获得焦点的窗体显示和非标题栏窗体拖动。两种位图的皮肤运行时的界面如下:

背景知识

通知窗口就是将一般的窗体附加上一层皮肤,这里所谓的皮肤就是一张位图图片,该位图图片通过窗体的OnPaintbackground事件被绘制到窗体表面,在附加位图之前需要调整窗体的可视属性,由于绘制操作是针对于窗体客户区域的,所谓客户区域就是指窗体标题栏下方以及窗体边框以内的所有区域,所以需要将窗体的边框和外观属性FormBorderStyle调整为:None,这样所绘制的图像就会填充整个窗体。

首先,我们会用到Region对象,Region对象可以精确的描绘出任意形状的轮廓范围,通过一个位图图像创建Region对象后再将其传递给窗体的Region属性就可以使窗体按照Region所定义的轮廓显示出来。作为皮肤使用的位图文件可以通过任何图像编辑软件诸如:Photeshop来创建和编辑,只是注意一点,需要将图片的背景色调成特定颜色以便程序绘制时将其清除,我们在这里使用的背景色为粉红色。为了能够让Region对象按照图像中感兴趣的内容边框来创建窗体,我们还需要使用GraphicsPath类将图像轮廓按照一定路径标注下来,稍后便按照该路径创建Region对象。

然后通过窗体的绘图事件将位图的内容显示在窗体表面,我们没有直接使用OnPaintbackground事件而是重载了该方法,这样做的好处就是一些低层的绘制操作还继续交由.Net框架运行时来处理,我们只考虑实际需要的绘制操作即可。在OnPaintbackground方法中我们启用了双重缓冲区绘图技术,所谓该技术就是指先在内存中的一块画布上把将要显示的图像显示出来或进行处理,等到操作完成再将该画布上所显示的图像放置到窗体表面,这样的机制可以非常有效的降低闪烁的出现,使图像显示更加平滑。

通知窗体从屏幕的右下方进行升起停留一段时间后再慢慢回落,这里需要用到返回屏幕区域的大小范围的.Net框架方法Screen.GetWorkingArea(WorkAreaRectangle),通过一定算法计算出通知窗体显示前的初始位置。

最后,我们将要显示的文本按照一定格式和Rectangle对象所指定的区域范围绘制到窗体表面。通知窗体的关闭操作是通过设定一个区域,当用户用鼠标单击时检测单击坐标是否在该区域内,若在区域内就可以执行隐藏通知窗体的代码。

我们注意了,当QQ和MSN的通知窗口显示时其主窗体的焦点没有丢失,也就是说程序没有将自身的焦点转移到显示的通知窗体上。经过测试,我们无论怎么样调用.Net框架提供的窗体显示例程譬如:Form.Show都无法保证主窗体的焦点不丢失,在VC环境下我们可以使用Win32API的 ShowWindows函数来完成复杂的窗体显示操作,但是.Net框架根本没有提供类似的方法,那么我们能否通过.Net框架调用该API函数来显示窗体呢?

幸好.Net框架提供了P/Invoke平台调用,利用平台调用这种服务,托管代码就可以调用在动态链接库中实现的非托管函数,并可以封送其参数,我们可以轻松的显示但不获得焦点的窗体。程序中用到的Windows API以及常量的定义都保存在WinUser.h头文件中,其对应的动态链接库文件就是user32.dll,使用.Net框架提供的 DllImportAttribute类对导入的函数进行定义,然后就可以非常方便的在程序中调用该函数了。

由于我们将通知窗体的标题栏隐藏了,所以对窗体拖动操作还需要我们自己动手进行处理。本文介绍了如何更加高效的进行拖动窗体操作,有些网友在对于非标题栏拖动窗体编程时偏向组合使用鼠标事件来进行,这样做的本质没有任何不妥,但是频繁的事件响应和处理反而使程序性能有所降低。我们将继续使用 Win32API的底层处理方法来解决该问题,就是向窗体发送标题栏被单击的消息,模拟实际的拖动操作。

我们会通过2个计时器来完成窗体的显示、停留和隐藏,通过设置速度变量可以改变窗口显示和隐藏的速度。

[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

[DllImportAttribute("user32.dll")] //winuser.h

private static extern Boolean ShowWindow(IntPtr hWnd, Int32 nCmdShow);

SendMessage向消息循环发送标题栏被按下的消息来模拟窗体的拖动,ShowWindow用来将特定句柄的窗体显示出来,注意第二个参数 nCmdShow,它表示窗体应该怎样显示出来,而我们需要窗体不获得焦点显示出来,SW_SHOWNOACTIVATE可以满足我们要求,继续在 WinUser.h文件中搜索找到该常量对应的值为4,于是我们就可以这样调用来显示窗体了:

ShowWindow(this.Handle, 4);

我们创建了一个自定义函数ShowForm用来封装上面的ShowWindow用来是显示窗体,同时传递了所用到的几个Rectangle矩形区域对象,最后调用ShowWindows函数将窗体显示出来,代码片段如下:

public void ShowForm(string ftitletext, string fcontenttext, Rectangle fRegionofFormTitle,

Rectangle fRegionofFormTitlebar, Rectangle fRegionofFormContent, Rectangle fRegionofCloseBtn)

{

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;

TitleRectangle = fRegionofFormTitle;

TitlebarRectangle = fRegionofFormTitlebar;

ContentRectangle = fRegionofFormContent;

CloseBtnRectangle = fRegionofCloseBtn;

ShowWindow(this.Handle, 4); //#define SW_SHOWNOACTIVATE

}

CurrentState变量表示窗体的状态是显示中、停留中还是隐藏中,两个计时器根据窗体不同状态对窗体的位置进行更改,我们会使用SetBounds来执行该操作:

this.SetBounds(WorkAreaRectangle.Width - this.Width, WorkAreaRectangle.Height - currentTop, this.Width, this.Height);

当窗体需要升起时将窗体的Top属性值不断减少,而窗体回落时将Top属性值增加并超过屏幕的高度窗体就消失了,虽然原理很简单但仍需精确控制。

SetBackgroundBitmap函数首先将窗体背景图像保存到BackgroundBitmap变量中,然后根据该位图图像轮廓和透明色创建Region,BitmapToRegion就用于完成Bitmap到Region的转换,程序再将这个Region付值给窗体的Region属性以完成不规则窗体的创建。

public void SetBackgroundBitmap(Image image, Color transparencyColor)

{

BackgroundBitmap = new Bitmap(image);

Width = BackgroundBitmap.Width;

Height = BackgroundBitmap.Height;

Region = BitmapToRegion(BackgroundBitmap, transparencyColor);

}

public 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;

}

通知窗体背景以及文字的绘制在重载的OnPaintBackground方法中完成,而且利用了双重缓冲区技术来进行绘制操作,代码如下:

protected override void OnPaintBackground(PaintEventArgs e)

{

Graphics grfx = e.Graphics;

grfx.PageUnit = GraphicsUnit.Pixel;

Graphics offScreenGraphics;

Bitmap offscreenBitmap;

ffscreenBitmap = new Bitmap(BackgroundBitmap.Width, BackgroundBitmap.Height);

ffScreenGraphics = Graphics.FromImage(offscreenBitmap);

if (BackgroundBitmap != null)

{

offScreenGraphics.DrawImage(BackgroundBitmap, 0, 0,

BackgroundBitmap.Width, BackgroundBitmap.Height);

}

DrawText(offScreenGraphics);

grfx.DrawImage(offscreenBitmap, 0, 0);

}

上述代码首先返回窗体绘制表面的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.Hide();

currentTop = 1;

}

if (ContentRectangle.Contains(e.Location )) //单击内容区域

{

System.Diagnostics.Process.Start("http://www.Rithia.com"); }

}

}

结论

该程序可以很好的进行通知窗体的显示、停留和隐藏操作,并且具备简单的换肤机制,在利用了双重缓冲区绘图技术后,可以保证窗体的绘制平滑且没有闪烁。

如何利用C#实现任务栏通知窗口,大家通过本文都有了大概的了解了吧,希望能够有所收获吧1

c#缩小至任务栏窗体弹出_C#实现任务栏通知窗口相关推荐

  1. c#缩小至任务栏窗体弹出_C# winform 不通过窗体的ShowInTaskbar属性,怎么实现窗体不在任务栏显示?...

    估计你是做 Splash form吧. 可以这样做的. 默认 mainForm.Opacity = 0,SpalashForm 设定ShowInTaskBar= false, 在 Main(param ...

  2. 遮罩窗体弹出登录页面代码实现

    先上效果图(本人喜欢胡巴,背景用了胡巴),鼠标滑过页面,图片变暗,透明度为0.4,同时弹出登录窗口. 接下来先看css代码(写的可能不是很规范,根据调整样式顺序写的) body{background- ...

  3. Win10任务栏总是弹出推荐Edge浏览器广告的解决方法

    这篇文章给大家分享的是有关Win10任务栏总是弹出推荐Edge浏览器广告的解决方法的内容.小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧. 继 Outlook 网站."开始 ...

  4. Vue项目后台部分4,分类管理以及详情管理,添加和修改,loading效果,窗体弹出

    目录 分类管理 顶部 底部 展示列表 添加分类和修改分类 删除分类 添加详情实例 详情列表展示 loading 详情管理 数据展示与分页器 上架与下架 单个详情内容的查看 深度选择器 代码 分类管理 ...

  5. 怎么打开本地计算机策略编辑器,打开本地组策略编辑器; 2.在弹出的本地组策略编辑器窗口...

    因为工作需要有些用户会给电脑设置一个固定的IP地址,但是在其他用户接触这台电脑时可能会更改到IP地址.这时如不提前更改回来,会引起一些不必要的麻烦,那要如何防止其他用户更改IP呢?接下来小编就教大家W ...

  6. 建行找不到服务器或DNS错误,Win7用浏览器上网总是弹出提示是否停止运行此脚本窗口...

    腾讯视频/爱奇艺/优酷/外卖 充值4折起 最近,小编的朋友用浏览器上网,总是弹出提示"是否停止运行此脚本"窗口.询问小编后,得出结论,应该是流氓程序通过病毒篡改了浏览器的注册表,以 ...

  7. 如何解决老是弹出“com surrogate已停止工作”窗口

    如何解决老是弹出"com surrogate已停止工作"窗口 1.首先我们右键单击计算机选择打开"属性",之后点击打开"高级系统设置". 2 ...

  8. uniapp原生子窗体(弹出层为例子)

    在当前目录下新建一个目录和一个nvue页面 pages.json文件 {"path": "msVideo/msVideo","style": ...

  9. html 导航右侧弹出层,CSS导航栏及弹窗示例代码

    最近整理了CSS导航栏及弹窗,具体如下: CSS导航 先来看下效果: Insert your title body{ font-size:12px; } #discuss{ width:990px; ...

最新文章

  1. 深入理解分布式技术 - 微服务为什么需要API 网关
  2. Py之openpyxl:openpyxl库的简介、安装、使用方法之详细攻略
  3. 如何将本地 Windows 电脑中的文件复制(上传)到远程的 Windows 服务器主机上
  4. 『号外』 排名进入3000,特致感谢!
  5. Makefile知识点
  6. 疫情后全国热门博物馆榜单出炉 第一名竟不是故宫
  7. 今天开始学习QT for sysbiam 1
  8. Houdini特效资源如何导入?Houdini工程文件导入教程
  9. 公式编辑器中怎么打出分数中间的一横
  10. C语言画图形(图形库graphics的使用)
  11. 语音和音乐信号中的预加重处理
  12. 基于python管理系统论文_基于Python的运动计费管理系统
  13. 苹果怎么安装未签名的app_Windows端超简单安装未签名ipa应用
  14. 对象的高级使用-插入图片对象(转)
  15. JavaScript初学入门(JS打印9*9乘法表,JS制作简易计算器)
  16. 最短路:求最长最短路,求最短路的路径
  17. tensorflow中tf.nn.xw_plus_b
  18. meso-四(4-烷氨基甲酰苯基)卟啉(AFPP);5-(4-氨基苯基)-10,15.20-三苯基卟啉(TPP-NH2);5,10,15,20-四吡啶基苯基卟啉(H2TPyP)齐岳供应
  19. 统计系列(二)常见的概率分布
  20. 中国气象历史数据china1942_2022降水、温度、气压、风速、风向、云量,逐小时部分为逐三小时

热门文章

  1. echart dataZoom区域缩放
  2. 遇见苹果联合创始人:全球首场全VR极客大会,DEF CON CHINA Party首秀AI+VR未来会展
  3. ubuntu换国内源
  4. 工作的几个技术小点总结
  5. Windows系统安装jdk1.8
  6. APP加固:助力移动应用安全合规
  7. 在…视域下是什么意思_视域与视阈
  8. java模拟乒乓球比赛
  9. 羊城杯和天翼杯的补充
  10. VMware vSAN两份文档