Application.Current.MainWindow is null in startup event handler - wpf

I have a strong background in windows forms, and I'm starting to work in WPF. Consider the following event handler in my Application code behind:
Private Sub Application_Startup(ByVal sender As Object, ByVal e As System.Windows.StartupEventArgs) Handles Me.Startup
Debug.Print(Application.Current.MainWindow Is Nothing)
End Sub
This prints "True", meaning the Application.Current.MainWindow is null. How can I access the main window instance as soon as the application is run? (i.e. I know this event is fired as soon as the application is run)

"If you need access to the main window during startup, you need to manually create a new window object from your Startup event handler." - source: http://msdn.microsoft.com/en-us/library/system.windows.application.startup(v=vs.110).aspx
So basically you will have to create your main window when the Application.Startup event is called. Also you can remove StartupUri="MainWindow" from your App.xaml and simply show the instance of main window you create.
App.xaml
<Application x:Class="Namespace.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml" <---- Remove this
Startup="Application_Startup"
>
<Application.Resources>
</Application.Resources>
</Application>
App.xaml.cs
private void Application_Startup(object sender, StartupEventArgs e)
{
MainWindow wnd = new MainWindow();
wnd.Show();
}
I hope this answers your question.

Related

Closing Settings dialog causes application to quit

So here's the setup. I have a standalone WPF application which I've built with VB. Contained in the application is two windows, MainWindow and SettingsWindow, which opens as a dialog and is called from a subprocedure in MainWindow.xaml.vb on the event of a clicked "Settings" button (I like working with code-behind much better than XAML if I can help it).
In SettingsWindow.xaml.vb, I have a subprocedure handling the event of clicking a close button in the window. It executes Me.Close(). The intent is to close SettingsWindow and return focus to MainWindow, but instead, the entire application terminates.
Additionally, clicking the close button in MainWindow closes MainWindow, but doesn't terminate the application in Visual Studio's debug mode, while closing SettingsWindow does.
When building the application and running it outside of Visual Studio (running the .exe itself in Windows), closing SettingsWindow returns to MainWindow as intended, but attempting to re-open SettingsWindow causes the entire application to crash.
I'm relatively new to Visual Studio, and I can post code as needed. Thanks ahead of time for the help.
First, here is my `Application.xaml.vb' file handling app startup:
Class Application
Public Shared initmain As MainWindow = New MainWindow()
Public Shared Sub AppStart() Handles Me.Startup
initmain.UpdateSettings()
initmain.Show()
End Sub
End Class
The code above checks My.Settings, which works fine, then calls MainWindow open.
Here is the code in the MainWindow sub calling SettingsWindow:
Private Sub SettingStart() Handles SettingsButton.MouseUp
Dim SettingWin As SettingsWindow = New SettingsWindow()
' "If" blocks here that check My.Settings before opening the window
SettingWin.ShowDialog()
End Sub
Finally, here is the code that closes SettingsWindow:
Private Sub CloseButtonClick() Handles CloseButton.MouseUp
Me.Close()
End Sub
If you need it, here is Application.xaml:
<Application x:Class="Application"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="AppStart">
<Application.Resources>
</Application.Resources>
</Application>
Update:
It looks like the issue has to do with the fact that the instance of MainWindow in Application.xaml.vb is Public Shared, but I need to be able to call to that instance from other places to update settings in realtime, so I can't change this. Workarounds?
Additionally, the program now functions properly outside of Visual Studio, no longer crashing.
You need to set the ShutdownMode="OnLastWindowClose" in Application.xaml
<Application x:Class="Application"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml"
ShutdownMode="OnLastWindowClose" >
<Application.Resources>
</Application.Resources>
</Application>
MainWindow.xaml.vb
Class MainWindow
Private Sub ButtonSetting_Click(sender As Object, e As RoutedEventArgs) Handles ButtonSetting.Click
Dim SettingWin As SettingWindow = New SettingWindow()
Dim rslt As Boolean = False
rslt = SettingWin.ShowDialog()
End Sub
End Class

In what file is the event-handler wiring stored in VS2010 WPF application

In Visual Studio 2010 Designer, if you press [F4] while the WPF MainWindow has focus, you get a Properties view. If you click on the Events tab, and then double-click "Loaded", a handler for the window's Loaded event is automatically created in MainWindow.xaml.cs:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
}
but where is the "wiring" code that attaches this handler to the event?
You will notice that in MainWindow.xaml there is Loaded attribute on window node which points to your event handling method:
<Window ... Loaded="Window_Loaded">
...
</Window>
That is how xaml parser knows what method to wire up

Window is not recognizing an application resource

To reproduce the error:
Create a new MVVM-Light WPF application.
Copy MainWindow.xaml to MainWindow2.xaml Rename MainWindow2's class name to MainWindow2 (and the constructor)
Rename MainWindow2 window class attribute to "x:Class="MvvmLight2.MainWindow2"
Remove StartupUri from App.xaml
Add the following to App:
protected override void OnStartup(StartupEventArgs e)
{
new MainWindow().Show();
new MainWindow2().Show();
}
Run the application and get error:
Cannot find resource named '{Locator}'. Resource names are case sensitive. Error at object 'System.Windows.Data.Binding' in markup file 'MvvmLight2;component/mainwindow.xaml' Line 10 Position 9.
To resolve the error:
Remove DataContext="{Binding Main, Source={StaticResource Locator}}" from both windows.
Add the following line to both windows' constructors:
DataContext = new ViewModelLocator().Main;
The application now runs.
The question is why doesn't it recognize the Locator even though it's defined as an application resource?
Update:
I just noticed that I can add the same resource on both xaml and code without any visible side effects. The question now becomes, is there a problem with this? Does it create a duplicate resource or it doesn't because they have the same key?
More than just hacking it, I'm trying to understand what's going on.
Managed to fix this by adding InitializeComponent() inside Application.Startup event handler:
App.xaml
<Application x:Class="SomeNamespace.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="Application_Startup"> <!-- Important to use Startup -->
App.xaml.cs
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
InitializeComponent(); // <-- Important to set this!
var window = new MainWindow();
window.Show();
}
}

Is there a way to start a WPF application without StartUpUri that doesn't break something else?

I've been trying for hours to get to the point where I can start a WPF application and have full control. I want to be able to create a ViewModel, create a View (Window), set the data context of the View to be the ViewModel, then show the View.
I've tried lots of methods, the most promising being to change the App.xaml to be a page and then adding my own Main method. Unfortunately this doesn't work properly because VS2010 then does not show the styles from the App.xaml in the designer, though they do work when running the app.
Is there a way to do what I want? If not, how do people normally start MVVM apps in WPF, creating a ViewModel outside of the View itself?
I would use the Startup event. You can add this to the App.xaml and remove the StartupUri line. When you add it, Visual Studio can create the event for you within the App.xaml.cs file. You can initialise your ViewModel and View within.
Here is one simple way...
<Application
x:Class="Demo.Ux.WpfApp.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
</Application>
Here is the basic App.xaml.cs
public partial class App
{
protected override void OnStartup(StartupEventArgs e)
{
try
{
var mainView = new MainView();
mainView.Show();
mainView.DataContext = new MainViewModel();
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
}
}
Application.MainWindow can be used as well. The first displayed Window will be assigned to MainWindow auto-magically. Of course, you can skip creating your mainView and write directly to MainWindow which would thin out the syntax as well.
MainWindow = new MainView();
MainWindow.Show();
MainWindow.DataContext = new MainViewModel();
One final note, I'm doing the Show before the data bind. You need to do this to avoid a situation where the MainViewModel throw an exception during creation. If the MainView hasn't been shown, the app will close without letting you see the error.
in our application, we have choosen the way which you already proposed: writing a new Main method. You also have to make some changes in the project application settings then (no startup object). The app xaml has to look something like this:
<Application x:Class="EVOCURA.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="Application_Startup"
Exit="Application_Exit">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--Custom Controls-->
<ResourceDictionary Source="<your resources here>"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
The code behind will look something like this:
public sealed partial class App : Application
{
static App()
{ }
public App()
{ }
private void Application_Startup(object sender, StartupEventArgs e)
{
// create the main window and assign your datacontext
MainAppWindow main = new MainAppWindow();
main.DataContext = <your datacontext here>
main.Show();
}
[STAThreadAttribute]
public static int Main(string[] args)
{
App app = new App();
app.InitializeComponent();
app.Run();
return 0;
}
}
Have a look at the Startup Event and notice, that no default StartupUri is specified im App.xaml
You could also pass the DataContext in a new constructor of your MainWindow, or create the DataContext directly in xaml.
The simplest way to assign an instance of the ViewModel to the DataContext of the view is in the code behind of the Window.
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
DataContext = new myViewModel();
}
}
For the first part of your question, you can have the control of your application in the StartUp event
<Application x:Class="myApplication.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml" Startup="Application_Startup">
<Application.Resources>
</Application.Resources>
</Application>
Code Behind :
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
// Place your code here
}
}

How to change StartupUri of WPF Application?

I am trying to modify App.cs and load the WPF XAML files from code behind but its not working as it should.
No matter whatever I try to set as StartupUri it doesnt start, the program quits after this.
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
LoginDialog dlg = new LoginDialog();
if (dlg.ShowDialog() != true)
return;
switch (dlg.ChoiceApp) {
case ChoiceApp.CustomerEntry:
StartupUri = new Uri("/MyApp;component/Forms/CustomerEntry.xaml",
UriKind.Relative);
break;
case ChoiceApp.VendorEntry:
StartupUri = new Uri("/MyApp;component/Forms/VendorEntry.xaml",
UriKind.Relative);
break;
}
}
}
Now I even did trace and found out that LoginDialog is working correctly and is returning values correctly but setting "StartupUri" does not work.
I checked in reverse assembly that DoStartup method of App gets called after OnStartup, so technically my StartupUri must load, but it doesnt, in App.xaml startup uri is not at all defined.
Note: Bug Confirmed
I noticed that ShowDialog sets Application.MainWindow and when dialog ends, it sets it back to null, and because of this setting StartupUri does not work after calling Modal Dialog in OnStartup or Startup event.
There is no error or exception about invalid uri or anything like that.
This method works without DialogBox being called in Startup event or OnStartup, i think calling showdialog on this method causes something like its mainwindow being set to expired window and it shuts down after this.
Akash, I ran into this exactly issue trying to implement a LoginDialog just like yours. The dialog does not have a bug, but rather the behavior is by design.
Not a bug. The default ShutdownMode of
Application is OnLastWindowClosed, so
as soon as the first window is closed
your application will start shutting
down! Change to OnExplicitShutdown and
it will work, but you'll have to
manage the shutdown.
See this previous StackOverflow question: WPF ShowDialog returns null immediately on second call
instead of overriding the OnStartup() method, hook into the event instead.
in the XAML
<Application x:Class="SOTestWPF.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="Application_Startup">
<Application.Resources>
</Application.Resources>
</Application>
in the code behind:
private void Application_Startup(object sender, StartupEventArgs e)
{
var rnd = new Random();
if (rnd.NextDouble() > 0.5)
StartupUri = new Uri("/SOTestWPF;component/Window1.xaml", UriKind.Relative);
else
StartupUri = new Uri("/SOTestWPF;component/Window2.xaml", UriKind.Relative);
}
This is only my test case and I have verified that it performs correctly (randomly :D)
Just try in OnStartup() :
StartupUri = new Uri("Forms/CustomerEntry.xaml", UriKind.Relative);
Do you still have a StartupUri specified in the XAML? If so, remove it and see if that helps.MSDN Source
If not, you may need to approach this differently: have your Dialog as your startup, then from that point open another Window based on the selected value.

Resources