Page 1 of 2
update UI in Task?
Posted: Thu Mar 15, 2018 7:06 pm
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
update UI in Task?
Posted: Thu Mar 15, 2018 8:24 pm
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
update UI in Task?
Posted: Thu Mar 15, 2018 9:58 pm
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);
}
update UI in Task?
Posted: Fri Mar 16, 2018 4:23 am
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
update UI in Task?
Posted: Fri Mar 16, 2018 7:42 am
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
update UI in Task?
Posted: Fri Mar 16, 2018 7:53 am
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
update UI in Task?
Posted: Fri Mar 16, 2018 8:02 am
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
update UI in Task?
Posted: Fri Mar 16, 2018 8:09 am
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
update UI in Task?
Posted: Fri Mar 16, 2018 10:05 am
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
update UI in Task?
Posted: Fri Mar 16, 2018 10:27 am
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