I have the following code:
var progress = new Progress<int>(valor => progressElement.Value = valor);
await Task.Run(() =>
{
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
var children = LogicalTreeHelper.GetChildren(canvas);
//count the number of children in a separate variable
var childaux = LogicalTreeHelper.GetChildren(canvas);
int numChildren = ((List<object>)childaux.OfType<object>().ToList()).Count();
progressElement.Maximum = numChildren;
int childcont = 0;
foreach (var child in children)
{
//long code work
childcont ;
((IProgress<int>)progress).Report(childcont);
Thread.Sleep(100);
}
}));
});
The result is, the progresss bar updating at the end of the loop, instead of refreshing in real time. I cant remove BeginInvoke because then i cant access my canvas element. Any help is appreciated.
CodePudding user response:
Assuming you are using a view model, bind the progress-bar value to some value, say MyProgress
, then update this value using a Progress<T>
:
int myProgress;
int MyProgress{
get => myProgress;
set{
myProgress;
OnPropertyChanged(nameof(MyProgress));
}
public async void StartSlowMethod(){
var progress = new Progress<int>();
progress.ProgressChanged = p => MyProgress = p;
var result = await Task.Run(() => SlowMethod(progress));
// Handle the result
}
public double SlowMethod(IProgress<int> progress){
for(int i = 0; i < 1000; i ){
progress.Report(i);
...
}
}
Note that this is written on free hand and not tested, there might be some errors I missed. I would recommend checking the official examples.
You will need to ensure the Max-value of the progress bar has the correct value. I usually prefer to report a double from 0-1, and scale this to the progress-range just before displaying. I also prefer to wrap all of this in a helper class to make it easier to start methods on a background thread, and display a progress bar while it is working.
CodePudding user response:
What you're doing there is starting up a background thread then that just starts up a process on the ui thread, which seems to be very expensive.
Unless this is some sort of much simplified version of your real code, the task is pointless. You might as well just run all that code on the ui thread.
Your big problem there though is
Thread.Sleep(100);
You're blocking the UI thread which is the thing would render any changes to UI.
If your method was instead async:
Application.Current.Dispatcher.BeginInvoke(new Action(**async** ()
You could then instead of sleeping the thread do:
await Task.Delay(100);
Which would free up the ui thread for 100ms
You should not use UI as a data store though. If you instead used mvvm and bound viewmodels which were templated out into UI then you could work with the data in those viewmodels rather than the things in the cannvas.
Then you could likely run your expensive code on a background thread and just dispatch progress changes back to the UI thread. In fact if you bound the value on a progress bar to a property on a viewmodel implements inotifypropertychanged then you would likely find such a simple binding's change notification was automatically marshalled back to the UI thread and you needed no dispatcher code.
CodePudding user response:
I haven't done this in a while, but I'd recommend using a "background worker" thread, which worked well for me a few times before