How to do not freeze wpf app on closing event - c#

I have a WPF app and I want to execute some method in the Closing event. It can take several minutes, that's why the WPF window freezes. Here is my code:
public MainWindow()
{
InitializeComponent();
this.Closing += MainWindow_Closing;
}
void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
Task.Run(new Action(ClaseAfterThreeSecond)).Wait();
}
void ClaseAfterThreeSecond()
{
Thread.Sleep(3000);
}
I want to do not freeze the WPF app until MainWindow_Closing ends when I click the X button (close button) on the WPF app. Any suggestions?
EDIT:
This way closes the window immediately. I don't need this:
async void MainWindow_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
await Task.Run(new Action(ClaseAfterThreeSecond));
}

You need to make your handler async, but as it async void it returns right after your first await call. Just cancel original closing event and close directly in your code:
private async void Window_Closing(object sender, CancelEventArgs e)
{
// cancel original closing event
e.Cancel = true;
await CloseAfterThreeSeconds();
}
private async Task CloseAfterThreeSeconds()
{
// long-running stuff...
await Task.Delay(3000);
// shutdown application directly
Application.Current.Shutdown();
}

Related

C# How to terminate a click-event handler with another click-event

I have a click-event handler to a button that forms an ssh connection. I want to terminate this function with another "cancel" button click.
However, when executing an event handler, "cancel" click event handler doesn't run while the first handler executing. I want to override the first handler with "cancel" handler.
private void button_sshconnection_Click(object sender, EventArgs e)
{ /* some code to create ssh connection */ }
private void button_cancel_Click(object sender, EventArgs e)
{ /* some code to terminate button_sshconnection_Click */ }
I tried a code-structure like the above code but as I said second function doesnt run while the first function running. If the structure is wrong, can someone tell me how to do this job.
Thanks in advance,
Onur
You can try implementing async version of your routine, e.g.
private CancellationTokenSource m_Cancellation;
private async void button_sshconnection_Click(object sender, EventArgs e) {
// if method is executing, do nothing. Alternative: cancel and start again
if (m_Cancellation != null)
return;
try {
using (m_Cancellation = new CancellationTokenSource()) {
var token = m_Cancellation.Token;
await Task.Run(() => {
//TODO: implement your logic here, please, note that cancellation is cooperative
// that's why you should check token.IsCancellationRequested
}, token);
}
}
finally {
m_Cancellation = null;
}
}
private void button_cancel_Click(object sender, EventArgs e) {
// If we can cancel, do it
m_Cancellation?.Cancel();
}

C# run time consuming method on button click without keeping the button pressed

I do have experience with software developping in Python (GUI platform PyQt) and I am learing software development in C#. I wanted to know how can I run a thread/task in C# that uses UI objects but keeping the UI "alive" and not keeping the button pressed. I did used "Invoke" method to share UI objects with thread/task and did not call any join method, but still button remain pressed during thread execution. Is there any way to run this method in background, but keeping the GUI responsive?
Thanks in advance!
private async void Button_Click(object sender, RoutedEventArgs e)
{
await Task.Run(new Action(this.Iterate_balance));
}
private async void Iterate_balance()
{
this.Dispatcher.Invoke(() =>
{
// the rest of code
}
}
use async/await pattern properly and you won't need Dispatcher at all:
private async void Button_Click(object sender, RoutedEventArgs e)
{
await Iterate_balance();
}
private async Task Iterate_balance()
{
button.Content = "Click to stop";
// some long async operation
await Task.Delay(TimeSpan.FromSeconds(4));
button.Content = "Click to run";
}
TRY THIS:
1.Add following using: using System.ComponentModel;
2.Declare background worker:
private readonly BackgroundWorker worker = new BackgroundWorker();
3.Register events:
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += worker_RunWorkerCompleted;
4.Implement two methods:
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
// run all background tasks here
}
private void worker_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e)
{
//update ui once worker complete his work
}
5.Run worker async whenever your need.
worker.RunWorkerAsync();
Also if you want to report process progress you should subscribe to ProgressChanged event and use ReportProgress(Int32) in DoWork method to raise an event. Also set following: worker.WorkerReportsProgress = true;
Hope this help.

Closing of a WPF Window still visible in taskbar

I have a small issue with my WPF application.
I have a splash image (as a XAML Window) and the main app (as another XAML window that gets caleld from Splash)
Even when I use this .Close() on Splash window and work on Main app, the Splash window is still visible in taskbar.
If I were to use this .Close on main app I would have two blank windows in taskbar application that I have to press X to close completely.
I've tried with Application.Current.Shutdown() as well but results were same.
Splash:
public Splash()
{
InitializeComponent();
}
private void Window_ContentRendered(object sender, EventArgs e)
{
CloseProcedure();
}
public async void CloseProcedure()
{
//Missing data loading which will be async.
UC_Main UCMain = new UC_Main();
MainWindow window = new MainWindow();
window.AppContent.Children.Add(UCMain);
this.Close();
await Task.Delay(500); //for fade effect.
window.Show();
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
Closing -= Window_Closing;
e.Cancel = true;
var anim = new DoubleAnimation(0, (Duration)TimeSpan.FromSeconds(0.5));
this.BeginAnimation(UIElement.OpacityProperty, anim);
anim.Completed += (s, _) => this.Close();
}
Main App
private void BTN_Close_MouseUp(object sender, MouseButtonEventArgs e)
{
this.Close();
}
private void titleBar_MouseDown(object sender, MouseButtonEventArgs e)
{
titleBar.Background = Brushes.White;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
Closing -= Window_Closing;
e.Cancel = true;
var anim = new DoubleAnimation(0, (Duration)TimeSpan.FromSeconds(0.2));
this.BeginAnimation(UIElement.OpacityProperty, anim);
anim.Completed += (s, _) => Application.Current.Shutdown();
}
}
The default shutdown mode of a WPF app is "OnLastWindowClose" which means as soon as the last window is closed, the applicaton shutdown procedure will be called, so you should not need to explicitly write System.Windows.Application.Shutdown().
You probably have something referencing the splash screen that isn't being closed off. When you open your main window, you should close the splash screen properly at that time and ensure that any reference to it is null.
If you post all your code, it will be easier to know the correct fix.
To prevent a window showing in the taskbar in the first place, set the ShowInTaskbar property of your Window XAML declaration:
<Window ShowInTaskbar="False" ...
I've come up with a solution for this.
Instead of trying to shut down the process and animate the fade inside the closing event handler, I wrote an asynchronous method that will fade out the screen, await for the same amount of time and then kill the process. Works like a charm.
private void BTN_Close_MouseUp(object sender, MouseButtonEventArgs e)
{
this.FadeOut();
}
async private void FadeOut()
{
var anim = new DoubleAnimation(0, (Duration)TimeSpan.FromSeconds(0.2));
this.BeginAnimation(UIElement.OpacityProperty, anim);
await Task.Delay(200);
this.Close();
System.Diagnostics.Process.GetCurrentProcess().Kill();
}

picturebox opening too late

I want to show "Loading.., please wait" gif by getting content from web.
I have tried the following code, but Picturebox opening too late.
private void buttonStart_Click(object sender, EventArgs e)
{
pictureBox1.Visible = true;
webSatList = new WebSatelliteList(this, XMLSatList, name);
webSatList.LoadTPList();
TPListToBeAdded = webSatList.GetTPListToBeAdded();
TPListToBeRemoved = webSatList.GetTPListToBeRemoved();
drawTPListGridView(TPListToBeAdded, TPListToBeRemoved);
}
public void drawTPListGridView(List<TPInfo> TPListToBeAdded, List<TPInfo> TPListToBeRemoved)
{
pictureBox1.Visible = false;
//draw TP List ..
}
Picturebox is openning after this line:
"TPListToBeRemoved = webSatList.GetTPListToBeRemoved();"
I have tried to fix this problem by using backgroundworker (the following code) and the same problem has been seen. Also, I have used the popup form instead of PictureBox nothing has changed.
private void buttonStart_Click(object sender, EventArgs e)
{
pictureBox1.Visible = true;
backgroundWorker1.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
backgroundWorker1.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
backgroundWorker1.RunWorkerAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
webSatList = new WebSatelliteList(this, XMLSatList, name);
webSatList.LoadTPList();
TPListToBeAdded = webSatList.GetTPListToBeAdded();
TPListToBeRemoved = webSatList.GetTPListToBeRemoved();
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
drawTPListGridView(TPListToBeAdded, TPListToBeRemoved);
}
public void drawTPListGridView(List<TPInfo> TPListToBeAdded, List<TPInfo> TPListToBeRemoved)
{
pictureBox1.Visible = false;
//draw TP List ..
}
How can i fix this problem? Any help would be appreciated.
Not entirely sure what you're trying to do here, but chances are you want to implement the async / await pattern.
Mark your button press as async
private async void buttonStart_Click(object sender, EventArgs e)
For anything that you need to wait for should then be awaited and it will keep your form redrawing so it doesn't freeze up. For example, something like:
await Task.Run(() => loadPictureBox());
Or you could make your loadpicturebox method asynchronous by giving it a signature of async Task.
The problem you're likely having is that the form will not update or refresh itself until the click method has exited. This means if you first make it display a loading image, and then load the next image in the same method that the form will freeze up until both operations have finished and the method has exited.
The async await pattern means that while it's doing some processing or whatever, let windows carry on drawing the form and handling actions on it.

method waiting for an winform event

in a programm i've been developing, a method is needed to wait until ENTER is clicked inside a specific textbox (generally, winform event called). i know i shold be doing this with threads, but dont know how to make a method that will do that. more specificly, i don't know how to call the event method on the thread, and cannot call in on Main, because it's blocked until this method is called.
the method stopping the Main thread is:
void WaitForInput()
{
while (!gotInput)
{
System.Threading.Thread.Sleep(1);
}
}
thanks for helpers.
Just subscribe to KeyDown (or KeyPress) event of your textbox:
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
// do your stuff
}
}
You can start by changing the WaitForInput method to be threaded by using Tasks like this:
private void WaitForInput()
{
Task.Factory.StartNew(() =>
{
while (!gotInput)
{
System.Threading.Thread.Sleep(1);
}
MessageBox.Show("Test");
});
}
Then catch the KeyPressed event of the textbox and change the state of the boolean gotInput to true like this:
private void KeyDown(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == (char)13)
gotInput = true;
}
Good luck
Use the async/await keywords from .NET 4.5. You could do something like this:
CancellationTokenSource tokenSource; // member variable in your Form
// Initialize and wait for input on Form.Load.
async void Form_Load(object sender, EventArgs e)
{
tokenSource = new CancellationTokenSource();
await WaitForInput(tokenSource.Token);
// ENTER was pressed!
}
// Our TextBox has input, cancel the wait if ENTER was pressed.
void TextBox_KeyDown(object sender, KeyEventArgs e)
{
// Wait for ENTER to be pressed.
if(e.KeyCode != Keys.Enter) return;
if(tokenSource != null)
tokenSource.Cancel();
}
// This method will wait for input asynchronously.
static async Task WaitForInput(CancellationToken token)
{
await Task.Delay(-1, token); // wait indefinitely
}
currently i'm stuck with a dinosaur computer that has XP on it (.NET 2008 and cannot upgrade until april or so). i've ended up following a solution from the comments, and makethe main thread wait and run the entries on threads.
thanks!

Categories