How to close WPF window inside of a Task - c#

I need to run a task to check the connection.
My windows should not be frozen during this check.
So I start the Task, and close my window at the end of this task.
But this returns an exception: InvalidOperationException:'The calling thread cannot access this object because another thread owns it'.
Like this :
private void Window_ContentRendered(object sender, System.EventArgs e)
{
Task.Run(() =>
{
ConnectionState = false;
if (NetworkTools.CheckGlobalConnection() == (ConnectionStatus.NetworkConnectionSuccess, ConnectionStatus.ServerConnectionSuccess))
{
ConnectionState = true;
}
this.Close();
});
}
How do I close my window at the end of the task without freezing it and without having this exception ?
(I have a progress bar that rotates indefinitely)

Or you could just use async await. Task.Run will offload and the await will create a continuation on current SynchronizationContext. In turn giving control back to the UI and closing on the right thread.
private async void Window_ContentRendered(object sender, System.EventArgs e)
{
await Task.Run(() =>
{
ConnectionState = false;
if (NetworkTools.CheckGlobalConnection() == (ConnectionStatus.NetworkConnectionSuccess, ConnectionStatus.ServerConnectionSuccess))
ConnectionState = true;
});
this.Close();
}
Also as noted, Calling ConfigureAwait(false), would not be the right thing to do in this case

Use Dispatcher to queue window closing logic on the unique UI thread.
Something like
Dispatcher.Invoke(
() =>
{
// close window here
});
Whatever is passed into .Invoke(...) as a delegate, is invoked on the UI thread and hence has right to access all UI elements. It is common (and the only correct) way to deal with UI-mutations within non-UI threads.

As an alternate method you can use ContinueWith
private void Window_ContentRendered(object sender, System.EventArgs e)
{
Task.Run(() =>
{
// Your code
}).ContinueWith((tsk) =>
{
this.Close();
}, TaskScheduler.FromCurrentSynchronizationContext());
}

Related

How to access UI component from Task in Window Closing event correctly?

I have a WPF application. In the window closing event i need to call an async method with return value that itself needs to access UI components.
To run this code, create a fresh wpf application, add this line to the MainWindow.xaml
Closing="Window_Closing"
and this code to the MainWindow.xaml.cs
private async void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
Console.WriteLine("Start");
// while the method is awaited, Window_Closing gives the control back to the caller
// since Window_Closing is void, WPF goes on and closes the application
// before the long running task has been finished => FAIL
var result = await Test();
// creates a dead lock. Breakpoint on the Task.Delay(1000) is hit
// then nothing happens and the application is locked => FAIL
var result = Test().Result;
// Task.Delay(1000) works, but when the Background shall be accessed...
// "The calling thread cannot access this object because a different thread owns it."
// => Fail
var result = Task.Run(() => Test()).Result;
// same like the first try.
// WPF goes on and closes the application
// before the result has ever been set => FAIL
var result = false;
await Application.Current.Dispatcher.BeginInvoke(async () =>
{
result = await Test();
});
Console.WriteLine(result);
e.Cancel = result;
}
public async Task<bool> Test()
{
await Task.Delay(1000);
this.Background = Brushes.AliceBlue;
return true;
}
How (without changing the Test method) do i get the desired/expected behavior?
Set e.Cancel = true; before any async call. If the Window shall be closed later, call Close() after detaching the Closing event handler.
A simple example:
private async void Window_Closing(object sender, CancelEventArgs e)
{
e.Cancel = true;
if (!await Test()) // your Test method or the one below
{
Closing -= Window_Closing;
Close();
}
}
Use a Dispatcher invocation to access UI elements from a Task action that runs in a background thread. For reference, see The calling thread cannot access this object because a different thread owns it.
public Task<bool> Test()
{
// a long running task on a background thread
return Task.Run(() =>
{
Dispatcher.Invoke(() =>
{
Background = Brushes.AliceBlue;
});
Thread.Sleep(1000);
return new Random().Next(2) == 0; // close or not
});
}

Invoke Method never returns

I have a thread running, which grabs images from a camera, saves them, and updates the pictureBox to display that bitmap. When I want to exit the application, I set my bool exitApplication to true, but it never returns on the join() call.
public void GrabThread()
{
Bitmap copyBmp;
while (!exitApplication)
{
mImage = m_camera.GrabImage();
SaveImage();
copyBmp = (Bitmap)mImage.bmp.Clone();
if (pictureBox1.InvokeRequired)
{
pictureBox1.Invoke(new MethodInvoker(
delegate ()
{
pictureBox1.Image = copyBmp;
}));
}
else
{
pictureBox1.Image = copyBmp;
}
m_camera.ReleaseImage();
}
}
My Exit code:
exitApplication = true;
threadGrab.Join();
m_camera.Stop();
Close();
When I call Break All from the Debug menu, the Thread hangs at
pictureBox1.Invoke(new MethodInvoker(
delegate ()
{
pictureBox1.Image = copyBmp;
}));
Why? And how can I prevent this? Currently I have to call Abort() on the thread to exit it.
This code deadlocks because pictureBox1.Invoke is trying to execute code in the UI thread. That thread though is blocked by the call to threadGrab.Join();
This can be fixed using async/await. await awaits already executing asynchronous tasks without blocking the way Join() does. When it finishes, execution resumes in the original synchronization context. In a WinForms or WPF application, this means execution resumes on the UI thread.
I assume the only thing that needs to run in the background is m_camera.GrabImage();. In that case, the code could look something like this :
public async void Button1_Click(object sender, EventArgs args)
{
await Task.Run(()=>m_camera.GrabImage();
//Back in the UI thread
SaveImage();
var copyBmp = (Bitmap)mImage.bmp.Clone();
pictureBox1.Image = copyBmp;
m_camera.ReleaseImage();
}
Task.Run will execute m_camera.GrabImage() in a threadpool thread. await will await for this to finish without blocking the UI thread. When it finishes, execution resumes in the UI thread which means there's no need for Invoke.
This could run in a loop too :
public async void Capture_Click(object sender, EventArgs args)
{
while(someCondition)
{
await Task.Run(()=>m_camera.GrabImage();
SaveImage();
var copyBmp = (Bitmap)mImage.bmp.Clone();
pictureBox1.Image = copyBmp;
m_camera.ReleaseImage();
}
}
It may not the right answer but you can using both OnClosing and OnClosed to Stop your camera then close your app, I don't know does it work in Winform but it worked with me in WPF
protected override void OnClosing(CancelEventArgs e)
{
e.Cancel = false; //prevent your application to shutdown
Camera.Stop(); //stop your camera
base.OnClosing(e);
}
protected override void OnClosed(EventArgs e)
{
base.OnClosed(e);
Application.Current.Shutdown(); //then shutdown again
}
Via 2 step, your camera will stop without Join any thread. And note that this code is written in Wpf, not Winform but I thought you can find a similar way to do in Winform
Hope this answer may help you.

wpf c# background worker works when execution finished

I have a wpf application where I want to start a loading indicator before a task and end after task done. But the indicator starts after the task executes.
What I am trying is as follows.
private void RunAllScriptsChildwdwBtnOK_Click(object sender, RoutedEventArgs e)
{
worker.RunWorkerAsync(); // this supposed to start progress bar
_RunAllScripts_Click();
}
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
this.Dispatcher.Invoke(() =>
{
... Start loading indicator
});
}
private void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
... End loading indicator
}
But loading indicator starts and ends (as supposed in worker events) only after
_RunAllScripts_Click(); method execution is complete.
(I found that after unsubscribing from worker_RunWorkerCompleted event, progress bar starts and stays as is because no code to end it).
Also I want to add that, breakpoint hits worker_DoWork method before the execution, but UI updates after execution as I indicated above.
Thanks for all help you will be able to provide.
If i was you i would use the async + await keyword for this
private async void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
// this is where you would enable your indicator
Button.IsEnabled = false;
await Task.Run(
() =>
{
// this is where you put your work, which should be executed in the background thread.
Thread.Sleep(2000);
});
// this is where you would disable it
Button.IsEnabled = true;
}
Using async/await will work. The await keyword will allow you to run work without affecting/blocking the UI thread (allowing message pumping to still occur). Once the work has finished, any code after the await keyword will execute.
Note that I have also wrapped the await work in an InvokeAsync call, as it appears that additional work you are doing required UI thread access.
private async void RunAllScriptsChildwdwBtnOK_Click(object sender, RoutedEventArgs e)
{
//TODO ... Start loading indicator
await Task.Run(async ()=>
{
await Application.Current.Dispatcher.InvokeAsync(()=>
{
_RunAllScripts_Click();
});
});
//TODO ... End loading indicator
}
Dear kind people helping me about this subject, thank you all.
This works for me, hope it works for all.
BackgroundWorker bwTestAll = new BackgroundWorker() { WorkerReportsProgress = true };
bwTestAll.DoWork += new DoWorkEventHandler(TestAll);
bwTestAll.RunWorkerCompleted += TestAll_RunWorkerCompleted;
//this is where I initialize my loading ring and other stuff and marshall background
//worker to do the main work
Dispatcher.Invoke(new Action(() =>
{
EnableLoading = true;
RunAllScriptsTest.IsEnabled = false;
}), DispatcherPriority.ContextIdle);
bwTestAll.RunWorkerAsync();
//this is my main work
void TestAll(object sender, DoWorkEventArgs e)
{
presenter.RunAllScripts(true);
}
//this is where I do my post-work stuff
private void TestAll_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
Dispatcher.Invoke(new Action(() =>
{
/
EnableLoading = false;
RunAllScriptsTest.IsEnabled = true;
DbExecGrid = this.ExecutionResults;
ShowOrHideExecGrid(this.EnableOrDisableGrid);
}), DispatcherPriority.ContextIdle);
}
*Please Notice that Dispatcher with "DispatcherPriority.ContextIdle" works for me.

C# progressbar IsIndeterminate while exectue a function

I'd like to have a indeterminate progress bar going while I'm doing a function. The problem is that while the function is working the UI freeze until the end so I end up with:
void Button1_Click(object sender, RoutedEventArgs e)
{
progressBar1.IsIndeterminate = true;
Task.Factory.StartNew(() =>
{
scrape();
});
}
The thing is that as for backgroundworker, my scrape function don't trigger. I put just the scrape(); onclick it work just fine. The scarpe is something like:
void scrape()
{
string url = "www.site.com";
var web = new HtmlWeb();
var doc = web.Load(url);
foreach (HtmlNode node in doc.DocumentNode.SelectNodes("//p[#class='bio']"))
{
//scrape things
}
progressBar1.IsIndeterminate = false;
}
The progress bar go in indeterminate but the scrape() don't trigger and the bar still in indeterminate state.
Any help?
your probably getting an exception in the code that's running on the background thread.
either catch your exception by registering to
AppDomain.UnhandledException
Or more specifically UnobservedTaskException which is like UnhandledException but specific to exceptions thrown from Tasks.
or make your method async and await the operation in a try catch clause :
async void Button1_Click(object sender, RoutedEventArgs e)
{
progressBar1.IsIndeterminate = true;
try
{
await Task.Factory.StartNew(() =>
{
scrape();
});
}
catch(AggregateException ae)
{}
finally
{
progressBar1.IsIndeterminate = false;
}
}
You should make your scrape method async and then await it in the button1_click and keep the progressBar1.IsIndeterminate = false; call in the button1_click method as you are not allowed to change that from another thread.
You are calling assigning a property from another thread. WPF does things to prevent cross thread accesses like this to keep all the UI logic in the UI thread (the main thread). If you want, you can call a method from the UI thread using a Dispatcher. You can access the dispatcher from any control using myControl.Dispatcher.Invoke(MyMethod).
It's also possible to check if you need to call a method from the dispatcher or not. Use if (myControl.Dispatcher.CheckAccess()). If this value is true, you are on the UI thread and you don't need to invoke from the dispatcher.

Is it possible to change views, once during the start of an event handler and once during end?

I get data from database on a click.
I have an event handler which when triggered should show "data retrieving..." in status bar and should change to "Ready" again just before the event handler ends.
But the text updates only once, the second Ready one. How is it generally done?
private void Next_Click(object sender, RoutedEventArgs e){
this.footerText = "Waiting for dataRetreival";
someRandomTimeTakingMethod(); //Gets Data from DB.
this.footerText = "Ready";
}
Even though code executes line 2, the view updates only when the function is over, ie only the second one actually works.
You should put your data-intensive work on a background thread so the UI can update properly. This provides the best user experience.
To elaborate on FZysset's answer with some code...
private async void Next_Click(object sender, RoutedEventArgs e)
{
footerText.Text = "Waiting for dataRetreival";
IsEnabled = false;
await SomeRandomTimeTakingMethodAsync();
IsEnabled = true;
footerText.Text = "Ready";
}
private async Task SomeRandomTimeTakingMethodAsync()
{
await Task.Delay(TimeSpan.FromSeconds(new Random().Next(2, 5)));
// or await Task.Run(() => { ... });
}
The above example allows you to leverage await/async that was introduced in .NET 4.5. Notice how nicely it flows? No nonsense!
We're putting stuff onto the background thread so the UI can remain unblocked (thus it will show your updates to your status bar and allow user interaction.) Of course, you have to be careful not to update anything on the UI from your background thread.
If you are using an older version of .NET, you can just use TPL without async/await:
private void Next_Click(object sender, RoutedEventArgs e)
{
footerText.Text = "Waiting for dataRetreival";
IsEnabled = false;
Task.Factory.StartNew(() =>
{
SomeRandomTimeTakingMethod();
}).ContinueWith(t =>
{
IsEnabled = true;
footerText.Text = "Ready";
}, TaskScheduler.FromCurrentSynchronizationContext());
}
private void SomeRandomTimeTakingMethod()
{
Thread.Sleep(TimeSpan.FromSeconds(new Random().Next(2, 5)));
}
Two important things to note about the latter example:
You must provide TaskScheduler.FromCurrentSynchronizationContext() to the ContinueWith call, or you will encounter exceptions because the continuation is not on the UI thread. You must get the context in a method that isn't running on a background thread.
You will want to check for exceptions on the Task object in your ContinueWith.
This example is very rudimentary though. If you were to have a bunch of background operations kicked off with click handlers, you'd want to give yourself some helper classes/services to make life easier. (And investigate MVVM, which I cannot tell if you are using.)
A colleague of mine gave a presentation on using various asynchronous patterns in C# and .NET. You can check it out here: https://github.com/mtusk/TplLunchAndLearn
That's because you're "someRandomTimeTakingMethod" is launched on the UI Thread. Therefore it will not update the view until it is finished.
To go around this you have the following possibilities :
Make your method "someRandom..." asynchronous with a task, and use the await operator : http://msdn.microsoft.com/en-us/library/hh191443.aspx
Launch your randomTimeTaking method into a thread, and launch an event when your execution is finished, to update the footer text
I strongly recommend you the first option, for some sample : http://msdn.microsoft.com/en-us/library/hh873191.aspx
You need to run those lines asynchronously. You can do that using the Task class:
private void Next_Click(object sender, RoutedEventArgs e){
Task.Factory.StartNew(() => footerText = "Waiting for dataRetreival");
someRandomTimeTakingMethod(); //Gets Data from DB.
Task.Factory.StartNew(() => footerText = "Ready");
}
There is one way to do it using Dispatcher. The original post is here.
The code is:-
private void Next_Click(object sender, RoutedEventArgs e){
UpdateUI("Please wait for data retrieval", delegate() { someRandomTimeTakingMethod(); });
this.footerText = "Ready";
}
public delegate void NoArgsDelegate();
public void UpdateUI(string description, NoArgsDelegate operation)
{
this.FooterText= description;
DispatcherFrame frame = new DispatcherFrame();
DispatcherOperation dispatcherOperation = Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.ContextIdle, operation);
dispatcherOperation.Completed += delegate(object sender, EventArgs e)
{
frame.Continue = false;
};
Dispatcher.PushFrame(frame);
}
If my understanding is right, this uses Asynchronous programming, not different thread. The thread will update UI first and then call the someRandomTimeTakingMethod().

Categories