I have got a ResourceDictionary full of PathGeometry's for my Icons, is it possible I can give this ResourceDictionary a name so I can use some C# to view all of my Icons, and just to make things more grouped together?
So I would use one of the PathGeometry's like so for example
App.Current.Resources.Icons["refresh1"] as Geometry
or
App.Current.Resources["Icons"]["refresh1"] as Geometry
Currently I use App.Current.Resources["refresh1"] as Geometry to use one of the PathGeometry's.
Edit:
Unclear why this is, well, unclear. Poster below understood the question but was unclear in my reason for wanting to do this. However my reason isn't required, I just want an answer to what I'm asking, not a discussion on WHY do I want to do this.
This is one of those very straightforward questions that people will spend more time arguing about than answering! :) People can argue the merit of it as much as they like but once you start doing things like dynamically loading ResourceDictionaries (say, from external plugin DLLs) topics like this suddenly become very relevant indeed!
To answer your question, yes, of course this is possible. The main caveat here is that if you just add ResourceDictionaries to your application resources dictionary then the XAML compiler will get confused about what you're trying to do. The trick here is to explicitly specify a single top-level ResourceDictionary and then add all your resources, including your key'd ResourceDictionaries, as the content of that:
<Application.Resources>
<ResourceDictionary xmlns:sys="clr-namespace:System;assembly=mscorlib">
<ResourceDictionary x:Key="Dictionary1">
<sys:String x:Key="str1a">String 1A</sys:String>
<sys:String x:Key="str1b">String 1B</sys:String>
<sys:String x:Key="str1c">String 1C</sys:String>
</ResourceDictionary>
<ResourceDictionary x:Key="Dictionary2">
<sys:String x:Key="str2a">String 2A</sys:String>
<sys:String x:Key="str2b">String 2B</sys:String>
<sys:String x:Key="str2c">String 2C</sys:String>
</ResourceDictionary>
<!-- All other application resources go here -->
</ResourceDictionary>
</Application.Resources>
Here's some XAML that statically binds to the dictionaries to show that this method works as intended:
<StackPanel>
<ListBox ItemsSource="{StaticResource Dictionary1}" DisplayMemberPath="Value" />
<ListBox ItemsSource="{StaticResource Dictionary2}" DisplayMemberPath="Value" />
</StackPanel>
Result:
If you want to access the resources in code then you'll need to cast the first array lookup to a ResourceDictionary, then index that by the actual item key:
var str = (App.Current.Resources["Dictionary1"] as ResourceDictionary)["str1a"].ToString();
If that's too messy then you can clean it up with a helper class like so:
public class Global
{
static Global _global = new Global();
public static Global Dictionaries { get { return _global; } }
public ResourceDictionary this[string index]
{
get { return App.Current.Resources[index] as ResourceDictionary; }
}
}
Which you would then use like this:
var str = (string)Global.Dictionaries["Dictionary1"]["str1a"];
Related
I've been writing a CAD-style program in .Net, for this I have to have a lot of Brushes and Custom DashStyles.
So far I have defined them in a static class. for example:
public static readonly Brush GridBrushInModel = Brushes.DarkGray;
Now I can use the brush whenever I want. I have also Freezed them though.
My question is, is this the way this should be done? Or there are better ways? For example defining in ResourceDictionary? How it is done?
Shared resources in a WPF application are typically stored as a ResourceDictionary. Each dictionary should have its own XAML file (if you wish to split up your resources).
They are pretty easy to define:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="MyCoolBrush" Color="Black"/>
</ResourceDictionary>
Note that I gave the element a x:Key attribute. This is what you use to reference the resource later.
Finally, you have to merge the dictionary into the using code. This can be done at any level, though its most commonly done in the Window.Resources or in App.xaml.
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/MyBrushes.xaml"/>
</ResourceDictionary.MergedDictionaries>
...
</ResourceDictionary>
</Window.Resources>
Once you have them, you can reference them in XAML like this:
<Grid Background={StaticResource MyCoolBrush}/>
Is it possible to add one resource dictionary into other one?
In Dictionary2.xaml define MergedDictionaries (right after the opening ResourceDictionary tag):
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Path/to/Dictionary1.xaml" />
</ResourceDictionary.MergedDictionaries>
there's a catch: each time you merge dictionaries you effectively create a copy of the merged dictionary. And it's recursive - if you have Dict3.xaml and Dict4.xaml that both load Dictionary2.xaml, you will have three instances of Dictionary1.xaml created
The solution is a SharedResourceDictionary. The implementation in the tutorial should be seen as a starting point and will probably need some level of tweaking - depending on use scenario. Google "wpf SharedResourceDictionary" for some gotchas and solutions.
From answer to this question by XAMeLi
A snippet straight from a sketchflow project I am working on that shows how to merge resource dictionaries in xaml:
<Application.Resources>
<!-- Resources scoped at the Application level should be defined here. -->
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Microsoft.Expression.Prototyping.SketchControls;component/ScrollViewerStyles.xaml"/>
<ResourceDictionary Source="/[ProjectABC];component/[fileXYZ].xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
This shows merging two additional resource dictionaries into another resource dictionary.
(Note that the order can become important if you have default styles defined in more than one place as they will override each other)
Something like:
ResourceDictionary resources = new ResourceDictionary();
resources.Source = new Uri("/MyModule;component/MyModule.xaml",
UriKind.RelativeOrAbsolute);
Application.Current.Resources.MergedDictionaries.Add(resources);
Might be what you're looking for. We use code like this in our Prism Modules.
I have a WPF Control Library that is being added to a windows forms application. We want to allow the controls to be localizable, however I am not sure how to FULLY accomplish this without duplicating code. This is what I am doing now.
Basically, in the windows forms app, before the main application kicks off, I am instantiating an App.xaml that live within the forms app (containing my links to my resources that also live within the forms app). This works perfectly for runtime.
However, my user controls all have Content="{StaticResource SomeVariableName}", which end up being blank. I can fix this by having an app.xaml and appropriate resource dictionaries in my control library that match those in my windows forms app. However, this is duplicated code.
Things I have already tried to no avail:
Instantiate the App.xaml that lives within the user control library from within my forms app. This does not work because the URIs to my resources is looking for an embedded resource, not my local resource dictionary (I could then simply copy the resource files from the control to an appropriate location within my forms app on build). Could I leverage DeferrableContent here? There is not much online as far as I could find on this attribute and how it should be used, though.
I would like to use post builds for both App and dictionaries, however, the App instantiation is a static reference to a compiled App.xaml as far as I can tell. So, App.xaml must live within the form at least
I did try to have a duplicated App.xaml with a post build moving the resourcedictionary.xaml. I figured that a duplicated app.xaml is ok since that is the driving force and you might not want to rely on one from the control anyway (which circles back and makes you wonder if you should then have the App.xaml in the control at all? Unless you want to allow a default that uses embedded resources....) That too failed saying it could not find the resource even though it was placed where the URI should have been pointing to. The decompiled code points to Uri resourceLocater = new Uri("/WindowsFormsApplication3;component/app.xaml", UriKind.Relative);
So, Is there any way to allow for this to work AND have design time viewing of the component defaults AND avoid duplication? Or, is the duplication OK in this case? If my 2nd bullet's sub-item seems ok (duplicated App.xaml with build copied resourcedictionaries), how do I make it not look for a component level item, but instead a file level one?
Last question (and I can post this separately if necessary) that I just paid attention to. My App.xaml is being built into the code, so that does not allow me to create new ResourceDictionaries on the fly anyway. Is there any way to do this?
Final option...possibly the best one?
- I plan on using Andre van Heerwaarde's code anyway, so should I just check for the existence of a file and add it as a merged resource on the fly? Basically, have one App.xaml in my user control that links to a default embedded ResourceDictionary. And, then have the code look for the appropriate localized resources on the fly, which can be relative file paths? The only downside I see here is that the default cannot be changed on the fly...which I could probably even have that look in a specified place (using some sort of convention) and have that preferred over the built-in one?
Oh, and my reason for not wanting embedded resources is so that end users can add/modify new localized resources after the build is deployed.
I can add code if it will help you visualize this better, just let me know.
UPDATE
I am now running into a further problem with styling and not just localizing.
Here is an example of one of the internal buttons on one of the controls:
<Button Style="{StaticResource GrayButton}"
Some more things I tried/thought:
I cannot create an app.xaml (that would never be used) with the ResourceDictionary set up as ApplicationDefinitions are not allowed in library projects. I could embed this in the control's resources, but then that would always take precedence over any application level resources and I lose customizability.
Here is a connect case that actually sounds like what I am looking for, however it does not provide any real solution to this
The solution (beyond the top..which does not work) that I can think of that might work (and have yet to try) also seems like a lot of work for something that I would think should be simple. But, I might be able to create some dependency properties in the control that I can Bind to and then allow those to be overriden by the project that will be using the control. As I said, that seems like a lot of work for a pretty simple request :). Would this even work? And more importantly, is there a better, simpler solution that I am missing?
I've run into this problem once, and I resolved it by dropping the whole "Resources are objects indexed by key in canonical dictionaries" thing.
I mean, the simple fact of defining a resource in one project and referencing it in another by it's "key" should give goosebumps to any sane person. I wanted strong references.
My solution to this problem was to create a custom tool that converts my resource xaml files to static classes with a property for each resource:
So MyResources.xaml:
<ResourceDictionary>
<SolidColorBrush x:Key="LightBrush" ... />
<SolidColorBrush x:Key="DarkBrush" ... />
</ResourceDictionary>
Becomes MyResources.xaml.cs
public static class MyResources {
static MyResources() {
// load the xaml file and assign values to static properties
}
public static SolidColorBrush LightBrush { get; set; }
public static SolidColorBrush DarkBrush { get; set; }
}
For referencing a resource, you can use the x:Static instead of StaticResource:
<Border
Fill="{x:Static MyResources.LightBrush}"
BorderBrush="{x:Static MyResources.DarkBrush}"
... />
Now you got strong references, autocompletion and compile time check of resources.
I too had a problem dealing with Styling Themes and available static resources. So, I created a stand-alone library that basically had nothing but the themes to be used all nested like your MERGED resources of your prior linked question.
Then, in the Windows form (.xaml), I just put reference to that library, something like
<Window x:Class="MyAppNamespace.MyView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" ... />
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Common base theme -->
<ResourceDictionary Source="pack://application:,,,/MyLibrary;component/Themes/MyMainThemeWrapper.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Rest of XAML for the WPF window>
</Window>
The "component" appears to refer to the root of the given "MyLibrary" project. In the actual project, I created a subfolder called "Themes", hence the source includes... ;component/Themes/...
The "MyMainThemeWrapper.xaml" is very much like your nested Merged Resource dictionaries, and it sees everything perfectly from other libraries.
Here's my partial solution to your problem. I haven't tried to handle loose resources, but I have some success with sharing resources between WinForms and WPF.
Create a class library to contain your resources in .ResX files (e.g. Resources.resx, Resources.fr.resx, etc)
Create your WPF controls in a WPF user control library
Create your WinForms host
Reference the resources in your resource library from WPF using the Infralution.Localization.Wpf markup extension and culture manager, e.g.
<TextBlock Text="{Resx ResxName=ResourceLib.Resources, Key=Test}"/>
Put the content of your WPF user controls into one or more resource dictionaries as control templates,e,g
<ControlTemplate x:Key="TestTemplate">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<TextBlock Text="{Resx ResxName=ResourceLib.Resources, Key=Test}"/>
</Grid>
</ControlTemplate>
Use the resource template in your user controls
<UserControl x:Class="WpfControls.UserControl1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300" >
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ResourceDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<ContentControl Template="{StaticResource TestTemplate}" />
</UserControl>
Add a couple of lines of code to make things work
public partial class UserControl1 : UserControl
{
// we require a reference to the resource library to ensure it's loaded into memory
private Class1 _class1 = new Class1();
public UserControl1()
{
// Use the CultureManager to switch to the current culture
CultureManager.UICulture = Thread.CurrentThread.CurrentCulture;
InitializeComponent();
}
}
Here's a simple demo app called WindowsFormsHost.7z
I'm trying to access a resource dictionary in a UserControl code-behind via C# and I'm having little success.
Merged Dictionary:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Resources/BiometricDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Embedded Dictionary:
<UserControl.Resources>
<BitmapImage x:Key="imageDefault">/Resources/Images/default_thumb.png</BitmapImage>
<BitmapImage x:Key="imageDisconnected">/Resources/Images/disconnect_thumb.png</BitmapImage>
<BitmapImage x:Key="imageFailed">/Resources/Images/failed_thumb.png</BitmapImage>
<BitmapImage x:Key="imageSuccess">/Resources/Images/success_thumb.png</BitmapImage>
</UserControl.Resources>
Code behind:
var resourceDictionary = new ResourceDictionary();
resourceDictionary.Source = new Uri("/Resources/BiometricDictionary.xaml", UriKind.Relative);
I've tried all of the examples and helpful tips but coming up short. Right now, success would be the ability to load the dictionary. Any suggestions?
To access one of your UserControl's XAML resources in your codebehind, all you need to do is access the Resources property of the UserControl. Something like this:
BitmapImage myImage = (BitmapImage)this.Resources["imageDefault"];
Though, the preferred method is to use FindResource(), which will search the entire logical tree for a match to the key, rather than just the object it is called on.
BitmapImage myImage = (BitmapImage)this.FindResource("imageDefault");
Try to remove the forward slash infront of your location. The only time you should use /Resources is if you have to go up a library first. like ../Resources
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources/BiometricDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Hope this helps you.
So, you have a ResourceDictionary defined in a UserControl's assembly, and would like to access it from that UserControl's code-behind?
You should be able to. However, if the code you listed is in the constructor, you may not have access to the resource dictionary (might not be loaded yet). Try adding that same code to your UserControl's "loaded" event, and see if that works. If you're simply trying to access a resource, such as a style or template, using the "FindResource" or "TryFindResource" functions directly from your class should work as well (i.e. you don't need to have an object of type "ResourceDictionary").
Hope that helps!
d'Oh...after compiling to the local bin so that references are relative, I implemented the pack URI solution found here: ResourceDictionary in a separate assembly and then FindResource(x:key value here).
#PeterAllenWeb, #Pwninstein, thanks for your quick responses and getting me thinking again.
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.