App.xaml file does not get parsed if my app does not set a StartupUri?
Background: I'm creating a WPF app using MVVM, and using a DI container to build my ViewModels
My App.xaml looks like this:
<Application x:Class="WpfApp.App"
...xmlns etc...
StartupUri="MainWindow.xaml">
<Application.Resources>
<app:ServiceLocator x:Key="serviceLocator" />
</Application.Resources>
</Application>
MainWindow.xaml looks like this:
<Window x:Class="CompositeMefWpfApp.MainWindow"
...xmlns etc... >
<Control.DataContext>
<Binding Path="MainWindowViewModel" Source="{StaticResource serviceLocator}" />
</Control.DataContext>
Now, this all works fine, but the StartupUri
is hardcoded into the XAML, which I don't want.
Following guidance of several blogposts and articles I found, I removed the StartupUri
, and tried to create the MainWindow
by hooking OnStartup
in App.xaml.cs, like this:
protected override void OnStartup( StartupEventArgs e )
{
base.OnStartup(e);
new MainWindow().Show();
}
The problem is, the app crashes when trying to show the window, with this exception:
Cannot find resource named '{serviceLocator}'. Resource names are case sensitive. Error at object 'System.Windows.Data.Binding' in markup file 'WpfApp;component/mainwindow.xaml' Line 8 Position 45.
As far as I can tell, the <Application.Resources>
section is simply not being read out of the xaml file. I can put some code in the OnStartup to add the resource programatically like this:
Resources.BeginInit();
Resources.Add("serviceLocator", new ServiceLocator());
Resources.EndInit();
However that's an ugly hack, and doesn't help me if I wanted to put something else in the app.xaml file later on :-(
Should I be hooking some other event? Is there a way around this?
Rather than overriding OnStartup, try using an event instead:
<Application x:Class="My.App"
xmlns="..."
Startup="Application_Startup"
ShutdownMode="OnExplicitShutdown">
<Application.Resources>
<app:ServiceLocator x:Key="serviceLocator" />
</Application.Resources>
</Application>
Code behind:
public partial class App : Application
{
public App()
{ }
private void Application_Startup(object sender, StartupEventArgs e)
{
// TODO: Parse commandline arguments and other startup work
new MainWindow().Show();
}
}
The most simple workaround is the definition of a x:Name:
<Application x:Name="App" ...
<Application.Resources>
...
</Application.Resources>
</Application>
More information: http://connect.microsoft.com/VisualStudio/feedback/details/472729/wpf-cannot-find-resource-defined-in-the-app-xaml-file
I've run into a similar/this same issue. There's a VS code generation bug where the code necessary to connect <Application.Resources>
to the rest of the program sometimes is not inserted when <Application.Resources>
contains only one entry and does not have a StartupUri
attribute.
Details: http://bengribaudo.com/blog/2010/08/19/106/bug-single-application-resources-entry-ignored (disclaimer--link points to my blog)
To get around this know bug you can also just add resources programmatically.
var rd = new ResourceDictionary()
rd.Source = new Uri("pack://application:,,,/Resources;component/Colors.xaml");
Resources.MergedDictionaries.Add(rd);
This code can be placed inside the construtor for the App
class.