注:这篇文章主要介绍在 应用全局所有页面使用的自定义弹出框,如果仅在 MainPage 页面询问用户是否
1、 首先介绍如何自定义弹出框
在 WP 中,系统默认的弹出框有几个问题,首先是背景比较简陋,而且样式的扩展性比较低,另一个问题就
是会阻碍 UI 线程。当在页面的调用下面的代码:
if(MessageBoxResult.OK != MessageBox.Show("确定要退出吗?", "温馨提示", MessageBoxButton.OKCancel))
{
e.Cancel= true;
}
当在 MainPage 页面点击 Back 键,会弹出:
当在10秒钟的时候,如果用户没有回馈,那么应用就会被系统终结了。
下面开始做一个自定义的弹出框:
1)自定义一个控件,定义在 MyDialog.xaml 文件中:
相应的 C# 页面:
publicMyDialog()
{
InitializeComponent();
gridDialog.Visibility=System.Windows.Visibility.Collapsed;//把第一个实例赋值给全局静态对象
if (_instance == null)
_instance= this;
}#region “确定”弹出框
//用来控制异步线程中 弹出框 结果返回的时机
private static AutoResetEvent myResetEvent = new AutoResetEvent(false);staticMessageBoxResult messageBoxResult;//用一个单一实例,使得应用中的所有页面使用同一个实例
staticMyDialog _instance;staticMyDialog Instance
{get{//if (_instance == null)//_instance = new MyDialog();
return_instance;
}
}//用来控制当弹出框显示的时候,如果用户点击 Back 按键,则隐藏弹出框,//在 App.xaml.cs 中的 RootFrame_Navigating 事件中调用
public static boolDialogIsOpen
{get{if (Instance != null && Instance.gridDialog.Visibility ==Visibility.Visible)
{
Instance.btnCancle_Click(null, null);return true;
}else{return false;
}
}
}///
///显示包含指定文本和“确定”按钮的消息框///
/// 要显示的消息
/// 在所有情况下均为 System.Windows.MessageBoxResult.OK
public static Task Show(stringmessageBoxText)
{return Task.Factory.StartNew(() =>{
Instance.Dispatcher.BeginInvoke(delegate{
Instance.gridDialog.Visibility=Visibility.Visible;
Instance.contentContainer.Content=messageBoxText;
Instance.txtTitle.Text= "";
Instance.btnCancle.Visibility=Visibility.Collapsed;
Instance.ShowMessageBoxSB.Stop();
Instance.ShowMessageBoxSB.Begin();
});
myResetEvent.WaitOne();returnmessageBoxResult;
});
}///
///显示包含指定文本、标题栏标题和响应按钮的消息框///
/// 要显示的消息
/// 消息框的标题
/// 一个值,用于指示要显示哪个按钮或哪些按钮
/// 一个值,用于指示用户对消息的响应
public static Task Show(string messageBoxText, stringcaption, MessageBoxButton button)
{return Task.Factory.StartNew(() =>{
Instance.Dispatcher.BeginInvoke(delegate{
Instance.gridDialog.Visibility=Visibility.Visible;
Instance.contentContainer.Content=messageBoxText;
Instance.txtTitle.Text=caption;if (button ==MessageBoxButton.OKCancel)
{
Instance.btnCancle.Visibility=Visibility.Visible;
}else{
Instance.btnCancle.Visibility=Visibility.Collapsed;
}//Instance.UpdateLayout();
Instance.ShowMessageBoxSB.Stop();
Instance.ShowMessageBoxSB.Begin();
});
myResetEvent.WaitOne();returnmessageBoxResult;
});
}private void btnOk_Click(objectsender, RoutedEventArgs e)
{
txtTitle.Text= "";
contentContainer.Content= null;
gridDialog.Visibility=System.Windows.Visibility.Collapsed;
messageBoxResult=MessageBoxResult.OK;//使异步线程的 Show() 方法继续执行
myResetEvent.Set();
}private void btnCancle_Click(objectsender, RoutedEventArgs e)
{
txtTitle.Text= "";
contentContainer.Content= null;
gridDialog.Visibility=System.Windows.Visibility.Collapsed;
messageBoxResult=MessageBoxResult.Cancel;
myResetEvent.Set();
}#endregion
另一个问题就是,让这个自定义控件变成全局的,也就是所有工程里的页面,共享
这一个实例。思路就是,因为 App 类中的一个 RootFrame 对象,它是 PhoneApplicationFrame 类型的
,它是用来显示工程内所有页面的对象,并且控制各个页面的导航等功能的,它有一个 Template 属性,只要在
默认的框架上,加入自定义的控件,那么这个控件就可以在所有页面中显示了,相应的样式(在 App.xaml 中定义):
在 App 的构造函数中,重新设置 RootFrame 的样式,为上面定义的样式:
RootFrame.Style = App.Current.Resources["MyPhoneApplicationFrameStyle"] as Style;
在 App 的 InitializePhoneApplication() 方法中,注册 Navigating 事件:
RootFrame.Navigating += RootFrame_Navigating;
相应的函数:
void RootFrame_Navigating(objectsender, NavigatingCancelEventArgs e)
{//如果自定义弹出框在显示,则隐藏它,并且取消导航
if (MyDialog.DialogIsOpen && e.NavigationMode ==NavigationMode.Back)
{
e.Cancel= true;
}
}
在页面中调用弹出框的方法:
private async void Button_Click_OkAndCancel(objectsender, RoutedEventArgs e)
{if (MessageBoxResult.OK == await MyDialog.Show("Hello 北京天安门", "提示", MessageBoxButton.OKCancel))
{
txtTile.Text= "点击了OK";
}else{
txtTile.Text= "点击了Cancle";
}
}
显示结果:
当用户在 MainPage 页面,点击 Back 物理按键时,因为此时不会触发 RootFrame.Navigating 的导航事件,所以
需要在 MainPage 中判断一下是否有其它的弹出框,判断如果没有其它的弹出框,则弹出询问用户
是否关闭当前应用的弹出框:
protected override async voidOnBackKeyPress(System.ComponentModel.CancelEventArgs e)
{//首先取消默认 Back 键关闭应用
e.Cancel = true;//如果 MainPage 页面中,弹出了其它的弹出框,关闭它,//在 DialogIsOpen 属性中实现
if(MyDialog.DialogIsOpen)
{//可以做其它事情
}else{if (MessageBoxResult.OK == await MyDialog.Show("确定要退出吗?", "温馨提示:", MessageBoxButton.OKCancel))
{//终止当前应用程序。该方法是在 WP8 中加入的,WP7 中木有
App.Current.Terminate();
}
}base.OnBackKeyPress(e);
}
显示效果:
因为本工程中自定义弹出框和 自定义Toast 通知都为系统级别的,所以所有的页面共享这个
单一实例。如果页面只有在推出时,询问用户 “是否退出应用” 则只需要参考自定义控件,把它
简化后,添加到 MainPage 中使用即可,不需要放到 PhoneApplicationFrame 的 Template
属性中。
2) 自定义一个 Toast 通知,目的是增加对消息样式的控制,并且可以增加消息弹出的动画等。
自定义的 Toast 显示框已经在 上面的 MyDialog.xaml 文件中定义了,下面定义 C# 页面:
#region 自定义 Toast 消息框
const string myToast = @"
";//把上面动画字符串转换成相应的 XAML 动画对象
Storyboard StoryBoardToast = System.Windows.Markup.XamlReader.Load(myToast) asStoryboard;//显示自定义 Toast 消息
public static void ShowToastMessage(stringmessage)
{
Instance.StoryBoardToast.Stop();
Instance.txtToast.Text=message;foreach (var t inInstance.StoryBoardToast.Children)
Storyboard.SetTarget(t, Instance.borderToast);
Instance.StoryBoardToast.Begin();
}#endregion
在任何一个 Page 页面中调用:
private void Button_Click_Toast(objectsender, RoutedEventArgs e)
{
MyDialog.ShowToastMessage("收到 10 条新消息");
}
则,显示结果: