Progress window when doing computation in the UI thread - c#

I would like to achieve this:
Create a new window, which is just a spinning loader and some static text, and display it.
Do some computation on the UI thread (it has to be done there)
Close the loader window
Currently, when I call .Show() on the loader window, it doesnt load at all, it just hangs out as a blank window (the UI thread is blocked, which I dont want)
What can I do to enable the loader window to display its contents at the same time my computation is done?
private void CopyToClipboard(object sender, RoutedEventArgs e)
{
var selected = mDataGrid.SelectedItem;
var selectedIndex = mDataGrid.SelectedIndex;
var progressWindow = mProgressDialog.Create(this,"Copying data to clipboard");
progressWindow.Show();
try
{
mDataGrid.ClipboardCopyMode = DataGridClipboardCopyMode.IncludeHeader;
mDataGrid.SelectionMode = DataGridSelectionMode.Extended;
mDataGrid.SelectAllCells(); // SLOW !!!!
ApplicationCommands.Copy.Execute(null, mDataGrid); //SLOW !!!!
}
catch (Exception ex)
{
mLog.Error("Copying to clipboard", ex);
MessageBox.Show("Error while copying to clipboard");
}
finally
{
mDataGrid.SelectionMode = DataGridSelectionMode.Single;
mDataGrid.UnselectAllCells();
this.Select(selectedIndex, selected);
progressWindow.Close();
}
}

As you said, your calculation is done on the UI thread, that's why your window is blank, it is not "repainted" due to the UI thread being busy.
So unless you do your long running calculation on another thread, you're quite stuck.

Only thing I was able to achieve is this:
progressWindow.Show();
this.DoEvents();
public static void DoEvents(this Window _)
{
Application.Current.Dispatcher.Invoke(DispatcherPriority.Background,
new Action(delegate { }));
}
The "loader window" gets at least displayed, so the user knows something is happening. It usually lasts under a second, so this solution is acceptable to me.

Related

Closing the Form opened in another thread

I'm getting some troubles with my Winforms C# app.
I wish to make form named Popup closing after some operations in main thread are done. The problem is an exception caused by cross-thread form closing.
private void loginButton_Click(object sender, EventArgs e)
{
LoginProcess.Start(); // Running Form.show() in new thread
ActiveAcc.IsValid = false;
ActiveAcc.Username = userBox.Text;
try
{
LoginCheck(userBox.Text, passBox.Text);
}
catch (IOException)
{
MessageBox.Show("..");
return;
}
catch (SocketException)
{
MessageBox.Show("..");
return;
}
if (ActiveAcc.IsValid)
{
MessageBox.Show("..");
Close();
}
else
{
Popup.Close(); // Error caused by closing form from different thread
MessageBox.Show("");
}
}
public Login() // 'Main' form constructor
{
InitializeComponent();
ActiveAcc = new Account();
Popup = new LoginWaiter();
LoginProcess = new Thread(Popup.Show); //Popup is an ordinary Form
}
I've been trying to use various tools such as LoginProcess.Abort() or Popup.Dispose() to make it work properly, but even if app is working on runtime environment its still unstable due to Exceptions which are thrown.
I would be grateful for any help, and I am sorry for ambiguities in issue describing.
Why don't you let the UI thread do UI stuff like opening and closing Forms, and spawn the other thread (or background worker, or async task) to do the other stuff?
IMO, having other threads attempt to interact with elements on the UI thread (e.g., have a background thread directly set the text of a label or some such) is asking for heartache.
If you simply must keep your code as is, here is a fairly simple thing you could do. In Popup, add a static bool that defaults to true. Also in Popup, add a timer task that once every X milliseconds checks the status of that boolean. If it finds that the value has been set to false, let Popup tell itself to close within that timer tick.
I'm not crazy about this design, but it could look something like:
public partial class Popup : Form
{
public static bool StayVisible { get; set; }
private System.Windows.Forms.Timer timer1;
public Popup()
{
StayVisible = true;
this.timer1.Interval = 1000;
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
InitializeComponent();
}
private void timer1_Tick(object sender, EventArgs e)
{
if (!StayVisible) this.Close();
}
}
Then, from another thread, when you want Popup to close, call
Popup.StayVisible = false;
Better yet, you would fire an event that Popup would receive so that it could close itself. Since you intend to use multiple threads, you'll have to deal with raising events cross-thread.

How close window that opened in a thread in wpf?

By specification i build an application that monitors certain resource. Now i have a main window that when an event occurs throws an alert that specification must be a big window with bright colors.
I gave solution to the problem as follows
private void StatusChanges(Alarm m, EventArgs e)
{
//Compares two 32-bit signed integers for equality and, if they are equal, replaces one of the values.
if (Interlocked.CompareExchange(ref running, 1, 0) == 0)
{
newWindowThread = new Thread(new ThreadStart(() =>
{
try
{
// Create and show the Window
Alert tempWindow = new Alert();
tempWindow.Show();
//if (cancelRun)
// tempWindow.Close();
// Start the Dispatcher Processing
System.Windows.Threading.Dispatcher.Run();
}
catch (Exception)
{
throw;
}
finally
{
running = 0;
}
}));
// Set the apartment state
newWindowThread.SetApartmentState(ApartmentState.STA);
// Make the thread a background thread
newWindowThread.IsBackground = true;
newWindowThread.Start();
}
}
else
{
if (Interlocked.CompareExchange(ref running, 0, 1) == 1)
{
try
{
System.Windows.Threading.Dispatcher.FromThread(newWindowThread).InvokeShutdown();
Alert tempWindow = new Alert();
tempWindow.Close();
}
catch (Exception)
{
//throw;
MessageBox.Show("Todo esta ok");
}
}
//running = 0;
}
}
This method is executed from at interval by a Timer
my goal was when the alarm will end the process of removing the alert window, I used Thread.Abort (); did not work, and sometimes the program threw an exception and showed no visual studio in aplition happening outside, try using threa.Join (); but it didn't work, until
System.Windows.Threading.Dispatcher.FromThread use (newWindowThread) InvokeShutdown ().;
this at least stopped the showing up of the window, but when I create the
Alert tempWindow = new Alert();
in the initialize constructor
SoundPlayer player = new Sound ("../../Sounds/BOMB_SIREN-BOMB_SIREN-247265934.wav");
player.PlayLooping();
and when I close the window
player.Stop();
Now if not but I do the next
Alert tempWindow = new Alert();
tempWindow.Close();
the sound continues to play
I know that's not the best solution, but it works.
I would like to give me your opinion, and give me suggestions on how to improve the code.
Taking a quick look at your code, what you're doing is closing the temp. window incorrectly. You are creating a brand new Window when you want to close the existing one.
This should be updated so that the existing temp Window is saved so that that the same instance can be closed later.
I've also updated the Closing of the temp Window so that it's thread shutdown's after it has closed.
I updated the code as follows:
Alert _tempWindow;
private void StatusChanges(Alarm m, EventArgs e)
{
//Compares two 32-bit signed integers for equality and, if they are equal, replaces one of the values.
if (Interlocked.CompareExchange(ref running, 1, 0) == 0)
{
newWindowThread = new Thread(new ThreadStart(() =>
{
try
{
// Create and show the Window
_tempWindow = new Alert();
_tempWindow.Close += OnTempClosed;
_tempWindow.Show();
System.Windows.Threading.Dispatcher.Run();
}
catch (Exception)
{
throw;
}
finally
{
running = 0;
}
}));
// Set the apartment state
newWindowThread.SetApartmentState(ApartmentState.STA);
// Make the thread a background thread
newWindowThread.IsBackground = true;
newWindowThread.Start();
}
}
else
{
if (Interlocked.CompareExchange(ref running, 0, 1) == 1)
{
try
{
_tempWindow.Dispatcher.BeginInvoke((Action)_tempWindow.Close);
}
catch (Exception)
{
//throw;
MessageBox.Show("Todo esta ok");
}
}
//running = 0;
}
}
private void OnTempClosed(object sender, EventArgs e)
{
System.Windows.Threading.Dispatcher.FromThread(newWindowThread).InvokeShutdown();
}
The idea is to work with the UI just from the one thread. So rather than creating a whole new dispatcher, why not simply dispatch the new window logic to the existing UI thread (after all, doesn't the timer already end-up on the UI thread all by itself?).
Why are you starting the new window in a new thread? What are you hoping to accomplish by that?
Your close code doesn't make sense:
Dispatcher.FromThread(newWindowThread).InvokeShutdown();
Alert tempWindow = new Alert();
tempWindow.Close();
Let's walk through this. First, you shutdown the dispatcher of the second UI thread. This means that all the messages that would flow to the window - don't.
Then, you create a new window, on the original UI thread, and you close that. What's that supposed to accomplish? You want to close the window that you've opened before, not a new window!
In practice, you need to keep a reference to the original window you've opened - the reference to it's thread isn't really important. Now, if you really want to keep using your two-UI-threads approach, you'd do this instead:
alertWindow.Dispatcher.Invoke(alertWindow.Close);
This will tell the message loop on the (already opened) alert window to close the window, as soon as it can.
With that out of the way, there's just too many things wrong with your code. For one, you're using way too low level operations (Interlocked.CompareExchange to maintain alert / no alert logic?). Two, your comments are completely useless - you're just saying what the next line does, literally - that's not helpful. Instead of
// Compares two 32-bit signed integers for equality and,
// if they are equal, replaces one of the values.
You want to use something descriptive of what you're trying to achieve, not how you're doing it:
// If we're the first ones to attempt to change the running flag,
// we'll create a new alert window.
Note how that comment also quite nicely shows that you're doing something very weird.
Three, you're needlessly creating threads (and even more so, UI threads - there should really only be one except for very specific circumstances). There's no reason whatsoever to create the new window in a new thread. Why are you doing that?
Four, your exception handling isn't very helpful either. For one, you can omit the catch clause if all you're doing is rethrowing the exception anyway - you can just use try ... finally alone. More importantly, the exception is going to be propagated to that new thread, not your old one - this will most likely cause your application to crash completely.

How to implement a popup window with a counter

I would like to implement a simple popup window in Windows Forms, which will show a simple timer to the user while some slow-running process is executing. The premise is simple; show to the user that something is indeed going on and the application is not frozen. Note that this slow-running process is not a loop, nor is it something that I can tap into.
What I want is a simple popup window, showing some message along the lines "Elapsed time: x seconds", where x is incremented every second.
The basic concept is the following:
public void test()
{
//Some code which does stuff
//Popup window with counter
//Perform long running process
//Close popup window with counter
//Some other code which does other stuff
}
I tried to do it using various ways, including background workers, threads, and of course timers. But I did not manage to make it work as I wanted. And I would prefer not to post any of my code so as not to "lead" the responses to a specific way of doing this.
So what would be the best way to do this work?
Thanks.
UPDATE:
In reply to some comments, since I cannot paste any code in the replies section, I'm editing my original question to accomodate this. One of the implementations that I tried is to spawn the popup window in a separate thread. Although I got no runtime errors, the popup window did not refresh correctly. It indeed poped-up, but no text would show within it, and the counter would not refresh. Here's the code:
private void test()
{
frmProgressTimer ofrmProgressTimer = new frmProgressTimer(); //Instance of popup Form
System.Threading.Tasks.Task loadTask = new System.Threading.Tasks.Task(() => ProgressTimer(ofrmProgressTimer));
loadTask.Start();
//Perform long running process
System.Threading.Tasks.Task cwt = loadTask.ContinueWith(task => EndProgressTimer(ofrmProgressTimer));
}
private void ProgressTimer(frmProgressTimer ofrmProgressTimer)
{
ofrmProgressTimer.Show();
ofrmProgressTimer.Invoke((Action)(() =>
{
ofrmProgressTimer.startTimer();
}));
}
private void EndProgressTimer(frmProgressTimer ofrmProgressTimer)
{
ofrmProgressTimer.Invoke((Action)(() =>
{
ofrmProgressTimer.stopTimer();
ofrmProgressTimer.Close();
}));
}
And here's my popup form code:
public partial class frmProgressTimer : Form
{
private int counter = 0;
private Timer timer1;
public frmProgressTimer()
{
InitializeComponent();
timer1 = new Timer();
timer1.Interval = 1000;
timer1.Tick += new EventHandler(timer1_Tick);
}
public void startTimer()
{
timer1.Start();
}
public void stopTimer()
{
timer1.Stop();
}
private void timer1_Tick(object sender, EventArgs e)
{
counter += 1;
labelText.Text = counter.ToString();
}
}
This is actually quite easy to do. Create your dialog, define your long running operation to take place in a non-UI thread when it is shown, add a continuation to that operation which closes the dialog when the task finishes, and then show the dialog.
MyDialog dialog = new MyDialog();
dialog.Shown += async (sender, args) =>
{
await Task.Run(() => DoLongRunningWork());
dialog.Close();
};
dialog.ShowDialog();
The code to have the ticking over time should be entirely contained within the dialog, and based on the question it seems you already have that well under control with a simple Timer.
Make a new form, which will pop up, and show a timer. That way it won't be interrupted with all the work on your main form, and the timer will work continuously.
Remember when showing a new from to use newForm.ShowDialog() not newForm.Show(). Your can google the differences
I would simply start your work on a separate thread. Launch a modal form with your timer output. To display the timer use an actual timer instance set to update every second. When the timer event fire update your dialog.
Finally once you're thread completes close the dialog so your main form is active again.
First of all you need to make it not closeable by the user (as if modal dialogs weren't annoying enough) but closeable by your code. You could accomplish this by subscribing to the FormClosing event of the form. Let's say your popup form's name is Form2:
private bool mayClose = false;
public void PerformClose()
{
this.mayClose = true;
this.Close();
}
private void Form2_FormClosing(object sender, FormClosingEventArgs e)
{
if (!this.mayClose)
e.Cancel = true;
}
Create a Timer, provide a Tick event handler, enable it and set its Interval to 500 milliseconds:
Create a label to host your desired text. Let's call it label1.
Within and surrounding your Tick event handler do something like this:
private DateTime appearedAt = DateTime.UtcNow;
private void timer1_Tick(object sender, EventArgs e)
{
int seconds = (int)(DateTime.UtcNow - this.appearedAt).TotalSeconds;
this.label1.Text = string.Format(#"Ellapsed seconds: {0}", seconds);
}
Make sure your long running process is happening on a background thread, not on the GUI thread.
Say your long running process can be thought of as the execution of a method called MyProcess.
If that is the case, then you need to call that method from a secondary thread.
// PLACE 1: GUI thread right here
Thread thread = new Thread(() =>
{
// PLACE 2: this place will be reached by the secondary thread almost instantly
MyProcess();
// PLACE 3: this place will be reached by the secondary thread
// after the long running process has finished
});
thread.Start();
// PLACE 4: this place will be reached by the GUI thread almost instantly
Show the form right before the long running process starts. This can be done in any of the 2 places (marked in the previous section of code) called PLACE1 or PLACE2. If you do it in PLACE2 you will have to marshal a call back to the GUI thread in order to be able to interact with the WinForms framework safely. Why am I bringing this up ? It's because maybe the long running process is not started from within the GUI thread at all and you absolutely need to do this.
Close the form right after the long running process finishes. This can be done only in PLACE3 and you absolutely need to marshal a call.
To wrap the earlier 2 bullets and the answer, you could do this:
private void DoIt()
{
Form2 form2 = new Form2();
Action showIt = () => form2.Show();
Action closeIt = () => form2.PerformClose();
// PLACE 1: GUI thread right here
Thread thread = new Thread(() =>
{
form2.BeginInvoke(showIt);
// PLACE 2: this place will be reached by the secondary thread almost instantly
MyProcess();
form2.BeginInvoke(closeIt);
// PLACE 3: this place will be reached by the secondary thread
// after the long running process has finished
});
thread.Start();
// PLACE 4: this place will be reached by the GUI thread almost instantly
}
Finally I've managed to resolve this in the most simplistic manner. And it works like a charm. Here's how to do it:
//Create an instance of the popup window
frmProgressTimer ofrmProgressTimer = new frmProgressTimer();
Thread thread = new Thread(() =>
{
ofrmProgressTimer.startTimer();
ofrmProgressTimer.ShowDialog();
});
thread.Start();
//Perform long running process
ofrmProgressTimer.Invoke((Action)(() =>
{
ofrmProgressTimer.stopTimer();
ofrmProgressTimer.Close();
}));
You can see the code for the popup window in the original post/question, with the only difference that the tick function changes the label text as:
labelText.Text = string.Format("Elapsed Time: {0} seconds.", counter.ToString());
Thank you to everybody for trying to help me out.

Best way to display a progress form while a method is executing code?

I have a WinForm load method that takes a long time to gather some data to display to the user.
I display a form with a large font with the word "Loading" while this method is executing.
However sometimes this error comes up and the "Loading" progress form does not close and then eventually my whole application will just exit:
Error creating window handle. at System.Windows.Forms.NativeWindow.CreateHandle(CreateParams cp)
Is there a better way to display my progress/loading form while I am executing code in the load method?
This is my code:
//I launch a thread here so that way the Progress_form will display to the user
//while the Load method is still executing code. I can not use .ShowDialog here
//or it will block.
//Progress_form displays the "Loading" form
Thread t = new Thread(new ThreadStart(Progress_form));
t.SetApartmentState(System.Threading.ApartmentState.STA);
t.IsBackground = true;
t.Start();
//This is where all the code is that gets the data from the database. This could
//take upwards of 20+ seconds.
//Now I want to close the form because I am at the end of the Load Method
try
{
//abort the Progress_form thread (close the form)
t.Abort();
//t.Interrupt();
}
catch (Exception)
{
}
A BackgroundWorker is a great way to perform a long running operation without locking the UI thread.
Use the following code to start a BackgroundWorker and display a loading form.
// Configure a BackgroundWorker to perform your long running operation.
BackgroundWorker bg = new BackgroundWorker()
bg.DoWork += new DoWorkEventHandler(bg_DoWork);
bg.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_RunWorkerCompleted);
// Start the worker.
bg.RunWorkerAsync();
// Display the loading form.
loadingForm = new loadingForm();
loadingForm.ShowDialog();
This will cause the following method to be executed on a background thread. Note that you cannot manipulate the UI from this thread. Attempting to do so will result in an exception.
private void bg_DoWork(object sender, DoWorkEventArgs e)
{
// Perform your long running operation here.
// If you need to pass results on to the next
// stage you can do so by assigning a value
// to e.Result.
}
When the long running operation completes, this method will be called on the UI thread. You can now safely update any UI controls. In your example, you would want to close the loading form.
private void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// Retrieve the result pass from bg_DoWork() if any.
// Note, you may need to cast it to the desired data type.
object result = e.Result;
// Close the loading form.
loadingForm.Close();
// Update any other UI controls that may need to be updated.
}
Ive successfully tested this on .NET 4.0. (WinForms) I'm reasonably certain that this will work on .NET 4.0+ and should be a useful code snippet to reuse in most of your projects that require closing forms at the end of a process.
private void SomeFormObject_Click(object sender, EventArgs e)
{
myWait = new YourProgressForm();//YourProgressForm is a WinForm Object
myProcess = new Thread(doStuffOnThread);
myProcess.Start();
myWait.ShowDialog(this);
}
private void doStuffOnThread()
{
try
{
//....
//What ever process you want to do here ....
//....
if (myWait.InvokeRequired) {
myWait.BeginInvoke( (MethodInvoker) delegate() { closeWaitForm(); } );
}
else
{
myWait.Close();//Fault tolerance this code should never be executed
}
}
catch(Exception ex) {
string exc = ex.Message;//Fault tolerance this code should never be executed
}
}
private void closeWaitForm() {
myWait.Close();
MessageBox.Show("Your Process Is Complete");
}
I would take the code that you have in your load method and place that into a thread. Setup a progress bar somewhere on your form and increment it at key stages in the code that's gathering the data - be careful not to do this in the thread itself though, i.e. don't tamper with ui elements in a separate thread, you'll need to invoke them using a delegate.

disabling a window

In my application I have a Button. If the button is clicked as select against a database is executed and the result is shown in a ListView.
As the select is quite complex, it takes some time to retrieve the data.
When I click the Button, the Application-Window should be disabled until the data is loaded.
But when I set the IsEnabled-Property of the Window to false, the window gets disabled after the data is loaded.
I tried to disable the Window in an other thread with a BackgroundWorker. But then I get an exception that the window is already in use by another thread.
How can I disable the Window bevore it retrieves the data?
You did the wrong thing in a background thread. You have to affect the UI from the UI thread, and your data loading should occur in a background thread.
The simplest approach is to use a BackgroundWorker to load your data, store that data in a class-level variable, and when your background work is complete, the UI re-enables and loads the data from the class-level variable.
I'd think you'd move the database activity to the background thread to leave your UI responsive (even if it's only to disable it) rather than the other way around.
try this:
BackgroundWorkerHelper.DoWork<Type of object you want to retrieve>(
() =>
{
//Load your data here
//Like
using (MarketingService svc = new MarketingService())
{
return svc.GetEmployeeLookupTable();
}
},
(args) =>
{
this.IsEnable = true;
if (args.Error == null)
{
Your Target Datasource= args.Result;
}
});
this.IsEnable = false;
I will suggest "BusyDialog" window in addition to background thread.
Yous busy dialog can be an animation displaying it is doing something, and modally blocking any user input as well.
public partial class BusyDialog : Window
{
public BusyDialog()
{
InitializeComponent();
}
public static T Execute<T>(DependencyObject parent, Func<T> action)
{
Window parentWindow = null;
if (parent is Window)
{
parentWindow = parent as Window;
}
else
{
parentWindow = Window.GetWindow(parent);
}
T val = default(T);
Exception le = null;
BusyDialog bd = new BusyDialog();
bd.Owner = parentWindow;
ThreadPool.QueueUserWorkItem((o) =>
{
try
{
val = action();
}
catch (Exception ex)
{
le = ex;
}
bd.EndDialog();
});
bd.ShowDialog();
if (le != null)
{
Trace.WriteLine(le.ToString());
throw new Exception("Execute Exception", le);
}
return val;
}
private void EndDialog()
{
Dispatcher.Invoke((Action)delegate() {
this.DialogResult = true;
});
}
}
Now you can use following way to call your method asynchronously,
List<Result> results = BusyDialog.Execute( this ,
()=>{
return MyLongDatabaseLoadingCall();
});
This is what happens,
BusyDialog is displayed modally, blocking any user input as well as displaying busy animation
A call to your method MyLongDatabaseLoadingCall is executed in ThreadPool.QueueUserItem, which asynchronously calls your method in different thread (Same as background threading functionality suggested by others here).
Animation continues till the call is executing
When your method ends, BusyDialog is ended and everything is back to how it was.

Categories

Resources