0

I have a long operation that creates some tools on the main UI.

When I run this operation the UI hangs. I want to update a ProgressBar to indicate the operation status percentage but it doesn't update.

My code looks like this:

Sub DoWork()
    DoLongOperation()
End sub

sub DoLongOperation()
 For K as integer = 0 to 50
   'Here I create tools and add them to a StackPanel in the window
   'While doing this I update a progressBar value
   ProgressBar1.value=Some value
 Next
End sub

How do I do this? I tried async await but it can't deal with operations that creates tools to the main UI.

TheLethalCoder
  • 6,668
  • 6
  • 34
  • 69
  • You should be using `IProgress` with `await`. See [here](http://blog.stephencleary.com/2012/02/reporting-progress-from-async-tasks.html) for details. – Matthew Watson Feb 03 '16 at 10:03
  • http://stackoverflow.com/a/18033198/2882256 – Alex B. Feb 03 '16 at 10:21
  • That's great, but doesn't meet what I want to do. My operation can't be run in a separate thread, because it creates UI elements, not just a long operation. So, I may say. It can't has solution ever. – Montasir Hef Feb 04 '16 at 02:12

3 Answers3

0

The reason why the ProgressBar is not updated is that you still blocks the main thread. Your long operation should be invoked in an additional thread. So you can use for example the Task.Run method. One more thing to remember, in an additional thread you can NOT change any UI, to do that you should use for example Dispatcher property of your window.

private void Button_Click(object sender, RoutedEventArgs e)
{
    Task.Run(() =>
    {
        for(int i = 0; i < 50; i++)
        {
            Thread.Sleep(1000); // some long operation

            var progress = i*2;

            Dispatcher.Invoke(() =>
            {
                progressBar1.Value = progress;
            });
        }
    });
}
0

As said before in order to update the UI, or still make it unblocked and usable while doing really long operation you must do that operation in other thread. You can use BackgroundWorker to do that. If you don't wanna create new methods only for the new thread you can delegate them so you can do something like this:

Imports System.ComponentModel
Sub DoLongOperation()
    Dim bgWorker As New System.ComponentModel.BackgroundWorker()
    bgWorker.WorkerReportsProgress = True

    AddHandler bgWorker.DoWork, 
        Sub(sender, args)
            For K as integer = 0 to 50
                'Here I create tools and add them to a StackPanel in the window
                'While doing this I update a progressBar value

                bgWorker.ReportProgress(123)
            Next
        End Sub

    AddHandler bgWorker.ProgressChanged,
        Sub(sender, args)
            ProgressBar.Value = args.ProgressPercentage
        End Sub

    AddHandler bgWorker.RunWorkerCompleted,
        Sub(sender, args)

        End Sub

End sub

Since you cannot add/remove or do other things from other thread on the StackPanel, you can put the item that you want to add into the bgWorker.ReportProgress() method and then update it from .ProgressChanged event so it will be like that:

AddHandler bgWorker.DoWork, 
    Sub(sender, args)
        For K as integer = 0 to 50
            'Here I create tools and add them to a StackPanel in the window
            'While doing this I update a progressBar value

            Dim item As Object 'stackpanel's item that you want to add later on. Can be any type

            bgWorker.ReportProgress(123, item)
        Next
    End Sub

AddHandler bgWorker.ProgressChanged,
    Sub(sender, args)
        ProgressBar.Value = args.ProgressPercentage
        StackPanel.Chlidren.Add(args.UserState)
    End Sub
Kuba Wasilczyk
  • 1,118
  • 3
  • 11
  • 20
  • Great. I understood it. I did not test it till now. But, I will as soon as possible. Thanks. – Montasir Hef Feb 03 '16 at 22:16
  • I've got this error "The calling thread must be STA, because many UI components require this." I made the creation operation in a separate sub and tagged it with _ and didn't work also. – Montasir Hef Feb 03 '16 at 23:10
  • You can try to invoke the method `Application.Current.Dispatcher.Invoke((Action)delegate{}` or create new `Thread` – Kuba Wasilczyk Feb 05 '16 at 08:59
  • try to look [here](http://stackoverflow.com/questions/2329978/the-calling-thread-must-be-sta-because-many-ui-components-require-this) for the answer – Kuba Wasilczyk Feb 05 '16 at 09:00
0

I got it. I divided my creation sub into 50 parts. At the end of creation of one tool I can set the progress bar. After that, I can create the second one. and go on.