利用C#实现标注式消息提示窗口

  近一段时间由于项目需要一直专注于UI方面的编程,为了更加友好的将提示信息呈现给用户,我们必须对标准的Windows消息提示窗口进行处理。我们大家在Windows XP下使用U盘、闪存等移动存储设备,当插上或拔下这些设备时任务栏区域都会显示一个淡黄色背景,且具有标注样式的提示窗口弹出来,这样的提示即友善又美观,那么这到底是怎么实现的呢?其实道理并不复杂,该标注式提示窗口本身就是一个不规则窗体,当显示时它会将标注窗口的箭头指向不同控件。如下图:


一般情况下的p标注式提示窗口

屏幕边缘的标注式提示窗口

  一、技术要点

  就像本文开头所说的"标注式消息提示窗口"其实就是一个具有不规则外形的窗体,但却具备了更加复杂的属性和行为。标注的箭头会根据不同控件指向不同的位置,当需要标注的控件过于接近屏幕的边缘时,标注窗口还会自动调整显示位置以及箭头的长短和大小。

  我们为新创建的窗体取名为InfoWindow。在类的头部定义intArc和intArrowHeight两个私有变量,可以适当调整它们的值来微调提示窗口的位置和箭头的大小与位置。

  提示窗口的箭头位置无非具有左上、右上、左下和右下四个可能性,我们为此定义了枚举类型的变量ArrowLocation,根据提示窗口位于屏幕的不同位置,GetArrowLocation可以计算提示窗口的位置并且返回适当的ArrowLocation,定义如下:

……
public enum ArrowLocation
{
 TopLeft,
 TopRight,
 BottomLeft,
 BottomRight
}
  SetInfoWindowRegion函数非常重要,它在Form.Load事件即装载和显示提示窗体时被调用,当计算出新的提示窗口的位置和箭头显示位置后,调用SetBounds将更新后的位置和大小应用到提示窗口,gPath是GraphicsPath类型的私有变量,它表示标注式窗口的不规则图形路径,该图行路径也是根据提示窗口的位置和箭头显示的位置来创建,gPath.AddArc方法用来绘制提示窗口四个边角的弧度部分,和AddLine方法一起描绘出提示窗口包括箭头的轮廓,一切就绪后我们就用这个gPath对象传递给Region对象,当将这个Region对象赋给Form窗体的Region属性后,窗体就具备了标注式提示窗口样式的不规则外形了,部分代码如下:

private void SetInfoWindowRegion()
{
 if (!this.IsHandleCreated)
  return;
 System.Drawing.Size windowSize = this.Size;
 Point[] ArrowPoints = new Point[3];
 Point topLeftPoint = Point.Empty;
 Point bottomRightPoint = (Point)windowSize;
 switch (this.GetArrowLocation)
 {
  case ArrowLocation.TopLeft:
   ……
  case ArrowLocation.TopRight:
   ……
  case ArrowLocation.BottomLeft:
   ……
  case ArrowLocation.BottomRight:
   ……
 }
 ……
 ……
 if ((this.GetArrowLocation == ArrowLocation.TopLeft) ||
(this.GetArrowLocation == ArrowLocation.TopRight))
 {
  gPath.AddArc(topLeftPoint.X, rectY2 - arcRadius, arcDia, arcDia, 90, 90);
  gPath.AddLine(topLeftPoint.X, rectY2, topLeftPoint.X, rectY1);
  gPath.AddArc(topLeftPoint.X, topLeftPoint.Y, arcDia, arcDia, 180, 90);
  gPath.AddLine(rectX1, topLeftPoint.Y, ArrowPoints[0].X, topLeftPoint.Y);
  gPath.AddLines(ArrowPoints);
  gPath.AddLine(ArrowPoints[2].X, topLeftPoint.Y, rectX2, topLeftPoint.Y);
  gPath.AddArc(rectX2 - arcRadius, topLeftPoint.Y, arcDia, arcDia, 270, 90);
  gPath.AddLine(bottomRightPoint.X, rectY1, bottomRightPoint.X, rectY2);
  gPath.AddArc(rectX2 - arcRadius, rectY2 - arcRadius, arcDia, arcDia, 0, 90);
  gPath.AddLine(rectX2, bottomRightPoint.Y, rectX1, bottomRightPoint.Y);
 }
 else
 {
  gPath.AddLine(rectX1, topLeftPoint.Y, rectX2, topLeftPoint.Y);
  gPath.AddArc(rectX2 - arcRadius, topLeftPoint.Y, arcDia, arcDia, 270, 90);
  gPath.AddLine(bottomRightPoint.X, rectY1, bottomRightPoint.X, rectY2);
  gPath.AddArc(rectX2 - arcRadius, rectY2 - arcRadius, arcDia, arcDia, 0, 90);
  gPath.AddLine(rectX2, bottomRightPoint.Y, ArrowPoints[0].X, bottomRightPoint.Y);
  gPath.AddLines(ArrowPoints);
  gPath.AddLine(ArrowPoints[2].X, bottomRightPoint.Y, rectX1, bottomRightPoint.Y);
  gPath.AddArc(topLeftPoint.X, rectY2 - arcRadius, arcDia, arcDia, 90, 90);
  gPath.AddLine(topLeftPoint.X, rectY2, topLeftPoint.X, rectY1);
  gPath.AddArc(topLeftPoint.X, topLeftPoint.Y, arcDia, arcDia, 180, 90);
 }
 gPath.CloseFigure();
 this.Region = new Region(this.gPath);
}
  ShowInfoWindow函数用来将提示窗口显示出来,该函数需要将提示窗口附着的控件和需要显示的文本传递过来。然后,AnchorPointFromControl根据控件的位置返回提示窗口的箭头应该显示的坐标,代码如下:

public static Point AnchorPointFromControl(Control anchorControl)
{
 if (anchorControl == null)
 throw new ArgumentException();
 Point controlLocation = anchorControl.Location;
 System.Drawing.Size controlSize = anchorControl.Size;

 if (anchorControl.Parent != null)
  controlLocation = anchorControl.Parent.PointToScreen(controlLocation);
 return controlLocation + new Size(controlSize.Width / 2, controlSize.Height / 2);
}
  PointToScreen表明将工作区点的位置映射成屏幕坐标统一进行计算。上述代码最后以行说明提示窗口的箭头显示在附着控件的中点。

  将提示窗口的背景颜色设置成Info,外观如下图:

  我们发现这样的外观有点别扭,没错!因为提示窗口缺少黑色边框!所以,还需要在窗体的OnPaint事件中添加代码,如下:

protected override void OnPaint(PaintEventArgs e)
{
 Pen p = new Pen(Color.Black , 2);
 e.Graphics.DrawPath(p, gPath);
 base.OnPaint(e);
}

  二、程序实现

  启动Visual Studio 2005,新建Visual C#的Windows 应用程序项目,并取名为ShowInfoWindow,添加4个Button组件、1个Label组件、1个textBox组件和3个Panel组件,其中3个Button用来显示标注式消息提示窗口并分别附着在三个组件之上,代码如下:

……
private InfoWindow iw;
……
private void button1_Click(object sender, EventArgs e)
{
 iw = new InfoWindow();
 iw.ShowInfoWindow(label1, "关于标签组件的提示说明。");
}
private void button3_Click(object sender, EventArgs e)
{
 iw = new InfoWindow();
 iw.ShowInfoWindow(button2, "关于按钮组件的提示说明。");
}

private void button4_Click(object sender, EventArgs e)
{
 iw = new InfoWindow();
 iw.ShowInfoWindow(textBox1, "关于文本框组件的提示说明。");
}
  然后,我们在项目中添加新Windows窗体,取名为InfoWindow,将InfoWindow的BackColor设为Info,FormBorderStyle设为None,将ShowIcon和ShowInTaskbar都设为False,在窗体上放置1个Label组件和1个Button组件,分别用来显示消息内容和关闭提示窗口的操作。具体实现请参见文章附带的源码,这里不再详述。

  三、总结

  本文演示了标注式消息提示窗口的创建和显示,利用GraphicsPath对象、Region对象以及屏幕坐标映射等方法有效的实现了提示窗口的外观和样式,提示窗口可以自动附着在相应控件之上,并且根据附着控件在屏幕上的位置自动调整提示窗口箭头的位置和大小。演示程序在Windows XP SP2以及.Net 框架 2.0环境下运行通过。

时间: 2024-08-28 07:06:57

利用C#实现标注式消息提示窗口的相关文章

c#标注式提示窗口

问题描述 c#自己写了一个自定义控件,想让控件有标注式提示窗口,望广大c#高手赐教.小弟刚注册,暂时没分.敬请见谅. 解决方案 解决方案二:当我还是一个三角的时候,提了一个40分问题,但是没有一个人回答,我当时就在想,如果谁能回答问题,不管回什么,我都把分全部给他.这样我就可以结贴了,因为我不想做一个不结贴的人.所以,当我看到一个三角的提问题但是没有人回答的时候,我很同情你们的遭遇...帮你们顶一下.我也是从一个三角走过来的.

利用.net来发送即时消息:)

用了我一下午的时间终于写完并整理好了利用.net来发送即时消息的材料(当然了,还有上午的数据库设计:)    数据库设计:info表:id fromstu_id tostu_id content term其中id是主键,fromstu_id是发送信息的用户的学号(这是和我做的学友录连在一起的),tostu_id是接受信息的用户的学号,content是消息的内容,term是判断是否为新消息.下面的代码家在校友录中的if not ispostback中 '/////////////////////判

Java Socket聊天室编程(一)之利用socket实现聊天之消息推送_java

相关阅读:Java Socket聊天室编程(二)之利用socket实现单聊聊天室 网上已经有很多利用socket实现聊天的例子了,但是我看过很多,多多少有一些问题存在. 这里我将实现一个比较完整的聊天例子,并解释其中的逻辑. 由于socket这一块比较大,所以我将分出几篇来写一个比较完整的socket例子. 这里我们先来实现一个最简单的,服务器与客户端通讯,实现消息推送的功能. 目的:服务器与客户端建立连接,客户端可以向服务器发送消息,服务器可以向客户端推送消息. 1,使用java建立socke

求助!网页如何实现响应式消息提示

问题描述 求助!网页如何实现响应式消息提示 大侠们好,想实现的网页效果:A网页点击下单,B网页提示有人下单 我不知道问题的名字有没有错...这个是不是叫响应式消息提示? 想知道这种效果是如何实现的,学Web不久,如果有代码参考就更好了. 解决方案 最简单的轮询: 1.B网页用Ajax定时请求某个查询是否有新订单的网页,如果有新的订单就返回. 2.B网页使用长连接,B网页请求后服务器端一直不返回,直到有新订单就返回结果给浏览器,因为要保持连接,所以B网页使用人数不宜过多. 当然最好的,最即时的,最

google地图-利用Google地图标注高速线路的问题

问题描述 利用Google地图标注高速线路的问题 现在有这样一个需求,利用Google地图API来标注高速公路路线,比如:京沪高速,我可以通过地图直接标注出来这条线路. 本来想着通过Gmark来标点,然后用标点连线,结果连线时发现是一条直线,而不是沿着高速公路连线.而通过waypoint,因为有点数限制,无法实现,不知道大家有没有什么好方法?

2.关于QT中的Dialog(模态窗口),文件选择器,颜色选择器,字体选择器,消息提示窗口

 1 新建一个空项目 A 编写 .pro文件 QT += gui widgets   HEADERS += \     MyDialog.h   SOURCES += \     MyDialog.cpp B 编写MyDialog.h #ifndef MYDIALOG_H #define MYDIALOG_H   #include <QDialog>   class MyDialog:public QDialog {     Q_OBJECT public:     explicit My

s2sh框架中如何优美的实现触发式消息系统?

问题描述 触发式消息,也就是根据某个特定的事件,比如评论.收藏等事件,会触发对应的消息提醒,生成一条消息记录,通知相关的用户. 现在就是在每个方法中强行插入生成消息的代码,这样的方式代码很丑,而且时间越多越不可控,耦合性高,经验丰富的工程师们,你们是如何优美的实现这个功能的? 解决方案 原理:这应该是观察者模式.需要被通知的相关用户就是观察者,通知者在感知评论.收藏等事件时通知所有相关的观察者.实现:就像LS各位说的,使用Spring AOP,面向切面编程,而且可插拔,所有耦合度很低.解决方案二

制作MSN、QQ的消息提示窗口

用过QQ和MSN聊天工具的人都知道,只要好友上线,就会在托盘的位置处显示一个提示窗口,可以是拉帘式的,或者是淡入淡出的形式出现:想想何不为自己的程式也加一个漂亮的提示窗口呢:) 说做就做. 一.MSN拉帘式窗口制作 分三部分:1.窗口的显示:2.窗口的停留:3.窗口的消失: 如果达到这样郊果,系统中要有三个定时器,进行分别控制.定义的定时器如下: #define ID_TIMER_POP_WINDOW 1 #define ID_TIMER_DISPALY_DELAY 2 #define ID_T

利用js编写响应式侧边栏_javascript技巧

为了练手,自己学敲网站时刚好碰到需要制作侧边栏,在网上也查了各种插件以及框架都可以实现这个功能,但是想自己学着用js原生学一个试试,于是就初略完成了侧边栏的实现,可以让初学者参考参考,代码能力有限.  其中主要设计的就是animate()函数,animate() 方法执行 CSS 属性集的自定义动画.该方法通过CSS样式将元素从一个状态改变为另一个状态.CSS属性值是逐渐改变的,这样就可以创建动画效果.只有数字值可创建动画(比如 "margin:30px").字符串值无法创建动画(比如