【发布时间】:2014-07-17 12:51:26
【问题描述】:
尽管使用了应在单独线程上创建的任务,但我遇到了 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() 中运行更密集的操作然后我再次显示窗口时,动画会冻结。
如果我在密集操作完成后让窗口保持打开状态,动画会按预期继续。
为了澄清,流程如下:
显示加载屏幕 > 做一些工作 > 隐藏加载屏幕 > 显示另一个窗口 > 在关闭该窗口之前等待用户交互 > 再次显示加载窗口 > 做更多工作 > 再次隐藏加载窗口 > 显示主应用程序窗口
在Task 的线程(不涉及任何UI 代码)的do some more work 部分,我的动画冻结了。
我正在使用 .Net 4.0.3。
如果我删除对Application.Current.Dispatcher.Invoke 的调用,我会收到预期的跨线程冲突异常,告诉我调用线程不拥有我要访问的对象,因此我的代码肯定不会在 UI 线程上运行.
这是什么原因造成的,我该如何防止它发生?
【问题讨论】:
标签: c# wpf multithreading animation