1

Here is the code that instantiate a window with an indeterminate progress bar, this code is called in a viewmdodel of some view :

Views.InstallingWindow installing = new Views.InstallingWindow();
installing.Show();
Application.Current.Dispatcher.Invoke(timeConsumingMethod, DispatcherPriority.Normal);
installing.Close();

And here is the xaml of the window

<Window x:Class="Blabla.Views.InstallingWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:local="clr-namespace:PatcherClient.Views"
    mc:Ignorable="d"
    Title="InstallingWindow" Height="150" Width="300"
    WindowStartupLocation="CenterScreen">
<Grid>
    <StackPanel Margin="10">
        <ProgressBar Width="200" Height="20" Margin="10" Orientation="Horizontal" IsIndeterminate="True" />
        <TextBlock HorizontalAlignment="Center" Name="StatusText" Margin="10" Height="50" Foreground="Black" Text="Installing ..."/>
    </StackPanel>
</Grid>

On my computer, the progress bar does not animate. How to fix it ?

Sylvain B.
  • 719
  • 5
  • 31
  • 1
    You can't execute timeConsumingMethod on the UI thread if you want it to be able to animate the ProgressBar. – mm8 Jun 27 '17 at 12:40
  • Running long time consuming methods is not what the dispatcher is for, possibly related: https://stackoverflow.com/questions/1644079/change-wpf-controls-from-a-non-main-thread-using-dispatcher-invoke – user6144226 Jun 27 '17 at 12:40
  • Use `BeginInvoke` instead. – XAMlMAX Jun 27 '17 at 12:44

2 Answers2

4

Dispatcher.Invoke executes your timeConsumingMethod delegate on the UI thread so it will be blocked.

Try this instead in an async method:

installing.Show();
await Task.Run(() => timeConsumingMethod());
installing.Close();

If your timeConsumingMethod accesses any UI component (eg. sets control properties), then you should wrap these accesses into Dispatcher.Invoke only.

György Kőszeg
  • 17,093
  • 6
  • 37
  • 65
3

This will execute timeConsumingMethod on the dispatcher thread:

Application.Current.Dispatcher.Invoke(timeConsumingMethod, DispatcherPriority.Normal);

And the dispatcher cannot both execute your method and animate the ProgressBar simultaneously.

You want to execute timeConsumingMethod on a background thread. The easiest way to do this is to start a new Task and then close the window once the task has completed:

Views.InstallingWindow installing = new Views.InstallingWindow();
installing.Show();
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
    timeConsumingMethod();
}).ContinueWith(task =>
{
    installing.Close();
}, System.Threading.CancellationToken.None, System.Threading.Tasks.TaskContinuationOptions.None, 

System.Threading.Tasks.TaskScheduler.FromCurrentSynchronizationContext());

Note that for this to work, you cannot access any UIElement in your timeConsumingMethod() method. A UIElement may only be accessed on the UI thread on which it was originally created.

mm8
  • 163,881
  • 10
  • 57
  • 88
  • thanks for this fast answer ! Because I use .Net 3.5 i worked out a solution using method.BeginInvoke(), and in the callback I use the Dispatcher to close the window. Actually, I had not noticed that the Dispatcher is for the UI objects, which is what everybody pointed out... – Sylvain B. Jun 27 '17 at 14:10