多个 MessageDialog 应用程序崩溃
Multiple MessageDialog app crash
我在应用程序的多个位置使用 MessageDialogues
。问题是,只要任何 MessageDialog
(或系统警报,例如功能警告)处于活动状态并且另一个我的 MessageDialog
被调用,应用程序就会无一例外地崩溃或出现 UnathorizedAccessException
。
这就是我调用 MessageDialog 的方式:
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
MessageDialog msg2 = new MessageDialog(_resourceLoader.GetString("MsgGPSUnavailable"));
msg2.ShowAsync();
});
我想我应该等待对话框关闭,但是通过使用 Dispatcher
我将这个对话框排队到主 UI 线程,它自己处理它,或者不处理?感谢您对此问题的任何解释。
编辑 - 我一步一步地进行并得到了以下代码,它包含在同一个 class 中。当我 运行 应用程序时,会调用 LoadDataToModel。这没关系,对话框由 msgGPSDisabled 显示。之后引发事件并调用 locator_StatusChanged 。这也可以,并显示对话框。
现在是奇怪的部分。当我不在 LoadDataToModel 中调用 msgGPSDisabled 并且仅在 locator_StatusChanged 中调用时,应用程序在显示对话框后立即崩溃。没有异常并且 App.g.i.cs 在第 47 行打开(DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION)。即使我在任何可以使用的地方使用try-catch。当我在 locator_StatusChanged 中使用没有 Dispatcher 的 msgGPSDisabled 时,会引发异常。无法捕捉,"item not found"
public async Task LoadDataToModel()
{
await msgGPSDisabled();
this.IsBusy = true;
await LoadDataGarvis(Stations); //rozparsuje raw data a načte je do modelu
InitializePins();
this.IsBusy = false;
}
void locator_StatusChanged(Geolocator sender, StatusChangedEventArgs args)
{
switch (sender.LocationStatus)
{
case Windows.Devices.Geolocation.PositionStatus.Disabled:
try
{
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await msgGPSDisabled();
IsGPSBusy = false;
IsGPS = false;
});
}
catch (UnauthorizedAccessException)
{
throw;
}
catch (Exception) {throw; }
case Windows.Devices.Geolocation.PositionStatus.NoData:
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await msgGPSUnavailable();
});
}
}
private async Task msgGPSDisabled()
{
MessageDialog sss = new MessageDialog(_resourceLoader.GetString("MsgGPSDisabled"));
await sss.ShowAsync();
}
您的 lambda 仍应等待异步调用,以便当调度程序运行它时,它不会继续,直到消息框关闭。
我没试过,但这应该会有所帮助(注意 async
和 await
关键字的用法):
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
MessageDialog msg2 = new MessageDialog(_resourceLoader.GetString("MsgGPSUnavailable"));
await msg2.ShowAsync();
});
编辑:
正如 Filip 已经解释的那样,您不能同时显示多个消息框。他还建议了一些您可以用来避免该问题的方法。
在您的场景中(报告 GPS 状态变化),最好在 UI 中将状态显示为标签,因为您实际上不需要用户在反正。您甚至可以在列表中更改值时收集它们,并使用 ItemsControl
显示它们,以便用户可以观察更改历史记录(可能带有时间戳)。这完全取决于您想要实现的目标。
两个MessageDialogs
不能同时显示。如果您想继续使用 MessageDialogs
,您有几个选择,并且最好有某种 MessageDialogService
来管理调出对话框的调用:
- 需要打开新对话框时关闭现有对话框。这是最简单的选项,也可能是最好的选项,尽管您可能会冒着取消对话的风险,该对话可能在某种程度上很重要,具体取决于您的对话内容。
- 排队对话,这样旧的对话就不会被解雇,但新的对话会在旧的对话被解雇后出现。这将确保用户关闭所有对话框,但如果您的应用程序可以以某种方式开始显示数百个对话框,这可能会成为一个问题。
- 只有在没有显示的情况下才打开一个新的。现在这有可能不会显示更新的消息,这听起来比第一个选项更有问题。
如果您想使用队列选项 - 您可以使用此代码:
using System;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Popups;
using Windows.UI.Xaml;
namespace WinRTXamlToolkit.Controls.Extensions
{
/// <summary>
/// MessageDialog extension methods
/// </summary>
public static class MessageDialogExtensions
{
private static TaskCompletionSource<MessageDialog> _currentDialogShowRequest;
/// <summary>
/// Begins an asynchronous operation showing a dialog.
/// If another dialog is already shown using
/// ShowAsyncQueue or ShowAsyncIfPossible method - it will wait
/// for that previous dialog to be dismissed before showing the new one.
/// </summary>
/// <param name="dialog">The dialog.</param>
/// <returns></returns>
/// <exception cref="System.InvalidOperationException">This method can only be invoked from UI thread.</exception>
public static async Task<IUICommand> ShowAsyncQueue(this MessageDialog dialog)
{
if (!Window.Current.Dispatcher.HasThreadAccess)
{
throw new InvalidOperationException("This method can only be invoked from UI thread.");
}
while (_currentDialogShowRequest != null)
{
await _currentDialogShowRequest.Task;
}
var request = _currentDialogShowRequest = new TaskCompletionSource<MessageDialog>();
var result = await dialog.ShowAsync();
_currentDialogShowRequest = null;
request.SetResult(dialog);
return result;
}
}
}
我在应用程序的多个位置使用 MessageDialogues
。问题是,只要任何 MessageDialog
(或系统警报,例如功能警告)处于活动状态并且另一个我的 MessageDialog
被调用,应用程序就会无一例外地崩溃或出现 UnathorizedAccessException
。
这就是我调用 MessageDialog 的方式:
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
MessageDialog msg2 = new MessageDialog(_resourceLoader.GetString("MsgGPSUnavailable"));
msg2.ShowAsync();
});
我想我应该等待对话框关闭,但是通过使用 Dispatcher
我将这个对话框排队到主 UI 线程,它自己处理它,或者不处理?感谢您对此问题的任何解释。
编辑 - 我一步一步地进行并得到了以下代码,它包含在同一个 class 中。当我 运行 应用程序时,会调用 LoadDataToModel。这没关系,对话框由 msgGPSDisabled 显示。之后引发事件并调用 locator_StatusChanged 。这也可以,并显示对话框。 现在是奇怪的部分。当我不在 LoadDataToModel 中调用 msgGPSDisabled 并且仅在 locator_StatusChanged 中调用时,应用程序在显示对话框后立即崩溃。没有异常并且 App.g.i.cs 在第 47 行打开(DEBUG && !DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION)。即使我在任何可以使用的地方使用try-catch。当我在 locator_StatusChanged 中使用没有 Dispatcher 的 msgGPSDisabled 时,会引发异常。无法捕捉,"item not found"
public async Task LoadDataToModel()
{
await msgGPSDisabled();
this.IsBusy = true;
await LoadDataGarvis(Stations); //rozparsuje raw data a načte je do modelu
InitializePins();
this.IsBusy = false;
}
void locator_StatusChanged(Geolocator sender, StatusChangedEventArgs args)
{
switch (sender.LocationStatus)
{
case Windows.Devices.Geolocation.PositionStatus.Disabled:
try
{
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await msgGPSDisabled();
IsGPSBusy = false;
IsGPS = false;
});
}
catch (UnauthorizedAccessException)
{
throw;
}
catch (Exception) {throw; }
case Windows.Devices.Geolocation.PositionStatus.NoData:
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await msgGPSUnavailable();
});
}
}
private async Task msgGPSDisabled()
{
MessageDialog sss = new MessageDialog(_resourceLoader.GetString("MsgGPSDisabled"));
await sss.ShowAsync();
}
您的 lambda 仍应等待异步调用,以便当调度程序运行它时,它不会继续,直到消息框关闭。
我没试过,但这应该会有所帮助(注意 async
和 await
关键字的用法):
CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
MessageDialog msg2 = new MessageDialog(_resourceLoader.GetString("MsgGPSUnavailable"));
await msg2.ShowAsync();
});
编辑:
正如 Filip 已经解释的那样,您不能同时显示多个消息框。他还建议了一些您可以用来避免该问题的方法。
在您的场景中(报告 GPS 状态变化),最好在 UI 中将状态显示为标签,因为您实际上不需要用户在反正。您甚至可以在列表中更改值时收集它们,并使用 ItemsControl
显示它们,以便用户可以观察更改历史记录(可能带有时间戳)。这完全取决于您想要实现的目标。
两个MessageDialogs
不能同时显示。如果您想继续使用 MessageDialogs
,您有几个选择,并且最好有某种 MessageDialogService
来管理调出对话框的调用:
- 需要打开新对话框时关闭现有对话框。这是最简单的选项,也可能是最好的选项,尽管您可能会冒着取消对话的风险,该对话可能在某种程度上很重要,具体取决于您的对话内容。
- 排队对话,这样旧的对话就不会被解雇,但新的对话会在旧的对话被解雇后出现。这将确保用户关闭所有对话框,但如果您的应用程序可以以某种方式开始显示数百个对话框,这可能会成为一个问题。
- 只有在没有显示的情况下才打开一个新的。现在这有可能不会显示更新的消息,这听起来比第一个选项更有问题。
如果您想使用队列选项 - 您可以使用此代码:
using System;
using System.Threading.Tasks;
using Windows.Foundation;
using Windows.UI.Popups;
using Windows.UI.Xaml;
namespace WinRTXamlToolkit.Controls.Extensions
{
/// <summary>
/// MessageDialog extension methods
/// </summary>
public static class MessageDialogExtensions
{
private static TaskCompletionSource<MessageDialog> _currentDialogShowRequest;
/// <summary>
/// Begins an asynchronous operation showing a dialog.
/// If another dialog is already shown using
/// ShowAsyncQueue or ShowAsyncIfPossible method - it will wait
/// for that previous dialog to be dismissed before showing the new one.
/// </summary>
/// <param name="dialog">The dialog.</param>
/// <returns></returns>
/// <exception cref="System.InvalidOperationException">This method can only be invoked from UI thread.</exception>
public static async Task<IUICommand> ShowAsyncQueue(this MessageDialog dialog)
{
if (!Window.Current.Dispatcher.HasThreadAccess)
{
throw new InvalidOperationException("This method can only be invoked from UI thread.");
}
while (_currentDialogShowRequest != null)
{
await _currentDialogShowRequest.Task;
}
var request = _currentDialogShowRequest = new TaskCompletionSource<MessageDialog>();
var result = await dialog.ShowAsync();
_currentDialogShowRequest = null;
request.SetResult(dialog);
return result;
}
}
}