Is there some equivalent of wpf's Application.MainWindow in compact framework?
The issue i have is that i run the SplashScreen, it does some work, and i need to close it and open the login page.
I try to just close the form and open another one, but it closes the entire application, as the SplashScreen is the one I pass to the Application.Run() method
I don't want to hide the form, because it causes problems when I want to hide my entire application and call another application from my code(the splash screen seems to be still there even though i do call the hide method), so i need to close the splash screen and open the login screen
You have a few options.
Don't pass your splash screen to Application.Run. Send in your main form, then have it create the instance of the splash and handle showing and hiding it. Really that screen (or any view for that matter) shouldn't be doing any "work" anyway - the work should be being done elsewhere and the UI should only react by displaying status, progress, validating user input etc.
Use two sequential calls to Application.Run:
i.e.
Application.Run(new SplashScreen());
Application.Run(new MainForm());
Set: App.xaml (property) -> Build Action -> "Page"
Set in App.xaml.cs Main():
[System.STAThreadAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "4.0.0.0")]
public static void Main()
{
SplashScreen ss = new SplashScreen();
ss.showDialog();
App app = new App();
app.InitializeComponent();
app.Run();
}
Set App.xaml:
<Application
//something
StartupUri="MainWindow.xaml">
//something
</Application>
Related
I'm working on a custom splash-screen (since the standard one doesn't fit my needs). But there is one option I'd like to have from it - auto-close. But to implement it, I need to understand how the common SplashScreen selects the moment to close.
So, is there any sort of event to message the splash screen, to tell it that it should be closed? What event does the common splash-screen use, at least?
The WPF SplashScreen class uses a very simple trick, it calls Dispatcher.BeginInvoke().
The expectation is that the UI thread is grinding away getting the program initialized and is therefore not dispatching anything. It is "hung". Not forever of course, as soon as it is done, it re-enters the dispatcher loop and now the BeginInvoked method gets a chance, the ShowCallback() method runs. Poorly named, should be "CloseCallback" :) A 0.3 second fade covers up any additional delay in getting the main window to render.
In general, calling Dispatcher.BeginInvoke() on the UI thread looks like a weird hack but is very useful. An excellent way to solve gritty re-entrancy problems.
Very simple, not the only way to do it. The main window's Load event could be a useful trigger.
Instead of having an image file with Build Action set to Splash Screen, you can have more control over the splash screen by creating and showing it yourself in the Application's OnStartup event handler. The show method of SplashScreen has a parameter to stop it closing automatically and then you can tell it when to close using the Close method:
Firstly remove the StartupUri tag from App.xaml:
<Application x:Class="Splash_Screen.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
</Application.Resources>
</Application>
Change the Build Action of your image file to Resource
Then create and show the splash screen in the OnStartup event handler:
public partial class App : Application
{
private const int MINIMUM_SPLASH_TIME = 1500; // Miliseconds
private const int SPLASH_FADE_TIME = 500; // Miliseconds
protected override void OnStartup(StartupEventArgs e)
{
// Step 1 - Load the splash screen
SplashScreen splash = new SplashScreen("splash.png");
splash.Show(false, true);
// Step 2 - Start a stop watch
Stopwatch timer = new Stopwatch();
timer.Start();
// Step 3 - Load your windows but don't show it yet
base.OnStartup(e);
MainWindow main = new MainWindow();
// Step 4 - Make sure that the splash screen lasts at least two seconds
timer.Stop();
int remainingTimeToShowSplash = MINIMUM_SPLASH_TIME - (int)timer.ElapsedMilliseconds;
if (remainingTimeToShowSplash > 0)
Thread.Sleep(remainingTimeToShowSplash);
// Step 5 - show the page
splash.Close(TimeSpan.FromMilliseconds(SPLASH_FADE_TIME));
main.Show();
}
}
I want to implement a help-form into my app, which can get the focus, even if a dialog is shown. At the moment i dispose the actual instance of my help if it can't get focused, but i dont think thats the right way. So i want to ask, if theres a option to show a form, seperated from the logic of my main-application.
Things i tried:
calling as a AppDomain (MSDN)
putting the help into seperate app and call it as a process
In both ways, the help(-form) can't get the focus back, when a dialog was called.
I dont want to use the help provided with C#, because i need to show the help(-pages) inside the application.
Thanks
PS: I'm using .Net 2.0.
You can do this by creating an STA thread and using Application.Run() to display the form from that separate thread. Application.Run() will create a separate Message Pump for the other form; this is what keeps it separate.
If you do that, you have to be VERY CAREFUL when communicating between the forms. You will need to use Control.Invoke() or some other inter-thread mechanism to call UI-changing methods on the second form from the first form (and vice-versa).
But if you do this, then the first form can be showing a modal dialog, and the second form will still be focusable.
Note that the second window may be behind the first window because there will be no way to specify the relative Z-order between them.
Showing the second form can be done like this:
private static void ShowIndependentForm()
{
Thread thread = new Thread(ShowIndependentFormImpl);
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
}
private static void ShowIndependentFormImpl()
{
Application.Run(new Form2());
}
You can just call ShowIndependentForm() where appropriate; probably from the main form after you have created it, but my test code in Main() looks like this:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
ShowIndependentForm();
Application.Run(new Form1());
}
Important
Because the second form has its own message pump, closing the first form will NOT close the program unless you set Thread.IsBackground to true. If you don't, you will have to explicitly close the second form (via calling a method in the second form using Control.Invoke() or some other way) when the first form closes if you want the program to close automatically.
I am using a splash screen for a c# which runs on startup and checks the app license.
I show the splash from the main form like this:
public partial class Form1 : Form
{
static bool stopThreads = false;
static bool gridChanged = false;
public Form1()
{
InitializeComponent();
Thread th = new Thread(new ThreadStart(DoSplash));
th.Start();
th.Join();
}
private void DoSplash()
{
Splash sp = new Splash();
sp.ShowDialog();
}
Now from the splash form I am trying exit the application when the license is invalid, but it only exits the splash and it enters the main form.
I tried exiting with :
Enviroment.Exit();
Application.Exit();
Form1 f = new Form1();
this.Close();
But none closes the main form, only the splash.
How can I close the entire app from the splash form class?
Try Application.ExitThread()
Yes, these calls only cause the thread to exit. You created a new thread. There's little point to be gracious about it in this case, Environment.Exit(1) will get the job done. The huff-and-puff version is Control.BeginInvoke() to run code on the main UI thread. You'll need a reference to Form1 to make that call.
Btw, you'll also have a big problem with SystemEvents, they run on the wrong thread because the very first window you created was created on thread other than the main UI thread. The most typical mishap is a deadlock when you lock and unlock the work station. You'll need to wait until at least one window is created on the UI thread. Form1's OnLoad() method override or Load event would be a good place to start the splash. Or just use the built-in support for splash screens.
You could use Application.Exit() or Environment.Exit().
These probably aren't the "cleanest" way to shut down your app, but if you're just bailing at the splash screen, it's unlikely it'll cause any issues.
Edit: If you want to quit without showing the splash screen at all if the licence is invalid, you should check the licence before showing the splash screen, and just exit before then.
Never introduce multithreading in application unless absolutely necessary.
As Sir Walter put it,
Else thou shalt enter into a world of pain.
Moreover, any UI interactions, such as displaying a window or working with controls, must be done on main thread only.
If you want to do something while the form is on the screen, call Show instead of modal ShowDialog so execution does not get blocked.
Application.Exit ()
will do nicely if you call it on the main thread, as you should.
If you want to show splash screen before main form is shown, you should not do it in main form's InitializeComponent. Instead, change code in Program.cs to show splash screen first:
Application.Run (new SplashScreenForm ());
Somewhere in SplashScreenForm (I don't know why you need it at all, honestly) you should check for license, and if it's fine, close the window, create MainForm instance and call its ShowDialog. If it's bad—just close the window, and since it was the last form, application would stop.
My program puts an icon in the system tray because the user may minimize to it. However, if the application crashes, or I stop the app from running in VS it leaves the icon in it until I hover over it with the mouse. Sometimes I'll look down there and there will be 10 or so icons.
I can I make sure the icon goes away?
There is no way to do this. Windows does not poll your program to see if it's still alive. Therefore, your icon will stay in the system tray until you either tell it to leave (normal exit), or the user does something that initiates a call to your program (such as mouseover). Only then does the shell know that your program has died and it needs to clean up your icon.
You have several options:
1) Write a root structured exception handler that ensures the destruction of the icon before exit. This one takes some effort - but you can basically add your own unhandled exception handler to Windows which will be called in the case of many forms of "boom" which will then allow you some last-ditch clean up opportunity.
2) Write a monitor application that sleeps on your main processes handle, and kills the icon when it wakes up (the main application has died). To make this latter scenario work well, you'll likely want the main process to NOT kill the icon itself - or you could create a way to tell the monitor app that it should exit w/o destroying the icon (have the monitor app sleep on both the process handle and a named event - and if woken by the event then it simply dies w/o cleaning up after the main app).
Before you exit, set the NotifyIcon Visible property to false.
You need to call Dispose on the NotifyIcon for it to leave the tray.
EDIT: There's an event you can hook into in your Program.cs. For all Windows apps, you'll have something like this:
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
The Application class has a few events you can take advantage of to know when your app dies:
Application.ThreadException += new System.Threading.ThreadExceptionEventHandler(Application_ThreadException);
Application.ApplicationExit += new EventHandler(Application_ApplicationExit);
I don't know what happens when one kills the process, but if it crashes due to an exception, of course one can handle it. The way to do it best, depends on the type of application: Console, Forms, a service, ...
But in all cases, you should be able to use a try / finally structure in your Program.cs, and in the 'finally' section Dispose() the TrayIcon.
For example, in a Forms application, make your NotifyIcon (called TrayIcon in my example below) in your Form class public, and change the "Application.Run(new Form1())" line in Program.cs as follows:
Form form = new Form1();
try { Application.Run(form); }
finally { form.TrayIcon.Dispose(); }
We can hide trayIcon before form closing.
Add FormClosing Event
NotifyIcon ni;
...
...
private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
{
ni.Visible = false;
}
I have an application.
First I display a splash screen, a form, and this splash would call another form.
Problem: When the splash form is displayed, if I then open another application on the top of the splash, and then minimize this newly opened application window, the splash screen becomes white. How do I avoid this? I want my splash to be displayed clearly and not affected by any application.
You need to display the splash screen in a different thread - currently your new form loading code is blocking the splash screen's UI thread.
Start a new thread, and on that thread create your splash screen and call Application.Run(splash). That will start a new message pump on that thread. You'll then need to make your main UI thread call back to the splash screen's UI thread (e.g. with Control.Invoke/BeginInvoke) when it's ready, so the splash screen can close itself.
The important thing is to make sure that you don't try to modify a UI control from the wrong thread - only use the one the control was created on.
The .NET framework has excellent built-in support for splash screens. Start a new WF project, Project + Add Reference, select Microsoft.VisualBasic. Add a new form, call it frmSplash. Open Project.cs and make it look like this:
using System;
using System.Windows.Forms;
using Microsoft.VisualBasic.ApplicationServices;
namespace WindowsFormsApplication1 {
static class Program {
[STAThread]
static void Main(string[] args) {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
new MyApp().Run(args);
}
}
class MyApp : WindowsFormsApplicationBase {
protected override void OnCreateSplashScreen() {
this.SplashScreen = new frmSplash();
}
protected override void OnCreateMainForm() {
// Do your time consuming stuff here...
//...
System.Threading.Thread.Sleep(3000);
// Then create the main form, the splash screen will close automatically
this.MainForm = new Form1();
}
}
}
I had a similar issue you might want to check out. The Stack Overflow answer I got worked perfectly for me - you may want to take a look.