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.
Related
I converted an application to a DLL library where my main window became a user control. I was using the method of creating a System.Windows.Application object manually to store my resources but I want move away from that and have my user control be self sufficient, so I can simply do something like:
CustomUserControl control = new CustomUserControl ( object_to_pass);
It will then take care of everything else internally. The basic layout of the control is a frame that hosts multiple pages, like a wizard style app.
I am having two main issues:
Setting up references to the view models
I thought that instead of using System.Windows.Application.FindResource which I extensively used, I will use a similar function on the user control class and pass a reference to my user control around via a singleton.
To do this I use mvvm-light's SimpleIoc container in a class called 'ViewModelLocator' to keep track of all the view models. Problem is, this was a resource in App.xaml loaded from the datacontext binding of the user control.
This cannot be done anymore as the user control itself has to instantiate the resources containing it further along in its own xaml:
<UserControl x:Class="WUP.Views.WarmUpPluginUserControl"
mc:Ignorable="d"
...
...
<!--This will not work-->
DataContext="{Binding Source={StaticResource Locator}, Path=MainWindowLogic}">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Path/To/ViewModelLocator/Resource.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
Now I have to instantiate the ViewModel locator in the user control code behind and set it up as a resource with Resources.Add. This forces me to switch to dynamic resource for all references to the ViewModel locator from all other views (Pages). Not only does this cause issues described further on, its ugly as I can no longer access member function with the path like I used to:
DataContext="{Binding Source={StaticResource Locator}, Path=MainWindowLogic}">
Referencing resources from xaml, Dynamic vs Static
The resources I use are brushes, colours, templates and converters, each in their own resource dictionary, and I add them in the right order to avoid dependency issues.
The method in the first part works ok for accessing resources from the ViewModel via the reference to the user control in the singleton. The problem now is how to have the resources loaded in each view of the app. I tried the brute force method of sticking them all in Page.Resources or UseControl.Resources but that gave me resource not found errors in some pages despite them existing there. I am looking into why this happens but I am not sure
I then tried Dr.WPF's method of creating a singleton class that you can use to create a single instance of resources and expose them as a dependent property. This forces me to use dynamic resources again for all my views.
This is fine for all my resources except the converters, and I get errors for all converters originally referenced in this way:
Visibility="{Binding Functions.DictatesActions, Converter={StaticResource BooleanToVisibilityConverter}, UpdateSourceTrigger=PropertyChanged}"
So I don't know how to deal with this is the dynamic scenario.
I am seriously thinking to abandon this approach and just use System.Windows.Application to store all my resources, despite it potentially causing issues with other user controls in the hosting application (winforms). Please let me know if there is a better way!
I finally managed to fix my issues:
Setting up references to the view models
Here I just had to do it all from the code behind. As I mentioned I used a ViewModelLocator to keep track of all my VMs, so I set up the references as resources in the actual user control constructor:
Resources["Start"] = view_model_locator.Start;
Resources["SelectUnit"] = view_model_locator.SelectUnit;
Resources["HardwareChecks"] = view_model_locator.HardwareChecks;
Resources["ConfigurationChecks"] = view_model_locator.ConfigurationChecks;
...
I then included the reference to the user control in the ViewModel locator as static property:
ViewModelLocator.WarmUpPluginUserControl = this;
Then I could access it from the other views in their code behind like this:
DataContext = ViewModelLocator.WarmUpPluginUserControl.FindResource("Start");
I could also use it in the VMs in the same way that I used the Application.Current.FindResource(). It's not the most elegant solution, but it worked
Referencing resources from xaml, Dynamic vs Static
Here I stuck with the brute force method of including all the resources at the top of every page:
<WUP:WUPPage.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/WarmUpPlugin;component/Resources/Colors.xaml"/>
<ResourceDictionary Source="pack://application:,,,/WarmUpPlugin;component/Resources/Styles.xaml"/>
...
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
This did cause issues for me in the beginning where I had resource not found errors, but this was due to some of my dictionaries having dependencies other ones so I had to add them in to the relevant dictionaries via MergedDictionaries.
I did not notice this dependency issue when the application was standalone as all the resources required were already loaded in the application scope.
I intend to replace this with the Dr WPF method, but I would still have to change all my XAML references to dynamic from static then deal with the converters not being able be accessed via dynamic resource references.
Is it possible to set code behind a resource dictionary in WPF. For example in a usercontrol for a button you declare it in XAML. The event handling code for the button click is done in the code file behind the control. If I was to create a data template with a button how can I write the event handler code for it's button click within the resource dictionary.
I think what you're asking is you want a code-behind file for a ResourceDictionary. You can totally do this! In fact, you do it the same way as for a Window:
Say you have a ResourceDictionary called MyResourceDictionary. In your MyResourceDictionary.xaml file, put the x:Class attribute in the root element, like so:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyCompany.MyProject.MyResourceDictionary"
x:ClassModifier="public">
Then, create a code behind file called MyResourceDictionary.xaml.cs with the following declaration:
namespace MyCompany.MyProject
{
partial class MyResourceDictionary : ResourceDictionary
{
public MyResourceDictionary()
{
InitializeComponent();
}
... // event handlers ahead..
}
}
And you're done. You can put whatever you wish in the code behind: methods, properties and event handlers.
== Update for Windows 10 apps ==
And just in case you are playing with UWP there is one more thing to be aware of:
<Application x:Class="SampleProject.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:rd="using:MyCompany.MyProject">
<!-- no need in x:ClassModifier="public" in the header above -->
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- This will NOT work -->
<!-- <ResourceDictionary Source="/MyResourceDictionary.xaml" />-->
<!-- Create instance of your custom dictionary instead of the above source reference -->
<rd:MyResourceDictionary />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
I disagree with "ageektrapped"... using the method of a partial class is not a good practice. What would be the purpose of separating the Dictionary from the page then?
From a code-behind, you can access a x:Name element by using:
Button myButton = this.GetTemplateChild("ButtonName") as Button;
if(myButton != null){
...
}
You can do this in the OnApplyTemplate method if you want to hookup to controls when your custom control loads. OnApplyTemplate needs to be overridden to do this. This is a common practice and allows your style to stay disconnected from the control. (The style should not depend on the control, but the control should depend on having a style).
Gishu - whilst this might seem to be a "generally not to be encouraged practice" Here is one reason you might want to do it:
The standard behaviour for text boxes when they get focus is for the caret to be placed at the same position that it was when the control lost focus. If you would prefer throughout your application that when the user tabs to any textbox that the whole content of the textbox was highlighted then adding a simple handler in the resource dictionary would do the trick.
Any other reason where you want the default user interaction behaviour to be different from the out of the box behaviour seems like good candidates for a code behind in a resource dictionary.
Totally agree that anything which is application functionality specific ought not be in a code behind of a resource dictionary.
Adding on....these days, with the advent of {x:Bind ...}, if you want to put your DataTemplate into a shared ResourceDictionary file, you are required to give that file a code behind.
XAML is for constructing object graphs not containing code.
A Data template is used to indicate how a custom user-object is to be rendered on screen... (e.g. if it is a listbox item) behavior is not part of a data template's area of expertise. Redraw the solution...
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.
I'm having trouble with the code below:
<Window x:Class="ChangePage.PageSwitcher"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ChangePage"
Title="ECE Showcase"
WindowState="Maximized">
<Window.Resources>
<local:PageSwitcher x:Name="pageTransitionControl" TransitionType="SlideAndFade"/>
</Window.Resources>
In the code behind file PageSwitcher.xaml.cs I have the following line:
pageTransitionControl.TransitionType = whatever;
However this results in the following error:
The name 'pageTransitionControl' does not exist in the current context
I've been searching around the Internet for a few hours now trying to find a reason for this but haven't been able to figure it out yet. Build Action is set to Page, all files are saved, I've tried rebuilding, PageSwitcher is in the ChangePage namespace and PageSwitcher has a constructor.
Is there something else I am doing wrong?
You cannot assign names to resources. Resources have keys.
<Window x:Class="ChangePage.PageSwitcher"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ChangePage"
Title="ECE Showcase"
WindowState="Maximized">
<Window.Resources>
<local:PageSwitcher x:Key="pageTransitionControl" TransitionType="SlideAndFade"/>
</Window.Resources>
Then in the xaml.cs:
var pageTransitionControl = (PageSwitcher)Resources["pageTransitionControl"];
pageTransitionControl.TransitionType = whatever;
The item exists in the Window's Resources, not in the Window itself
You can either place it directly in the Window itself instead of it's Resources to make your code work, or assign it an x:Key instead of an x:Name and get it using
(PageSwitcher)this.Resources["pageTransitionControl"]
Seems you cannot directly access the resources in the code behind. You may need to use Resources["pageTransitionControl"] to access it.
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);