近一段时间由于项目需要一直专注于UI方面的编程
为了更加友好的将提示信息呈现给用户
我们必须对标准的Windows消息提示窗口进行处理
我们大家在Windows XP下使用U盘
闪存等移动存储设备
当插上或拔下这些设备时任务栏区域都会显示一个淡黄色背景
且具有标注样式的提示窗口弹出来
这样的提示即友善又美观
那么这到底是怎么实现的呢?其实道理并不复杂
该标注式提示窗口本身就是一个不规则窗体
当显示时它会将标注窗口的箭头指向不同控件
如下图
一般情况下的标注式提示窗口
屏幕边缘的标注式提示窗口
一技术要点
就像本文开头所说的标注式消息提示窗口其实就是一个具有不规则外形的窗体但却具备了更加复杂的属性和行为标注的箭头会根据不同控件指向不同的位置当需要标注的控件过于接近屏幕的边缘时标注窗口还会自动调整显示位置以及箭头的长短和大小
我们为新创建的窗体取名为InfoWindow在类的头部定义intArc和intArrowHeight两个私有变量可以适当调整它们的值来微调提示窗口的位置和箭头的大小与位置
提示窗口的箭头位置无非具有左上右上左下和右下四个可能性我们为此定义了枚举类型的变量ArrowLocation根据提示窗口位于屏幕的不同位置GetArrowLocation可以计算提示窗口的位置并且返回适当的ArrowLocation定义如下
……
public enum ArrowLocation
{
TopLeft
TopRight
BottomLeft
BottomRight
}
SetInfoWindowRegion函数非常重要它在FormLoad事件即装载和显示提示窗体时被调用当计算出新的提示窗口的位置和箭头显示位置后调用SetBounds将更新后的位置和大小应用到提示窗口gPath是GraphicsPath类型的私有变量它表示标注式窗口的不规则图形路径该图行路径也是根据提示窗口的位置和箭头显示的位置来创建gPathAddArc方法用来绘制提示窗口四个边角的弧度部分和AddLine方法一起描绘出提示窗口包括箭头的轮廓一切就绪后我们就用这个gPath对象传递给Region对象当将这个Region对象赋给Form窗体的Region属性后窗体就具备了标注式提示窗口样式的不规则外形了部分代码如下
private void SetInfoWindowRegion()
{
if (!thisIsHandleCreated)
return;
SystemDrawingSize windowSize = thisSize;
Point[] ArrowPoints = new Point[];
Point topLeftPoint = PointEmpty;
Point bottomRightPoint = (Point)windowSize;
switch (thisGetArrowLocation)
{
case ArrowLocationTopLeft:
……
case ArrowLocationTopRight:
……
case ArrowLocationBottomLeft:
……
case ArrowLocationBottomRight:
……
}
……
……
if ((thisGetArrowLocation == ArrowLocationTopLeft) ||
(thisGetArrowLocation == ArrowLocationTopRight))
{
gPathAddArc(topLeftPointX rectY arcRadius arcDia arcDia );
gPathAddLine(topLeftPointX rectY topLeftPointX rectY);
gPathAddArc(topLeftPointX topLeftPointY arcDia arcDia );
gPathAddLine(rectX topLeftPointY ArrowPoints[]X topLeftPointY);
gPathAddLines(ArrowPoints);
gPathAddLine(ArrowPoints[]X topLeftPointY rectX topLeftPointY);
gPathAddArc(rectX arcRadius topLeftPointY arcDia arcDia );
gPathAddLine(bottomRightPointX rectY bottomRightPointX rectY);
gPathAddArc(rectX arcRadius rectY arcRadius arcDia arcDia );
gPathAddLine(rectX bottomRightPointY rectX bottomRightPointY);
}
else
{
gPathAddLine(rectX topLeftPointY rectX topLeftPointY);
gPathAddArc(rectX arcRadius topLeftPointY arcDia arcDia );
gPathAddLine(bottomRightPointX rectY bottomRightPointX rectY);
gPathAddArc(rectX arcRadius rectY arcRadius arcDia arcDia );
gPathAddLine(rectX bottomRightPointY ArrowPoints[]X bottomRightPointY);
gPathAddLines(ArrowPoints);
gPathAddLine(ArrowPoints[]X bottomRightPointY rectX bottomRightPointY);
gPathAddArc(topLeftPointX rectY arcRadius arcDia arcDia );
gPathAddLine(topLeftPointX rectY topLeftPointX rectY);
gPathAddArc(topLeftPointX topLeftPointY arcDia arcDia );
}
gPathCloseFigure();
thisRegion = new Region(thisgPath);
}
ShowInfoWindow函数用来将提示窗口显示出来该函数需要将提示窗口附着的控件和需要显示的文本传递过来然后AnchorPointFromControl根据控件的位置返回提示窗口的箭头应该显示的坐标代码如下
public static Point AnchorPointFromControl(Control anchorControl)
{
if (anchorControl == null)
throw new ArgumentException();
Point controlLocation = anchorControlLocation;
SystemDrawingSize controlSize = anchorControlSize;
if (anchorControlParent != null)
controlLocation = anchorControlParentPointToScreen(controlLocation);
return controlLocation + new Size(controlSizeWidth / controlSizeHeight / );
}
PointToScreen表明将工作区点的位置映射成屏幕坐标统一进行计算上述代码最后以行说明提示窗口的箭头显示在附着控件的中点
将提示窗口的背景颜色设置成Info外观如下图
我们发现这样的外观有点别扭没错!因为提示窗口缺少黑色边框!所以还需要在窗体的OnPaint事件中添加代码如下
protected override void OnPaint(PaintEventArgs e)
{
Pen p = new Pen(ColorBlack );
eGraphicsDrawPath(p gPath);
baseOnPaint(e);
}
二程序实现
启动Visual Studio 新建Visual C#的Windows 应用程序项目并取名为ShowInfoWindow添加个Button组件个Label组件个textBox组件和个Panel组件其中个Button用来显示标注式消息提示窗口并分别附着在三个组件之上代码如下
……
private InfoWindow iw;
……
private void button_Click(object sender EventArgs e)
{
iw = new InfoWindow();
iwShowInfoWindow(label 关于标签组件的提示说明);
}
private void button_Click(object sender EventArgs e)
{
iw = new InfoWindow();
iwShowInfoWindow(button 关于按钮组件的提示说明);
}
private void button_Click(object sender EventArgs e)
{
iw = new InfoWindow();
iwShowInfoWindow(textBox 关于文本框组件的提示说明);
}
然后我们在项目中添加新Windows窗体取名为InfoWindow将InfoWindow的BackColor设为InfoFormBorderStyle设为None将ShowIcon和ShowInTaskbar都设为False在窗体上放置个Label组件和个Button组件分别用来显示消息内容和关闭提示窗口的操作具体实现请参见文章附带的源码这里不再详述
三总结
本文演示了标注式消息提示窗口的创建和显示利用GraphicsPath对象Region对象以及屏幕坐标映射等方法有效的实现了提示窗口的外观和样式提示窗口可以自动附着在相应控件之上并且根据附着控件在屏幕上的位置自动调整提示窗口箭头的位置和大小演示程序在Windows XP SP以 框架 环境下运行通过