c# – 如何在WPF应用程序上执行异步启动?
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了c# – 如何在WPF应用程序上执行异步启动?,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含6148字,纯文字阅读大概需要9分钟。
内容图文
![c# – 如何在WPF应用程序上执行异步启动?](/upload/InfoBanner/zyjiaocheng/787/5bf8c0b011d340458f58eec28f53b367.jpg)
我在async-await后面的曲线非常落后,所以这可能是一个“问题”.
我正在研究应该是一个非常小的UI应用程序,它使用WPF NotifyIcon库从系统托盘运行.
应用程序应以下列方式非常简单地运行(对用户而言):
>该计划开始
>如有必要,会有一个启动画面告诉用户程序正在运行并提示他们登录(如果他们在之前的迭代中尚未这样做).
> WPF NotifyIcon出现在系统托盘中.
>异步执行开始
我正在运行的问题是“异步执行开始”部分.在此之前发生的所有事情都可以正常工作,但是当程序开始尝试“运行”时,UI会锁定(我的意思是,用户可以像托盘图标上的疯狂人物一样点击并且上下文菜单拒绝显示).
这种锁定发生的时间长得令人无法接受.
这是启动代码:
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( ); }
}
}
我在这做错了什么?
解决方法:
我认为您的问题与您的Start方法有关.
但首先要做的事情.你的断点没有做你期望它做的事情.一旦Start方法实际完成并且执行剩余的函数,它就会中断,而不是一旦UI线程再次释放.您必须了解,一旦执行在Start函数中离开UI同步,UI线程就可以再次运行.
找出方法实际释放执行所需的时间的好方法是等待它返回Task对象.
var pendingTask = this.Start();
Debugger.Break();
await pendingTask;
一旦Start方法命中一个在内部异步执行的函数,Task对象就会返回.一旦pendingTask实际完成,await就会返回.
在你的情况下,我认为时间将是相似的,因为Start方法没有向后台发送足够的工作.
有几种方法可以解决这个问题.如果您的Start方法没有与UI交互,那么您没问题.您只需将整个方法发送到后台并完成它.这很简单:
await Task.Run(() => this.Start());
这会将任务发送到ThreadPool的一个线程中,并立即再次释放UI. Task.Run方法有一个重载,可以自动解包Start方法返回的内部Task.
如果您的方法与UI交互,则必须在内部更改方法.查找方法内部需要很长时间但不与UI交互并将它们包装到Task.Run方法调用中的部分,如上所示.
每个await将再次建立之前存在的SynchronizationContext.因此,能够改变UI的线程中的每个等待都将确保在同一个线程中执行延续.
所以像这样的工作没有问题:
someLabel.Label = "Working…";
await Task.Run(() => DoManyThings());
someLabel.Label = "Done! :D"
我希望有所帮助.在不知道你的Start方法做什么的情况下,我无法给你更多提示.但我希望这个答案能让你走上正轨.
我通常的免责声明:我通常使用VB.net,因此我的C#代码在语法方面可能存在缺陷.如果您发现任何错误,请随时编辑或告诉我有什么问题.
内容总结
以上是互联网集市为您收集整理的c# – 如何在WPF应用程序上执行异步启动?全部内容,希望文章能够帮你解决c# – 如何在WPF应用程序上执行异步启动?所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。