Merging ResourceDictionaries changing custom control pathing - wpf

Net 5 WFP app issue VS v.16.9
My custom control, which is supposed to display a plain isn't displaying at run. I think I got an idea what's going on but don't know how to fix it.
Merging resource dictionaries in my Generic.xaml file to references the source of my custom control causes the blue squiggly line and mouseover tooltip
"Cannot locate resource 'ControlsFolder/CustomControl.xaml'"
If I delete my Merged dictionary, the mouseover tooltip changes to say "Projectnamespace.ControlsFolder.CustomControl"
The tooltipped CustomControl being called in my MainWindow.xaml as follows.
<Control:CustomControl/>
The Merged of dictionaries that I think is causing the problem is ...
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/ControlsFolder/CustomContol.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
My CustomControls.cs has the constructor below
static CustomControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl), new FrameworkPropertyMetadata(typeof(CustomControl)));
}
Its my understanding that this constuctor searches the Themes/Generic.xaml file to figure out where the location of my customcontrol is to add default metadata to my customcontrol. But I think when I add my merged resource dictionary, its changing the pathing of my CustomControl call to search for my styles in /ControlFolder/CustomControl.xaml instead of default pathing which should be "Projectnamespace.ControlsFolder.CustomControl".
Or I made a simple mistake that's not intuitive at all for a beginner.
Maybe there is a property I can add to CustomControl that sets the pathing back to the other one. Does anyone have any ideas what the issue is?
But I should be seeing my style display on screen

Welcome to SO! Try this URI instead:
<ResourceDictionary Source="/YourProjectName;component/ControlsFolder/CustomContol.xaml" />

Related

How to make a globally available style WITHOUT App.xaml

I have a class library I am creating for an AutoCAD plugin that includes WPF control elements. I would like to add a theme to these elements, so I added the WPFThemes.DarkBlend package from Nuget.
To property utilize the theme, you are supposed to add the style reference to your App.xaml file like so:
<Application
...
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes\Styles.xaml” />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Now unfortunately, because this is only a class library, I do not have an App.xaml file in which to add this code. I am able to use my Window.Resources, but in doing so, I am afraid that the styles in the reference are not exposed to the Window element. Is this because the Window is not within the scope of its own resources? Below is an image that shows my current situation:
Thank you!
After creating an x:Key in the style called "DarkWindow", I found that I was able to apply the style directly in the PluginWindow.xaml window properties like this:
<Window Style="{DynamicResource DarkWindow}" />

WPF Dynamically change resource file and theme

My project uses a ProjectTheme.xaml file for all WPF windows through out the project.
The ProjectTheme.xaml file references a style theme as follows
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<!-- In order to modify the project's theme, change this line -->
<ResourceDictionary Source="/MyProject;component/Themes/WPFThemes/Customized.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
All WPF Windows references WindowBase.xaml
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MyProject;component/View/WindowBase.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
WindowBase.xaml references customized titlebar Bar1.xaml
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MyProject;component/Themes/WPFThemes/Bar1.xaml" />
</ResourceDictionary.MergedDictionaries>
Bar1.xaml references ProjectTheme.xaml
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MyProject;component/ProjectTheme.xaml"/>
</ResourceDictionary.MergedDictionaries>
So the heriarchy is
Window1 references WindowBase.xaml
WindowBase references Bar1.xaml
Bar1 references ProjectTheme.xaml
ProjectTheme.xaml reference the real theme resource file.
This works fine.
Now I want to dynamically change the project theme at run time without quitting the app.
Assuming that I have several theme style files
Customized.xaml
Customized1.xaml
Customized2.xaml
My question is
if it possible to dynamically update ProjectTheme.xaml file at run time to change the line
from
<ResourceDictionary Source="/MyProject;component/Themes/WPFThemes/Customized.xaml" />
to
<ResourceDictionary Source="/MyProject;component/Themes/WPFThemes/Customized1.xaml" />
to achieve my objective?
If yes, how do I do it?
If no, what is the reason and what is the best (other) way to achieve my purpose?
I have tried the following but none of them work: the style does not change.
way 1
Application.Current.Resources.MergedDictionaries.Clear();
Uri NewTheme = new Uri(#"/MyProject;component/Themes/WPFThemes/Customized2.xaml", UriKind.Relative);
ResourceDictionary dictionary = (ResourceDictionary)Application.LoadComponent(NewTheme);
Application.Current.Resources.MergedDictionaries.Add(dictionary);
way 2
Application.Current.Resources.MergedDictionaries.RemoveAt(0);
Uri NewTheme = new Uri(#"/MyProject;component/Themes/WPFThemes/Customized2.xaml", UriKind.Relative);
ResourceDictionary dictionary = (ResourceDictionary)Application.LoadComponent(NewTheme);
Application.Current.Resources.MergedDictionaries.Insert(0, dictionary);
Note:
In my real theme style files (Customized.xaml...) I used a combination of dynamic resource and static resource. Does that matters?
Thanks in advance.
There are a few things to consider here.
First, anything defined with StaticResource will not get updated on a change. If you want a control to support changing the theme at runtime, you need to use DynamicResource so it knows to look for changes.
Your overall approach to changing the theme is correct. The easiest way to accomplish this is using Application-scoped resource dictionaries, making sure your ResourceDictionary is defined in your App.xaml. For adding a new resource, I've used snippets similar to the following:
ResourceDictionary dict = new ResourceDictionary();
dict.Source = new Uri("MyResourceDictionary.xaml", UriKind.Relative);
Application.Current.Resources.MergedDictionaries.Add(dict);
The part you may be confusing yourself over is when using resources within base classes. When you define a resource in a class, the resource will be local to an instance of that type. Think of the XAML compiling into it's own InitializeComponent() method on classes, meaning you can't change the original XAML and expect the changes to go to all instances. On the same note, changing the resources on a class instance doesn't effect other instances.
Since your question really contains two separate concerns (application theming and changing control resources), I would focus on ensuring your application resources are updating properly and using DynamicResource, and hopefully the information I've provided would be sufficient for understanding why certain other resources may not be updating yet.

How can i make a prefix so i can address a folder in xaml

I always have problems making new prefixes in xaml. Most of the time, i get the message that the URI cannot be found in the assembly. My setup:
I have a WPF project (in a solution with class libs and asp.NET projects) with a MainWindow.xaml file. The XAML starts with : Window x:Class="MainWindow" ... .
So as default, there's no namespace given to it. In that same project i made a folder "Folder". In that folder, i have resx-files. What i need to do is make a prefix in xaml so i can address those files. I was thinking of :
xmlns:p="clr-namespace:WpfApplication.Folder"
and then for my controls
<Label Content="{x:Static p:NameResxFile.KeyName></Label>
However, the prefix generates the "URI cannot be found in the assembly" error. I'm i just failing at making prefixes?
Thanks in advance.
EDIT
If you cannot make a namespace ref to a folder, what is happening here?
xmlns specifies namespaces, it does not bother with folders or files, if you need access to an external resource you can load it into your control's resources via ResourceDictionary.
There was something like this i think:
<Window.Resources>
<ResourceDictionary x:Key="ExternalRes" Source="Folder/File.xaml"/>
....
</Window.Resources>
To reference an element of resource dictionary you should add that dictionary to your control's Resources collection or register it in the App.xaml file. After that you could just use StaticResource extension to get access to the element. Your code will look like this:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Folder/NameResxFile.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<!-- ... -->
<Label Content="{StaticResource KeyName}"/>
Things get trickier if you want to put the resource dictionary to another assembly and reference it. For this purpose refer to PackURIs in WPF article.

Headaches with configuring themed resources from c# (Windows Phone)

I have Windows Phone App and I want to have a different themed resources based on whether the user has a dark or light theme and which accent color they have chosen.
In my app initialize code I detect the users theme and accent color then load the appropriate Resource Dictionary, Dark, Light etc. I add the Resource Dictionary to the App.Current.Resources.MergedDictionaries collection.
The problem comes when I want to reference a value in the (dynamically loaded) from my App.Xaml. In the below example the key is "DefaultBackgroundImageOpacity". For some reason the values in the ResourceDictionary that I load into the MergedDictionaries collection never get found when the App.Xaml parsing happens. I've played with loading the resources before I call InitializeComponent() and after. Neither seem to resolve the issue.
Any ideas?
// Simplified version of adding some xaml...
// Note I'm loading the key "DefaultBackgroundImageOpacity"
this.Resources.MergedDictionaries.Clear();
var myTestXaml = "<ResourceDictionary xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml' xmlns:System='clr-namespace:System;assembly=mscorlib'> <System:Double x:Key='DefaultBackgroundImageOpacity'>0.2</System:Double></ResourceDictionary>";
this.Resources.MergedDictionaries.Add((ResourceDictionary)XamlReader.Load(myTestXaml));
// This always fails saying that the key "DefaultBackgroundImageOpacity"
// can not be foudnd - even though it was loaded just above
InitializeComponent();
// Phone-specific initialization
InitializePhoneApplication();
I suspect your App.Xaml contains something like this:-
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/somedictionary.xaml" />
...
</ResourceDictionary.MergedDictionaries>
...
</ResourceDictionary>
</Application.Resources>
If that is so then InitializeComponent will replace the default ResourceDictionary which you have manipulated in your code hence your changes to MergeDictionaries will not be present since that instance of a ResourceDictionary is no longer referenced.
If you are not setting up any MergedDictionaries in the App.Xaml then make sure you add resources directly to Applicaiton.Resources and are not creating an new instance of ResourceDictionary in the xaml.

Static resource shared in merged dictionaries

I'm currently working on having dictionaries of styles and templates that I can dynamically apply to my application. Before this "new wanted" dynamical behavior, I had several resource dictionaries, one for each styled control, that I merged in the App.xaml:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ColorsDictionary.xaml"/>
<ResourceDictionary Source="ControlsTemplatesDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Now, I'd like my application to be styled, so I decided to merge all my previous resources into a new one called "MyFirstTemplates" and to add only this dictionary to the App.xaml.
New dictionary "MyFirstTemplates.xaml":
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">"
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ColorsDictionary.xaml"/>
<ResourceDictionary Source="ControlsTemplatesDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
New App.xaml:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MyFirstTemplates.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style TargetType="{x:Type Window}"/>
</ResourceDictionary>
</Application.Resources>
Note: The default style for the Window is to correct a bug of WPF 4, see Adding a Merged Dictionary to a Merged Dictionary
Now that I have made this change, I cannot use a color resource from "ColorsDictionary.xaml" as a StaticResource in "ControlsTemplateDictionary.xaml" anymore. If I change back to merging these files in the app.xaml, everything works. To make it work, I have to change these StaticResource for DynamicResource. Do you have any idea why this doesn't work anymore?
Thank you :-)
By moving the dictionaries out of App.xaml the resources from each dictionary aren't in the other's resource tree during loading of MyFirstTemplates.xaml. Your original setup first loaded ColorsDictionary which was then available through App resources to ControlsTemplatesDictionary while it loaded. In your new setup, in order for the color resource to be available in App resources it needs to be loaded through MyFirstTemplates, which in turn requires loading of both dictionaries, which in turn requires access to the color resource... so it's sort of an infinite loop of references that can't be resolved statically. DynamicResource can wait until everything is loaded and then access the color without issue.
To fix either use Dynamic or merge ColorsDictionary directly into ControlsTemplatesDictionary.
Great answer by John explaining why this is happening.
So the problem is that when using merged dictionaries within a merged dictionary, the inner dictionaries can't "use" each other as StaticResource.
Basic solutions:
Use DynamicResource
Use just a single level of hierarchy from App.xaml when using StaticResource
Both of these solutions have problems. DynamicResource has a performance problem. The 2nd solution limits you on how you organize your XAML resources.
Alternative solution:
I created a small simple program (provided below in GitHub) that will run as a pre-build event and merge XAML files from a folder into one long .XAML file. Well, they need to be with a different extension (.txaml), otherwise they will be compiled.
This allows to structure resources folders and files however you want, without WPF’s limitations. StaticResource and the designer will work always.
The code in GitHub contains a simple solution that contains the merging program. It merges 2 folders into 2 files. One for App.xaml resources and the other for Generic.xaml resources. The .xaml files in a "Controls" project (There's also "Main" project).
Blog post explaining this

Resources