Search code examples
c#wpf

Dispatcher not updating progress bar WPF


I am using the following code in WPF application to show progress bar:

XAML:

<Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <Button Grid.Row="1" x:Name="btn_StartLengthyTask" Click="btn_StartLengthyTask_Click" Width="200" Height="30" >Start Lengthy Task</Button>
        <ProgressBar Grid.Row="2" x:Name="pb_LengthyTaskProgress" Margin="10,20"  Value="0" ></ProgressBar>
        <TextBlock Grid.Row="3" x:Name="lbl_CountDownTimer" HorizontalAlignment="Center" > 00:10</TextBlock>
        <TextBlock Grid.Row="4" x:Name="lbl_TaskStatus" >Status...</TextBlock>

C#:

private void btn_StartLengthyTask_Click(object sender, RoutedEventArgs e)
        {
            try
            {
                lbl_TaskStatus.Text = "Starting long Task...";
                Thread.Sleep(1000);
                lbl_TaskStatus.Text = "In Progress...";
                pb_LengthyTaskProgress.Value = 0;
                Task.Run(() =>
                {
                    for (int i = 0; i < 100; i++)
                    {
                        Thread.Sleep(50);
                        this.Dispatcher.Invoke(() => //Use Dispather to Update UI Immediately  
                        {
                            var test = OMS.MyOrders.Model.MyOrderExecutor.ExportImages(true);//Method taking long time to response
                            pb_LengthyTaskProgress.Value = i;
                            lbl_CountDownTimer.Text = i.ToString();
                        });
                    }
                });
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

Problem: Progress bar is not updating on UI. Thanks in advance


Solution

  • Never call Thread.Sleep. Either use a DispatcherTimer, or an async Click event handler that calls await Task.Delay(...) in a loop. Dispatcher.Invoke is not needed. Wrap the long running method in an awaited Task.Run call.

    private async void btn_StartLengthyTask_Click(object sender, RoutedEventArgs e)
    {
        lbl_TaskStatus.Text = "Starting long Task...";
    
        await Task.Delay(1000);
    
        lbl_TaskStatus.Text = "In Progress...";
        pb_LengthyTaskProgress.Value = 0;
    
        for (int i = 1; i <= 100; i++)
        {
            await Task.Delay(50);
    
            var test = await Task.Run(() =>
                OMS.MyOrders.Model.MyOrderExecutor.ExportImages(true));
    
            pb_LengthyTaskProgress.Value = i;
            lbl_CountDownTimer.Text = i.ToString();
        }
    }
    

    Update: it seems you only want make a single call to a long running method. The method should then report progress via the IProgress interface:

    public bool ExportImages(bool b, IProgress<double> p)
    {
        // call p.Report
    }
    

    It would be used like this:

    private async void btn_StartLengthyTask_Click(object sender, RoutedEventArgs e)
    {
        lbl_TaskStatus.Text = "Starting long Task...";
        await Task.Delay(1000);
        lbl_TaskStatus.Text = "In Progress...";
    
        var progress = new Progress<double>();
        progress.ProgressChanged += (s, p) =>
        {
            pb_LengthyTaskProgress.Value = p;
            lbl_CountDownTimer.Text = p.ToString();
        };
    
        var result = await Task.Run(() =>
            OMS.MyOrders.Model.MyOrderExecutor.ExportImages(true, progress));
    }