I have an issue with UI processes freezing in my application despite using a Task that should be created on a separate thread.
I have an image that I am rotating in a loading window that is being shown/hidden as needed, here is the 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>
I also have an event handler in my code-behind for the window that is restarting the animation when the window's visibility is changed:
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();
}
}
I have a controller that is responsible for showing/hiding/changing the message in the loading window, which has methods as follows:
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();
}));
}
In my App.xaml.cs
file I have code similar to the following to start my application's loading process:
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);
I use TaskCreationOptions.LongRunning
to ensure that I actually get a new thread.
Because the code in PerformNotSoIntensiveLoadingOperation()
doesn't take very long to run (on my PC anyway), the loading animation works fine.
When I hide the window and a more intensive operation is run in PerformIntensiveLoadingOperations()
then I show the window again, the animation freezes.
If I leave the window open after the intensive operation is complete, the animation continues as I would expect.
For clarification, the process is as follows:
Show loading screen > do some work > hide loading screen > show another window > wait for user interaction before closing that window > show the loading window again > do some more work > hide loading window again > show main application window
It's during the do some more work
part on the Task
's thread (which doesn't touch any UI code) that my animation is freezing.
I am using .Net 4.0.3.
If I remove my calls to Application.Current.Dispatcher.Invoke
I get the expected cross-thread violation exception telling me that the calling thread does not own the object I am trying to access, so my code is definitely not being run on the UI thread.
What is causing this and how can I prevent it from happening?