Reference custom resource defined in another xaml file - wpf

I am trying to create a new resource in one xaml file and reference it in another xaml file.
i.e I define
<Window.Resources>
<ImageBrush x:Key="TileBrush" TileMode="Tile" ViewportUnits="Absolute" Viewport="0 0 32 32" ImageSource="MyImageButton.png" Opacity="0.3">
</ImageBrush>
</Window.Resources>
And attempt to use it in another xaml file by
<Grid>
<Button Background="{StaticResource TileBrush}" Margin="5" Padding="5" FontWeight="Bold" FontSize="14">
A Tiled Button
</Button>
</Grid>
However I get the error "StaticResource reference 'TileBrush' was not found."
I can reference the resource from the same xaml file but don't know how to do so from another file.

In WPF, the resource references works as a tree. Each control have resource, and children control can access parent's resources. The global application resource dictionary is in the App.xaml file. In this file you can include several resource dictionaries as a Merged Dictionary. See this code sample:
<?xml version="1.0" encoding="utf-8"?>
<Application ...>
<Application.Resources>
<ResourceDictionary>
<vm:ViewModelLocator x:Key="Locator" d:IsDataSource="True" />
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="View\SomeFileDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
The SomeFileDictionary.xaml is located in the View folder of my project structure. And has looks like this:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ViewModel="clr-namespace:Cepha.ViewModel"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
... >
<DataTemplate DataType="{x:Type ViewModel:SomeType}">
<TextBox .../>
</DataTemplate>...
And each dictionary key or data template defined in this file (or App.xaml), can be referenced in any place of your project. Hope this helps...

You should define this in the app.xaml file. These resources are shared throughout the entire project

Related

Only last icon in XAML shown when icon used from resources

when I try to use a material design icon from the icon pack that is defined in the ResourceDictionary, only the first icon in XAML is rendered at run time. I've followed an example that can be found here.
Example follows:
App.xaml:
<Application x:Class="TestWpf.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:TestWpf"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<materialDesign:BundledTheme BaseTheme="Light" PrimaryColor="DeepPurple" SecondaryColor="Lime" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
<ResourceDictionary Source="pack://application:,,,/TestWpf;component/Dictionary1.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
Dictionary1.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes">
<materialDesign:PackIcon x:Key="CashIcon" Kind="Cash" />
</ResourceDictionary>
MainWindow.xaml:
<Window x:Class="TestWpf.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="450" Width="800">
<StackPanel>
<Button Content="{DynamicResource CashIcon}" />
<Button Content="{DynamicResource CashIcon}" />
<Button Content="{DynamicResource CashIcon}" />
</StackPanel>
</Window>
And the result is a window that looks like this:
In xaml editor all buttons have icons on them, as expected:
Why is this happening and, more important, how to fix this?
P.S. We've found out that if you create two Cash icons in the ResourceDictionary and apply each to a button, then they will both be shown but again, only once, you can't have say 3 buttons and 2 icons in ResourceDictionary.
one more solution is to use a non-shared resource (x:Shared Microsoft docs)
<materialDesign:PackIcon x:Key="CashIcon" Kind="Cash" x:Shared="False"/>
x:Shared Attribute: When set to false, modifies WPF resource-retrieval behavior so that requests for the attributed resource create a new instance for each request instead of sharing the same instance for all requests.
A scenario for x:Shared="false" is if you define a FrameworkElement or FrameworkContentElement derived class as a resource and then you introduce the element resource into a content model. x:Shared="false" enables an element resource to be introduced multiple times in the same collection (such as a UIElementCollection). Without x:Shared="false" this is invalid because the collection enforces uniqueness of its contents. However, the x:Shared="false" behavior creates another identical instance of the resource instead of returning the same instance.
The PackIcon type is a Control. An element in the visual tree in WPF can only have a single parent. In other words, the pack icon is still a single instance added as child of the first button, then moved to the second, then to the third. You will in fact have to create multiple instances of the pack icon.
Instead of creating resources, you could use the PackIcon markup extension.
<StackPanel>
<Button Content="{materialDesign:PackIcon Cash}"/>
<Button Content="{materialDesign:PackIcon Cash}"/>
<Button Content="{materialDesign:PackIcon Cash}"/>
</StackPanel>
Depending on your actual scenario, you could alternatively create a DataTemplate, which will automatically create instances of the pack icons for each button.
<DataTemplate x:Key="CashPackIconTemplate">
<materialDesign:PackIcon Kind="Cash" />
</DataTemplate>
<StackPanel>
<Button ContentTemplate="{StaticResource CashPackIconTemplate}"/>
<Button ContentTemplate="{StaticResource CashPackIconTemplate}"/>
<Button ContentTemplate="{StaticResource CashPackIconTemplate}"/>
</StackPanel>

Add items dynamically to ResourceDictionary. Convert File path to resource Key

I have ResourceDictionary in my application. I need to add some items from c# code to this collection:
<UserControl.Resources>
<ResourceDictionary>
</ResourceDictionary>
</UserControl.Resources>
As key for resources i want to use path to file.
For example:
c:\some folder\##file.txt
What is the best wey to convert this file path to valid ResourceDictionary Key?
<UserControl.Resources>
<ResourceDictionary xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String x:Key="c:some folder#file.txt">
whatever
</sys:String>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<Label Content="{StaticResource c:some folder#file.txt}" />
</Grid>
Remove back slash and encode special characters.
This article on CP tell you how use loose XAML files at Runtime, also some other. Have a look.

Referencing a Parent's ResourceDictionary in a UserControl

I have a library of WPF UserControls, and a ResourceDictionary that is shared within the library.
All the UserControls in this library appear only within a single 'shell' parent control, which is really just a container for a collection of smaller controls. I'm able to access the ResourceDictionary from my shell control as expected when I add the following XAML
<Control.Resources>
<ResourceDictionary Source="MyResources.xaml" />
</Control.Resources>
However I can't access the ResourceDictionary from child controls that sit inside the 'shell' control.
I was under the impression that WPF should check locally for resources, and then traverse upwards until appropriate resources are found?
Instead I'm getting
Cannot find resource named '{BoolInverterConverter}'.
Resource names are case sensitive. Error at
object 'System.Windows.Data.Binding' in markup file...
Obviously I can (and am) referencing the ResourceDictionary in my child controls; but each and every control now needs to reference this dictionary and I believed that this was not necessary.
Any ideas, am I doing something strange or is my expectation of behaviour incorrect?
What's going on is described here, though the documentation's a little opaque. If you add a ResourceDictionary to an element'sResources property without specifying a key, WPF expects that you're merging in resource dictionaries, and it populates the dictionary with the contents of the dictionaries in its MergedDictionaries property. It ignores the actual contents of the ResourceDictionary with no key.
So what you want to do is this:
<Control.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MyResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Control.Resources>
Edit:
A working example:
MainWindow.xaml:
<Window x:Class="MergedDictionariesDemo.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:MergedDictionariesDemo="clr-namespace:MergedDictionariesDemo" Title="MainWindow" Height="350" Width="525">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<MergedDictionariesDemo:UserControl1 />
</Grid>
</Window>
Dictionary1.xaml:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="UCBrush"
Color="Bisque" />
</ResourceDictionary>
UserControl1.xaml:
<UserControl x:Class="MergedDictionariesDemo.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">
<Border Margin="10" BorderBrush="Navy" BorderThickness="1" CornerRadius="10">
<TextBlock Margin="10"
Background="{DynamicResource UCBrush}">
The background of this is set by the resource UCBrush.
</TextBlock>
</Border>
</UserControl>

Accessing resources during initialization of control

lets assume we have the following control definition
<ctrl:ChildWindow x:Class="Control.Editor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ctrl="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
xmlns:local="clr-namespace:Control"
Width="400" Height="300"
local:AttachedProperties.DialogResult="{Binding Path=DialogResult}"
Title="{Binding Path=Caption}" Style="{StaticResource Title}" DataContext="{Binding}" HasCloseButton="False">
<ctrl:ChildWindow.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Control;component/Resources/BaseAppearance.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</ctrl:ChildWindow.Resources>
</ctrl:ChildWindow&gt
The problem is that the style on the root control cannot be set because the ResourceDictionary is not loaded.
How can I get access to the StaticResource Title during the initialization of the control, when I don't have access to the App class? I'm also not sure that it would be possible, if I would have access to it.
Regards
I'd recommend accessing your resource and doing the work in the .Loaded() event of your control.
Edit: On second thought... I think I know what you're doing now... You have a resource set in your App.xaml class, but you want to access it in your control.
Easy way around the problem is to set it to a DynamicResource instead... but this is less performant.
What is the BuildAction set to on your App.xaml in the property's tab?
If it is ApplicationDefinition... then you should be able to access your resource as you currently are.
I found the common way without using code behind. I knew it is possible. ^^
<ctrl:ChildWindow x:Class="Control.Editor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ctrl="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
xmlns:local="clr-namespace:Control"
Width="400" Height="300"
local:AttachedProperties.DialogResult="{Binding Path=DialogResult}"
Title="{Binding Path=Caption}" DataContext="{Binding}" HasCloseButton="False">
<ctrl:ChildWindow.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Control;component/Resources/BaseAppearance.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</ctrl:ChildWindow.Resources>
<crtl:ChildWindow.Style>
<StaticResource ResourceKey="Title" />
</crtl:ChildWindow.Style>
</ctrl:ChildWindow&gt

Why can't I find DataTemplates in merged resource dictionaries?

In my MainWindow.xaml, I have the following reference to a ResourceDictionary:
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
In MainSkin.xaml, I define a datatemplate:
<DataTemplate x:Key="TagTemplate">
...
</DataTemplate>
Deeper within my application, I attempt to use this data template:
<ContentControl DataContext="{Binding Tag}" ContentTemplate="{StaticResource TagTemplate}"/>
The code compiles successfully, but when I attempt to load a Page or UserControl that contains this StaticResource, I get an exception saying that the TagTemplate can't be found.
What am I doing wrong?
In order to access the contents of a resource defined in a XAML file, you need to "include" that XAML file in each page and control that uses it. So every XAML files will need to have the MergedDictionaries entry that you have in MainWindow.xaml.
Alternatively you can add those merge dictionaries to App.xaml and those resources are included implicitly:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MainSkin.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Are you using that StaticResource in the same Window where it is declared? Otherwise I think that you cannot have access to that.

Resources