Retrieve a value from ResourceDictionary in code behind - xaml

I have a Windows 10 universal app with a few resource dictionaries for styles and such.
In one such resource dictionary, I have a color, MyBlue that I want to access via the code behind. How is this achieved?
I've tried this.Resources["MyBlue"], but since MyBlue is defined in a separate resource dictionary and not directly in the page resources it doesn't exist in this collection.
Here's my app.xaml:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Styles/PodStyles.xaml"/>
<ResourceDictionary Source="Styles/ButtonStyles.xaml"/>
<ResourceDictionary Source="Styles/OfferStyles.xaml"/>
<ResourceDictionary Source="Styles/MyStyles.xaml"/>
<ResourceDictionary>
<!--Global View Model Locator-->
<vm:ViewModelLocator x:Key="Locator"
d:IsDataSource="True"/>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>

Have you tried
Application.Current.Resources["MyBlue"]

MergedDictionaries are available via Application.Current.Resources.MergedDictionaries.
I was able to obtain the appropriate ResourceDictionary by Uri and then access the item in it by key name:
var mergedDict = Application.Current.Resources.MergedDictionaries.Where(md => md.Source.AbsoluteUri == "ms-resource:///Files/Styles/MyStyles.xaml").FirstOrDefault();
newColor = (Windows.UI.Color)mergedDict["MyBlue"];

I will try to sum-up all the solution as all previous solution seems to have part of the whole answer.
So there's two main cases :
Case A. Your value is in the main ResourceDictionary of your App.xaml
Where App.xaml is :
...
<Application.Resources>
<ResourceDictionary>
<Color x:Key="backgroundColor">LightBlue</Color>
</ResourceDictionary>
</Application.Resources>
...
If so read your value by using :
Application.Current.Resources["backgroundColor"]
Case B. Your value is in an external ResourceDictionary merged in your App.xaml
Where App.xamlis :
...
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Styles/Constants.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
...
Where Constants.xaml is :
<?xml version="1.0" encoding="utf-8" ?>
<?xaml-comp compile="true" ?>
<ResourceDictionary xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<Color x:Key="backgroundColor">LightBlue</Color>
</ResourceDictionary>
In this case use #earthling solution :
var mergedDict = Application.Current.Resources.MergedDictionaries.Where(md => md.Source.OriginalString.Equals("Styles/Constants.xaml")).FirstOrDefault();
var color = (Color)mergedDict["pressedColor"];

The ResourceDictionary you want to reference has to be pulled into the application resources somehow. Either merge it into your Application's resources (if it's common enough to be included all the time, at a slight hit to your app's initialization), or merge it into your Page's resources (at a slight hit to the first page initialization).
You include foreign ResourceDictionaries with this syntax:
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="path/to/resource.xaml" />
</ResourceDictionary>
<... your other resources for this page go here .../>
</ResourceDictionary>
</Page.Resources>

Related

How to reference a dictionary in another project WPF

[Custom Control Wpf Library]
Test
-->Resources
-->Resources
-->Dictionary1.xaml (Reusable across multiple project)
-->Dictionary2.xaml(Reusable across multiple project)
[WPF main UI] TestRD
-->Resources
-->Resources
-->Dictionary9.xaml(Specific to the project)
XAML:
Dictionary1.xml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="brush" Color="Red"/>
</ResourceDictionary>
Dictionary2.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="brush" Color="Green"/>
</ResourceDictionary>
Dictionary9.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!--How can I use the resource from another DLL here-->
</ResourceDictionary>
How can I reference Dictionnary1 or dictionary2 in Dictionary9?
Is it advisable, especially in a modular project?
I have some styling I would like to move globally so I can reuse and stop copy and pasting them.
How can I reference Dictionnary1 or dictionary2 in Dictionary9?
Try this:
Dictionary9.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!--How can I use the resource from another DLL here-->
<ResourceDictionary Source="pack://application:,,,/Resources;component/Resources/Dictionary1.xaml" />
<ResourceDictionary Source="pack://application:,,,/Resources;component/Resources/Dictionary2.xaml" />
</ResourceDictionary>
Note that the 1st "Resources" is your lib name and the 2nd "Resources" is the folder name inside your lib.
You can find more info here.
Is it advisable, especially in a modular project?
IMO, is totally OK if you're reusing or need to separate these dictionaries.

Overriding Resources from Generic.xaml in UWP Applications

This is my Generic.xaml where I define default background color for my control (CustomControlBackground):
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TomShane.Framework.Controls">
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="CustomControlBackground" Color="Gray"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
<Style TargetType="local:CustomControl">
<Setter Property="Background" Value="{ThemeResource CustomControlBackground}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="local:CustomControl">
<Border
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
In App.xaml I want to override the default background from Generic.xaml:
<Application
x:Class="TomShane.Framework.Demo.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:TomShane.Framework.Demo"
xmlns:ctrl="using:TomShane.Framework.Controls"
RequestedTheme="Dark">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Default">
<SolidColorBrush x:Key="CustomControlBackground" Color="Red"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
With this technique I am able to override UWP system resources (like SystemControlBackgroundBaseLowBrush), but when I try to override the CustomControlBackground, the control's background stays always gray.
What is the thing I am missing?
The logic is wrong.
We can only override upper scope ResourceDictionary in lower scope ResourceDictionary. Actually, it's about the resource lookup behavior but not about which xaml file is loaded first
In WinRT xaml app, the Resource Dictionary scope looks like the following(not MS official picture):
Based on above, we can override system default resource in app.xaml, we can override system/application resource in page.xaml, and etc.
So in your code, actually, the resource defined in generic.xaml will override the resource you defined in app.xaml. If we simply remove the resource CustomControlBackground from the generic.xaml, you will get the red background.
[update]
If you do want to have a default theme, you can keep the design, but we cannot use the x:Default for the resourcedictionary.
For example, if the requestedtheme is Dark(in your case). You can simply use the following to override the default one in app.xaml.
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="CustomControlBackground" Color="Yellow"/>
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
Or you can only put the theme into a separate file and reference it only in the client app's app.xaml. But you have to rewrite the file to use different values.
Its unfortunate that there isn't a good way to define theme resources for custom controls in such a way that they can be overridden in the same way that the built-in resources, like SystemControlBackgroundBaseLowBrush, can be. But there is a workaround if you're willing to move things around at runtime. The trick is to end up with a situation where your default resources are not contained in Generic.xaml, but are instead defined in a resource dictionary that is merged with the application's resources, where they CAN be overridden. There's more than one way to accomplish this.
One approach would be to create a separate resource dictionary that has an x:Class with an associated code file. Define your default theme resources in it. Then figure out a way to merge an instance of the class into the application's resources at runtime. One way would be in the static constructor of your control. Be careful not to merge more than one instance.
A somewhat different approach, which I like better, is to use code-behind of the resource dictionary in which you define the control's default style. But you can't have code-behind for Generic.xaml, because the framework will never instantiate an instance of the class. Instead, move the style into its own resource dictionary (with x:Class and code-behind) and add an instance of the new dictionary to the merged dictionaries of Generic.xaml. You will be able to move things around in the constructor of this resource dictionary, after the call to InitializeComponent. For example, if in the xaml file you define a resource dictionary in the MergedDictionaries collection and place your theme resources in in it, then at runtime you would be able to remove that dictionary from the MergedDictionaries collection and merge it into the application's resources.
A limitation of either approach is if you want to override resources by theme, i.e. Light and Dark. Placing an overriding resource directly into the resources of App.xaml will work, but will not allow different values for different themes. To achieve this, it is necessary to define the overriding resources inside the ThemeResources of a resource dictionary which itself is placed in the application resource's MergedDictionaries. (I don't know but it kind of seems like this might be a bug.)
CustomControl.xaml:
<ResourceDictionary x:Class="CustomControlLibrary.Themes.CustomControlTheme"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:CustomControlLibrary">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary x:Key="Light">
<SolidColorBrush x:Key="CustomControlBackground" Color="#000000" />
</ResourceDictionary>
<ResourceDictionary x:Key="Dark">
<SolidColorBrush x:Key="CustomControlBackground" Color="#FFFFFF" />
</ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="local:CustomControl">
<Setter Property="Background" Value="{ThemeResource CustomControlBackground}" />
</Style>
</ResourceDictionary>
CustomControl.xaml.cs:
namespace CustomControlLibrary.Themes
{
partial class CustomControlTheme
{
public CustomControlTheme()
{
InitializeComponent();
if(MergedDictionaries.Count > 0)
{
var themeResources = MergedDictionaries[0];
MergedDictionaries.RemoveAt(0);
Application.Current.Resources.MergedDictionaries.Insert(0, themeResources);
}
}
}
}
Generic.xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:themes="using:CustomControlLibrary.Themes">
<ResourceDictionary.MergedDictionaries>
<themes:CustomControlTheme />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

How to use style in another resources?

I have a based style that target type is button call BaseButtonStyle in ResourceDictionaries/CustomControlStyles/BaseButtonStyle.xaml.
And I create a new button style that extended from BaseButtonStyle call AddButtonStyle.
Both styles I have added in App.xaml as followings.
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--Buttons-->
<ResourceDictionary Source="ResourceDictionaries/CustomControlStyles/BaseButtonStyle.xaml" />
<ResourceDictionary Source="ResourceDictionaries/CustomControlStyles/AddButtonStyle.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Then I add a button that refer to AddButtonStyle but it raised an error
Cannot find a Resource with the Name/Key BaseButtonStyle
What's wong with my reference?
Try to use an absolute path:
"ms-appx:///ResourceDictionaries/CustomControlStyles/BaseButtonStyle.xaml"
Of course I don't know if that path fit your file location.

DataTemplate not found in merged ResourceDictionary

I'm currently developing a universal app for Windows Phone 8.1 and Windows 8.1.
I share most of my code, but I'm willing to keep style resources appart.
Some context : First, right now I've only started the WP8.1 projects so everything is related to this platform. In this WP8.1 project, I have a MainPage.xaml which contains a Pivot control. One of the PivotItem is a UserControl called, for clarity purposes, MyUserControl.
I created a resource dictionary Styles.xaml into my "Assets" directory, in BOTH platform projects. Then, I registered those 2 new files into my shared App.xaml like this :
<Application.Resources>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
<ResourceDictionary x:Key="ResourceDictionary">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Assets/Styles.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
In the WP8.1 project Styles.xaml file, I created a DataTemplate :
<DataTemplate x:Key="MyDataTemplate">
<TextBlock Text="This is a textblock" />
</DataTemplate>
In MyUserControl, I've added a ListView and bound it on the ItemTemplate property to MyDataTemplate like this :
<ListView ItemTemplate="{StaticResource MyDataTemplate}" ItemsSource="{Binding MyContent}" />
When I run the solution and I end up with this error :
Cannot find a Resource with the Name/Key "MyDataTemplate"
Does anyone know why I encounter this error ? What did I do wrong ?
An odd thing : when I right click > "Go To Definition" on the MyDataTemplate bound in the ListView.ItemTemplate property, it routes me to the right place.
Thank you in advance for your help !
Your code is wrong, here is the correct one
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Assets/Styles.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
</ResourceDictionary>
</Application.Resources>

Silverlight style works at runtime but not design-time

I have a Silverlight project and a few custom styles, but the problem is these work at runtime, but not in design-time. I have merged my styles into the App.xaml, but they seem to be ignored and I get a "The resource ... could not be resolved." message.
Any ideas where I should start looking? The properties for the styles all have the same Build Action, Custom Tool, etc, but they just fail at design-time.
EDIT: This is the contents of the App.xaml file:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Assets/Styles.xaml"/>
<ResourceDictionary Source="Assets/CoreStyles.xaml"/>
<ResourceDictionary Source="Assets/SDKStyles.xaml"/>
<ResourceDictionary Source="Assets/AppStyles.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
The AppStyles.xaml is my custom resource dictionary.