這篇是 Windows Phone 自定義彈出框和 Toast 通知 的簡化版,區別就是之前的自定義 MessageBox
控件是整個 app 中所有頁面共享的,下面的簡化版 MessageBox 只是應用於 MainPage 頁面中,“詢問
用戶是否退出”的自定義彈出框。
在應用或游戲程序中,經常的一個場景就是在用戶點擊 Back 鍵時,應用詢問用戶是否退出,通常是重寫
Page 頁面的事件:
protected override async void OnBackKeyPress(System.ComponentModel.CancelEventArgs e)
但是,當彈出 MessageBox 10秒鍾用戶沒有響應,應用則會退出,其實並不是 MessageBox 的 Bug,只要阻塞
OnBackKeyPress() 方法超過 10秒 ,應用都會退出的,如果先調用 e.Cancle = true 取消導航,再詢問用戶
是否退出,就不會出現這個問題。如果用戶點擊“確定”,則調用 App.Current.Terminate(); 結束當前應用進程。
一種簡單的方式是:
protected override void OnBackKeyPress(System.ComponentModel.CancelEventArgs e) { e.Cancel = true; this.Dispatcher.BeginInvoke(delegate { if (MessageBox.Show("確定退出?", "提示", MessageBoxButton.OKCancel) == MessageBoxResult.OK) { App.Current.Terminate(); } }); base.OnBackKeyPress(e); }
另一種方式:
為了解決這個問題,方式之一就是自定義一個 MessageBox 控件:
頁面的交互如下圖:

第一步:自定義一個名為 MyDialog 的用戶控件,頁面的主要 xaml:
<Grid x:Name="gridDialog" Background="#33000000"> <Grid Height="280" x:Name="gridBox" VerticalAlignment="Top" Background="#ff1ba0e1"> <Grid.Projection> <PlaneProjection/> </Grid.Projection> <Grid.RowDefinitions> <RowDefinition Height="60"/> <RowDefinition Height="150"/> <RowDefinition Height="70"/> </Grid.RowDefinitions> <TextBlock Text="提示信息" x:Name="txtTitle" FontSize="25" Margin="25,10, 0, 0"/> <!--該控件的 Content 屬性的內容值可以是字符串或者其它 UIElement--> <ContentControl HorizontalAlignment="Left" FontSize="30" Margin="40, 0, 0, 0"
Content="文本內容文本" Grid.Row="1" x:Name="contentContainer"/>
<StackPanel Grid.Row="2" Orientation="Horizontal"> <Button x:Name="btnOk" Content="OK" Width="200" Click="btnOk_Click"/> <Button x:Name="btnCancle" Content="Cancel" Width="200" Click="btnCancle_Click"/> </StackPanel> </Grid> </Grid>
自定義彈出框相應的 C# :
public partial class MyDialog : UserControl { public MyDialog() { InitializeComponent(); gridDialog.Visibility = System.Windows.Visibility.Collapsed; // 把第一個實例賦值給全局靜態對象 if (_instance == null) _instance = this; } //用來控制異步線程中 彈出框 結果返回的時機 private static AutoResetEvent myResetEvent = new AutoResetEvent(false); static MessageBoxResult messageBoxResult; //用一個單一實例,使得應用中的所有頁面使用同一個實例 static MyDialog _instance; static MyDialog Instance { get { //if (_instance == null) // _instance = new MyDialog(); return _instance; } } /// <summary> /// 顯示包含指定文本、標題欄標題和響應按鈕的消息框 /// </summary> /// <param name="messageBoxText">要顯示的消息</param> /// <param name="caption">消息框的標題</param> /// <param name="button">一個值,用於指示要顯示哪個按鈕或哪些按鈕</param> /// <returns>一個值,用於指示用戶對消息的響應</returns> public static Task<MessageBoxResult> Show(string messageBoxText, string caption, MessageBoxButton button) { return Task<MessageBoxResult>.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(); return messageBoxResult; }); } private void btnOk_Click(object sender, RoutedEventArgs e) { txtTitle.Text = ""; contentContainer.Content = null; gridDialog.Visibility = System.Windows.Visibility.Collapsed; messageBoxResult = MessageBoxResult.OK; // 使異步線程的 Show() 方法繼續執行 myResetEvent.Set(); } private void btnCancle_Click(object sender, RoutedEventArgs e) { txtTitle.Text = ""; contentContainer.Content = null; gridDialog.Visibility = System.Windows.Visibility.Collapsed; messageBoxResult = MessageBoxResult.Cancel; myResetEvent.Set(); } // 用來控制當彈出框顯示的時候,如果用戶點擊 Back 按鍵,則隱藏彈出框, // 在 App.xaml.cs 中的 RootFrame_Navigating 事件中調用 public static bool DialogIsOpen { get { if (Instance != null && Instance.gridDialog.Visibility == Visibility.Visible) { Instance.btnCancle_Click(null, null); return true; } else { return false; } } } }
第二步:在 MainPage.xaml 頁面中,添加如下代碼:
<local:MyDialog Grid.RowSpan="2" xmlns:local="clr-namespace:SimpleDialogDemo"/>
在相應的 C# 頁面,重寫 OnBackKeyPress 方法:
protected override async void OnBackKeyPress(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); }
