WPF 自定义MessageBox系列
第一节 简单MessageBox
第二节 倒计时MessageBox
第三节 自定义按钮的MessageBox
第四节 界面操作分离的MessageBox
第五节 替换系统的MessageBox(本节)
前言
在上一篇《C# wpf 实现自定义界面操作分离的MessageBox》中我们得到了一个完整的MessageBox功能逻辑,将功能逻辑进行拓展,程序启动的时候,hook系统的MessageBox,变成调用自己的MessageBox。那我们只需要定义一个自己的MessageBox界面,绑定必要的属性,即可直接替换系统的MessageBox,或者说可以修改系统MessageBox的样式。
一、如何替换?
1、自定义MessageBox界面
在《C# wpf 实现自定义界面操作分离的MessageBox》中我们定义一个DataContext对象,我们只需要定义一个自己需要MessageBox界面样式,然后绑定如下属性即可。
/// <summary>
/// 消息框的标题
/// </summary>
public string Title{get;get;}
/// <summary>
/// 消息框的文本内容
/// </summary>
public string Context{get;get;}
/// <summary>
/// Yes按钮的文本
/// </summary>
public string YesText{get;get;}
/// <summary>
/// No按钮的文本
/// </summary>
public string NoText{get;get;}
/// <summary>
/// Yes选项剩余时间(非系统MessageBox必须)
/// </summary>
public TimeSpan? YesLeftTime{get;get;}
/// <summary>
/// No选项剩余时间(非系统MessageBox必须)
/// </summary>
public TimeSpan? NoLeftTime{get;get;}
/// <summary>
/// No按钮命令
/// </summary>
public ICommand NoCommand{get;get;}
/// <summary>
/// Yes按钮命令
/// </summary>
public ICommand YesCommand{get;get;}
/// <summary>
///Cancel按钮命令
/// </summary>
public ICommand CancelCommand{get;get;}
2、使用钩子
使用钩子钩系统函数的方法不少,这里不提供具体方法。基本目的就是使得程序对MessageBox的调用,都进入自己的钩子函数中。其实查看源码就知道wpf的MessageBox底层调用了MessageBoxW,所以我们只需要将MessageBoxW方法钩掉。
3、替换逻辑
在钩子过程中使用自己的显示逻辑。Windows的MessageBox类型有很多,如果全部都制作相应的界面其实会比较复杂。但是在wpf中的MessageBox只提供了4种样式的MessageBox。所以在wpf中我们只需要实现这种样式即可。
namespace System.Windows
{
//
// 摘要:
// 指定在消息框显示的按钮。 使用作为参数的 Overload:System.Windows.MessageBox.Show 方法。
public enum MessageBoxButton
{
//
// 摘要:
// 该消息框显示 确定 按钮。
OK = 0,
//
// 摘要:
// 该消息框显示 确定 和 取消 按钮。
OKCancel = 1,
//
// 摘要:
// 该消息框显示 是, ,否, ,和 取消 按钮。
YesNoCancel = 3,
//
// 摘要:
// 该消息框显示 是 和 否 按钮。
YesNo = 4
}
}
这4种样式在WinApi中对应:
#define MB_OK 0x00000000L
#define MB_OKCANCEL 0x00000001L
#define MB_YESNOCANCEL 0x00000003L
#define MB_YESNO 0x00000004L
所以在钩子过程中我们实现相应的Switch即可,下面是基于《C# wpf 实现自定义界面操作分离的MessageBox》代码的示例
Int32 ret = 0;
MessageBoxResult mr = null;
switch (uType)
{
case 0:// MB_OK
MessageBoxHelper.ShowDialog<T>(lpCaption, lpText, "确定");
isHandled = true;
ret = 1;//IDOK
break;
case 1:// MB_OKCANCEL
mr= MessageBoxHelper.ShowDialog<T>(lpCaption, lpText, "确定", true);
isHandled = true;
if (mr.Button == MessageBoxResultButton.Yes)
{
ret = 1;//IDOK
}
else if (mr.Button == MessageBoxResultButton.Cancel)
{
ret = 2;//IDCANCEL
}
break;
case 3:// MB_YESNOCANCEL
mr = MessageBoxHelper.ShowDialog<T>(lpCaption, lpText, "是", "否", true);
isHandled = true;
if (mr.Button == MessageBoxResultButton.Yes)
{
ret = 6;//IDYES
}
else if (mr.Button == MessageBoxResultButton.No)
{
ret = 7;//IDNO
}
else if (mr.Button == MessageBoxResultButton.Cancel)
{
ret = 2;//IDCANCEL
}
break;
case 4:// MB_YESNO
mr = MessageBoxHelper.ShowDialog<T>(lpCaption, lpText, "是", "否");
isHandled = true;
if (mr.Button == MessageBoxResultButton.Yes)
{
ret = 6;//IDYES
}
else if (mr.Button == MessageBoxResultButton.No)
{
ret = 7;//IDNO
}
break;
}
return ret;
二、代码
笔者使用自己的钩子实现的代码。注意:下面资源的钩子写在了dll中不可见,且只能钩MessageBoxA和MessageBoxW,如果不符合自己的要求可以在《C# wpf 实现自定义界面操作分离的MessageBox》基础上自己实现一套钩子。
完整代码:
https://download.csdn.net/download/u013113678/34215409
替换MessageBox接口方法原型定义如下:
/// <summary>
/// 钩子过程委托
/// </summary>
/// <param name="hWnd">窗口句柄</param>
/// <param name="lpText">内容</param>
/// <param name="lpCaption">标题</param>
/// <param name="uType">消息框类型</param>
/// <param name="isHandled">是否拦截</param>
/// <returns>结果</returns>
public delegate Int32 MessageBoxHandleHandler(IntPtr hWnd, string lpText, string lpCaption, UInt32 uType, ref bool isHandled);
/// <summary>
/// 替换系统的MessageBox
/// </summary>
/// <param name="messageBoxHandleHandler">钩子过程</param>
public static void HookSystemMessageBox(MessageBoxHandleHandler messageBoxHandleHandler)
/// <summary>
/// 替换系统的MessageBox
/// </summary>
/// <typeparam name="T">用于替换的绑定MessageBoxDataContext属性的窗口类型</typeparam>
public static void HookSystemMessageBox<T>() where T : Window, new()
三、示例
使用自定义的MessageBox,下列代码中的MyMessageBox是自定义的。
//调用系统MessageBox显示消息
System.Windows.MessageBox.Show("内容","标题");
System.Windows.MessageBox.Show("内容", "标题",MessageBoxButton.YesNo);
//替换系统MessageBox
MessageBoxHelper.HookSystemMessageBox<MyMessageBox>();
//调用系统MessageBox显示消息
System.Windows.MessageBox.Show("内容","标题");
System.Windows.MessageBox.Show("内容", "标题",MessageBoxButton.YesNo);
//还原系统MessageBox
MessageBoxHelper.UnHookSystemMessageBox();
//调用系统MessageBox显示消息
System.Windows.MessageBox.Show("内容","标题");
System.Windows.MessageBox.Show("内容", "标题",MessageBoxButton.YesNo);
显示效果:
总结
替换系统MessageBox属于一种切面编程技巧,方便于对旧项目的改造,尤其是涉及到频繁调用的代码,如果手动修改无疑是工作量巨大的,使用切面技巧可以很大程度的避免麻烦。