update UI in Task?

This forum is meant for anything you would like to share with other visitors
User avatar
SHirsch
Posts: 281
Joined: Tue Jan 30, 2018 8:23 am

update UI in Task?

Post by SHirsch »

Hi all,

source is following code:

Code: Select all

SELF:obtnInstallService:Enabled := FALSE
AWAIT Task.Run(Action{{ => 
   VAR helper := WindowsServiceHelper{}
   helper:InstallService()
   SELF:CheckButtons()
}})
In another thread there was a suggestion to move CheckButtons (update of UI, with checking InvokeRequired) after AWAIT because 'It's generally better to avoid updating the UI thread from inside a task.' (by Nick).
My questions:
1. Why (is better to avoid...)?
2. How should progressbar update be done in long running task?

Regards,
Stefan
NickFriend
Posts: 248
Joined: Fri Oct 14, 2016 7:09 am

update UI in Task?

Post by NickFriend »

Hi Stefan,

As to why, I'm sure Robert or Chris can give a better answer than me, but I think the basic idea is that when you start a task it runs on a separate thread from the UI. If you then carry out processes that directly affect the UI, then you're literally crossing your threads!... anyway you get the idea ;)

To update the UI thread, you need to use a TaskScheduler. In pseudo code (C# as I'm not reliable in X#)

Code: Select all

TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
await Task.Factory.StartNew(()=> { MyMethod(uiScheduler, otherparams...); });

private void MyMethod(TaskScheduler uiScheduler, otherparams...)
{
    do stuff....

    // update the UI
    Task.Factory.StartNew(()=>
    {
       // from here you can now safely access any methods or properties affecting the ui
    }, CancellationToken.None, TaskCreationOptions.None, uiScheduler);

    continuing doing more stuff
}
HTH

Nick
ic2
Posts: 1798
Joined: Sun Feb 28, 2016 11:30 pm
Location: Holland

update UI in Task?

Post by ic2 »

I would also like to add the so called non recommended way to do this. Not sure why it's not recommended, usually that it not explained, as it works fine. Nick's method certainly works 'by the book' but you need to 'await' all your methods to use it.

Dick

this.UpdateProgressBar(1);
ExtensionMethods.ProcessUITasks();

public static void ProcessUITasks()
// When called, forces the UI to redraw.
{
DispatcherFrame frame = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background, new DispatcherOperationCallback(delegate (object parameter) {
frame.Continue = false;
return null;
}), null);
Dispatcher.PushFrame(frame);
}
User avatar
wriedmann
Posts: 3644
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

update UI in Task?

Post by wriedmann »

Hello,

AFAIK there is BackgroundWorker class ( https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker(v=vs.110).aspx ) for operations on a separate thread, usable for WPF applications.

It can be used as follows:

Code: Select all

_oWorker := BackgroundWorker{}
_oWorker:WorkerReportsProgress := true
_oWorker:WorkerSupportsCancellation := true
_oWorker:DoWork += ExecuteProcessAsync
_oWorker:ProgressChanged += ProgressProcessAsync
_oWorker:RunWorkerCompleted += CompletedProcessAsync
_oWorker:RunWorkerAsync()

method ProgressProcessAsync( oSender as object, e as ProgressChangedEventArgs ) as void
	
_oProgress:Text	:= ( string ) e:UserState
	
return   

method CompletedProcessAsync( oSender as object, e as RunWorkerCompletedEventArgs ) as void	

_oProgress:Text	:= ( string ) e:UserState
_oWorker := null                            
	
return  

method ExecuteProcessAsync( oSender as object, e as DoWorkEventArgs ) as void
local oProcess as AsyncProcess
	
oProcess := AsyncProcess{ _oWorker }
oProcess:Process()
	
return
I have also done a sample in X#/WPF - I've attached a XIDE export file of a complete sample of it.
Or you can look here:

https://www.dotnetperls.com/backgroundworker

Wolfgang
Attachments
BackgroundworkerApp.zip
(3.1 KiB) Downloaded 22 times
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
NickFriend
Posts: 248
Joined: Fri Oct 14, 2016 7:09 am

update UI in Task?

Post by NickFriend »

Hi Dick,

The code you posted is a way of forcing the UI to update when you've blocked it by some long-running process, it has nothing to do with async programming or await. The whole point of asynchronous programming is to avoid blocking the UI in the first place. This is what task-based async is all about.

I've used all three proposed methods - in fact I think I gave you that ProcessUITasks code way back in the Vulcan ng days, before I knew better ;-) - and I can categorically state that task based is the way to go for async. It results in very clear code, which is very important if you apply async programming large scale, and it works 100%.

BackgroundWorker as illustrated by Wolfgang will work, but it results in much more convoluted code (though I'm sure Wolfgang will disagree!).

Nick
Juraj
Posts: 161
Joined: Mon Jan 09, 2017 7:00 am

update UI in Task?

Post by Juraj »

Hi Wolfgang,

My English is not very good but I will try to formulate my question.
I want to ask what is the best solution.
The client runs an app and works in it. While running an app, I need to track the partition of the database where a new table appears. If it appears, add it to another table and it will be deleted. This will happen several times during app launch.
The question is what to use - bacgroudworker in the application or write another program as a system service?

Juraj
User avatar
SHirsch
Posts: 281
Joined: Tue Jan 30, 2018 8:23 am

update UI in Task?

Post by SHirsch »

Hi all,

in the current project I'm using Windows.Forms. So complete call looks like this:

Code: Select all

PRIVATE METHOD CheckButtons() AS VOID
    IF SELF:InvokeRequired == TRUE
        SELF:BeginInvoke(Action{CheckButtons})
        RETURN
    ENDIF
    //savely executed UI Code
RETURN 
So it doesn't matter from which task or thread the method is called it's always executed savely.

In WPF I have following code that is called from tasks or threads:

Code: Select all

void runCopyFileEvent(CopyInfo cpi)
{
   this.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
         new UpdateProgressBarDelegate(UpdateProgressBar), cpi);
}
This way I do not have to implement UI update handling in every task or thread. Just call the method inside the task and everything else is handled in UI class. OK, the method is not called directly from threads, here I use delegates. Advantage in my opinion is that the code of the thread is independable of UI framework.

Any drawbacks?

Regards,
Stefan
User avatar
wriedmann
Posts: 3644
Joined: Mon Nov 02, 2015 5:07 pm
Location: Italy

update UI in Task?

Post by wriedmann »

Hi Juraj,

I would do that definitely in a separate Windows service. It is a separate work that has nothing to do with the currently running application.

If you need a sample for a Windows service, I can give you one.

Wolfgang
Wolfgang Riedmann
Meran, South Tyrol, Italy
wolfgang@riedmann.it
https://www.riedmann.it - https://docs.xsharp.it
ic2
Posts: 1798
Joined: Sun Feb 28, 2016 11:30 pm
Location: Holland

update UI in Task?

Post by ic2 »

Hello Nick,
The whole point of asynchronous programming is to avoid blocking the UI in the first place. This is what task-based async is all about.

I've used all three proposed methods - in fact I think I gave you that ProcessUITasks code way back in the Vulcan ng days, before I knew better ;-) - and I can categorically state that task based is the way to go for async. It results in very clear code, which is very important if you apply async programming large scale, and it works 100%.
I agree with you and as I wrote, the code you gave here (and earlier to me) works. But the program needs to be async all over for it to work. I've applied the code I gave in a program which was not async and that code also works 100%.

I do not consider it less readable, actually on the contrary: I just add one line of code after the update of the progress bar and it remains visible. Like with the VO ApplcationExecWhileEvent() which worked even better because you only had to issue it once.

I must add that I consider it pretty poor that in .Net there is something like a progress bar but when applied it simply doesn't work. Until you transform your program to a fully separated set of tasks, or have it followed by the "ProcessUITasks" task method. If .Net was well designed a programmer shouldn't have to worry about how it works, as long as it works 'out of the box'. I always thought that was the whole idea of .Net but the more I work with it the more I methods and namespaces I see which do not work without a lot of extra research and programming.

Dick
NickFriend
Posts: 248
Joined: Fri Oct 14, 2016 7:09 am

update UI in Task?

Post by NickFriend »

Dick wrote:I do not consider it less readable
Hi Dick,

Re. the readability, I was referring to BackgroundWorker which is a valid alternative for async programming.

Anyway, I was giving an opinion over the general approach to async and Stefan obviously has different requirements, and an approach which I'm not qualified to comment on.

Nick
Post Reply