I have a set of styles and brushes defined in a ResourceDictionary that I am loading as a MergedDictionary in XAML of my top-level control:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MyAssembly;component/Styles.xaml"/>
</ResourceDictionary.MergedDictionaries>
I am trying to optionally replace some of these styles & brushes if a different XAML file exists in the XAP with its own ResourceDictionary. I am trying to merge in this dictionary at runtime before InitializeComponent() is called on my user control. I am using the following code to attempt to do this:
public static class StyleLoader
{
public static void MergeStyle(string xamlUri)
{
try
{
XDocument xaml = XDocument.Load(xamlUri);
ResourceDictionary rd = XamlReader.Load(xaml.ToString()) as ResourceDictionary;
Application.Current.Resources.MergedDictionaries.Add(rd);
}
catch (XmlException ex)
{
// if the file doesn't exist, we can't add it
}
}
}
The resource dictionary from the optional file is loaded fine and merged, however my original set of styles always seems to be overriding this. If I comment out the merged dictionary in XAML and simply load them at runtime in order it works perfectly:
StyleLoader.MergeStyle("/MyAssembly;component/Styles.xaml");
StyleLoader.MergeStyle("BrushReplacements.xaml");
InitializeComponent();
My problem with this solution is that without the default styles in XAML, I can not open the project in Blend. Anyone have any ideas for a solution that will keep my default styles known to Blend but allow me to optionally override them at runtime with a dynamically loaded resource dictionary? Thanks!
Here is a solution where colors/brushes are applied with bindings instead of referring directly to the static resources:
http://blogs.msdn.com/corrinab/archive/2009/11/24/9927729.aspx
Part two:
http://blogs.msdn.com/corrinab/archive/2009/12/02/9931283.aspx
Currently I think something like this is the best way of dealing with dynamically switching themes at runtime. But it does require a lot of work to port an existing application to use a mechanism like this.
Related
I have a WPF application without an application.xaml, since I need to do the Main() method by myself. Therefore I neither have an ApplicationDefinition nor an application resource. I currently attach the resource dictionary to the application at the application startup
Of course the WPF Designer complains now about missing resources.
So I want to get rid of two problems:
- I don't want to attach the resource dictionary manually at startup
- I want to get the resources also work at design time in the WPF designer
Is there any help for this problem?
Thanks
Martin
There is a possibility to use the Main() method for yourself
Just delete the following property from the xamlcode of Application.xaml:
StartupUri="MainWindow.xaml"
Then add the following in the code-behind of the Application.xaml:
public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e)
{
// My code goes here, but nothing ever happens.
base.OnStartup(e);
}
}
Now you can still use the Application.xaml for your resources and also have your specific startup procedure.
I have a program wrriten in WPF (C#) where all of the elements has a style that points a static resource located in a ResourceDictionary by the name "Styles.xaml";
Since I need to have the styles customed, I use this file to change colors and fonts to elements all across the program.
The problem is, In order to see the changes I need to recompile, and to have another EXE wich is a slight differ version of the program (differ by colors).
I don't want to maintain many versions of the program.
Is it possible to have a ResourceDictionary (or any other file) outside of the compiled EXE to function as css does with HTML?
Meaning, is it possible to achieve the following: replacing the file in the folder where the EXE lies will be sufficient in order to change the colors?
Thank you.
You can obtain a ResourceDictionary instance from .xaml file (not necessary included in your project) by calling XamlReader.Load method and casting the resulting object afterwards. From that point it comes down to manipulating this dictionary in code behind. On application level you can call something like
Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(dictionary);
where dictionary is the instance you have loaded. The same can be done for individual controls.
Thanks to Nikita Brizhak , I have succeeded.
Here is the full "HOW TO":
1) You need to add an OnStartUp method to App.xaml.cs. Here is the syntax:
protected override void OnStartup(StartupEventArgs e)
2) You have to clear all the resources.
Application.Current.Resources.Clear();//This is if you have on your App.xaml a load of your resource. Clear it and than load another.
//It is good to have this so you can see you style while working on the project, But on runtime to replace.
Application.Current.Resources.MergedDictionaries.Clear();
3) you Have to load your dynamic resource
Final Code:
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Application.Current.Resources.Clear();
Application.Current.Resources.MergedDictionaries.Clear();
StreamReader stream = new StreamReader("Styles.xaml");
Application.Current.Resources.MergedDictionaries.Add(System.Windows.Markup.XamlReader.Load(stream.BaseStream) as ResourceDictionary);
}
I am trying to use a .NET 4 SplashScreen in a Prism based WPF application. I have used the SpashScreen by setting the build action on the image to SplashScreen.
The application used to keep on crashing with a System.Resources.MissingManifestResourceException. Finally I figured out that if I add a StartupUri="MainWindow.xaml" in the App.Xaml file, the SplashScreen works fine.
<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">
</Application>
But in a prism application, we cannot have a StartupUri. Everything is done in the Bootstrapper.
So what do I need to do manually that StartupUri did to make the SplashScreen work?
Update 1: The complete exception message is:
System.Resources.MissingManifestResourceException was unhandled
Message=Could not find any resources appropriate for the specified culture or the neutral culture. Make sure
"Application.g.resources" was correctly embedded or linked into
assembly "Application" at compile time, or that all the satellite
assemblies required are loadable and fully signed.
Update 2:
I have figured out the adding or removing the StartupUri does not matter. What matters is that I have an additional WPF Window (other than App.xaml) or 2 dummy entries in the App.Resources tag.
<Application.Resources>
<Style x:Key="Dummy"/>
<Style x:Key="Dummy1"/>
</Application.Resources>
If I do not do this, the Application.g.resources file is not created in obj file and hence not embedded in the executable.
Adding two dummy resource entries was brought to my attention by this blog post.
Update 3:
My question was answered by Bob Bao on MSDN forum here. Also It seems Kent was trying to point me in the same direction.
Do not set the build action of the image to SplashScreen. Instead:
Add the code in the App OnStartup method as:
protected override void OnStartup(StartupEventArgs e)
{
SplashScreen splashScreen = new SplashScreen("splashscreen.png");
splashScreen.Show(true);
base.OnStartup(e);
Bootstrapper bootstrapper = new Bootstrapper();
bootstrapper.Run();
}
"splashscreen.png" is one image in the project, and its "Build Action"
is "Resource".
Simply define your own entry point which firstly shows the splash screen and then bootstraps Prism. In your project properties, set the entry point to your custom entry point.
internal static class Entry
{
public static void Main(string[] args)
{
var splashScreen = ...;
splashScreen.Show();
var bootstrapper = ...;
bootstrapper....;
}
}
Please check this adress : http://prismsplashscreen.codeplex.com/
There is a full example with prism
I'm am adding the following code to the constructor of App.xaml.cs in my WP7 application.
Resources["PhoneBackgroundBrush"] = new ImageBrush
{
ImageSource = new BitmapImage(new Uri("/images/bg.png", UriKind.Relative))
};
after running it I get NotImplementedException once the applicaion is opened.
Any idea of how can we do this?
What I'm trying to achieve is a single theme application like Facebook that always have white theme regardless of phone's theme.
As a note, getter on the resources always work, so it's not that accessing phone resources is totally blocked from code. (I'm using this to determine current theme and accent of phone).
var a = Resources["PhoneBackgroundBrush"]; // this works fine
For simple examples it isn't complicated when you know how, however you shouldn't rename PhoneBackgroundBrush, but create a new key for your resources.
You need to create the resources in App.xaml (or you can pull in MergedDictionaries other places too, but that's more advanced). Test.jpg needs to be a resource file in your project.
In App.xaml:
<Application.Resources>
<ImageBrush x:Key="MyImageBrush" ImageSource="Test.jpg"/>
</Application.Resources>
In a page for example:
Background="{StaticResource MyImageBrush}"
If you're starting to re-template a control (say a Button), that when you need to crack open Blend and modify the default styles of controls. It's pretty well documented, and builds on these same principles...
Basically the reason you're getting a NotImplementedException is because the setter for the Application.Resources is implemented like this:
set
{
throw new NotImplementedException();
}
The reason Facebook is all white, is because they defined their own colour resources, and used them everywhere. I've also made a always-white themed app. It just requires a little extra effort.
I'm trying to set the current WPF Application ResourceDictionary programatically. (I have a WindForms project, so no "App.xaml" to do that for me).
Note: If anyone knows how to bind the equivalent of an Application.Resources to a ElementHost and all of its child controls hierarchy, this is the ultimate objective here.
I added to my code:
Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri("/Middlewerks;component/Resources.xaml", UriKind.RelativeOrAbsolute) });
Now it works perfectly in the application, the styling is fine (i.e.: Grids' backgrounds are red. It's just a test style).
But if I open this form in the designer, Visual Studio goes crazy. The whole window uses my style!
Here's a screenshot: http://localhostr.com/files/8368cc/Failure.jpg
The cool part is that I found how to edit the Visual Studio 2010 ugly blue skin. The sad part is that won't make my customers happy when they develop with my control.
Feel free to try it and tell me how I should implement my resources without screwing everything up.
XAML Code: (shown in screenshot)
EDIT: Here is my temporary, very hackish solution so I can keep on developing. It really is a pain that "Application.Current" works on Visual Studio.
if (Application.Current.MainWindow == null || !Application.Current.MainWindow.Title.EndsWith(" - Microsoft Visual C# 2010 Express"))
{
Application.Current.Resources.MergedDictionaries.Add(new ResourceDictionary() { Source = new Uri("/Middlewerks;component/Resources.xaml", UriKind.RelativeOrAbsolute) });
}
When I worked on a WinForms project that used WPF areas, I just used MergedDictionaries to bring in the resources I needed, whenever I needed them.
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="DefaultResourceDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
Can you get away with that? You can still put a code behind on the dictionary if you need to do more programmatically.
You can use this at any level on any element. That is, it doesn't have to be a Window as shown here.