Merging Resources Dictionaries - c#

i'm trying merging wpf resource dictionaries on the code behind but for some reasion this isn't working. If i try merge the dictionaries on the document itself it's running for instance:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication212;assembly=WpfApplication212">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Theme.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type local:URComboBox}" BasedOn="{StaticResource ComboBoxStyle}">
</Style>
This is working, but if i comment the ResourceDictionary.MergedDictionaries and in code try this:
ResourceDictionary skin = new ResourceDictionary();
skin.Source = styleLocation;
ResourceDictionary skinFather = new ResourceDictionary();
skinFather.MergedDictionaries.Add(skin);
skinFather.Source = styleLocationFather;
This will break because can't find the resource.

You can't use the Source property to load a Resource Dictionary from code.
From MSDN:
"Merged dictionaries can be added to a Resources dictionary through code. The default, initially empty ResourceDictionary that exists for any Resources property also has a default, initially empty MergedDictionaries collection property. To add a merged dictionary through code, you obtain a reference to the desired primary ResourceDictionary, get its MergedDictionaries property value, and call Add on the generic Collection that is contained in MergedDictionaries. The object you add must be a new ResourceDictionary. In code, you do not set the Source property. Instead, you must obtain a ResourceDictionary object by either creating one or loading one. One way to load an existing ResourceDictionary to call XamlReader.Load on an existing XAML file stream that has a ResourceDictionary root, then casting the XamlReader.Load return value to ResourceDictionary."
Hence, some pseudo code:
ResourceDictionary myResourceDictionary = XamlReader.Load(someXamlStreamReader);
anotherResourceDictionary.MergedDictionaries.Add(myResourceDictionary);
Here is another example of how to do it:
Uri uri = new Uri("/PageResourceFile.xaml", UriKind.Relative);
StreamResourceInfo info = Application.GetResourceStream(uri);
System.Windows.Markup.XamlReader reader = new System.Windows.Markup.XamlReader();
Page page = (Page)reader.LoadAsync(info.Stream);

Related

Accessing a DynamicResource in code-behind [duplicate]

I have a DataTemplate defined in a xaml file that I want to access via C# code.
Can anyone please tell me how can I access it?
I added a new ResourceDictionary file and its name is Dictionary1.xaml.
I have a data template such as:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<DataTemplate x:Key="mytemplate">
<TextBlock Text="Name:" Background="Blue"/>
</DataTemplate>
</ResourceDictionary>
not I have a ListBox called listBox1 and I want to assign it to it's Itemtemplate property
but I'm not getting how can i do it?
Since Application.Current was null in my case, I've ended up using this:
var myResourceDictionary = new ResourceDictionary();
myResourceDictionary.Source =
new Uri("/DllName;component/Resources/MyResourceDictionary.xaml",
UriKind.RelativeOrAbsolute);
and then getting the specified key I needed by using
myResourceDictionary["KeyName"] as TypeOfItem
(source)
Where exactly are you defining it?
If you define it in the ResourceDictionary of your object, then
Application.Current.Resources[typeof(yourDataTemplateTargetType)]
should work. If you are defining it as a member of something else, like say, an ItemsControl, you need to get a handle to the ItemsControl instance and call the ItemTemplate property.
Edit: Ok, I think we're getting somewhere. So you are defining a ResourceDictionary in its own file. Before you can use it in your UI and access it from your code behind, you need to merge that ResourceDictionary into your application. Are you doing this?
If you are, then the next step is to get this resource. Each FrameworkElement has a method called FindResource. This method is great because it walks up the ResourceDictionary tree and attempts to locate the resource with the key. So, if you want to access this resource from a UserControl, you can do the following in the code behind:
FindResource(typeof(yourDataTemplateTargetType));
If this doesn't work for you, please show us exactly how you are declaring this resource dictionary and how it is getting merged into your application's resources.
If you for example have a template for Button in your resource dictionary in the App.xaml file you can access it using the following code:
Application.Current.Resources[typeof(Button)]
If you have merged resource dictionary using code like below
<Window x:Class="MainWindow">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="DefaultStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
</Window>
Then, instead of Application.Current.Resources["ResourceKey"] you need to specify Control name (in this case MainWindow) also like below
var style = Application.Current.MainWindow.Resources["ResourceKey"];
// OR
var style = Application.Current.MainWindow.TryFindResource("ResourceKey");
If you're getting the resources within the same project, try this:
yourControl.Style = FindResource("YourResourceKey") as Style;
Otherwise, try this:
ResourceDictionary res = (ResourceDictionary)Application.LoadComponent(new Uri("/ProjectName;component/FolderName/ResourceDictionaryName.xaml", UriKind.Relative));
yourControl.Style = (Style)res["YourResourceKey"];
You can access a resource dictionary you added to your project as follows:
var rd = new ResourceDictionary();
rd.Source = new Uri("ms-appx:///Dictionary1.xaml");
Then you can access a resource stored in the resource dictionary like so:
someObject.Property = rd["mytemplate"];
NOTE:
You will have to modify the URI to the resource dictionary according to the location you created it relative to the project's base directory.
I found the answer here
https://learn.microsoft.com/en-us/dotnet/framework/wpf/advanced/how-to-use-a-resourcedictionary-to-manage-localizable-string-resources
create a ressource dictionary "ColorResources.xaml"
add to it:
Blue
edit your app.xml and add:
use the color from your code
var color = (System.Windows.Media.Color)Application.Current.FindResource("ButtonColor1");
and voilĂ 
ps : admin can you fix the code? it does not show up, thanks
Any of the above approaches work getting the resource based on the location, if you are following MVVMm I would recommend doing it this way:
create a Service like ProvideDataTemplateService, (to create a service usual inherit from Behavior )
Use Container of Your choice to inject this service where you would like to have aces to DataTemple.
For the life of me, although I was able to load my resource dictionary via XAML, I wasn't able to load it via "code behind" (in C#).
So I resorted to have a view loading it: (MyView.xaml)
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/My.Proj;component/My/Path/myResourceDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Then access it in my UT by instanciating that view and accessing it:
new MyView().Resources.MergedDictionaries[0]
Hacky, but works.
Just to add another answer here in case you don't have a view or Application.Current is null. I realize this is probably uncommon but in my case I have an addin to a parent application and Application.Current is null; I also want to pass one of my resources to the parent as an ImageSource so I don't have a XAML view created to get resources from directly.
You can also make the dictionary into a code behind creatable object. Just set the x:Class attribute in the XAML and then create a .xaml.cs file in the code behind. Your updated XAML, lets call the code file MyDictionary.xaml, would then look something like this:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Class="Some.Namespace.MyDictionary"
mc:Ignorable="d">
...Resources...
</ResourceDictionary>
And the code behind (MyDictionary.xaml.cs) would look something like this:
using System.Windows;
namespace Some.Namespace
{
public partial class MyDictionary : ResourceDictionary
{
public MyDictionary()
{
InitializeComponent();
}
}
}
Don't forget to call InitializeComponent() as that's what loads the resources. Actually not sorry, see edit below
After you do this you can simply construct an instance of the class anywhere in code and reference the resources by key like this:
var dictionary = new MyDictionary();
var resource = dictionary["whateverKey"] as WhateverResourceType;
Thanks to this post for leading to the idea.
EDIT
Just ran into one potential issue with this. I got a 'Cannot re-initialize ResourceDictionary instance' exception with this setup on some of my controls. On further research this could be related to calling InitializeComponent in the constructor. Instead I removed the constructor from the code behind and added a static method to get an initialized instance as follows:
public static MyDictionary ConstructInitializedInstance()
{
var dictionary = new MyDictionary();
dictionary.InitializeComponent();
return dictionary;
}
You could also just create and initialize in your code behind.

Including Optional Resource in WPF Window

I want to be able to use either a default bitmap resource or one provided by a separate assembly in a WPF window.I thought I could do this by defining the default bitmap in the Window.Resources section, and then search for and load if found the resources from the separate optional assembly:
[xaml file for window]
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<BitmapImage x:Key="J4JWizardImage" UriSource="../assets/install.png"/>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
[code behind for window constructor]
try
{
var resDllPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Olbert.JumpForJoy.DefaultResources.dll");
if( File.Exists( resDllPath ) )
{
var resAssembly = Assembly.LoadFile( resDllPath );
var uriText =
$"pack://application:,,,/{resAssembly.GetName().Name};component/DefaultResources.xaml";
ResourceDictionary j4jRD =
new ResourceDictionary
{
Source = new Uri( uriText )
};
Resources.Add( J4JWizardImageKey, j4jRD[ "J4JWizardImage" ] );
}
}
catch (Exception ex)
{
}
InitializeComponent();
However, the default image was always displayed, even when the separate resource assembly was present. Apparently, resources defined within a Window definition take precedence over resources added when the window is constructed.
So I removed the Window.Resources section, added a standalone resource xaml file:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Olbert.Wix.views">
<BitmapImage x:Key="DefaultWizardImage" UriSource="../assets/install.png"/>
</ResourceDictionary>
and modified the window constructor code so that if the separate assembly wasn't found, the resource from the standalone xaml file would be added instead:
if( File.Exists( resDllPath ) )
{
// same as above
}
else
Resources.Add( J4JWizardImageKey, TryFindResource( "DefaultWizardImage" ) );
This worked when the separate assembly was present. However, it failed when the separate assembly was left out, because the default image resource was not found. That may be because this Window isn't part of a WPF app; it's the UI for a Wix bootstrapper project.
It feels like there should be a simpler solution for what I'm trying to do, which I imagine is pretty common whenever a WPF library is designed (i.e., you need some way to allow customization of bitmaps, but you also want to provide a default/fallback).
It sounds like you're only ever getting the initial value of the resource, as of when the XAML was parsed. If it's not there at that time, there's nothing; if it's a thing then, it's only ever that thing.
That is the behavior you'll see when you use StaticResource to retrieve the resources rather than DynamicResource. DynamicResource will update the target when the resource is replaced.
<Label Content="{DynamicResource MyImageSomewhere}" />

Merged Resource Dictionaries of Windows does not reflect on child controls

Resource dictionary merged to the Window as given in the code below.
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="DefaultTheme.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
I had created a custom control for context menu and had created the corresponding style in the DefaultTheme.xaml file. Even though at the window level it had the resource dictionary merged, the styles were not accessible for the custom controls.
Since it was custom control I had to merge the dictionary in the C# constructor like this -
const string defaultThemePath = "DefaultTheme.xaml";
var dictionary = new ResourceDictionary { Source = new Uri(defaultThemePath, UriKind.Relative) };
Resources.MergedDictionaries.Add(dictionary);
If the resource dictionary is merged, it should be available for child controls is an expectation. Is my understanding wrong about merged resource dictionary?
Edit
As #Rohit Vats has rightly pointed, my custom control is context menu and it is not part of the visual child of the window. Hence it does not have the resource dictionary inherited.
I just quickly check by putting one SolidColorBrush in random ResourceDictionary and merge it under App resources and use that resource from ContextMenu and ContextMenu was able to access the resource.
From MSDN, StaticResource lookup behaviour works like this:
The lookup process checks for the requested key within the resource
dictionary defined by the element that sets the property.
The lookup process then traverses the logical tree upward, to the
parent element and its resource dictionary. This continues until the
root element is reached.
Next, application resources are checked. Application resources are
those resources within the resource dictionary that is defined by the
Application object for your WPF application.
As evident from the above assertion, it looks for logical parent and not Visual parent and in case resource is not found anywhere, it looks for resource under App resources.
So, your case for ContextMenu will work if you merge resources under App resources.

What is the difference between Application.Current.Resources and Resources

I have global ResourceDictionary defined in my App.xaml file.
<Application.Resources>
<ResourceDictionary>
<Style x:Key="yolo" TargetType="Grid" />
</ResourceDictionary>
</Application.Resources>
I've had set breakpoint in random Page and checked difference in debugger:
Application.Current.Resources = 1
Resources = 0
What's the this.Resources?
Application.Current.Resources
Contains the resources that are declared in the App.xaml file, which can be seen the entire application.
this.Resources
Contains the resources that are defined locally for any control, such as Window, UserControl and are only available within this control.
Example with Window:
XAML
<Window ...
xmlns:sys="clr-namespace:System;assembly=mscorlib"
<Window.Resources>
<sys:String x:Key="MyString">TestString</sys:String>
</Window.Resources>
Code-behind
public MainWindow()
{
InitializeComponent();
string test = this.Resources["MyString"] as string;
}
What's the this.Resources?
It's the resource dictionary of the control whose code-behind code we are writing in.
On the other hand Application.Current.Resources is the resource dictionary of the application object itself.
If you are writing code inside App.xaml.cs, then Resources and Application.Current.Resources will refer to the same object.

How to change XAML value using C#?

I have a style file for Styles in WPF XAML with name Brushes.xaml which stores all colors for the WPF.
Code Here:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Color x:Key="DefaultColor">SteelBlue</Color>
<Color x:Key="LightDefaultColor">LightSteelBlue</Color>
</ResourceDictionary>
I want to change the value of DefaultColor using C# code.
Use the DynamicResource extension instead of the StaticResource extension on all references to keys that can change at runtime.
Then you can use code like the following to change the value.
Application.Current.Resources["Default Color"] = System.Windows.Media.Colors.Red;
This can be done per object to...
public MyWindow()
{
InitializeComponent();
this.Resources["Default Color"] = System.Windows.Media.Colors.Red;
PART_DynamicButton.Resources["Default Color"] = System.Windows.Media.Colors.Red;
}
This is higher performance than clearing your entire merged resource dictionary and adding a new one if you only need to modify a few values.
Just remember that DynamicResource extension only works on DependencyProperties and Freezable objects instantiated in Xaml are usually frozen which prevents modifing their DependencyProperties. So don't try to change the color of a SolidColorBrush if the brush was instaniated in xaml.
Here is a workaround
<! -- Xaml -->
<SolidColorBrush x:Key="App_Page_Background" Color="White"/>
<Page Background="{DynamicResource App_Page_Background}"/>
// C# code
Application.Current.Resources["App_Page_Background"] = new SolidColorBrush(Colors.Red);
Rather than changing the XAML contents you should create one XAML file for each theme.
Then you can change the theme at runtime like this:
ResourceDictionary skin = new ResourceDictionary();
skin.Source = new Uri(#"" + themeName + ".xaml", UriKind.Relative);
Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(skin);

Categories

Resources