I have a simple WPF application I'm using for experimenting.
I have two themes defined in seperate xaml files, changing the xaml to point to them worked fine. By the way, in the xaml I'm using a straight ResourceDictionary element, not a ResourceDictionary.MergedDictionaries one.
I want to let the user select which theme to use, so I'm reseting the source property in code behind - but whilst the debugger tells me I've successfully set the value the applications appearance doesn't change.
So, how do you successfully apply a theme at runtime?
EDIT: This is how I'm declaring my "style" in the xaml:
<Window x:Class="WpfUI.winMain">
<Window.Resources>
<ResourceDictionary Source="Themes\Blah.xaml"></ResourceDictionary>
</Window.Resources>
// The windows grid and other controls...
</Window>
The simple answer is, you need to clear the applications merged resource dictionaries. Here is some code to get you started
ResourceDictionary dictionary = GetThemeResourceDictionary(yourTheme)
if (dictionary != null)
{
App.Current.Resources.MergedDictionaries.Clear();
App.Current.Resources.MergedDictionaries.Add(dictionary);
}
public ResourceDictionary GetThemeResourceDictionary(string theme)
{
if (theme != null)
{
Assembly assembly = Assembly.LoadFrom("WPF.Themes.dll");
string packUri = String.Format(YourThemeFolder/{0}.xaml", theme);
return Application.LoadComponent(new Uri(packUri, UriKind.Relative)) as ResourceDictionary;
}
return null;
}
If you want a really nice packaged solution, i would reccommend WPF themes. It introduces a ThemeManager class and a set of built in themes which are really incredible. If you have any difficulty installing it or adding a new theme, contact me :)
Related
I have a WPF application and I've recently added a new function: FontFamily choose.
What I want is: When selecting a certain FontFamily from the Combobox, it will apply on all the other windows, so how? Notice this is a sub window, maybe the parent window or other windows are still open, and I want to apply it onto all the other windows.
PS: I used something like: FontFamily="{Bind .....}", but it doesn't apply; what's more, there're too many things....I wonder if there's a global way to cope with it?
You could iterate over all Windows of the application to set their FontFamily:
foreach (Window window in Application.Current.Windows)
{
window.FontFamily = new FontFamily(...);
}
The child elements of the Windows would all get the new font due to property value inheritance of the FontFamily property.
Alternatively, create a global font resource in App.xaml
<Application.Resources>
<FontFamily x:Key="GlobalFont">Segoe UI</FontFamily>
</Application.Resources>
and consume it as a DynamicResource in all Windows
<Window ... FontFamily="{DynamicResource GlobalFont}">
Then change it by
Application.Current.Resources["GlobalFont"] = new FontFamily(...);
I am working with a very large Silverlight 5 application that needs to implement theming. Unfortunately I can't use the C1 (Component One) or Silverlight Toolkit theme mechanisms due to the enormity of xaml and code changes I would have to implement. I am forced to do something a bit out of the box.
As a starting point I created a demo project by referencing a post on Stack Overflow Using Mef to Import a WPF DataTemplate written by #Scott Whitlock. The post described how to dynamically load a Silverlight/WPF resource dictionary and add it to the App.Current.Resources.MergedDictionaries collection within the Silverlight/WPF application.
I created 4 projects. The first being the Silverlight 5 application itself, the second, third, and forth are silverlight class libraries for defining all the theme particulars. Each class library has an entry point which is a derived type of ResourceDictionary.
On AppStart event, the application loads the default theme class library, which is essentially a blank slate with all default styles defined in Silverlight. By loading I mean the the DefaultTheme resource dictionary defined within the class library is added to the App.Current.Resources.MergedDictionaries collection.
When the user selects another theme from a combo box within the app, the code removes the existing default theme and adds the blue or red, or whatever other theme's entry point resource dictionary to the App.Current.Resources.MergedDictionaries collection.
However, even though no errors have been thrown when this action occurs, the styles themselves are never re-applied. I have verified that each theme has the same style keys across the board.
Any ideas on how to force the App.Current.RootVisual re-apply the styles from the newly added resource dictionary after a "theme switch" ?
Thanks,
Try searching for the current ResourceDictionary first and removing it before adding the new ResourceDictionary.
string themeName = "White";
string oldThemeName = "Black";
string oldResourcePathString = String.Format("/Library.Name;component/Themes/{0}Theme.xaml", oldThemeName);
StreamResourceInfo sriOldTheme = Application.GetResourceStream(new Uri(oldResourcePathString, UriKind.Relative));
if (sriOldTheme != null)
{
StreamReader sr = new StreamReader(sriOldTheme.Stream);
object resourceObject = XamlReader.Load(sr.ReadToEnd());
ResourceDictionary resource = resourceObject as ResourceDictionary;
if (resource != null)
{
Application.Current.Resources.MergedDictionaries.Remove(resource);
}
}
string resourcePathString = String.Format("/Library.Name;component/Themes/{0}Theme.xaml", themeName);
StreamResourceInfo sriTheme = Application.GetResourceStream(new Uri(resourcePathString, UriKind.Relative));
if (sriTheme != null)
{
StreamReader sr = new StreamReader(sriTheme.Stream);
object resourceObject = XamlReader.Load(sr.ReadToEnd());
ResourceDictionary resource = resourceObject as ResourceDictionary;
if (resource != null)
{
Application.Current.Resources.MergedDictionaries.Add(resource);
}
}
I never tested the code, so check for typos, but this should work whether you set the ResourceDictionary in App.xaml or programatically from MainPage.xaml.cs
I have a Resource Dictionary containing all my custom styles for the programs controls.
The Dictionary is mergerd with the application's resources as displayed below:
<ResourceDictionary x:Key="Controls">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Controls.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
I can easily access the different styles through xaml:
<Button Style="{StaticResource Button}" />
But whenever I try assigning controls with this style through code, it fails.
I've tried:
Button.Style = Application.Current.Resources("Button")
Button.Style = CType(Application.Current.Resources("Button"), Style)
And different approaches similar to the ones above.
During testing some of the different ways to get the styles, I was faced with "Resource not found" but when using the above ones the program seemed to find the style.
I could successfully run the program - but without any visual proof that the style was indeed applied.
How do I properly assign a control a style found in a Resource Dictionary?
Use Application.Current.Resources["Button"].
For any descendants: here is how I succeded to apply a style from a resource to a dynamically created control through code. (Given that you have a Resource Dictionary containing the style)
First step: Include the Resource Dictionary
To make a Resource Dictionary easily accessible from code, add it through code.
VB
Dim myResourceDictionary As New ResourceDictionary
myResourceDictionary .Source = New _
Uri("/YourApplication;component/YourDictionary.xaml",
UriKind.RelativeOrAbsolute)
C#
var myResourceDictionary = new ResourceDictionary
{
Source = new Uri("/YourApplication;component/YourDictionary.xaml", UriKind.RelativeOrAbsolute)
};
Replace "YourApplication" with your solution name, and "YourDictionary" with your Resource Dictionary file.
Second step: Assign the Style
To make use of the newly imported Resource Dictionary, simply assign a control a style;
VB
Dim myButton As New Button
Dim myButtonStyle As Style = myResourceDictionary("YourStyleKey")
myButton.Style = myButtonStyle
C#
var myButtonStyle= myResourceDictionary["YourStyleKey"] as Style;
var myButton = new Button { Style = myButtonStyle };
Special thanks to user Stefan Denchev for giving me an article covering this.
As C# isn't my strong side, please edit this if I've made any mistake.
I have the following requirements:
My WPF application consists of several modules (assemblies), some of them UI-related.
I want to create a single assembly containing a common set of styles for certain controls (e.g. a custom default button style) that should be applied automatically in all other UI-related assemblies, just by including that one assembly, and without me having to specify explicit resource keys.
I do not provide styles for every kind of control, so those without a custom style should keep the default Aero theme (including content templates etc.).
I do not want to write my own, extended Button class, or something like that.
I want this to work in Visual Studio at design-time as well, both in the final app and within the other UI-related modules.
As the styles are defined inside an assembly, I obviously cannot have an App.xaml there. I therefore assume that I have to include them from Generic.xaml. As Generic.xaml only serves as a fallback when there is no style defined in the standard (Aero) theme, WPF ignores my styles in Generic.xaml.
The next step would probably be to create my very own theme (that somehow merges the default Aero styles). But how do I tell VS to use that theme in both the app and the modules, instead of e.g. Aero? I guess I have to do this declaratively as I need design-time support for my custom styles.
Simply adding a reference to the style assembly will be insufficient; you'll have to do something to make WPF merge the resources in. But we can do this in such a way that you'll only need to add a single line of C# (or a few lines of XAML) to your application assembly.
The most straightforward solution is probably to create a strongly-typed ResourceDictionary in your shared styles assembly, and add it into your app-level ResourceDictionary at start-up.
For example, create a CustomStyles.xaml in your shared styles assembly, and pull all of your style resources into that file (either directly or via MergedDictionaries). Make sure the Build Action is set to "Page", and add an x:Class directive to the ResourceDictionary element like so:
<ResourceDictionary x:Class="YourNamespace.CustomStyles"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Your styles declared or imported here -->
</ResourceDictionary>
For styles meant to replace built-in or third-party control styles, you can declare the styles as implicit, i.e., leave the x:Key off entirely, or use the control's type as the key, e.g., x:Key="{x:Type ComboBox}".
Adding the x:Class directive probably won't be enough to make Visual Studio generate a CustomStyles() constructor that actually loads the XAML content, so you'll probably need to add a CustomStyles.xaml.cs file manually and give it a constructor that calls InitializeComponent() (VS should still generate this):
namespace YourNamespace
{
partial class CustomStyles
{
public CustomStyles()
{
InitializeComponent();
}
}
}
In your application, you need to get this dictionary merged into your Application.Resources dictionary. You can do this from the App.xaml file if you like:
<Application x:Class="YourNamespace.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:cs="clr-namespace:YourNamespace;assembly=YourCustomStylesAssembly">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<cs:CustomStyles />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
...or you can do it on the C# side:
public partial class App
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
this.Resources.MergedDictionaries.Add(new CustomStyles());
}
}
Now, the tricky part is going to be getting these styles to work in the XAML Designer. One solution that comes to mind is to add a custom attached property that you can set on all your views, and which is only applied if you're running in the designer:
partial class CustomStyles
{
public static readonly DependencyProperty EnableDesignTimeStylesProperty =
DependencyProperty.RegisterAttached(
"EnableDesignTimeStyles",
typeof(bool),
typeof(CustomStyles),
new PropertyMetadata(
default(bool),
OnEnableDesignTimeStylesChanged));
private static CustomStyles DesignTimeResources;
private static void OnEnableDesignTimeStylesChanged(
DependencyObject d,
DependencyPropertyChangedEventArgs e)
{
if (!DesignerProperties.GetIsInDesignMode(d))
return;
var element = d as FrameworkElement;
if (element == null)
return;
if (DesignTimeResources == null)
DesignTimeResources = new CustomStyles();
if ((bool)e.NewValue)
element.Resources.MergedDictionaries.Add(DesignTimeResources);
else
element.Resources.MergedDictionaries.Remove(DesignTimeResources);
}
public static void SetEnableDesignTimeStyles(
DependencyObject element,
bool value)
{
element.SetValue(EnableDesignTimeStylesProperty, value);
}
public static bool GetEnableDesignTimeStyles(DependencyObject element)
{
return (bool)element.GetValue(EnableDesignTimeStylesProperty);
}
}
Then, on your views, just set CustomStyles.EnableDesignTimeStyles="True" to force the designer to merge in the style resources. At runtime, DesignerProperties.GetIsInDesignMode(d) will evaluate to false, and you won't end up loading a new copy of your styles in every view; you'll just inherit them from the app-level resources.
I don't know a way to apply them all automatically. In fact I think the combo "automatic, designer-supported, and multiple assemblies" is impossible. However, it is easy enough to add a header reference to each of your controls:
Step 1: merge or add all your styles to a dictionary in a "styles" project referenced by all your other projects.
Step 2: include a reference to this dictionary in each of your control and other resource dictionary XAML files. It will look smoething like this:
<ResourceDictionary.MergedDictionaries>
<SharedResourceDictionary Source="pack://application:,,,/My.Ui.Resources;component/Themes/ColorSkins/LightTheme.xaml" />
...
Note the use of SharedResourceDictionary to not duplicate instances. See
http://blogs.msdn.com/b/wpfsdk/archive/2007/06/08/defining-and-using-shared-resources-in-a-custom-control-library.aspx
and
SharedResourceDictionary: edit resource in Blend
If all of your controls inherit from the same base, it may be useful to make your own base class that includes them programmatically.
I had been struggling with these same issues for a PRISM application I'd been working on. After doing some stack over flow research I was surprised to find that the simplest answer to having the resources work at design time was to add an App.Xaml to each of my UI based modules. When the application is complied, they will all be ignored. But at design time they will be used by the designer. Following the rest of the advice above you'd have an App.Xaml that has a merged resource dictionary pointing back to a resource library that has all your styles.
This is the simplest way I've found to get at styles during design time.
<Application x:Class="ProjHydraulics.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source= "pack://application:,,,/Infrastructure;component/ResourceDictionaries/ResourceLibrary.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type Rectangle}"/>
</ResourceDictionary>
</Application.Resources>
Building the knowledge of those before me, I put together a blog post detailing my experience with resource dictionaries in PRISM.
The current version of the Microsoft Live Labs PivotViewer control for SilverLight 4 has no way to style the elements of the control. Looking at the control in Reflector, I can see much of the style info is set in a ResourceDictionary in the assembly (assets/defaultcolors.xaml). What I would like to do is create my own copy of this file, then replace it at runtime on the PivotViewer control.
By subclassing the PivotViewer control and overriding OnApplyTemplate I can grab the child elements and set properties such as Background. I have not had any success Clear()'ng the MergedDictionaries and adding in my own:
public override void OnApplyTemplate() {
base.OnApplyTemplate();
/* can change things this way */
CollectionViewerView cvv = ((CollectionViewerView)((Grid)this.GetTemplateChild("PART_Container")).Children[0]);
((Grid)cvv.Content).Background = new SolidColorBrush(Colors.Black);
/* can't change things this way */
CustomDictionary gd = new CustomDictionary();
cvv.Resources.MergedDictionaries.Clear();
cvv.Resources.MergedDictionaries.Add(gd);
}
I'm afraid this isn't going to work in Silverlight because it uses only Static Resources. ( Styles Don't Update )
Changing a resource dictionary only works before InitializeComponent() is called, which is called in the constructor of the PivotViewer.
I've been trying to style the PivotViewer Control too. I hope there is another way, besides searching through the Visual Tree and changing properties.