I have a long running process we'll call ProcessA so I want to put a "Please Wait..." type dialogue box to discourage the user from trying to interact with the application and ensure they don't get "Not Responding" type info / errors (e.g. offering the opportunity to End Process)
My first thought is to set up a Background Worker to do the job and display a modal dialogue box in the foreground, and close it when ProcessA completes. However, the problem is that ProcessA throws up its own dialogue box at the end and then does some printing.
So if the user clicks anywhere on the application, ProcessA's dialogue box will disappear behind the application's window.
So how can I display the "Please Wait", but also prevent interaction with the main application while not having it show up as "Not Responding" while ProcessA is running?
ProcessA runs from a DLL and I don't have access to its code. (So I can't simply close the "Please Wait" when it's ready to show its own dialogue box)
My eventual resolution was just to use a Modeless "Please Wait", not perfect but hopefully will be acceptable for now...
_pleaseWaitForm.Show();
_pleaseWaitForm.Refresh();
DisableProcessWindowsGhosting();
ProcessA(); // Long running process
_pleaseWaitForm.Close(); // or maybe should it be Dispose()?
Since ProcessA ties up the UI it prevents user interaction anyway, and DisableProcessWindowsGhosting() prevents the "Not Responding" message or the user from closing the application if they get impatient.
A little bit sloppy so I'm still open to a more elegant answer...
Here is a naive sample.
class MyForm : Form {
private void Form_Load() {
ThreadPool.QueueUserWorkItem(_=>{
Form frmDialog = new Form();
//Cross-thread InvokeRequired
this.BeginInvoke(new Action(()=>{
frmDialog.ShowDialog(this);
}));
// to do long-time work here
// Cross-thread InvokeRequired
this.Invoke(new Action(()=>{
frmDialog.Dispose();
// frmDialog.Close(); the Close method doesn't release the frmDialog, just make it invisible. (Memory leak !!!)
}));
}
}
Related
I want to show a dialog as modal( i.e. blocks all interaction with all the other ui in my application) But I don't want that call to block execution, how do I do that?
This isn't pretty, but for reasons out of my control, we just have one thread, and can't create more.
currently when we do a time consuming progress, we want to show a progress bar, but in order for it to update while the process is progressing, we have to call DoEvents() (eek).
This unsurprisingly causes a few issues sometimes, but if we could easily disable all ui except the progress bar, that might help.
I got this idea from reading this answer here:
https://stackoverflow.com/a/5183623/259846
Edit:
A modal dialog is simply one that disables all other UI - only the modal dialog can be interacted with. This is separate from whether or not the operation to show this dialog is blocking or not. I don't see any reason why you couldn't in theory have a function that shows a modal dialog box without blocking until the box is closed.
I want to show a progress bar, and I want this progress bar to update as a process progresses, I doesn't matter how I go about doing this, the fact is, that if you want to update one dialog, you have to update the whole UI. As such I want the rest of the UI except for the progress window to be disabled so it can't be interacted with while my process is in progress.
And yes, I can only have one thread, no parallelism.
Disable all other elements manually when opening the DialogBox. Then enable them when closing the box.
for example:
Button.Enabled = false;
Check this out for disabling all controls
How to disable all controls on the form except for a button?
You can also put up a transparent rectangle in front of everything that you can't click through. Then hide it when dialogbox closes.
I've thought of one way of doing it.
My question probably doesn't clearly indicate my use case, this solution still uses the blocking call to create the modal dialog, but still allows me to execute my 'do work, update progress' loop with the dialog open.
var progress = new Form();
this.BeginInvoke(new Action(() =>
{
for (int i = 0; i < 100; i++)
{
System.Threading.Thread.Sleep(16); // do a bit of my process
// Update progress dialog
Application.DoEvents();
}
progress.Close();
}));
progress.ShowDialog();
I'm trying to make a new form appear when I click on a label. I'm using Windows Application Forms.
Here's the code:
private void label1_Click(object sender, EventArgs e)
{
Form parpokeru = new Form();
parpokeru.Show();
parpokeru.ShowDialog();
}
When I click on the label, a error appears (Unhandled exception has occurred in your application...). Can anyone tell me how to fix it?
Call .Show() or .ShowDialog(). Not both.
Show() will display your second form, while still allowing the user to access the first form.
ShowDialog() will display your second form as "modal". Execution of code in the first form stops while the second form is open (at least on the main thread.. for example, timers will continue to run), and the user will not be able to access your first form while the second is open.
When you call the Show method your form is shown on video like another window and the code returns immediately from the call. So your code continues and all the forms of your application are available for the user interaction. It is calledd a modeless dialog
On the contrary, ShowDialog is a blocking call. The code doesn't return from this call until something happen inside the called form that terminates the visualization of the form. As an example comes to ming a call to the methid Hide or a click on a button with its DialogResult property set to something different from DialogResult.None. At this point the code from ShowDialog returns and the normal processing continue. While the code is blocked inside the ShowDialog the application is blocked and the user cannot interact with other forms or menus or whatever is displayed on video. It is called modal dialog
Another difference is ShowDialog returns value (a DialogResult enum value) that can can be used to determine how the user closed the form (DialogResult.Cancel, DialogResult.OK), also ShowDialog does not call the Dispose method at closing time. This will allow to retrieve property from the Modal Dialog like user inputs for further processing.
I cannot imagine what happen in the internal processing of your form if, after a modeless call to Show you call immediately a ShowDialog on the same form instance. However, an exception is really the minimum to expect from this code.
I try to show a MESSAGE to the user while an operation is executed. The MESSAGE won't show any button. Just a MESSAGE (text) and maybe a background image.
The problem is the following:
MessageBox does not seem to be the good control (because of button and it blocks the running process).
Form.ShowDialog() also blocks the running process. I don't know what to do.
I want to show the message, run my process, and dispose the message when the process is done.
How to achieve this in C# ?
Create a simple form with the message (or expose a public property to be able to change the message, or a constructor with message parameter to pass it in) and show the form using this Show overload. Then disable the (entire) original (owner) form (or just disable the controls you don't want accesible).
So, in your "main" form do this:
Form f = new MessageForm();
f.Show(this); //Make sure we're the owner
this.Enabled = false; //Disable ourselves
//Do processing here
this.Enabled = true; //We're done, enable ourselves
f.Close(); //Dispose message form
Also, consider using a BackgroundWorker.
create custom form, and write own behavior
Create a custom form set it to minimal styles etc. Make sure it knows when the process is complete, say by passing in a reference to the process and checking it every now and then.
One way you could do it is create a worker class that raises an event when it is finished.
execute the worker class in a new thead so it runs it the backgroud.
create the modal form with a method to close it as the event handler for the "finished" event.
This way the ui still works and responds and the form will close once the worker thread is finished.
I am displaying dialog on the main form, now one for loop is working behind but when that dialog will displayed code execution will be stopped, but I don't want to let stop the execution of the code, is any other any way to do that ?
EDIT: I am using now thread to do that and my code is like
Thread t;
private void StartParsingByLoop()
{
t = new Thread(new ThreadStart(RunProgress));
t.Start();
for (int i = 0; i < dialog.FileNames.Length; i++)
{
cNoteToParsed.AllContrctNotesFilePath.Add(dialog.FileNames[i].ToString());
}
cNoteToParsed.ParseContractNote();
if (cNoteToParsed.NOfContrctNoteParsed > 0)
LoadTransactionsInDataGridView();
t.Abort();
}
private void RunProgress()
{
frmProgress progressForImportingTran = new frmProgress("Importing Transactions", "ok");
progressForImportingTran.ShowDialog();
}
Now I have problem is that the dialog that shows the progress does not behave like dialog and gives access of the main form and if we try to access the main form then dialog goes to hide. And I dont want to make the dialog be hide.
You can let a different thread handle the loop.
Response to edit: Can you provide more details, perhaps some code? what is the loop doing? what form do you display?
(this answer is based on the assumption that we are taking about a winforms app)
Show the form using the Show method, rather than ShowDialog. By passing a reference to the main form, the dialog will stay on top of the main form even if it is not modal:
TheDialog dialog = new TheDialog();
dialog.Show(this);
Note though that the user can still interact with the controls on the main form, so you might want to disable some controls, depending on your scenario.
You state in your question that there are requirements that prevent you from using threading for this. This kind of requirement strikes me as odd, and it is a pity because this is one of the typical scenarios when you would want to use some sort of asynchronous construct. By performing heavy work on the UI thread, you get some drawbacks, including:
The UI will not be responsive - if you want to allow the user to cancel the work by clicking a button, that will be tricky to achieve in a robust manner.
The UI will not redraw properly since the UI thread is busy performing other work.
Do not use ShowDialog() but Show() to display the dialog windows. Show() will immediately return to the caller whereas ShowDialog() will hold the execution. Note that when using Show() your dialogs won't be modal anymore.
OK, so you don't need modal dialog, you need a mechanism for your user not to be able to select your main form while processing is enabled.
Modal dialog doesn't mean that execution is stopped - it just happens somewhere else.
There are several methods to do this, and you ruled out new thread creation (don't know why, it would solve it elegantly). If don't use thread, you'll have another problem - your processing should be done in CHUNKS and every little while you'll have to do something like Application.DoEvents() to enable your application to process messages and not be frozen to the user.
So, if you can create a method in the main form that shows your 'please wait' dialog which will perform some work and later do some more until is finished, you can do this:
create a new form (wait dialog)
start a timer inside of it and wire timer to the PARENT form
timer interval should be 1
ShowDialog() the form
on timer event do small amount of work (don't allow it to go more then 1/10 of seconds)
Can you do that?
Anyway:
task can't be split into small workable pieces
you can't use threads
you want your UI to be responsive
PICK 2. You can't have all 3 in Winforms.
Ok, so I found something weird over the weekend. I have a WPF app that spawns off some threads to perform background work. Those background threads then Post work items to my Synchronization Context. This is all working fine except for one case. When my threads finish sometimes they will post an action onto the dispatcher that will open up a Popup window. What ends up happening is that if 2 threads both post an action on the Dispatcher it starts processing one, then if I open up a Popup window with Window.ShowDialog(); the current execution path pauses waiting for feedback from the dialog box as it should. But the problem arises that when the dialog box opens the Dispatcher then goes and starts immediately starts running the second action that was posted. This results in two code paths being executed. The first one with a message box being held open, while the second one is running wild because my application state is unknown because the first action never completed.
I've posted some example code to demonstrate the behavior I'm talking about. What should happen is that if I post 2 actions and the 1st one opens up a dialog box the second action shouldn't run until after the 1st action has been completed.
public partial class Window1 : Window {
private SynchronizationContext syncContext;
public Window1() {
InitializeComponent();
syncContext = SynchronizationContext.Current;
}
private void Button_ClickWithout(object sender, RoutedEventArgs e) {
// Post an action on the thread pool with the syncContext
ThreadPool.QueueUserWorkItem(BackgroundCallback, syncContext);
}
private void BackgroundCallback(object data) {
var currentContext = data as SynchronizationContext;
System.Console.WriteLine("{1}: Thread {0} started", Thread.CurrentThread.ManagedThreadId, currentContext);
// Simulate work being done
Thread.Sleep(3000);
currentContext.Post(UICallback, currentContext);
System.Console.WriteLine("{1}: Thread {0} finished", Thread.CurrentThread.ManagedThreadId, currentContext);
}
private void UICallback(object data) {
System.Console.WriteLine("{1}: UI Callback started on thread {0}", Thread.CurrentThread.ManagedThreadId, data);
var popup = new Popup();
var result = popup.ShowDialog();
System.Console.WriteLine("{1}: UI Callback finished on thread {0}", Thread.CurrentThread.ManagedThreadId, data);
}
}
The XAML is just a Window with a button that calls Button_ClickWithout OnClick. If you push the button twice and wait 3 seconds, you will see you get 2 dialogs popping up one over the other, where the expected behavior would be the first one pops up, then once it's closed the second one will pop up.
So my question is: Is this a bug ? or how do I mitigate this so I can have only one action be processed at a time when the first action halts execution with a Window.ShowDialog() ?
Thanks,
Raul
As I'm awaiting an answer on my question (Advice on using the Dispatcher Priority and Binding) I thought that would pay-it-forward™.
What you are experiencing is Nested Pumping on the Dispatcher. I recommend reading the MSDN article on the WPF Threading Model, especially the section titled 'Technical Details and Stumbling Points' that is two-thirds down the page. The sub-section describing the Nested Pumping is copied below for convenience.
Nested Pumping
Sometimes it is not feasible to completely lock up the UI thread.
Let’s consider the Show method of the MessageBox class. Show doesn’t
return until the user clicks the OK button. It does, however, create a
window that must have a message loop in order to be interactive. While
we are waiting for the user to click OK, the original application
window does not respond to user input. It does, however, continue to
process paint messages. The original window redraws itself when
covered and revealed.
Some thread must be in charge of the message box window. WPF could
create a new thread just for the message box window, but this thread
would be unable to paint the disabled elements in the original window
(remember the earlier discussion of mutual exclusion). Instead, WPF
uses a nested message processing system. The Dispatcher class includes
a special method called PushFrame, which stores an application’s
current execution point then begins a new message loop. When the
nested message loop finishes, execution resumes after the original
PushFrame call.
In this case, PushFrame maintains the program context at the call to
MessageBox.Show, and it starts a new message loop to repaint the
background window and handle input to the message box window. When the
user clicks OK and clears the pop-up window, the nested loop exits and
control resumes after the call to Show.
A modal dialog box does not prevent the owner window from processing messages, otherwise you'd see it fail to redraw as the modal dialog was moved over its surface (just as an example).
In order to achieve what you want, you have to implement your own queue on the UI thread, possibly with some synchronization to "wake it up" when the first work item arrives.
EDIT:
Also if you examine the call stack of the UI thread while the 2nd modal dialog box is up, you might find out that it has the first ShowDialog call above it in the stack.
EDIT #2:
There might be an easier way of doing this, without implementing your own queue. If instead of the SynchronizationContext, you would use the Dispatcher object, you would be able to call BeginInvoke on it with a priority of DispatcherPriority.Normal, and it will get queued properly (check).