It says, I can use Startup event for many things, like initialization, creating multiple forms, blablabla.
However, try to create a new WPF application and add this event handler:
private void App_Startup(object sender, StartupEventArgs e)
{
Window window = new Window();
}
And after closing main window your application will hang up in the memory. Pausing executing in VS at that moment will drop ugly crash call stack window with a lot of calls somewhere far away from my source code.
Any thoughts?
P.S.: I need to instantiate all of my windows for self-learning configuration purpose once. Should I use some other place?
Let me give a better example (example above is good to reproduce the problem, seems it's hard to understand what I am actually doing). I remove StartupUri and then:
private void App_Startup(object sender, StartupEventArgs e)
{
// un-comment this line to reproduce the problem:
// Window window = new Window();
// actual implementation will be
// Window1 window1 = new Window1();
// Window2 window2 = new Window2();
// Window3 window3 = new Window3();
// Window4 window4 = new Window4();
// ...
// start main window as usually
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
}
And why do I need instances of windows (window1, ..2, ..3, ...)? Because their content will be inspected to create a list of controls for which I want to save configuration. Some of these windows will never be shown to the user (to example, if he is not admin), some of them are popups, some are editors, etc. So I do not want to display them. But at any application startup the configuration has to be created and saved. And I am looking now for the place to do so.
Surprisingly, using dedicated event Startup seems have some problems with creating multiple windows but not displaying them. Question is why and how to solve it.
Testing a bit more. Try this code and explain me, why application is closed without showing any window?
private void App_Startup(object sender, StartupEventArgs e)
{
Window window = new Window();
window.Close(); // closing without opening window
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
//mainWindow = new MainWindow(); // can be un-commented, for absolutely no effect
mainWindow.Show();
mainWindow.Show();
mainWindow.Show();
mainWindow.Show(); // you will not see mainWindow at all, all Show doing nothing
}
More questions. What do I do?
Something what works, but smells:
private void App_Startup(object sender, StartupEventArgs e)
{
Window window = new Window();
MainWindow mainWindow = new MainWindow();
mainWindow.ShowDialog();
Shutdown();
}
Notice, calling ShowDialog (which will make event handler waiting for that window closing) and calling Shutdown right after.
It is still not clear what is the problem in the Startup event handler to create instances of some windows. Any ideas?
By doing this, the window you just created has become the main window: the one specified in StartupUri will only be created right after the start up event. By default, the main window must be closed for the application to shutdown. Since it's never shown, the user has no opportunity to do this and the application seems to hang forever. What you're seeing when the execution is paused is a normal message loop, there is no crash here. Add window.Show() to see your real 'main' window.
Remove the StartupUri attribute from your App.xaml if you decide to instantiate the main window manually. Alternatively, keep the attribute and instantiate the additional windows after the Loaded event from the main window has been fired.
I have solution, which seems pretty good to me. Idea is similar to winforms - do everything in the Main.
This, however, is a bit tricky in wpf (I used this question as a guide):
Remove StartupUri from App.xaml;
Set App.xaml property Build Action to Page (this sounds strange, but it works for desktop application). This will remove Main method from auto-generated classes (App.g.cs and App.g.i.cs).
Add Main method manually into Application:
.
public partial class App : Application
{
[STAThread]
public static void Main()
{
Window window1 = new Window();
Window window2 = new Window();
Window window3 = new Window();
// ...
MainWindow start = new MainWindow();
start.ShowDialog();
SomeOtherWindow next = new MainWindow();
next.ShowDialog();
}
}
Now I can directly control which window to show and when (program flow control), as well as there is no more bug with not-closing application when creating instances of windows without displaying them.
The suspicious things are this Page setting and that fact, what I do not instantiate Application, nor I call Run(). I do not know yet, if it will be a problem in the future. Would be nice to know it for sure.
It might be necessary to init application still (to load resources?), then
[STAThread]
public static void Main()
{
App app = new App();
app.InitializeComponents();
// ... the rest
// possibly app.MainWindow = start; or app.MainWindow = next;
// if only 1 window, then app.Run(new MainWindow());
}
Related
In my MainWindow (see pic) I have a listbox and a button called btnToevoegen, which opens up my second window (see second pic), in that window, a class called oToevoegen gets information for 3 ints.
Now as you can see, when the user is done putting in the info on the second window, he can press the 'volgende' ('next' in dutch) button. See code
public void btnVolgende_Click(object sender, RoutedEventArgs e)
{
MainWindow wdwMain = new MainWindow();
Toevoegen oToevoegen = new Toevoegen();
wdwMain.lstFinanceInfo.Items.Add(oToevoegen);
wdwMain.lstFinanceInfo.Items.Refresh();
this.Close();
}
When that button is pressed, I want the class to be added to the lstBox called, lstFinanceInfo (on my mainWindow), but I can't figure out how. The method I've tried doesn't work.
You have several problems in that code.
When you do
MainWindow wdwMain = new MainWindow();
That creates a new instance of MainWindow. You don't then .Show() this so it's just hanging about in memory. You presumably already have an instance of mainwindow though. Since this is another instance of Mainwindow rather than the one that's visible, you can do whatever you like to it and nothing will visibly change.
It is technically possible to make a control public - BUT - this is bad practice. Don't do that.
Add a public method to MainWindow something like:
public void AddItem (Toevoegen tv)
{
lstFinanceInfo.Items.Add(tv);
}
Because that's public, you can call it from some other class such as your second window.
All you need to do is get a reference and call that method you added. Application has a mainwindow property which will contain that so grab it out there and cast to Mainwindow:
MainWindow mw = Application.Current.MainWindow as MainWindow;
mw.AddItem(oToevoegen);
why don't you try wdwMain.lstFinanceInfo.Items.Add(oToevoegen.Text);
but first make sure oToevoegen has a Text property (where it has the text you wanna display)
This is my App constructor for my WPF application:
public partial class App
{
public App()
{
Run(new Login(false));
}
}
And this is my Login constructor:
public Login(bool ignoreSettings)
{
InitializeComponent();
if (ignoreSettings)
{
TxtUsername.Text = SettingsSaver.LoadUsername();
TxtCrmUrl.Text = SettingsSaver.LoadCrmUrl();
return;
}
if (string.IsNullOrWhiteSpace(SettingsSaver.LoadUsername()) || string.IsNullOrWhiteSpace(SettingsSaver.LoadCrmUrl())) return;
try
{
CrmConnector.ConnectToCrm();
MainWindow mainWindow = new MainWindow();
mainWindow.Show();
}
catch (SecurityAccessDeniedException)
{
MessageBox.Show(#"Uw inloggegevens zijn niet correct. Gelieve deze te controleren en opnieuw te proberen.");
}
finally
{
Close();
}
}
It starts the App constructor and goes through the Login constructor just fine, but once it reaches the App Constructor again after finishing the Login constructor, it crashes with an InvalidOperationException, with additional information: "Cannot set visibility or call Show, ShowDialog, or WindowInteropHelper.EnsureHandle after the window has been closed.
The goal of the constructor is as follows: When the application is first started, I want to check if there are existing settings for this application. If they exist, I want to use those settings to connect to a 3rd party (Dynamics CRM 2011), open the main application window, and then close the Login screen. if they are not there, I want the user to set the settings.
HOWEVER, I also want to be able to start this window from a button on my main screen, in which case it should ignore the default settings and launch the login window again, allowing me to set the settings again.
I already managed to get it to work using 2 constructors, but Resharper complains when i does that because I basically ignore the parameter in the second constructor (the one which I launch from the button on the main screen. I'm trying to have 1 unified constructor so Resharper does not complain. Is that possible?
Edit: I don't want to keep my login window because I create a new window when I change the settings, using the following code in my MainWindow:
private void BtnSettings_Click(object sender, RoutedEventArgs e)
{
Login login = new Login(true);
login.Show();
Close();
}
edit: some clarification:
I don't want to show multiple windows. What I want is:
on startup, launch Login.xaml;
when Login.xaml is launched, check if the settings have already been set;
if no settings, show Login.Xaml for setting;
if Settings set, start MainWindow with saved settings.
In addition, I have a button on MainWindow which has to force-start Login.xaml but not check if there are settings. These are currently separate constructors and I would like to make 1 constructor of them.
Your update makes it a bit clearer what it is you want to achieve. I suggest restructuring the Login window to make it more single responsibility and pushing the validation logic up into the App class so that it is responsible for managing initialization flow. A recipe is as follows:
Alter App.Xaml.cs so that it looks something like this; importantly there is no StartupUri:
<Application
x:Class="MyNamespace.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources />
</Application>
Where MyNamespace is the namespace of your App class.
Now you are going to manually start the application from App.OnStartup
public partial class App
{
protected override void OnStartup(System.Windows.StartupEventArgs e)
{
base.OnStartup(e);
if (!AreSettingsSet())
{
this.MainWindow = new Login();
this.MainWindow.ShowDialog(); // Waits until closed.
// Recheck the settings now that the login screen has been closed.
if (!AreSettingsSet())
{
// Tell the user there is a problem and quit.
this.Shutdown();
}
}
this.MainWindow = new MainWindow();
this.MainWindow.Show();
}
private bool AreSettingsSet()
{
// Whatever you need to do to decide if the settings are set.
}
}
To summarise: remove your validation logic from the Login window to App, only show Login if needed and only show MainWindow if the settings are actually valid.
You will need to do some tweaking and then you can show several windows or single window multiple times.
Remove StartupUri from App.xaml.
Set Build action to Page for App.xaml.
This will disable autogenerating of App.g.ics and you can create own application entry point:
public partial class App : Application
{
[STAThread]
public static void Main()
{
App app = new App();
app.InitializeComponent();
app.ShowWindow1();
app.ShowWindow1(); // show second time same window (new instance)
}
public void ShowWindow1()
{
// show window1 in separate method, so that instance will be deleted after method ends
Window1 window1 = new Window1();
// optional (as it seems)
// MainWindow = window1
widow1.Show();
}
}
Try Visibility.Hidden instead of Close if you want to keep it
Update:
this.Visibility=Visibility.Hidden;
I faced a similar challenge,
make sure that any functions that may close the window you are opening are called after the window has loaded.
for the window you are opening set in the xaml file
Loaded="Window_Loaded"
then in the cs file,
private void Window_Loaded(object sender, RoutedEventArgs e)
{
//call your function here
}
This resolved my issue, hope that helps
I have a WPF window in a project with a XAML file and associated C# code behind file. If I set "StartupUri=MainWindow.xaml" in App.xaml to this window the window opens as expected when I start my application.
However, I want my application to to take command line parameters and then decided if it should open the GUI or not. So instead I've set "Startup=Application_Startup" in my App.xaml file which is defined as shown below.
private void Application_Startup(object sender, StartupEventArgs e)
{
if (e.Args.Length > 1)
{
//do automated tasks
}
else
{
//open ui
MainWindow window = new MainWindow();
this.MainWindow = window;
window.Show();
}
}
Yet when I run this the window displayed is totally blank.
Adding window.InitializeComponent() seems to do the trick:
MainWindow window = new MainWindow();
Application.Current.MainWindow = window;
window.InitializeComponent();
window.Show();
I usually like to have a little explanation on why something does or doesn't work. I have no clue in this case. I can see that the examples online don't include InitializeComponent, and yet I produce the same exact error as you do (event without checking for args).
I created a sample application, and removed the StartupUri and set the Startup to the method you provided. Everything seems to work as expected, the content of the window is displayed, so maybe, as Daniel mentioned, you're missing the call to InitializeComponent method in your MainWindow constructor.
I'm trying to run a window, close it, and then run a second window, in a similar way that seems to work with Windows Forms.
namespace WpfApplication1
{
public partial class App : Application
{
[STAThread]
public static void Main()
{
Application app = new Application();
//windowMain.Show();
app.ShutdownMode = ShutdownMode.OnExplicitShutdown;
MainWindow windowMain = new MainWindow();
app.Run(windowMain);
Window1 window1 = new Window1();
window1.Show();
app.Run(window1);
}
}
}
I've set the Build Action in the App.xaml properties from ApplicationDefinition to Page, but the programme throws an exception when window1 is initialised. What am I doing wrong?
Edit: I've modified the xaml in App.xaml as suggested by first answer and edited main as suggested by the comment.
<Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml"
ShutdownMode="OnExplicitShutdown">
<Application.Resources>
</Application.Resources>
</Application>
but I'm still getting the exception.
OK this is what I've divined so far. The Solution Builder looks for a Main() function. Why its not a WinMain() function I'm still not a hundred per cent clear on. If there is no Main(), you get an error. You can have more than one Main() as long as the Project properties: "Application" page/tab: property: "StartUp Object" is set to point to one of the main()s. This is done from an automatically created drop down list.
When a “WPF Application” project is created, Visual Studio(VS) create an xaml file called “App.xaml”. This is a class declaration where “App” is derived from the “Application” Class. VS also automatically generates hidden files for an xaml file. It creates a “name.g.i.cs” file, when the xaml file is created. It creates a “name.g.cs” file the first time the project is built after the creation of the xaml file. In this case it creates “App.g.cs” and “App.g.i.cs”. These files are hidden by default. To view them, press the “Show all files” button at the top of the Solution Explorer, they can be found in “\ obj\86\Debug” folder. When you delete an xaml file the “name.g.i.cs” and the “name.g.cs” files remain and are not deleted.
The “App.xaml” file’s “build Action” property is set to “Application Definition” when created by VS. When this property is set to “Application Definition” a Main() function is automatically created in “name.g.i.cs”:
[System.STAThreadAttribute()]
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
public static void Main()
{
WpfApplication8.App app = new WpfApplication8.App();
app.InitializeComponent();
app.Run();
}
When this property is set to “Page”, the Main() function is automatically removed by VS. You can create new “Application” derived classes in code or in xaml. I haven’t found a neat way to do it in xaml. There doesn’t seem to be a template for an xaml “Application” derived class. I created a “.cs” code file and then renamed it to an .xaml file. For some reason VS won’t allow you to have more than one xaml “Application” declaration file set to “Application Build”, it doesn’t even give you the option of choosing one in the "Project: Properties: Application": “Startup Object” property.
As you can see in the hidden Main(), an instance of “App” is instantiated and run. If using your own Main() function: an instance of, the base “Application” class, or an “Application” derived class (whether declared in code or in xaml), can be declared and run. The “Application” class should only be instantiated once and should only be run once. If the “Application” derived class is declared in xaml then a simple application can be run by using the StartUpUri property in the xaml file: StartupUri="Windowname.xaml". Alternatively the top level UI programme logic can be placed in a Startup event handler. If “Startup="Application_Startup" is placed in the “App.xaml” file then an event handler can be written:
private void Application_Startup(object sender, StartupEventArgs e)
{
MainWindow windowMain = new MainWindow();
windowMain.ShowDialog();
Window1 window1 = new Window1();
window1.ShowDialog();
Shutdown();
}
You have to use ShowDialog() here, because it blocks until the window is closed. If you used Show() instead, it would show one window, then immediately show the other one and shutdown the application. In this case there's no need to call the Run() method yourself, that's done automatically.
The “Application” class instance can be run in code whether its declared in code or in xaml. You can then perform initialisation code prior to calling Run(). This would be placed in the Application_ Startup() event handler using the other way. However, if the “Application.Run” call is ever made in the programme, then no windows should be opened (using show() or ShowDialog()) in Main() or anywhere outside of the Application Class or within events and functions called from those events, called during “Application.Run()”.
The Application class has a ShutdownMode property (Application.ShutdownMode). The default for this is: “OnMainWindowClose”. This property can also be set to “OnLastWindowClose” or “OnExplicitShutdown” in code or in the xaml. You will need to reset this if you don't want the programme to close down when the MainWindow is closed.
I think for my purposes it is better not to use the Application class at all and just call the windows using Show() and “ShowDialog()”. This way I can use WPF pages but I could also call Windows Forms, or DirectX screens, as long as they are not open at the same time, or have no UI at all, if the programme is running remotely. Is there any reason for not doing it this way?
I think your application is shuting down when you close the first window. You need to set Application.ShutdownMode to OnExplicitShutdown.
If all you want to do is to show one window, when that closes, show another and when that closes, shutdown the whole application, you should keep the Build action as ApplicationDefinition, set ShutdownMode to OnExplicitShutdown (probably in App.xaml, but you can it in code-behind too) and put the following code in an event handler of the Startup event of your application:
private void Application_Startup(object sender, StartupEventArgs e)
{
MainWindow windowMain = new MainWindow();
windowMain.ShowDialog();
Window1 window1 = new Window1();
window1.ShowDialog();
Shutdown();
}
You have to use ShowDialog() here, because it blocks until the window is closed. If you used Show() instead, it would show one window, then immediatelly show the other one and shutdown the application.
There's no need to run the Run() method yourself, that's done automatically.
I everyone. I currently have a problem with my focus when using a splash screen. I am using VS2008, with .NET framework 2.0. Also, I have linked my project with the VisualBasic.dll since I use the ApplicationServices to manage my single instance app and splash screen.
Here is a code snippet simplified of what I tried debugging.
namespace MyProject
{
public class Bootstrap
{
/// <summary>
/// Main entry point of the application. It creates a default
/// Configuration bean and then creates and show the MDI
/// Container.
/// </summary>
[STAThread]
static void Main(string[] args)
{
// Creates a new App that manages the Single Instance background work
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
App myApp = new App();
myApp.Run(args);
}
}
public class App : WindowsFormsApplicationBase
{
public App()
: base()
{
// Make this a single-instance application
this.IsSingleInstance = true;
this.EnableVisualStyles = true;
// There are some other things available in the VB application model, for
// instance the shutdown style:
this.ShutdownStyle = Microsoft.VisualBasic.ApplicationServices.ShutdownMode.AfterMainFormCloses;
// Add StartupNextInstance handler
this.StartupNextInstance += new StartupNextInstanceEventHandler(this.SIApp_StartupNextInstance);
}
protected override void OnCreateSplashScreen()
{
this.SplashScreen = new MyMainForm();
this.SplashScreen.StartPosition = FormStartPosition.CenterScreen;
}
protected override void OnCreateMainForm()
{
// Do your initialization here
//...
System.Threading.Thread.Sleep(5000); // Test
// Then create the main form, the splash screen will automatically close
this.MainForm = new Form1();
}
/// <summary>
/// This is called for additional instances. The application model will call this
/// function, and terminate the additional instance when this returns.
/// </summary>
/// <param name="eventArgs"></param>
protected void SIApp_StartupNextInstance(object sender,
StartupNextInstanceEventArgs eventArgs)
{
// Copy the arguments to a string array
string[] args = new string[eventArgs.CommandLine.Count];
eventArgs.CommandLine.CopyTo(args, 0);
// Create an argument array for the Invoke method
object[] parameters = new object[2];
parameters[0] = this.MainForm;
parameters[1] = args;
// Need to use invoke to b/c this is being called from another thread.
this.MainForm.Invoke(new MyMainForm.ProcessParametersDelegate(
((MyMainForm)this.MainForm).ProcessParameters),
parameters);
}
}
}
Now, what happens is that, when I start the application, the Splash Screen shows as expected, but when it is destroyed, it does not return the focus to the main form (Form1 in the test). The MainForm simply flashes orange in the taskbar. If I launch the application from the IDE (VS2008), focus works just fine. I am using XP Pro. Also, the main form is not on top of every other windows. If I comment out the OnCreateSplashScreen() method, the application gets focus normally.
To test normal execution, I am using the VS Command Prompt to launch my application. I am using the Release version of my project.
Any ideas?
Edit:
I also handle the StartUpNextInstance event to send my command-line arguments to my main form. For test purposes, it was removed here.
Edit: Added a bit more code. Now you have the full extent of my bootstrap.
The question is not in detail.
1) What the is the relationship between the SplashScreen and the main form of the application?
2) How does SplashScreen automatically close?
I'm sure the problem here is that the main form had already started loading up in the background while SplashScreen is yet to close. Due to bad timing, the main form loads up in the background and the SplashScreen unloads... hence the flash in the taskbar.
Make them appear in serial controlled manner. There are many ways. I cannot suggest exactly how since hardly any detail has been provided. Like what is VB doing in the project, how many threads are working, what are the custom forms used here.. etc.
EDIT:
Algorithm to implement Splash screen (IMHO) :)
1) Create a custom form - splash screen
2) Run it on a separate thread. Implement it's behaviour as you like.
3) In your splash screen form, write a handler to capture a custom unload event handler which closes the splash screen form.
4) Now, back in the main thread, create you main app form. Set its Visible property to false.
5) Write even handler of the main form's Load event. In this handler, fire an event to splash screen to unload. Then, make the main form visible.
You need to call the Activate() method on your main form in order to display it properly.
In some of my implementations of splash forms on a different thread, I've seen this situation as well.
usually if I call this.Activate(); as my first line of code in the form load it tends to work as expected.
putting this after hiding the splash usually doesnt work correctly.
other methods that can help.
this.BringToFront();
this.TopMost = true; //setting this to true and back to false sometimes can help
What if you set the focus explicitly in your Form1 Load event?
For some obscure reason, focus seems to be working correctly. I am going to try and incorporate more of my normal execution of my main program into the MyMainForm and try to find what really causes the focus change to fail. I will keep you all posted.
try calling form1.show() after this.MainForm = new Form1(); It worked for me.
I had the same issue and wanted easy solution for this.
I tried above but no luck, but ideas from above useful information.
I tried my trick and it works for me.
This is my Programs.cs code which works fine:
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
var mainWindow = new frmMain();
splashScreen.DisplaySplashScreen(mainWindow, 10000);
**mainWindow.TopMost = true;**
Application.Run(mainWindow);
}
}
Notice the mainWindow.TopMost = true;
Ok that was working enough but I did not want the mainWindow to stay on top.
So in Shown event i added this.TopMost = false;
private void frmMain_Shown(object sender, EventArgs e)
{
this.TopMost = false;
}
Now my application is working as I wanted it to work.
Note: Just posted to help any other user who face the same issue what I faced.
Happy Coding :)
in you main form's load event add
this.Activate();