Dynamically swapping *Local project* WPF Themes (Styles) at runtime - wpf

I am created a new project, where I will need to change the themes dynamically.
I have seen plenty of posts about this where you do this via the App.xml..
eg
Resources.MergedDictionaries.Clear();
Resources.MergedDictionaries.Add(new ResourceDictionary
{
Source = new Uri(Themes/MyTheme.xaml, UriKind.Relative)
});
This works fine, however, since this is going to be a large project, and I was going to have each View in it's own projects (ie following the Prism structure), I wanted each project to have i't own local theme .xaml files, and swap these (ie rather than having everything in the main application ("Shell") project.
My sample projects can be found at
https://dl.dropboxusercontent.com/u/30760237/ThemesSwap.zip
In here I have a helper that will change the themes at runtime (MainView2.ThemeHelper))
If you run the project as is, when you click the button, we can see the old theme (pink background) remove, but the new one (black background) does not load.
The only way I can get it to work is adding the following "link" into the App.xaml file (it is commented out in the attached)
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/MainView2;component/Themes/DarkTheme.xaml"/>
</ResourceDictionary.MergedDictionaries>
My question is, is there a way to NOT have to link these themes in the main project, and have them totally encaplusated in each of the view projects? I can't understand why we need to reference these in the main project?
Thanks in advance for any help
regards, Peter

Someone has told me the answer to this so thought would share it..
"You need to use the pack URI for a resource file that is compiled into a referenced assembly:"
ie have the "/MainView2;component" prefix...
Source = new Uri(="/MainView2;component/Themes/MyTheme.xaml, UriKind.Relative)

Related

XAML Intellisense doesn't work for Referenced Assembly Resources

I'm trying to create a project dll file that holds core styles, resources, controls, etc. This will then be referenced into various other projects to use as a graphic base to unify the appearance.
Anyway, I have the graphics project set up and I can use it in other projects and it all seems to display correctly. However, XAML intellisense for my static resources doesn't seem to be working.
Here is my resource dictionary reference:
<ResourceDictionary Source="pack://application:,,,/BIT.GraphicElements;component/BITCoreStyles.xaml" />
As you can see I'm using pack notation to reference it inside it's assembly. I can style elements like I would expect (StButtonAction is in the above dictionary):
Style="{StaticResource StButtonAction}"
However, I have to type it in manually. Intellisense doesn't give it as an option but if I type it manually is applies it successfully. It also works if I add the project to the solution and reference it there, however this is intended to be used by those that don't necessarily have access to the graphics project so that's not a good solution in the long run...
What am I missing? Is there a way to make Intellisense work for these?
Maybe this'll help future searchers even if it doesn't fix your specific problem.
When you have a solution involves external dll then you can occasionally find resources just don't get loaded at design time. At all. Even though you merge in app.xaml the things don't turn up until you run.
It looks like some sort of subtle bug to me.
The way I work round this is to use functionality originally intended for Blend. We are now using the blend designer in visual studio.
In solution explorer, add a resource dictionary to Properties. Yes. Properties. Create it first in your project and drag it up there. This must be called DesignTimeResources. This can then merge any resource dictionaries you will want to use just for design time or when you have an issue makes you think resource dictionaries aren't getting loaded for design time.
Mine is in army editor but uses resources from a referenced dll, UILIb. It looks like:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ArmyEditor">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/UILib;component/Resources/UILibResources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
This is a resource dictionary in a bit of an odd place but nothing particularly weird so far.
Next is the bit that many developers will never have done - editing your csproj.
Right click your project and choose unload.
Right click it again and edit it.
In there you will have entries for all the stuff makes up your project. And of course breaking this would be bad so maybe you want to back up first.
If you don't use blend then I think the important thing here is to add a node which says ContainsDesignTimeResources True, but here's mine:
<Page Include="Properties\DesignTimeResources.xaml" Condition="'$(DesignTime)'=='true' OR ('$(SolutionPath)'!='' AND Exists('$(SolutionPath)') AND '$(BuildingInsideVisualStudio)'!='true' AND '$(BuildingInsideExpressionBlend)'!='true')">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
<ContainsDesignTimeResources>true</ContainsDesignTimeResources>
</Page>
Save your edits.
Reload your project.
When the designer is open, you should find resources from it are loaded. Maybe this will help intellisense find them.
I mentioned this earlier but.. In my experience, such intellisense on resources is often unreliable.
This definitely solved a similar issue I have had in this solution. It is also useful when you want to temporarily overlay resource dictionaries for localisation/branding testing.

WPF Designer doesn't find dll-resources

Context:
I have a created a user control library project in Microsoft Visual Studio 2017 with a lot of Styles and ControlTemplates to be used in various future projects. Now I want to reference it in a new solution.
What works:
When I create a new solution with a new WPF-Application I can import the user control library project into the solution and add a reference to it in my new WPF-Application project - everything works fine this way. But this isn't a good solution for me because I don't want to copy the sourcecode everytime I use it.
What doesn't work:
...So I tried to not import it and referencing it as a project but to just reference the compiled dll-file of the user control library. In principle, everything should stay the same from the WPF-Applications point of view. But the Designer doesn't find the referenced resources correctly. At runtime it works perfectly well with no errors, but at designtime the Designer either ignores the referenced resources or crashes entirely.
Its behaviour doesn't seem to be very consistent anyway:
It finds some resources correctly, e.g. It shows referenced ColorBrushes defined in the user control library correctly (e.g.Background="{StaticResource MyBlue}")
Sometimes it seems to find a referenced Style-resource but not the resources nested within that Style (e.g. Style="{StaticResource TextBoxOutput}" shows error "The resource "MarginStandard" can't be found." when MarginStandard is used in TextBoxOutput)
Styles without a key which should apply by default don't apply automatically.
All this works correctly at runtime and when I reference the user control library as project rather than as dll.
What I already checked
User control library and WPF-Application use the same CPU-Architecture (AnyCPU).
User control library and WPF-Application use the same .net Version (4.6.1)
I tried different Build-Actions for compiling the user control library (resource, embedded resource, none).
The dll-file gets copied into the bin/debug-folder of the WPF-project.
All resource dictionaries from the user control library are imported in the App.xaml file of the WPF-project using the following Syntax:
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/UserControlLibraryName;component/RecourceDictionaryName.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
My Question
How can I use resources referenced as dll-file in such a way that the Designer also finds the resources correctly? My Styles and ControlTemplates alter the look of the Controls drastically, so its really necessary to be able to see the results at designtime.

WPF Centralize xaml/image Resources in Multi-Tier/Project Application Solution

My situation is this.
I have multiple projects under a WPF solution which make up a multi-tier application I am developing. Since separate projects will sometimes need to access the same image or xaml resource I'd like to centralize "Public" resources to one project that can be referenced by any other projects requiring these "Public" resources. For example, I have a BMP image that I use in various controls and windows in separate projects that serves as a banner/logo. It seems like I should be able to add this image as a resource to the public resources project and reference it from my other projects instead of adding the image separately to every project that needs it. If this is possible, what would it look like and how should I go about doing it? If my idea just sucks I'm open to suggestions, but the project is going to be quite large so I don't want to be adding resources all over the place.
Thanks!
P.S.
I have searched this topic quite a bit but there are so many garbage answers out there from people that don't know what they are doing. Given that I'm relatively new to WPF I'd rather get a direct answer to my problem.
Well after some tinkering and research I found a solution to my own question. It seems like bits and pieces of my answer were scattered all over the web so I'll try to explain the whole thing at once, in one place. To re-cap I basically wanted to centralize any resources used across projects within my solution. These are the steps I eventually followed to accomplish this.
1.
Create a User Control project in your solution (any project that can host Resource Dictionaries will do). I'll refer to this project as "the resource project".
2.
Add any resource files (images etc...) that you want to share between projects to the resource project. I try to organize files in sub-directories that make sense. You will want to set the build action to "Resource" so that it gets compiled into the output binary.
3.
Now add a resource dictionary to the resource project. In my case I wanted to reference several images so I made one called "ImageDictionary.xaml".
4.
Add references to the image files in the resource dictionary. I am referencing images here but its just a resource dictionary, you can put whatever in there. The string in the middle is just the relative path to the image file you are referring to.
<ImageSource x:Key="SomeImageKey">
Images/SomeImage.bmp
</ImageSource>
5.
Now go to a project that requires the resource dictionary you just made in step 4. It can be another user control or a window, whatever. Right-click on project references and add a reference to the resource project.
6.
Okay now the current project needs to reference the dictionary you made in step 4 that is defined in the resource project. I made a user control first so this is what my code would look like below... Note that I used merged dictionaries. I plan on using more later so I chose this way instead of a single dictionary.
"ResourceProject" below is the name of the project/assembly that you added the resource to.
"component" is a keyword that needs to be there, followed by the path to the dictionary xaml code file.
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/ResourceProjectAssembly;component/Resources/ImageDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
As you can see, referencing an external resource dictionary requires something called a "Pack URI". I found a short hand way of doing it. The long way is quite a bit uglier and as far as I know, there is no advantage to it. But here is what the long way looks like.
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/ResourceProjectAssembly;component/Resources/ImageDictionary.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
7.
Now that your current project has a reference to the resource dictionary in the resource project, you can use anything from that dictionary. I went ahead and put an image in my user control. Below is the code. Its just like referencing a dictionary locally.
<Image Source="{StaticResource SomeImageKey}" />
8.
Important Note!
This caused me a headache for a while before I found it. Make sure that the startup project in your solution has a reference to the resource project EVEN IF THE STARTUP PROJECT DOES NOT USE THE RESOURCE PROJECT. (I.e. right-click references, add reference etc...)
The resources will not get compiled into the output unless you do this. This part was tricky because the designer was even showing my images and I had no xaml errors but at runtime it would throw set property and cannot find file exceptions. This went away when I referenced my resource project from my startup project.
Hopefully this helps somebody.
Below are some links to a few places (including stackoverflow) that gave me what I needed to put all the pieces together.
http://blogs.msdn.com/b/wpfsldesigner/archive/2010/06/03/creating-and-consuming-resource-dictionaries-in-wpf-and-silverlight.aspx
Load WPF styles or other Static Resources from an external file or assembly
ResourceDictionary in a separate assembly

WPF: OnApplyTemplate is not called anymore when I moved code into another DLL

Let's try to explain it clearly.
I've got a custom control built as a WPF application and it works fine. I've moved all the code into a external DLL. After this change, when I load the application, the method OnApplyTemplate() is not called any more and the control is not rendered either
I've try with Generic.xaml file is into a Themes directory (with capital T) in the root of the DLL which has the control and/or a Themes directory into the StartUp project.
If this info is important here's where I've found the control: http://www.codeproject.com/KB/WPF/WPFOutlookCalendar.aspx
The settings of the project is
Output type: Class Library
Target framework: .Net Framework 4
I've create a simple DLL project and I've added the references manually
Do you have any idea about the solution?
Thanks in advance...
The main difference between a standard Wpf Applicaton and a WPF Custom Control library are the following lines of code.
[assembly: ThemeInfo(
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
//(used if a resource is not found in the page,
// or application resource dictionaries)
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
//(used if a resource is not found in the page,
// app, or any theme specific resource dictionaries)
)]
Adding these lines of code to your Assembly should fix your bug.
Thanks to dowhilefor, I've found the solution!
I've recreated a new library as a WPF Custom Control Library and it works.
It is a little overkill to recreate a new project so if someone can explain to me how to reconfigure an existing project, it'd be very nice ;)

Why does the Image element give me a design-time error with "add as link"ed images?

I need to display an image, which I've done without problems before, but today I decided to be tricky and use "add as link" instead. Well, now I get:
The file Images/hello.png is not part of the project or its 'Build Action' property is not set to 'Resource'.
Wait... its Build Action is set to Resource. I've seen a Silverlight solution that involves the usage of Merged Dictionaries to share files between Silverlight and WPF projects, but it's not clear to me that this would even apply to my WPF + Image issue.
Has anyone solved this problem before? I could make copies of all of the images, but that seems a little silly if I have a shared repository with clip art and the like.
Dave,
I've just tried to add image as a link to plain WPF application. Build action is "Resource" (don't confuse with "Embedded Resource"). I've added it to the root, and refer to it as <Image Source="/file_name.jpg"/> - all works fine.
The message you have is it compile or runtime? If it's a runtime, how do you refer to the image? Do you see it in Reflector, when you open your assembly (it should be under Resources folder)?
I have images in one assembly which I want to share into another. I've used Add as Link in my second assembly. In the project where the actual image files are located they are in a Resources\Images folder. In the project which links to those files the links are also in a Resources\Images folder. At runtime a XamlParseException claiming "cannot locate resource" is thrown.
My xaml which is referencing the image is in a UserControls folder.
In the project which actually contains the images the path ..\Resources\Images\Blah.png works fine as expected.
Opening the DLLs in Reflector makes it obvious that in the assembly with the linked images holds the images at the root level - the compiler is not respecting the folder location. So in the project with the linked files I have to use ..\Blah.png to find the resource.
Surely a bug in the compiler?

Resources