分享
 
 
 

一个带有定时关闭功能的消息框 - TimableMessageBox

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

利用 Windows Hook 以及其他一些 Win32 API ,可以对大家经常使用的 System.Windows.Forms.MessageBox 进行手术式的改进,以取得一些很酷的效果。

本例实现了如何给 System.Windows.Forms.MessageBox 加装一个定时器。本例的开发冲动来自联众飞行棋。

很多朋友认为用 System.Windows.Forms.Form + 多线程来做不是更容易吗,不幸的是——我的观点恰恰相反。在 .Net 上开发高级的 WinForm 应用,(至少到目前为止)传统的 Win32 API 是无法回避的,许多高级的应用和技巧必须要用到 Win32 API。与其怕繁难,不如勇敢面对。

本文不介绍 Windows Hook 的原理和 C# 实现,详细的参考资料请参阅:

http://msdn.microsoft.com/msdnmag/issues/02/10/CuttingEdge/(关于 Windows Hook 的详细介绍,本例使用其类)

http://msdn.microsoft.com/msdnmag/issues/02/11/CuttingEdge/(关于 MessageBox 的其他手术式改进实例)

在读本文之前,请先拜读以上两文。

本文不介绍有关 Win32 API 的 C# 调用原理。文中引用的某些 Win32 枚举常量及大量 Win32 API 来自我个人的一个标准 Win32 库(特别是 WindowsAPI 这个工具类)。大多数方法和枚举的名称与 MSDN 的名称保持一致,可以说是一目了然的。

具体的实现代码如下所示,实现的技术细节问题以注释形式夹杂在代码之间,这里不再多费口舌了。

最后要申明的是,这个实例刚刚出炉,所以还未在 Windows 98 上进行测试。

using System;

using System.Drawing;

using System.Windows.Forms;

using XXXX.Common.Win32;

namespace XXXX.Library.Forms

{

/// <summary>

/// 带有定时器功能的消息框

/// </summary>

/// <example>

/// <code>

///

/// TimableMessageBox tmMsgBox = new TimableMessageBox();

/// tmMsgBox.DelaySeconds = 15; // 默认值为 10 seconds.

/// // tmMsgBox.CusotmIcon = yourIcon // 可以在此设置自定义图标。

/// DialogResult dr;

/// dr = tmMsgBox.Show ( DialogResult.No, // 超时后的默认结果

/// "Your message text",

/// "Your title",

/// MessageBoxButtons.YesNo,

/// MessageBoxIcon.Question );

///

/// if ( dr == DialogResult.No )

/// return;

/// else

/// // Do your process ...

///

/// </code>

/// </example>

public class TimableMessageBox

{

// 一个缺省的时钟图标(如示意图中所示)

// 来自本程序集的一个作为嵌入资源的图标

//

恕不介绍如何设置及读入嵌入资源

private static Icon DefaultIcon = Resources.icoTimer01;

// 定义静态文本控件的样式

private const int StaticTextControlStyle =

(int)StaticStyles.SS_CENTER |

(int)WindowStyles.WS_CHILD |

(int)WindowStyles.WS_VISIBLE ;

// Win32 常数

private const int ID_ICONWINDOW = 0x0014;

private const int STM_SETICON = 0x0170;

// 域变量

protected LocalCbtHook m_cbt;

protected IntPtr m_hwnd = IntPtr.Zero;

protected IntPtr m_hwndStatic = IntPtr.Zero;

protected IntPtr m_closeButton = IntPtr.Zero;

protected bool m_bInited = false;

protected bool m_bTimeout = false;

protected bool m_isCustom = false;

protected bool m_needEnumChild = false;

protected bool m_needReplaceIcon = false;

protected Icon m_customIcon;

private int m_timerCount = 0;

private int m_delaySecond = 10;

/// <summary>

/// 默认构造函数

/// </summary>

public TimableMessageBox()

{

m_cbt = new LocalCbtHook(); // 此类取自 MSDN Magzine,不过已转移到我的 WIN32 库中。

m_cbt.WindowCreated += new CbtEventHandler(WndCreated);

m_cbt.WindowActivated += new CbtEventHandler(WndActivated);

}

/// <summary>

/// 获取和设置自定义图标。

/// </summary>

public Icon CustomIcon

{

get { return m_customIcon; }

set { m_customIcon = value; }

}

/// <summary>

/// 获取和设置可以停留的时间,以秒为单位。

/// </summary>

public int DelaySeconds

{

get { return m_delaySecond; }

set { m_delaySecond = value; }

}

private void WndCreated(object sender, CbtEventArgs e)

{

if (e.IsDialogWindow)

{

m_bInited = false;

m_hwnd = e.Handle;

}

}

private void WndActivated(object sender, CbtEventArgs e)

{

// 不是相同的窗口不作处理

if (m_hwnd != e.Handle)

return;

// 是否已初始化,只有第一次激活消息框时才需进行后继的更改。

if (m_bInited)

return;

else

m_bInited = true;

// 更换图标

if (m_needReplaceIcon)

{

IntPtr hwndIcon1 = WindowsAPI.GetDlgItem(m_hwnd, ID_ICONWINDOW);

IntPtr hIcon;

if (m_isCustom)

{

hIcon = m_customIcon.Handle;

}

else

{

hIcon = DefaultIcon.Handle;

}

WindowsAPI.SendMessage(hwndIcon1, STM_SETICON, hIcon, IntPtr.Zero);

}

#region 添加一个静态文本控件

// 获取静态文本控件可使用的字体。

IntPtr hFont;

IntPtr hwndText = WindowsAPI.GetDlgItem(m_hwnd, 0xFFFF);

if(hwndText != IntPtr.Zero)

hFont = new IntPtr(WindowsAPI.SendMessage(hwndText, (int)Msg.WM_GETFONT, IntPtr.Zero, IntPtr.Zero));

else

hFont = new IntPtr(WindowsAPI.SendMessage(m_hwnd, (int)Msg.WM_GETFONT, IntPtr.Zero, IntPtr.Zero));

Font fCur = Font.FromHfont(hFont);

// 获取静态文本控件位置的 x 和 y 坐标值

int x = 0, y = 0;

IntPtr hwndIcon = WindowsAPI.GetDlgItem(m_hwnd, ID_ICONWINDOW);

RECT rcIcon = new RECT();

WindowsAPI.GetWindowRect(hwndIcon, ref rcIcon);

POINT pt = new POINT();

pt.x = rcIcon.left;

pt.y = rcIcon.top;

WindowsAPI.ScreenToClient(m_hwnd, ref pt);

x = pt.x;

y = pt.y + rcIcon.bottom - rcIcon.top + 2;

// 创建一个静态文本控件并添加至消息框

m_hwndStatic = WindowsAPI.CreateWindowEx(0,

"static", "0", StaticTextControlStyle,

x, y , rcIcon.right - rcIcon.left, (int)fCur.GetHeight(),

m_hwnd, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero);

// 更新所需的字体

WindowsAPI.SendMessage(m_hwndStatic, (int)Msg.WM_SETFONT, hFont, new IntPtr(1));

// 找一个消息框上的按钮

if (m_needEnumChild)

WindowsAPI.EnumChildWindows(this.m_hwnd,

new WindowsAPI.EnumChildProc(this.EnumChildWindowsProc), 0);

// 移除钩子

m_cbt.Uninstall();

#endregion

}

// 计时信息更新定时器的处理例程

private void TimerUpdateElapseHandler(IntPtr hWnd, uint uiMsg, uint idEvent, int dwTime)

{

if (this.m_hwndStatic == IntPtr.Zero)

return;

// 显示计时消息文本

WindowsAPI.SetWindowText(this.m_hwndStatic, (++m_timerCount).ToString());

}

// 关闭消息框定时器的处理例程

private void TimerCloseElapseHandler(IntPtr hWnd, uint uiMsg, uint idEvent, int dwTime)

{

// 需要模拟一个单击操作

if (m_closeButton != IntPtr.Zero)

{

int buttonCommand = WindowsAPI.GetDlgCtrlID(m_closeButton);

// Console.WriteLine("Command : " + buttonCommand);

// 发送命令消息

WindowsAPI.SendMessage(m_hwnd, (int)Msg.WM_COMMAND, buttonCommand, m_closeButton);

}

else

{

// 直接发送关闭窗口消息

WindowsAPI.SendMessage(m_hwnd, (uint)Msg.WM_CLOSE, 0, 0);

}

// 设置超时标记

m_bTimeout = true;

}

// 枚举子窗口的处理例程

// 用来找一个按钮。

private bool EnumChildWindowsProc(IntPtr hwnd, int lPram)

{

// 获取子窗口的 windows class 名称。

STRINGBUFFER sb;

int lRet = WindowsAPI.GetClassName(hwnd, out sb, 512);

string sClassName = sb.szText;

//Console.WriteLine(sClassName);

// 检查是否为按钮。

if (sClassName.ToUpper() == "BUTTON")

{

m_closeButton = hwnd;

return false; // 只要找到就立即停止枚举

}

else

{

return true;

}

}

#region Methods

/// <summary>

/// 显示消息框的主方法,其他重载方法最终均调用本方法。

/// </summary>

/// <param name="timeoutDialogResult">超时后的对话框结果</param>

/// <param name="strMessageText">要显示在消息框上的消息文本</param>

/// <param name="strMessageBoxTitle">消息框的标题文本</param>

/// <param name="buttons">按钮组合</param>

/// <param name="icon">图标样式,如果设置有自定义图标,此处设置的图标样式无意义。

/// 如果设置为 MessageBoxIcon.None,则将使用内置的默认图标:一个小钟。</param>

/// <param name="defaultButton">缺省按钮的位置</param>

/// <returns> System.Windows.Forms.DialogResult </returns>

public DialogResult Show(DialogResult timeoutDialogResult,

string strMessageText, string strMessageBoxTitle,

MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)

{

// 是否为自定义图标

// 自定义的图标最优先被考虑。

m_isCustom = m_customIcon != null && m_customIcon != DefaultIcon;

// 是否需要更换图标

// 自定义图标时,必须要更换图标。

// 另外,当没有设置自定义图标并且未设置任何标准图标样式时,

// 那么就使用缺省的图标,此时也需要更换图标。

m_needReplaceIcon = m_isCustom || icon == MessageBoxIcon.None;

// 如果未指定图标类型,添加一个占位图标类型。

// 只要不是 MessageBoxIcon.None 都可以,

// 但要注意 Warning、Stop、Error 会发出一个不同的音响哦。

// 确保有一个图标,这是为了能在图标下方显示计时信息。

if (icon == MessageBoxIcon.None)

{

icon = MessageBoxIcon.Information;

}

// 重置各标记位

m_bTimeout = false;

m_timerCount = 0;

m_hwnd = IntPtr.Zero;

m_closeButton = IntPtr.Zero;

// 由于以下两种按钮样式的消息框的窗口标题栏上的 Close 按钮是禁用的,

// 此时发送 WM_CLOSE 消息是不能关闭消息框的。

// 所以采用了一个迂回的方法:先找到消息框上的任意一个按钮,

// 在关闭消息框定时器的事件处理例程中针对该按钮模拟一个命令操作,

// 以达到关闭窗口的目的。至于什么按钮是不重要的,因为超时后返回的

// 对话框结果是由调用方设定的缺省值参数:timeoutDialogResutl。

m_needEnumChild = ( MessageBoxButtons.AbortRetryIgnore == buttons ||

MessageBoxButtons.YesNo == buttons);

// 创建两个定时器

// 关闭消息框的定时器

IntPtr timerColse = WindowsAPI.SetTimer(

IntPtr.Zero, 0, m_delaySecond * 1000, new WindowsAPI.TimerProc( TimerCloseElapseHandler ));

// 更新计时信息的定时器

IntPtr timerUpdate = WindowsAPI.SetTimer(

IntPtr.Zero, 0, 1000, new WindowsAPI.TimerProc( TimerUpdateElapseHandler));

// 设置钩子

m_cbt.Install();

// 显示消息框

DialogResult dr = MessageBox.Show(

strMessageText, strMessageBoxTitle, buttons, icon, defaultButton);

// 销毁定时器

WindowsAPI.KillTimer(IntPtr.Zero, timerColse);

WindowsAPI.KillTimer(IntPtr.Zero, timerUpdate);

// 视是否超时,返回相应的结果。

if (m_bTimeout)

return timeoutDialogResult; // 若超时,则返回参数设定的值。

else

return dr; // 若未超时,则返回用户单击的值。

}

public DialogResult Show(DialogResult timeoutDialogResult,

string strMessageText, string strMessageBoxTitle,

System.Windows.Forms.MessageBoxButtons buttons, MessageBoxIcon icon)

{

return Show(timeoutDialogResult, strMessageText, strMessageBoxTitle,

buttons, icon, MessageBoxDefaultButton.Button1);

}

public DialogResult Show(DialogResult timeoutDialogResult,

string strMessageText, string strMessageBoxTitle,

System.Windows.Forms.MessageBoxButtons buttons)

{

return Show(timeoutDialogResult,strMessageText, strMessageBoxTitle,

buttons, MessageBoxIcon.None);

}

public DialogResult Show(DialogResult timeoutDialogResult,

string strMessageText, string strMessageBoxTitle)

{

return Show( timeoutDialogResult,strMessageText, strMessageBoxTitle,

System.Windows.Forms.MessageBoxButtons.OK );

}

public DialogResult Show(DialogResult timeoutDialogResult,

string strMessageText)

{

return Show(timeoutDialogResult, strMessageText, "");

}

#endregion

}

}

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有