C# 异步方法不是 运行 模式
C# async method isn't running modal
我的源代码中存在一个问题,导致异步方法不是模态方法。
我正在使用 Mahapps Metro Framework,我有一个 Logger-Class,其中有两个异步方法:
public class Logger : ILogger {
public void outputMessage(string message) {
Console.WriteLine(message);
}
public void outputUserMessage(string message) {
MessageBox.Show(message);
}
public async void outputMetroUserMessage(object window, String title, String message) {
MetroWindow mWindow = (MetroWindow)window;
await mWindow.ShowMessageAsync(title, message);
}
public async void outputMetroUserMessageWithHidingMDI(object window, string title, string message) {
UIGlobals.MainPageMdiChild.Visibility = Visibility.Hidden;
MetroWindow mWindow = (MetroWindow)window;
await mWindow.ShowMessageAsync(title, message);
UIGlobals.MainPageMdiChild.Visibility = Visibility.Visible;
}
}
还有一些其他 类 方法调用 Logger 方法。示例:
public partial class Login : MetroWindow {
public Login() {
InitializeComponent();
}
private void button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
DoLogin();
}
private void DoLogin() {
String email = txtEMail.Text;
String password = txtPassword.Password;
if (String.IsNullOrWhiteSpace(email)) {
Globals.Logger.outputMetroUserMessage(this, UserErrorMessageController.GetTitleByID(103), UserErrorMessageController.GetMessageByID(103));
} else if (String.IsNullOrWhiteSpace(password)) {
Globals.Logger.outputMetroUserMessage(this, UserErrorMessageController.GetTitleByID(104), UserErrorMessageController.GetMessageByID(104));
} else {
.
.
.
}
}
}
个人资料创建者:
public partial class ProfileCreator : MetroWindow {
public ProfileCreator(Network tempNetwork, UserProfile tempProfile) {
InitializeComponent();
.
.
.
}
private void btnSave_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
// TODO: Set cancelling when someting is missing
Save();
}
private void Save() {
getUserProfileValuesFromWindow();
Globals.TheSerializer.Serialize(tempProfile, Globals.PathToTemporaryFiles + "MyProfile.xml");
tempNetwork.NetworkParticipants.Add(tempProfile.ParticipantID);
Globals.TheSerializer.Serialize(tempNetwork, Globals.PathToTemporaryFiles + "MyNetwork.xml");
Globals.Logger.outputMetroUserMessage(this, "Erfolg", "Ihr Testsystem wurde erfolgreich angelegt.\nDrücken Sie erneut auf \"Testen\" und loggen Sie sich ein.");
Globals.Logger.outputMetroUserMessage(this, UserErrorMessageController.GetTitleByID(104), UserErrorMessageController.GetMessageByID(104));
}
当我在 Login-Class 中调用 Loggers 方法时,方法 运行 是预期的模态,但如果我从 ProfileCreator 调用它们,它们似乎不是模态的。我试图弄清楚,但我看不出 类 和参数有任何区别。也许你会看到一些我没有看到的东西。
感谢您的帮助!
问题是您没有使用 Task
。最佳做法是 return Task
来自所有 async
方法,这些方法不是从 WPF 中的用户单击事件调用的。您的代码应如下所示:
public class Logger : ILogger
{
public void outputMessage(string message)
{
Console.WriteLine(message);
}
public void outputUserMessage(string message)
{
MessageBox.Show(message);
}
public Task<MessageDialogResult> outputMetroUserMessage(object window, String title, String message)
{
MetroWindow mWindow = (MetroWindow)window;
return mWindow.ShowMessageAsync(title, message);
}
public async Task outputMetroUserMessageWithHidingMDI(object window, string title, string message)
{
UIGlobals.MainPageMdiChild.Visibility = Visibility.Hidden;
MetroWindow mWindow = (MetroWindow)window;
await mWindow.ShowMessageAsync(title, message);
UIGlobals.MainPageMdiChild.Visibility = Visibility.Visible;
}
}
并这样消费它:
public partial class Login : MetroWindow
{
public Login()
{
InitializeComponent();
}
private async void button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
await DoLogin();
}
private async Task DoLogin()
{
String email = txtEMail.Text;
String password = txtPassword.Password;
if (String.IsNullOrWhiteSpace(email))
{
await Globals.Logger.outputMetroUserMessage(this, UserErrorMessageController.GetTitleByID(103), UserErrorMessageController.GetMessageByID(103));
}
else if (String.IsNullOrWhiteSpace(password))
{
await Globals.Logger.outputMetroUserMessage(this, UserErrorMessageController.GetTitleByID(104), UserErrorMessageController.GetMessageByID(104));
}
else
{
// ...
}
}
}
请注意,我仅在 UI 的事件处理程序上使用 async void
,这应该是您唯一使用该模式的地方。请通读 this 以获取解释。
这是最后一部分,再次不要使用 async void 除非你是来自 UI 交互的事件处理程序的方法。
public partial class ProfileCreator : MetroWindow
{
public ProfileCreator(Network tempNetwork, UserProfile tempProfile)
{
InitializeComponent();
// ...
}
async void btnSave_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// TODO: Set cancelling when someting is missing
await Save();
}
async Task Save()
{
getUserProfileValuesFromWindow();
Globals.TheSerializer.Serialize(tempProfile, Globals.PathToTemporaryFiles + "MyProfile.xml");
tempNetwork.NetworkParticipants.Add(tempProfile.ParticipantID);
Globals.TheSerializer.Serialize(tempNetwork, Globals.PathToTemporaryFiles + "MyNetwork.xml");
await Globals.Logger.outputMetroUserMessage(this, "Erfolg", "Ihr Testsystem wurde erfolgreich angelegt.\nDrücken Sie erneut auf \"Testen\" und loggen Sie sich ein.");
await Globals.Logger.outputMetroUserMessage(this, UserErrorMessageController.GetTitleByID(104), UserErrorMessageController.GetMessageByID(104));
}
}
那是因为您调用了异步方法 "outputMetroUserMessage" 两次,而没有等待第一个方法的结果。你可以像这样重新定义你的方法:
public Task<MessageDialogResult> OutputMetroUserMessage(object window, string title, string message)
{
MetroWindow mWindow = (MetroWindow)window;
return mWindow.ShowMessageAsync(title, message);
}
然后等待消息输出:
private async void button1_Click(object sender, RoutedEventArgs e)
{
await this.OutputMetroUserMessage(this, "Title", "Message1");
await this.OutputMetroUserMessage(this, "Title", "Message2");
}
第二次调用将在用户确认消息后执行。
希望对您有所帮助。
我的源代码中存在一个问题,导致异步方法不是模态方法。 我正在使用 Mahapps Metro Framework,我有一个 Logger-Class,其中有两个异步方法:
public class Logger : ILogger {
public void outputMessage(string message) {
Console.WriteLine(message);
}
public void outputUserMessage(string message) {
MessageBox.Show(message);
}
public async void outputMetroUserMessage(object window, String title, String message) {
MetroWindow mWindow = (MetroWindow)window;
await mWindow.ShowMessageAsync(title, message);
}
public async void outputMetroUserMessageWithHidingMDI(object window, string title, string message) {
UIGlobals.MainPageMdiChild.Visibility = Visibility.Hidden;
MetroWindow mWindow = (MetroWindow)window;
await mWindow.ShowMessageAsync(title, message);
UIGlobals.MainPageMdiChild.Visibility = Visibility.Visible;
}
}
还有一些其他 类 方法调用 Logger 方法。示例:
public partial class Login : MetroWindow {
public Login() {
InitializeComponent();
}
private void button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
DoLogin();
}
private void DoLogin() {
String email = txtEMail.Text;
String password = txtPassword.Password;
if (String.IsNullOrWhiteSpace(email)) {
Globals.Logger.outputMetroUserMessage(this, UserErrorMessageController.GetTitleByID(103), UserErrorMessageController.GetMessageByID(103));
} else if (String.IsNullOrWhiteSpace(password)) {
Globals.Logger.outputMetroUserMessage(this, UserErrorMessageController.GetTitleByID(104), UserErrorMessageController.GetMessageByID(104));
} else {
.
.
.
}
}
}
个人资料创建者:
public partial class ProfileCreator : MetroWindow {
public ProfileCreator(Network tempNetwork, UserProfile tempProfile) {
InitializeComponent();
.
.
.
}
private void btnSave_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e) {
// TODO: Set cancelling when someting is missing
Save();
}
private void Save() {
getUserProfileValuesFromWindow();
Globals.TheSerializer.Serialize(tempProfile, Globals.PathToTemporaryFiles + "MyProfile.xml");
tempNetwork.NetworkParticipants.Add(tempProfile.ParticipantID);
Globals.TheSerializer.Serialize(tempNetwork, Globals.PathToTemporaryFiles + "MyNetwork.xml");
Globals.Logger.outputMetroUserMessage(this, "Erfolg", "Ihr Testsystem wurde erfolgreich angelegt.\nDrücken Sie erneut auf \"Testen\" und loggen Sie sich ein.");
Globals.Logger.outputMetroUserMessage(this, UserErrorMessageController.GetTitleByID(104), UserErrorMessageController.GetMessageByID(104));
}
当我在 Login-Class 中调用 Loggers 方法时,方法 运行 是预期的模态,但如果我从 ProfileCreator 调用它们,它们似乎不是模态的。我试图弄清楚,但我看不出 类 和参数有任何区别。也许你会看到一些我没有看到的东西。
感谢您的帮助!
问题是您没有使用 Task
。最佳做法是 return Task
来自所有 async
方法,这些方法不是从 WPF 中的用户单击事件调用的。您的代码应如下所示:
public class Logger : ILogger
{
public void outputMessage(string message)
{
Console.WriteLine(message);
}
public void outputUserMessage(string message)
{
MessageBox.Show(message);
}
public Task<MessageDialogResult> outputMetroUserMessage(object window, String title, String message)
{
MetroWindow mWindow = (MetroWindow)window;
return mWindow.ShowMessageAsync(title, message);
}
public async Task outputMetroUserMessageWithHidingMDI(object window, string title, string message)
{
UIGlobals.MainPageMdiChild.Visibility = Visibility.Hidden;
MetroWindow mWindow = (MetroWindow)window;
await mWindow.ShowMessageAsync(title, message);
UIGlobals.MainPageMdiChild.Visibility = Visibility.Visible;
}
}
并这样消费它:
public partial class Login : MetroWindow
{
public Login()
{
InitializeComponent();
}
private async void button_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
await DoLogin();
}
private async Task DoLogin()
{
String email = txtEMail.Text;
String password = txtPassword.Password;
if (String.IsNullOrWhiteSpace(email))
{
await Globals.Logger.outputMetroUserMessage(this, UserErrorMessageController.GetTitleByID(103), UserErrorMessageController.GetMessageByID(103));
}
else if (String.IsNullOrWhiteSpace(password))
{
await Globals.Logger.outputMetroUserMessage(this, UserErrorMessageController.GetTitleByID(104), UserErrorMessageController.GetMessageByID(104));
}
else
{
// ...
}
}
}
请注意,我仅在 UI 的事件处理程序上使用 async void
,这应该是您唯一使用该模式的地方。请通读 this 以获取解释。
这是最后一部分,再次不要使用 async void 除非你是来自 UI 交互的事件处理程序的方法。
public partial class ProfileCreator : MetroWindow
{
public ProfileCreator(Network tempNetwork, UserProfile tempProfile)
{
InitializeComponent();
// ...
}
async void btnSave_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// TODO: Set cancelling when someting is missing
await Save();
}
async Task Save()
{
getUserProfileValuesFromWindow();
Globals.TheSerializer.Serialize(tempProfile, Globals.PathToTemporaryFiles + "MyProfile.xml");
tempNetwork.NetworkParticipants.Add(tempProfile.ParticipantID);
Globals.TheSerializer.Serialize(tempNetwork, Globals.PathToTemporaryFiles + "MyNetwork.xml");
await Globals.Logger.outputMetroUserMessage(this, "Erfolg", "Ihr Testsystem wurde erfolgreich angelegt.\nDrücken Sie erneut auf \"Testen\" und loggen Sie sich ein.");
await Globals.Logger.outputMetroUserMessage(this, UserErrorMessageController.GetTitleByID(104), UserErrorMessageController.GetMessageByID(104));
}
}
那是因为您调用了异步方法 "outputMetroUserMessage" 两次,而没有等待第一个方法的结果。你可以像这样重新定义你的方法:
public Task<MessageDialogResult> OutputMetroUserMessage(object window, string title, string message)
{
MetroWindow mWindow = (MetroWindow)window;
return mWindow.ShowMessageAsync(title, message);
}
然后等待消息输出:
private async void button1_Click(object sender, RoutedEventArgs e)
{
await this.OutputMetroUserMessage(this, "Title", "Message1");
await this.OutputMetroUserMessage(this, "Title", "Message2");
}
第二次调用将在用户确认消息后执行。
希望对您有所帮助。