Я довольно за кривой с асинхронным ожиданием, так что это, вероятно, вопрос «duh».Как выполнить асинхронный запуск в приложении WPF?
Я работаю над тем, что должно быть очень минимальным пользовательским интерфейсом, которое запускается с панели задач с помощью библиотеки WPF NotifyIcon.
Приложение должно работать очень просто (для пользователя) следующим образом:
- Программа начинается
- При необходимости, есть заставка сообщает пользователю программа запущена и побуждая их для входа (если они еще не сделали этого в предыдущей итерации).
- WPF NotifyIcon появляется на панели задач. исполнение
- Асинхронный начинается
Проблема, в которую я бегу является «Асинхронное выполнение начинается» часть. Все, что происходит до тех пор, работает нормально, но когда программа начинает «запускать», пользовательский интерфейс блокируется (под которым я имею в виду, пользователи могут щелкнуть, как безумные люди, на значке Tray, и контекстное меню не появится).
Эта блокировка происходит в течение недопустимо длительного периода времени.
Это код запуска:
private async void AppStartup(object sender, StartupEventArgs e) {
this.TRSIcon = this.FindResource("TRSIcon") as TaskbarIcon;
if (Settings.Default.DoUpgrade) { //Upgrade if necessary.
Settings.Default.Upgrade();
Settings.Default.DoUpgrade = false;
Settings.Default.Save();
}
if (string.IsNullOrEmpty(Settings.Default.Username) || string.IsNullOrEmpty(Settings.Default.Password)) {
new Help().ShowDialog();
Tuple<string, string> UP;
if ((UP = Login.Instance.GetUserPassword()) != null) {
Settings.Default.Username = UP.Item1;
Settings.Default.Password = UP.Item2;
Settings.Default.Save();
} else
return;
}
await this.Start(); //<-----This is where the meat of the program runs and it hangs the UI until it finishes.
return; //<-----This is just so that I have a break point to see that await this.Start is blocking (I have to do it like that right? or do I?)
}
Это Resources.xaml
:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:Tools="clr-namespace:WPFTools.TaskbarNotification;assembly=WPFTools"
xmlns:TR="clr-namespace:TriviaRetriever">
<ContextMenu x:Key="TSRInterfaceMenu" x:Shared="false">
<MenuItem Header="Login" Command="{Binding cmdLogin}"/>
<MenuItem Header="Get My Trivia" Command="{Binding cmdDownload}"/>
<MenuItem Header="Register" Command="{Binding cmdRegister}"/>
<MenuItem Header="Lost Password" Command="{Binding cmdLostPassword}"/>
<MenuItem Header="About" Command="{Binding cmdAbout}"/>
<MenuItem Header="Log Out" Command="{Binding cmdLogout}"/>
<MenuItem Header="Exit" Command="{Binding cmdExit}"/>
</ContextMenu>
<Tools:TaskbarIcon
x:Key="TRSIcon"
MenuActivation="LeftOrDoubleClick"
IconSource="/TRIcon.ico"
DoubleClickCommand="{Binding cmdAbout}"
ContextMenu="{StaticResource TSRInterfaceMenu}">
<Tools:TaskbarIcon.DataContext>
<TR:TRSIViewModel/>
</Tools:TaskbarIcon.DataContext>
</Tools:TaskbarIcon>
</ResourceDictionary>
Это MVVM для контекстного меню команд:
public class TRSIViewModel {
public ICommand cmdLogin {
get {
return new DelegateCommand {
fncCanExecute = () => (Application.Current as App).Core == null,
actCommand = async () => {
Tuple<string, string> LoginPassword = Login.Instance.GetUserPassword();
if (LoginPassword != null) {
Settings.Default.Username = LoginPassword.Item1;
Settings.Default.Password = LoginPassword.Item2;
Settings.Default.Save();
await (Application.Current as App).Start();
}
}
};
}
}
public ICommand cmdLogout {
get {
return new DelegateCommand {
fncCanExecute = () => (Application.Current as App).Core != null,
actCommand = () => {
(Application.Current as App).Core.Terminate();
(Application.Current as App).Core = null;
}
};
}
}
public ICommand cmdRegister {
get {
return new DelegateCommand {
fncCanExecute = () => true,
actCommand = () => Process.Start(@"https://www.digigames.com/weekly_subscriptions/index.php")
};
}
}
public ICommand cmdLostPassword {
get {
return new DelegateCommand {
fncCanExecute = () => true,
actCommand = () => Process.Start(@"https://www.digigames.com/weekly_subscriptions/lost_password.php")
};
}
}
public ICommand cmdAbout {
get {
return new DelegateCommand {
fncCanExecute = () => true,
actCommand = () => (Application.Current as App).TRSIcon.ShowCustomBalloon(new About(), PopupAnimation.Slide, 5000)
};
}
}
public ICommand cmdExit {
get {
return new DelegateCommand {
fncCanExecute = () => true,
actCommand = () => {
if ((Application.Current as App).Core != null)
(Application.Current as App).Core.Terminate();
Application.Current.Shutdown(0);
}
};
}
}
public ICommand cmdDownload {
get {
return new DelegateCommand {
fncCanExecute = () => (Application.Current as App).Core != null,
actCommand = async () => await (Application.Current as App).Core.DownloadTrivia(true)
};
}
}
public class DelegateCommand : ICommand {
public Action actCommand { get; set; }
public Func<bool> fncCanExecute { get; set; }
public bool CanExecute(object parameter) {
return this.fncCanExecute != null && this.fncCanExecute();
}
public event EventHandler CanExecuteChanged {
add { CommandManager.RequerySuggested += value; }
remove { CommandManager.RequerySuggested -= value; }
}
public void Execute(object parameter) { this.actCommand(); }
}
}
Что я делаю Неправильно здесь?
Вы должны вернуть «Задачу», вы хотите избежать использования «async void» в качестве возвращаемого типа. См. Https://msdn.microsoft.com/en-us/magazine/jj991977.aspx –
@Will: Вероятно, проблема связана с методом 'Start', который не отображается. Пожалуйста, упростите минимальный, воспроизводимый пример - определите синхронный асинхронный код и просто спросите об этом. –
@RonBeyer Возврат задачи действителен, за исключением, или, как я слышал, при работе с обработчиками событий, которые являются методом запуска. – Will