How to add ResourceDictionary in Bootstrapper Application - c#

In my Bootstrapper Application I am using my own message box. This messagebox needs styling which is present in a dll. For other views (xaml) it is added as ResourceDictionary like:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MyApp;component/MyStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Or in general any C# application has app.xaml wherein this style can be added and it works. For Bootstrapper I am unable to add app.xaml or provide this ResourceDictionary. Any Pointers?

There are two things you have to do prior to being able to add Resource Dictionaries to the Application resources. Since the native Bootstrapper is hosting our WPF window there is no Application setup automatically. This can be done with simply creating a instance of System.Windows.Application. Second you have to set the Application.ResourceAssembly. After these two steps you are able to access the Applications Resources with Application.Current.Resources. For completeness a full Codesample:
public class CustomBootstrapper : BootstrapperApplication
{
protected override void Run()
{
if (Application.Current == null)
{
new Application();
}
if (Application.ResourceAssembly == null)
{
var assembly = typeof(CustomBootstrapper).Assembly;
Application.ResourceAssembly = assembly;
}
var myStyle = (ResourceDictionary)Application.LoadComponent(new Uri("/styling/MyStyle.xaml", UriKind.Relative));
Application.Current.Resources.MergedDictionaries.Add(myStyle);
var view = new MainWindow();
view.Show();
}
}
I hope this answers your Question

Related

How to open WPF application from an other WPF both using MVVM?

I am trying to us the demo code from wpf chrometabs and in the other application I just added a button that calls the constructor of the demo:
private void FrontendDebug_Click(object sender, RoutedEventArgs e)
{
Demo.MainWindow mainWindow = new Demo.MainWindow();
mainWindow.Show();
}
The problem is that it throws an exception at the InitalizeComponent:
I read somewhere that in MVVM, the DataContext should be set before calling InitializeComponent() but I don't know if that is the problem here or how to do it if it is.
The error that you get points out, that you use a StaticResource markup extension to reference a resource with key Locator that is not found in this line:
DataContext="{Binding Source={StaticResource Locator},Path=ViewModelMainWindow}"
The locator is defined in the application resources, so it is accessible in the whole application. Either you accidentially removed it or there is a typo in App.xaml. Make sure that it looks like this.
<Application x:Class="Demo.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="MainWindow.xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" d1p1:Ignorable="d" xmlns:d1p1="http://schemas.openxmlformats.org/markup-compatibility/2006">
<Application.Resources>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" xmlns:vm="clr-namespace:Demo.ViewModel" />
</Application.Resources>
</Application>
I am trying to us the demo code from wpf chrometabs and in the other application I just added a button that calls the constructor of the demo:
If you have another application that uses the demo project as library, the call below will only create an instance of the MainWindow. It will not bootstrap the application in the demo project.
Demo.MainWindow mainWindow = new Demo.MainWindow();
Consequently, the App object from the demo project is never created and its resources are not available. Even if it would be bootstrapped, the resources defined in App.xaml only apply to this application, nothing else.
Therefore, the mainWindow instance will try to find the resource in your application, not in the demo application. Since it is not defined there, you will get an exception. You could add the view model locator to your application's App.xaml resources, but keep in mind that is applies to all resources defined on the application level.

Why doesn't a WPF Theme show when MainWindow is called from another project?

I have a solution with three projects. One is for Testing using CodedUI, the other is a WPF Viewer application that shows the data from the tests, and the third is a project of Models.
If I set the WPF project to be the start up project in the solution and run it in debug mode the theme shows up just fine.
If I attempt to start the WPF MainWindow from the Test Project, everything shows the same, without the Theme. When the test project wants to show the data it does so by firing an event to this method in the WPF project.
public static class Start
{
public static EventHandler<List<SomeData>> NewData = ShowData;
private static void ShowData(object sender, List<SomeData> e)
{
MainWindow.NewData(sender, e);
var mw = new MainWindow();
mw.ShowDialog();
}
}
I have single stepped through this code, the data arriving and the mainwindow coming up just fine. But why wouldn't the code above include the theme?
There are no loading errors in output window.
The App.XAML is where the reference to the theme is located. But it is also where the StartupURI is located which is MainWindow.XAML. Do I need to somehow start App.XAML to get the theme?
Please advise...
How to start a WPF application and ensure the Theme works! (given the situation mentioned above)
var app = new App();
app.InitializeComponent();
app.Run();
Where App is your WPF application found here ==> App.xaml/App.cs and App.gs.i.cs.
The InitializeComponent call was the key. In the gs file this line is does the magic.
System.Windows.Application.LoadComponent(this, resourceLocater);
Notice the "this" keyword. It is a reference to the App class itself which in the XAML have these "references/attributes/resources"
<Application x:Class="AuomatedTest.Viewers.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes/ExpressionDark.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
The Theme itself was located in the project with these build attributes:
The bin directory had no folder named "Themes" in it; rather, it was the obj folder that contained the Theme; however, it was the "compiled" version of the file denoted by the .baml extension.
This means that the Page attribute of the XAML file compiles it. But what it doesn't tell us is how a reference like this is adequate.
<ResourceDictionary Source="Themes/ExpressionDark.xaml">
Today we know that without the Application.LoadComponent being called from the place we referred to the Theme (App.Xaml)that no Theme will be displayed. LoadComponent then knows how to interpret .BAML files and treat them as if they actually existed in the location that was referenced.

Merge DictionaryResource from Application in a ClassLibrary

I have a Windows Phone 8.1 solution that has a Standard Application Project and a Class Library Project. The idea is that I can, somehow, StaticResources down do the ClassLibrary so it can override the existing ones. I'll give you an example:
In my ClassLibrary I have a ClassLibraryDictionary.xaml with the following code:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ClassLibraryTest">
<SolidColorBrush x:Key="MyButtonColor" Color="#FF0000FF" />
</ResourceDictionary>
And the idea is that in my MainApplication I could have a Dictionary.xaml with the same StaticResource Key, and pass it to my ClassLibrary so it can override the default property, something like:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MainApplicationTest">
<SolidColorBrush x:Key="MyButtonColor" Color="#00FF00FF" />
</ResourceDictionary>
And passing it in code:
var mainAppDictionary = new ResourceDictionary();
mainAppDictionary.Source = new Uri("/using:MainApplicationTest;component/MainApplicationDictionary.xaml", UriKind.RelativeOrAbsolute);
classLibraryTest.SetResourceDictionary(mainAppDictionary);
The problem here is that I can't seem to use the ResourceDictionary instance in my ClassLibrary, and I'm not even sure this is the best way to do this.
So, how could I solve this?
I found out how to this and it is actually simple. The path I was using was not the correct one, so to sum it all up, the solution in XAML, if you were to do it in the App.xaml (in the MainApp) looks like this:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ms-appx://Common/yourclasslibraryname/ClassLibraryDictionary.xaml"></ResourceDictionary>
<ResourceDictionary Source="/Dictionary1.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
If you want to set it in code directly in the ClassLibrary you can do something like this:
In the MainApp.xaml.cs
myClassLibrary.MergeAllDictionaries(App.Current.Resources);
And in the ClassLibrary:
public void MergeAllDictionaries(ResourceDictionary appDictionary) {
// First copy all keys from MainApp to a new Dictionary
ResourceDictionary mainDictionary = new ResourceDictionary();
foreach(var keys in appDictionary.MergedDictionaries) {
foreach(var keys1 in keys) {
mainDictionary.Add(keys1.Key, keys1.Value);
}
}
// Then clear all
appDictionary.Clear();
// Get the ClassLibrary dictionary
ResourceDictionary classLibraryDictionary = new ResourceDictionary();
classLibraryDictionary.Source = new Uri("ms-appx://Common/yourclasslibraryname/ClassLibraryDictionary.xaml", UriKind.RelativeOrAbsolute);
// First add the ClassLibrary keys and values
appDictionary.MergedDictionaries.Add(classLibraryDictionary);
// Then add the old values, so that they overwrite the ClassLibrary ones
appDictionary.MergedDictionaries.Add(mainDictionary);
}

Why are Application-Resources only loaded when placed in a seperate file if OnStartup is overriden?

In App.xaml (no StartupUri):
...
<Application.Resources>
<ResourceDictionary Source="AppResources.xaml"/>
</Application.Resources>
...
This does work but declaring the ResourceDictionary directly in Application.Resources does not:
...
<Application.Resources >
<ResourceDictionary>
...
</ResourceDictionary>
</Application.Resources>
...
OnStartup in App.xaml.cs:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
if (e.Args.Length > 0)
{
// Handle args and start headless.
}
else
{
// Create window.
new Views.ShellWindow().ShowDialog();
}
this.Shutdown();
}
How can this behaviour be fixed?
Edit: I am using Visual Studio 2013 Professional.
Apparently, there is a VS code generation bug where the code necessary to connect to the rest of the program sometimes is not inserted when
The application has no StartupUri attribute set in App.xaml AND
there is only one resoure in the section of App.xaml
A possible workaround is to use an event instead of overriding OnStartup, see WPF - App.xaml file does not get parsed if my app does not set a StartupUri? for more information.

How to move App.xaml and not to break designer?

I have WPF project and some resources defined in App.xaml that are used in other files. When I try to move App.xaml to subdirectory designer is no longer able to find those resources. My project still compiles since I use 'Startup' event instead of 'StartupUri'. How to tell the designer where to search for resources? How it knows where they are when App.xaml is in root of project?
Update:
Project files:
App.xaml:
<Application x:Class="WpfApplication1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Startup="startup">
<Application.Resources>
<SolidColorBrush Color="LightGray" x:Key="brush" />
</Application.Resources>
</Application>
App.xaml.cs
namespace WpfApplication1
{
public partial class App : System.Windows.Application
{
private void startup(object sender, System.Windows.StartupEventArgs e)
{
new MainWindow().Show();
}
}
}
MainWindow.xaml:
<Window x:Class="WpfApplication1.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Height="300" Width="300" Background="{StaticResource brush}" />
MainWindow.xaml.cs
namespace WpfApplication1
{
public partial class MainWindow : System.Windows.Window
{
public MainWindow()
{
InitializeComponent();
}
}
}
Update 2:
Uploaded zipped solution to http://zalil.ru/30771604 (download will start automatically)
Mirror: http://www.speedyshare.com/files/27697250/WpfApplication1.zip
1. Move you App.xaml to your desired location
2. Refactor you App.xaml.cs namespace to accommodate the new change:
3. Rebuild your solution.
[4]. Go to your project properies and set the Startup object to your App.xaml file at the new location.
[5]. Run your application and it should work successfully :)
I can't duplicate the problem on my end. Here's what I tried:
I created a application with a style in the resources of App.xaml. I moved App.xaml to a subdirectory. I have another window that uses the style defined in the resources of App.xaml (he resides in a different directory) and it was able to resolve it just fine. I added ..\ to the beginning of my original StartupUri.
I did some searching, what version of Visual Studio are you using? Apparently there may be an bug related to your problem in VS2008:
http://bengribaudo.com/blog/2010/08/19/106/bug-single-application-resources-entry-ignored
He says a workaround for this bug is to set the x:Name attribute on Application. Hope that helps!
EDIT: I also tried handling the Startup event instead of using the StartupUri and it still worked fine.
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
new MainWindow().Show();
}
}
EDIT PART 2:
Okay, I enclosed the SolidColorBrush inside a ResourceDictionary as such:
<ResourceDictionary>
<SolidColorBrush Color="LightGray" x:Key="brush" />
</ResourceDictionary>
And the window picks up the brush. The designer doesn't like it, but when I change from StaticResource to DynamicResource it stops complaining.
EDIT 3:
I just thought of something. Do you have VS2010 SP1 installed? It fixed some bugs with the designer.
And sorry, my edit number 2 didn't work as expected. I noticed my blue squiggles were gone in the xaml, but I didn't check the designer. x__x

Categories