尽管使用了应该在单独的线程上创建的Task,但我的应用程序中的UI进程冻结存在问题。
我有一个图像,我正在一个正在显示/隐藏的加载窗口中旋转,这里是XAML:
<Image Name="LoadingCog" Source="/Resources/Cog.png" Margin="0,37" Width="60" Height="60" HorizontalAlignment="Center" RenderTransformOrigin="0.5,0.5">
<Image.Resources>
<Storyboard x:Key="LoadingCogRotateStoryboard">
<DoubleAnimation
Storyboard.TargetName="LoadingCogRotateTransform"
Storyboard.TargetProperty="Angle"
By="10"
To="360"
Duration="0:0:7"
FillBehavior="Stop"
RepeatBehavior="Forever" />
</Storyboard>
</Image.Resources>
<Image.RenderTransform>
<RotateTransform x:Name="LoadingCogRotateTransform" Angle="0" />
</Image.RenderTransform>
</Image>
我的代码隐藏中还有一个事件处理程序,用于在窗口的可见性发生变化时重新启动动画的窗口:
void LoadingScreenWindow_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
{
var storyBoard = (System.Windows.Media.Animation.Storyboard)LoadingCog.Resources["LoadingCogRotateStoryboard"];
if (IsVisible == true)
{
storyBoard.Seek(TimeSpan.Zero);
storyBoard.Begin();
}
else
{
storyBoard.Stop();
}
}
我有一个控制器负责在加载窗口中显示/隐藏/更改消息,其中的方法如下:
internal void ShowLoadingScreen(string loadingMessage)
{
Application.Current.Dispatcher.Invoke((Action)(() =>
{
_viewModel.LoadingMessage = loadingMessage;
_loadingWindow.Show();
}));
}
internal void HideLoadingScreen()
{
Application.Current.Dispatcher.Invoke((Action)(() =>
{
_loadingWindow.Hide();
}));
}
在我的App.xaml.cs
文件中,我有类似以下代码来启动我的应用程序的加载过程:
Task.Factory.StartNew(() =>
{
LoadingScreenController.ShowLoadingScreen("Loading application, please wait..");
PerformNotSoIntensiveLoadingOperation();
HideLoadingScreen();
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
LoginWindow.Show();
}));
LoadingScreenController.ShowLoadingScreen("Logging in, please wait..");
PerformIntensiveLoadingOperations();
LoadingScreenController.HideLoadingScreen();
System.Windows.Application.Current.Dispatcher.Invoke((Action)(() =>
{
MainWindow.Show();
}));
}, TaskCreationOptions.LongRunning);
我使用TaskCreationOptions.LongRunning
来确保我实际获得一个新线程。
因为PerformNotSoIntensiveLoadingOperation()
中的代码运行时间不长(无论如何都在我的电脑上),加载动画效果很好。
当我隐藏窗口并在PerformIntensiveLoadingOperations()
中运行更密集的操作时,我再次显示窗口,动画冻结。
如果我在密集操作完成后让窗口保持打开状态,动画将继续如我所料。
澄清一下,过程如下:
显示加载屏幕&gt;做一些工作&gt;隐藏加载屏幕&gt;显示另一个窗口&gt;在关闭该窗口之前等待用户交互&gt;再次显示加载窗口&gt;做更多的工作&gt;再次隐藏加载窗口&gt;显示主应用程序窗口
在do some more work
主题的Task
部分{我没有触及任何用户界面的代码},我的动画正在冻结。
我正在使用.Net 4.0.3。
如果我删除了对Application.Current.Dispatcher.Invoke
的调用,我得到了预期的跨线程冲突异常,告诉我调用线程不拥有我试图访问的对象,所以我的代码肯定没有运行在UI线程。
造成这种情况的原因是什么?如何防止它发生?
答案 0 :(得分:2)
在你的Task
中,你正在努力做UI工作......我不相信你能做到这一点。您无法从后台线程访问UI控件。您可能会发现正在忙于呈现或通知并使忙指示符冻结的UI线程。
要明确,所有UI工作都是在UI线程上完成的。因此,如果您尝试在后台线程中在UI中显示某些内容,则您将获得标准错误,或者代码将自动封送到UI线程中。
我在大型WPF应用程序中有类似的设置,它也遇到了同样的问题。在从数据库中提取数据时,忙指示符显示正常,但是当应用程序开始在UI中呈现数据时,忙指示符会冻结。
我甚至尝试使用动画Gif来解决这个问题,但当然他们在WPF应用程序中没有动画(自己)。编写代码来为Gif中的帧设置动画后,我发现它也遇到了同样的问题,我感到非常失望。