I have a C# Windows program that offers a 30-day free trial. After that, users must purchase the product or it will lock them out. I made a splash screen to occupy the user while the program queries my web server to see if the user is tampering with data to try to get the program to work without purchasing it.
On my Windows 8 machine (I'm using .Net Framework 3.5), the splash screen comes up nicely, then the MessageBox comes up (over top of the splash screen) to give them info like "your 30 day free trial has 2 days left".
However, on my XP machine, the MessageBox is hidden by the splash screen. Any ideas on how I can SendToFront() the MessageBox or SendToBack() the splash screen? If I just try to send the form on the splash screen to the back, that's not the same as sending the whole thread to the back.
Here's the code to show the splash screen:
static public void ShowSplashScreen() {
// Make sure it's only launched once.
if (ms_frmSplash != null)
return;
ms_oThread = new Thread(new ThreadStart(SplashScreen.ShowForm));
ms_oThread.Name = "SplashScreenThread";
ms_oThread.IsBackground = true;
ms_oThread.SetApartmentState(ApartmentState.STA);
ms_oThread.Start();
while (ms_frmSplash == null || ms_frmSplash.IsHandleCreated == false) {
System.Threading.Thread.Sleep(TIMER_INTERVAL);
}
}
Without seeing all of your code, I think what is happening is this:
You run your splash screen on a separate thread from your main GUI thread, then launch your message box from the main GUI thread (or yet another thread). Interfaces on separate threads will act independently (read: erratically) for their Z-order. Try moving your GUI elements (splash screen and message box) to the main thread. Run all long running processes (your server check) on another thread.
OK, I see what Servy and MikeH are saying, but my concern is that I do a bunch of work and it still doesn't work. Or, it just isn't predictable from machine to machine. So, the approach I'm going to take is to enhance the splash screen so that it presents the results from the server, rather presenting it in a separate window. That way, no Z-ordering to worry about at all...
Thanks for the thoughts, though, I might need them later on!
Related
I'm trying to implement a 'step-by-step guide' feature in my Windows Forms application. The guides are just step-by-step descriptions of how to do common tasks. I'm intending for users to work through the instructions while reading the guides. The window in which they're displayed therefore needs to be accessible when modal dialogs are being displayed.
Is this possible? If so, how do I do it?
When a modal is displayed, you cannot interact with any other windows in the same UI thread. It is possible (but probably not the best idea in the world) to run two distinct UI threads in the same process in C#-- but you will have to be very careful to keep things stable. In the main thread, run your normal modal form. In the secondary thread, you can run a non-modal form at the same time-- just keep a dedicate windows message pump running in that thread until you are done with the UI, and then shut down the message pump and exit the thread.
Thanks for andLabs, Tim and IInspectable for giving me the information I needed to solve the problem. The following does what I needed:
Thread myFormThread = new Thread(delegate()
{
Application.Run(new MyForm());
});
myFormThread.SetApartmentState(ApartmentState.STA);
myFormThread.Start();
It works fine. Obviously you need to be careful to use myForm.BeginInvoke if you need to access the controls on myForm from an event-handler of the main form. You also need to help the user to manage access to the form as it can get lost behind other windows.
So this may be a noobish type of question but here is what I'm wondering.
Let's say I have two screens, the first screen is idk, Screen1 for example. Let's say the user hits OK on Screen1 which takes them to Screen2.
What I'm currently doing is closing the first screen ( Screen1 ) so that the screen isn't in the background because the user will no longer need that screen nor should they be able to go back to that screen.
My question are essentially is these:
Let's say that I didn't close that previous screen in the background, how will that effect the program?
Will it slow the program down, because that screen is still running or will it not effect the program at all?
I'm just thinking in terms of efficiency. Does it really matter if I don't Close(); that previous screen?
There is of course some amount of overhead in having a window open. It will receive various window messages from the operating system and process them. However, if the window is not really doing anything, then the overhead is insignificant. For example, you can open 20 copies of the Windows calculator and not see any noticeable degradation in your computer's performance because those windows are not really doing anything until you interact with them.
Basically, the difference between closing the window vs not is insignificant (unless the window is running animations or playing a video or something else causing it to change regularly).
So, you should probably be making the decision as to whether to leave the window open or not based on the desired user experience for your application. Since you stated that the user should never go back to that screen, then it probably makes sense to close it, but that is really up to the application designer (or whoever is thinking about the user experience for the application).
Some of our non-technical users are having problems where a dialog MessageBox in our application can sometimes be displayed behind the main form and the application does not accept any input until the messagebox (which they can't see) is dismissed.
The application is written in C# and the message boxes are standard eg the code can be as simple as MessageBox.Show(message, caption) and the messageboxes can be created by the main UI thread (ie not some background thread). The Application does not have to be run full-screen, but 90% of our users do run it full-screen.
Most of the time ((maybe > 99%) the messageboxes display correctly and I have never managed to see how it goes wrong, but I have seen a machine when it has gone wrong.
One thing I did notice is that if you have an application which displays a dialog box, then when you look at your taskmanager, you normal only see one entry in the application list. Whenever the messagebox is hidden, you will see two entries, one for the main application and another entry for this message box.
It is easy enough to fix the problem once you know what has happened, but some of our non-technical users are confused by it and end up switching off their computers. (And the ones who are using Remote Desktop are even more confused when that doesn't solve the problem).
I do not think it is related to the operating system as I have seen it happen in Vista and have been told it also happens in a terminal session on a Windows 2003 server.
Does anything know why this is happening and more importantly if anything can be done to avoid it?
Some overloads of the MessageBox.Show() method take an IWin32Window parameter as the first argument. If you pass in your Form as that first argument it should prevent this from happening.
Is it always the same message box (for the same message?) coming the same form?
Ideally, you should try to find some way to reproduce the problem at will, or at least automatically. This will make your debugging easier and you can then be sure that your future change will have fixed the bug, rather than have to wait for a few weeks for the feedback from your users.
If it is always the same message and in the same window, resulting from the same action, and if the MessageBox call is reasonably easy to trigger from an user point-of-view and if your UI is relatively standard, you could automate the UI with an AutoIT script and have it run in a loop until the problem happens.
And/or, you could create a "debug"-build of your applications that you could give it to some users (preferably the ones that seem to run into the problem the most often) that would write the contents of a StackFrame object to a log file or something similar everytime before calling a MessageBox (you could create a wrapper around the MessageBox to make this easier).
Then, when one of your users gets the problem, you can look at the log file and see where it came from (source code file, line, calls stack etc). You could also compare this to the logs from other users and see if, everytime, the MessageBox comes from the same location or if it varies.
This would show you where the problematic MessageBox is called from and where.
There may be easier solutions (especially if your application has a lot of assemblies) involving some .Net debugger that you would attach to your application when the problem occurs in order to see the call stack, etc, but I only did this with native applications (using OllyDbg) so far and not .Net. Others may be able to expand further on this idea...
Confirm the problem. What we do to fix it is following:
Run new Task and display the message box
In the main UI thread while task is still running - wait in the loop that does DoEvents. Something like this
UPDATED 2015-12-17.
Reproduced problem yestarday. To repo in my case - minimize the app, "wait" popup occures (in our case it happens after some idle time), then in the task bar click on the main app icon. This "hides" the popup so it is not possible to bring it on the screen. The code below was tested and solves the problem. But I still do not understand what/why it happens.
private static DialogResult ShowMessageBox(
string message,
string caption,
MessageBoxButtons buttons,
MessageBoxIcon icon)
{
var showMessageBoxTask = new Task<DialogResult>(() =>
{
var form = new Form() {TopMost = true};
var result = MessageBox.Show(
form,
PrepareMessage(message),
caption,
buttons,
icon);
form.Dispose();
return result;
});
showMessageBoxTask.Start();
while (!showMessageBoxTask.IsCompleted && !showMessageBoxTask.IsFaulted)
{
Application.DoEvents();
}
return showMessageBoxTask.Result;
}
You say "the messageboxes can be created by the main UI thread", which I assume means they aren't always created by the main UI thread. Your problem sounds like MessageBox.Show is occasionally called from another thread.
In the parent form, add this before MessageBox.Show():
this.TopMost = false;
K I am looking at a primarily single thread windows forms application in 3.0. Recently my boss had a progress dialogue added on a separate thread so the user would see some activity when the main thread went away and did some heavy duty work and locked out the GUI.
The above works fine unless the user switches applications or minimizes as the progress form sits top most and will not disappear with the main application. This is not so bad if there are lots of little operations as the event structure of the main form catches up with its events when it gets time so minimized and active flags can be checked and thus the dialog thread can hide or show itself accordingly.
But if a long running sql operation kicks off then no events fire. I have tried intercepting the WndProc command but this also appears queued when a long running sql operation is executing. I have also tried picking up the processes, finding the current app and checking various memory values isiconic and the like inside the progress thread but until the sql operation finishes none of these get updated. Removing the topmost causes the dialog to disappear when another app activates but if the main app is then brought back it does not appear again.
So I need a way to find out if the other thread is minimized or no longer active that does not involve querying the actual thread as that locks until the sql operation finishes.
Now I know that this is not the best way to write this and it would be better to have all the heavy processing on separate threads leaving the GUI free but as this is a huge ancient legacy app the time to re-write in that fashion will not be provided so I have to work with what I have got.
Any help is appreciated
It sounds as if the long running operation is bound to the progress dialog? That's usually a bad idea and I wonder whether the progress can be showed at all.
However you should consider using a BackgroundWorker for your long running operations. So your GUI (the main form as well as the progress dialog stays alive).
This way you should be able to send the minimize event of the main form to the progress dialog which can react to it instantly.
Btw. the BackgroundWorker supports progress reports on its own.
My application (C#, VS2008) loads information from a database (SQL Server 2008 Express) over the network. During (possibly) longish waits, I want to have a 'Loading...' dialog box appear running on a different thread, but only if the operation takes more than a specific time period (say 500ms).
So, I have so far got my loading form being displayed after 500ms (if the operation lasts that long) without setting the loading dialog form's owner (i.e calling LoadingDialog.ShowDialog()), but when I try to call LoadingDialog.ShowDialog(IWin32Window owner) with owner set to the main form (passed in through the thread's parameter) I get the InvalidOperationException about accessing controls across threads.
My basic question is: Can I create and ShowDialog a form with the owner parameter set to a form on another thread? I want to do this so that the loading dialog is modal the rest of the application, i.e. like any other dialog takes the focus and disallows input elsewhere.
Can anyone offer a suggestion? I have read heaps about splash screens with no luck, also about Invoke and BeginInvoke with no luck. Is there a different way I should go about this?
Please feel free to ask for clarification if you don't understand.
Merci (as they say in French),
Jacob.
You should run your long process in a background thread, and then show your Loading... form as a non-modal dialog. That way, the user can see the Loading indication, but still interact with other parts of the system while its going on.
Another option would be to show the loading progress in a status bar or similar. The key is to make sure the long operation is done on a background thread and all the GUI logic is done on the GUI thread.
A good threading format might look like (this is just pseudocode for demonstration purposes - you need to use Invoke to get the appropriate threading):
RunInBackground(DoLongOperation);
ShowLoadingDialog();
...
DoLongOperation()
{
LongOperation();
RunOnGUI(FinishLongOperation);
}
FinishLongOperation()
{
CloseLoadingDialog();
}
All the UI components run on single sole thread which runs over the win32 message loop. You can not run any UI component in another thread. This architecture persists since Windows 3.1.
You CAN create as many UI threads as you want. The following code should do the trick:
Thread thread = new Thread(new ThreadStart(() => Application.Run(new MyForm())));
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();