How do I access a resource(style) through code? - wpf

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.

Related

How do i get Resources and use on code behind in another xaml file (WPF)?

How do i get Resources in another xaml file?
I'm in Data.cs file and i want to access Style resource in Example1.xaml file.
Style style1 = Application.Current.Resources["LabelTitleStyle"] as Style;
Actually i used like this, but it can only for Main xaml file.(App.xaml)
I want to get Resources in another xaml file.
Style style8 = Resources["NumberTitleText"] as Style;
Then, 'Resources' get Red underline, because "NumberTitleText" does not exist current context.
How can I get Resources from another xaml file and use on code behind?
Please Help me!! :(
+++++
ResourceDictionary res = Application.LoadComponent(new Uri("/.....example.xaml", UriKind.RelativeOrAbsolute)) as ResourceDictionary;
Style style8 = res["NumbertitleText"] as Style;
I tried this, but res is null. Why can't i bring that dictionary?
This worked for me:
ResourceDictionary dict = new ResourceDictionary();
dict.Source = new Uri("..\\myResourcesFile.xaml", UriKind.Relative);
if (Application.Current.Resources.MergedDictionaries.Count > 0)
{
Application.Current.Resources.MergedDictionaries.RemoveAt(Application.Current.Resources.MergedDictionaries.Count - 1);
}
Application.Current.Resources.MergedDictionaries.Add(dict);

Automatically apply default styles defined in separate assembly

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.

Exception when adding a ResourceDictonary at runtime

I'm trying to develop a way of switching Windows Phone 7 application style depending on a setting.
The styles look like this:
core styles are separated and defined in WP7Style_Dark.xaml and WP7Style_Light.xaml
the rest of the styles are declared in Styles.xaml
I use the following code to hook up the themes in App.xaml.cs:
var dictionaries = Resources.MergedDictionaries;
dictionaries.Clear();
string source = String.Format("/CommonUI;component/Resources/{0}.xaml", value == AppStyleSet.Light ? "WP7Style_Light" : "WP7Style_Dark");
//base styles
var themeStyles = new ResourceDictionary {Source = new Uri(source, UriKind.Relative)};
dictionaries.Add(themeStyles);
var generalStyles = new ResourceDictionary();
generalStyles.Source = new Uri("/CommonUI;component/Resources/Styles.xaml",UriKind.Relative);
dictionaries.Add(generalStyles);
When executing, setting generalStyles.Source throws an exception (which is a System.Exception stating 'Unspecified error'). I've discovered the exception goes away if I empty the Styles.xaml, but this is not a solution, of course.
What should I do?
Update 2: screw the stack trace, here's the problem narrowed down:
The theme styles define theme colors.
The general styles keep loading fine until they meet a binding, like this one
... <Setter Property="Color" Value="{StaticResource HighlightColor}" />
So, the StaticResource fails to be resolved and throws the exception. Can this be avoided somehow?
The problem I've found with this approach is that there seems to be some asynchronicity about how the resource dictionary loads itself from the URL in the Source property. Hence when one dictionary uses {StaticResource key} where key is in a previous dictionary it can fail.
One solution solution would be to extract the Xaml using Application.GetResourceStream and StreamReader. Then to use XamlReader to construct the ResourceDictionary. That way you can be sure that dependant dictionaries can find static resources they need.
Note you would need to ensure you have added each dictionary where so that it is part of the Application.Resources tree before loading additional dependant dictionaries.
WP7 is based on Silverlight 3+ and by default it is not possible to create a new resource dictionary in code, which is why you are receiving an System.Exception stating an "UnspecifiedError".
The workaround, simple create a ResourceDictionary as if you were using Silverlight3 - follow this tutorial.
I hope this solves the problem.

Changing ResourceDictionary doesn't visably affect Window in WPF

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 :)

How to get at ResourceDictionary style when it is loaded from external xap and assemblies are MEF-fed?

I've got the following setup:
The main application loads a XAP with an IPlugin implementation. The Plugin contains a 'DisplayPanel' that contains a referenced Control with other controls. The DisplayPanel here is simply a container control to show referenced Control.
This referenced Control, from an assembly, uses a Style from a ResourceDictionary xaml in this assembly. At least that's what I want to have. The problem is that the referenced Control throws an error:
Cannot find a Resource with the Name/Key PlayerPanelGrad [Line: 1500
Position: 127]
I've tried to get at the style by referencing theResourceDictionary through a Merged Resource dictionary reference:
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="TableControls;component/ControlsStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
But that doesn't work.
How would you approch this?
the only way i got it to work is by loading the Resource dictionary into the control (in a Class Library) programmatically before the InitializeComponent call:
public ActionPanel()
{
StreamResourceInfo sr = Application.GetResourceStream(
new Uri("TableControls;component/ControlsStyle.xaml", UriKind.Relative));
Application.Current.Resources.Add("plop",sr.Stream);
// Required to initialize variables
InitializeComponent();
}
This question may be of help, although, honestly, I'm still trying to figure it out myself:
Using MEF to import a WPF DataTemplate?
//load other.dll dynamically first,and then use the following code:
StreamResourceInfo srf = Application.GetResourceStream(new Uri("otherdll;component/Resources/Brush.xaml", UriKind.Relative));
StreamReader sr = new StreamReader(srf.Stream);
string stt = sr.ReadToEnd();
ResourceDictionary dict = XamlReader.Load(stt) as ResourceDictionary;
Application.Current.Resources.MergedDictionaries.Add(dict);
For future reference, my XAML file was found in a subdirectory of the solution which needed the / character but also file was further in a subdirectory named Assets within it.
<ResourceDictionary
Source="/MyAssemblyName;component/Assets/RadResources.xaml" />
Also the .XAML file was built as Page in the solution.

Resources