C# mixed use of Task and Dispatcher.Invoke, why it halts? - wpf

I tried below snippet:
public Task RunUiTask(Action action)
{
var task = Task.Factory.StartNew(() =>
{
Dispatcher.Invoke(DispatcherPriority.Background, action);
});
return task;
}
private void OnCreateTask(object sender, RoutedEventArgs e)
{
var task = RunUiTask(() =>
{
for(int i=0;i<10;i++)
{
ResultTextBlock.Text += i.ToString();
}
});
task.Wait(); //(a) Program stopped here
ResultTextBlock.Text += "Finished"; //(b) Never called;
}
I couldn't understand why, when OnCreateTask (a button click event handler) is called, the program halts at (a), and (b) is never called.
Note: I know I can use Dispatcher.BeginInvoke to make program responsive, but this is not my concern here.
Can any body tell why the program halts at (a), and why (b) is never called? Thanks.

The call of
Dispatcher.Invoke(DispatcherPriority.Background, action);
will execute Action in your UI Thread and will return after Action is executed.
The Problem is, that your UI Thead is blocked because of the task.Wait() in your OnCreateTask, so the Action will never be executed and you have a Deadlock.
EDIT
Instead of your task.Wait() you should use a Continuation and Update ResultTextBlock.Text
task.ContinueWith(t=>{
ResultTextBlock.Text += "Finished";
}, TaskScheduler.FromCurrentSynchronizationContext());

Related

DataGrid Update Instantly [duplicate]

I tried to use the below code to make a 2 second delay before navigating to the next window. But the thread is invoking first and the textblock gets displayed for a microsecond and landed into the next page. I heard a dispatcher would do that.
Here is my snippet:
tbkLabel.Text = "two mins delay";
Thread.Sleep(2000);
Page2 _page2 = new Page2();
_page2.Show();
The call to Thread.Sleep is blocking the UI thread. You need to wait asynchronously.
Method 1: use a DispatcherTimer
tbkLabel.Text = "two seconds delay";
var timer = new DispatcherTimer { Interval = TimeSpan.FromSeconds(2) };
timer.Start();
timer.Tick += (sender, args) =>
{
timer.Stop();
var page = new Page2();
page.Show();
};
Method 2: use Task.Delay
tbkLabel.Text = "two seconds delay";
Task.Delay(2000).ContinueWith(_ =>
{
var page = new Page2();
page.Show();
}
);
Method 3: The .NET 4.5 way, use async/await
// we need to add the async keyword to the method signature
public async void TheEnclosingMethod()
{
tbkLabel.Text = "two seconds delay";
await Task.Delay(2000);
var page = new Page2();
page.Show();
}
Method 4: .NET 5.0+
Task.WaitAll(new Task[] { Task.Delay(2000) });
This solution has no downsides. It can be used in loops which is for "DispatcherTimer" and "ContinueWith" impossible.

Update UI using invoke and delegate

In WinForm, i used Invoke and delegate to update UI,it's normal.
this.Invoke((EventHandler)(delegate { txData.Text = data; } ));
But in WPF ,i used the same, and the trouble is like this,it displayed "TargetParameterCountException" and "Parameter count mismatch",
this.Dispatcher.Invoke((EventHandler)(delegate { txData.Text = data; } ));
So could you please advice of what may went wrong here ? Thanks in advance.
Try use the application Dispatcher for invoking method on the UI thread:
Application.Current.Dispatcher.BeginInvoke((Action) (() => SomeFoo()));
Or
Application.Current.Dispatcher.Invoke((Action) (() => SomeFoo()));
EventHandler is defined like this :
public delegate void EventHandler(object sender, EventArgs e);
So, you need to pass required parameters,
this.Dispatcher.Invoke((EventHandler)(delegate { txData.Text = data; }), this, null);
But, standard way is this :
this.Dispatcher.Invoke(() => { txData.Text = data; });
or,
this.Dispatcher.Invoke(delegate { txData.Text = data; });

Busy indicator does not show until data shows

I have a control in my project that provides a busy indicator (rotating circle). I'd like it to run when a user selects a file to load data into a data grid. The busy indicator does not show up until my data grid is populated though. How can I get my busy indicator to show while the data is being retrieved? I believe I'm supposed to use a thread, but am not too knowledgeable with them yet and am trying to learn. I've tried many different ways and below is my latest attempt, which I do not know if I am anywhere close.
public void DoWork()
{
this.StartProgressBar();
Task.Factory.StartNew(() =>
{
UIDispatcher.Current.BeginInvoke(() =>
{
if (fileCompleted != null && !string.IsNullOrEmpty(fileCompleted.SelectedFile))
{
this.TestResults.Clear();
LoadedFileInfo info = this.Model.OpenTestCompleted(fileCompleted.SelectedFile);
foreach (var model in info.Sequence.Models)
{
foreach (var details in model.Tests)
{
this.TestResults.Add(new TestResultsModel(details, model.Name.Substring(0, model.Name.IndexOf('.'))));
}
}
}
});
});
}
private void StartProgressBar()
{
TaskScheduler scheduler = TaskScheduler.FromCurrentSynchronizationContext();
CancellationToken cancelationToken = new CancellationToken();
Task.Factory.StartNew(() => this.StopProgressBar()).ContinueWith(
m =>
{
this.ToggleProgressBar = true;
},
cancelationToken,
TaskContinuationOptions.None,
scheduler);
}
private void StopProgressBar()
{
this.ToggleProgressBar = false;
}
I really agree with #Ben, you should research how to use Tasks. You are creating background threads, and doing work on the UI thread in them anyway, which inevitably hold the UI thread. Try something simpler and see if it works. As far as your cancellation token goes, I don't see how and were you'd be able to reset it, as it is not a property in your class, so here's a sample without it..
How about something like this:
public void DoWork()
{
//done on the UI thread
this.ToggleProgressBar = true;
//done on the background thread, releasing UI, hence should show the progress bar
Task.Factory.StartNew(() =>
{
if (fileCompleted != null && !string.IsNullOrEmpty(fileCompleted.SelectedFile))
{
this.TestResults.Clear();
LoadedFileInfo info = this.Model.OpenTestCompleted(fileCompleted.SelectedFile);
foreach (var model in info.Sequence.Models)
foreach (var details in model.Tests)
this.TestResults.Add(new TestResultsModel(details, model.Name.Substring(0, model.Name.IndexOf('.'))));
}
//after all that (assumingly heavy work is done on the background thread,
//use UI thread to notify UI
UIDispatcher.Current.BeginInvoke(() =>
{
this.ToggleProgresBar = false;
}
});
}

Create a infinity loop and stop it

I'd like to create a infinity loop when I push a button and stop it when I release the button, so I create this, but it doesn't stop when I release the button...
can you help me?
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_DOWN) {
if (v.getId() == R.id.up) {
for(;;){
//make my action
if(event.getAction() == MotionEvent.ACTION_UP) {
break;
}
}
}
}
return false;
}
}
The infinity loop will drive the EventQueue into no respond to any other Events. So the MotionEvent.ACTION_UP could not be handled also. You should make the event handler method faster. Otherwise the event dispatching thread could hang.

Invoke mouse curser when using task

Im using task process and and during the process I want to invoke the mouse
courser ,how should I do that ?
Task.Factory.StartNew(() =>
{
try
{
_isEnabled = false;
_canBack = false;
....
I've tried with the following which is not work...
System.Windows.Input.Cursors.Wait;
Set the window cursor on entry and exit of task. Since you can access window object only on UI thread so for accessing cursor property you have to delegate it on UI thread.
Task.Factory.StartNew(() =>
{
Application.Current.Dispatcher.Invoke(new Action(() =>
Cursor = Cursors.Wait));
Thread.Sleep(5000); // Some time consuming operation here.
Application.Current.Dispatcher.Invoke(new Action(() =>
Cursor = Cursors.Arrow));
});
You can try like following:
Application.Current.Dispatcher.Invoke((Action) (() => { Mouse.OverrideCursor = Cursors.Wait; }));
Good luck

Resources