Get ResourceDictionary source from static member with design-time support - wpf

My basic goal is to have a ResourceDictionary in a dll which I can use in another WPF project via ResourceDictionary.MergedDictionaries. But I don't want to reference the ResourceDictionary by hard-coding the URI in the XAML of the referencing application, I want to instead reference some static member which will provide the URI.
I have some simplified code which is "working", but only at runtime. At design-time it throws errors and I get no IntelliSense support. For this simplified example, everything is in one assembly (no separate dll).
Dic.xaml (the resource dictionary I want to reference):
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush Color="Blue" x:Key="BlueBrush"/>
</ResourceDictionary>
Foo (the module to hold the static member with the URI):
(VB.NET version)
Public Module Foo
'[VBTest] is the name of assembly
Public ReadOnly Property URI As New Uri("pack://application:,,,/VBTest;component/Dic.xaml")
End Module
(C# version)
public static class Foo
{
//[VBTest] is the name of assembly
public static Uri URI { get; } = new Uri("pack://application:,,,/VBTest;component/Dic.xaml");
}
And then finally, the place in the application where I want to reference the ResourceDictionary:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:VBTest">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="{x:Static local:Foo.URI}"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Border Width="100" Height="100" Background="{StaticResource BlueBrush}"/>
</Window>
At design-time, I get two errors:
XDG0062 An error occurred while finding the resource dictionary "".
XDG0062 The resource "BlueBrush" could not be resolved.
However, the project will build and run just fine. And will show the intended blue square.
The question is, how can I get this to work at design-time?

I found a thankfully easy workaround. Maybe not the prettiest, but it is elegant. I took inspiration from this answer to a somewhat related question.
By creating my own class inheriting from ResourceDictionary, I can better control the loading behavior. That sounds like it would be complicated, but really all I had to do was set Source as part of the constructor and everything just worked.
The below is added to the code file (outsite of Module/static class Foo):
Public Class StylesResourceDictionary
Inherits ResourceDictionary
Public Sub New()
MyBase.New()
Source = URI
End Sub
End Class
Note that Source = URI is referencing Foo.URI from the question code.
And then the XAML file becomes:
<Window x:Class="MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:VBTest">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<local:StylesResourceDictionary/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Border Width="100" Height="100" Background="{StaticResource BlueBrush}"/>
</Window>
And bam, full design-time support without hard-coding the URI into the referencing application. Control of the ResourceDictionary and its URI is now in the domain of the dll, and can be referenced in a (kinda) static fashion using the dedicated class.

Related

WPF, Simple Injector and MaterialDesignThemes Static resource invalid

I have sample app written in WPF and using Simple Injector and Material Design Themes.
This is my program file:
private static Container Bootstrap()
{
// Create the container as usual.
var container = new Container();
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
// Register your types, for instance:
container.Register<IFreewayReviewCreatorDbContext, FreewayReviewCreatorDbContext>(Lifestyle.Scoped);
container.Register<IUnitOfWorkFactory, UnitOfWorkFactory>(Lifestyle.Transient);
container.Register<IUnitOfWork, UnitOfWork>(Lifestyle.Scoped);
container.Register<IReviewBodyBLL, ReviewBodyBLL>(Lifestyle.Transient);
// Register your windows and view models:
container.Register<MainWindow>();
container.Register<MainWindowViewModel>();
container.Verify();
return container;
}
private static void RunApplication(Container container)
{
try
{
var app = new App();
//app.InitializeComponent();
var mainWindow = container.GetInstance<MainWindow>();
app.Run(mainWindow);
}
catch (Exception ex)
{
//Log the exception and exit
}
}
In the code above view models add registered in Simple Injector.
Now in MainWindow I want to use StaticResource from Material Design. This is my code:
<Window x:Class="FreewayReviewCreator.MainWindow"
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"
xmlns:local="clr-namespace:FreewayReviewCreator"
xmlns:localvm="clr-namespace:FreewayReviewCreator.ViewModel"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
mc:Ignorable="d" Loaded="MainWindow_OnLoaded"
Title="MainWindow" Height="450" Width="800">
<Grid>
<StackPanel HorizontalAlignment = "Left">
<TextBox
Name="tbxPassword"
Text="{Binding Password, Mode = TwoWay}"
HorizontalContentAlignment="Center"
Style="{StaticResource MaterialDesignFloatingHintTextBox}"
MaxLength="28"
materialDesign:HintAssist.Hint="Enter your username"
/>
Error is in this line: Style="{StaticResource MaterialDesignFloatingHintTextBox}":
System.Windows.Markup.XamlParseException: ''Provide value on 'System.Windows.StaticResourceExtension' threw an exception.' Line number '44' and line position '21'.' Exception: Cannot find resource named 'MaterialDesignFloatingHintTextBox'. Resource names are case sensitive.
On this webpage is sample application with StaticResource (I took code from this app):
https://www.c-sharpcorner.com/article/wpf-application-with-googles-material-design/
and it works. The only one difference that I can see is that my application has Simple Injector and app from sample doesn't have.
References are the same in both of apps:
You should install the MaterialDesignThemes NuGet package and add the following resource dictionaries to your App.xaml file as described in the docs:
<?xml version="1.0" encoding="UTF-8"?>
<Application . . .>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Light.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignThemes.Wpf;component/Themes/MaterialDesignTheme.Defaults.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Primary/MaterialDesignColor.DeepPurple.xaml" />
<ResourceDictionary Source="pack://application:,,,/MaterialDesignColors;component/Themes/Recommended/Accent/MaterialDesignColor.Lime.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
This has nothing to do with simple injector or whatever IoC container you are using.
You need to import the resources into your app to be able to use them.
Assuming you provide the ResourceDictionaries to the App class as given in the answer of #mm8, you should load and apply the ResourceDictionaries by calling InitializeComponent() in the constructor of the App class.
Like this:
public partial class App : Application
{
public App()
{
this.InitializeComponent();
}
}
I see in your question that commented this line out. This is probably the result of following the provided startup code from Simple Injector documentation and after this adding the Material Design Themes.
This code is however necessary when you add MergedDictionaries to you App.xaml. So you need to add it back.

Use Resource Dictionary Styles in my class library project without merging dictionaries

I am creating a class library project that will contain WPF user controls. My Requirement is that all controls have the same style. My project looks like:
Things I have done in order to solve this problem:
Added all references needed by a WPF application System.Xaml, WindowsBase, etc.. so that I can have wpf controls in my class library project.
In AssemblyInfo.cs I have added:
[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
Added ResourceDictionary1.xaml To the project adding the style.
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<SolidColorBrush x:Key="Brush1" Color="#FF19199E"/>
</ResourceDictionary>
Now if I want to use a style on my UserControl1.xaml I do:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ResourceDictionary1.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid >
<Rectangle Fill="{StaticResource Brush1}" />
</Grid>
I know it works great but here is the catch. Every time I create a new userControl I will have to merge the dictionary. On a regular WPF application I could just merge the dictionary once on App.xaml and I will be able to use that dictionary on my entire application. How can I avoid having to merge the dictionary every time I create a new userControl? If I plan on addying a new resource dictionary I will have to go to all userControls and merge another dictionary. Perhaps I wrote the question title incorrectly and my question should have been how can I add a App.xaml file to a class library project
You should replace the source value ResourceDictionary1.xaml like the follow:
Source="pack://application:,,,/ControlsDLL;component/ResourceDictionary1.xaml">
or just simple as following:
<UserControl.Resources>
<ResourceDictionary Source="pack://application:,,,/ControlsDLL;component/ResourceDictionary1.xaml"></ResourceDictionary>
</UserControl.Resources>

Define a string as a static resource

IS there a way to define a constant string to be used as a static resource across the whole application?
I am running a Wpf application but there is no main xaml form. The application is a collection of xaml controls handled by a single classic .cs form.
You can define it as an application resource:
<Application x:Class="xxxxxx"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:clr="clr-namespace:System;assembly=mscorlib"
StartupUri="MainWindow.xaml">
<Application.Resources>
<clr:String x:Key="MyConstString">My string</clr:String>
</Application.Resources>
</Application>
Supplemantary to the answer by #FelicePollano - for code indentation to work I put this as a separate 'answer'.
If you happen to have your original constant defined in a .cs-file you can avoid duplicating its value in <Application.Resources> by this:
<x:Static x:Key="MyConstString" Member="local:Constants.MyString" />
For the reference local above to work you need to include the namespace xmlns:local="clr-namespace:Utils" in the tag <Application>.
The cs-class could then look like this:
namespace Utils
{
public class Constants
{
public const string MyString = "My string";
}
}
An example on usage in the xaml-code could then be:
<TextBlock Text="{StaticResource MyConstString}" />
Just add a resource dictionary XAML file, let's say it's named Dictionary.xaml (Visual Studio can create you one automatically)
Then, add your static resource in this dictionary.
To finish, reference the dictionary in all your XAML controls:
<UserControl.Resources>
<ResourceDictionary Source="Dictionary.xaml"/>
</UserControl.Resources>
You can use like this:
First, sample constant variable:
namespace Constants
{
public class ControlNames
{
public const string WrapperGridName = "WrapperGrid";
}
}
And second XAML using:
<TextBlock Text="{x:Static Member=Constants:ControlNames.WrapperGridName}"

Error when adding code behind for Silverlight resource dictionary: AG_E_PARSER_BAD_TYPE

It should be possible to add a code behind file for a resource dictionary in Silverlight, but I keep getting the same error, thrown from the InitializeComponent method of my App.xaml constructor: XamlParseException: AG_E_PARSER_BAD_TYPE.
The resource dictionary xaml file looks like this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Celerior.Annapurna.SL.ProvisiorResourceDictionary"
x:ClassModifier="public">
...
</ResourceDictionary>
If I remove the x:Class attribute everything works fine again (of course, I double-checked the class name and it's correct). My App.xaml file isn't really exciting and just contains a reference to the resource dictionary:
<Application xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Celerior.Annapurna.SL.App">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ProvisiorResourceDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
What am I doing wrong?
Kind regards,
Ronald Wildenberg
Silverlight does not support the x:ClassModifier thats only supported in WPF.
In addition x:Class isn't valid in a Resource dictionary. Certainly when trying to include the Xaml from the resource dictionary as a merged dictionary Silverlight wouldn't know what to do with the x:Class at that point.
Actually the above isn't strictly true x:Class is valid but the way you are including the dictionary in the application dictionary needs tweaking. Let me first just state that there is the assumption here that you actually need to sub-class ResourceDictionary (if not just drop the x:Class).
I'm also going to go out on a limb based on your inclusion of x:ClassModifier that you actually don't have a ProvisiorResourceDictionary.xaml.cs file in your project. Since SL always creates a public partial you need this file to contain at least:-
public partial class ProvisiorResourceDictionary
{
public ProvisiorResourceDictionary()
{
InitializeComponent();
}
}
That said if don't have something like this already then you may as well just drop x:Class altogether.
Now your app.xaml needs to look like this:-
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<common:ProvisiorResourceDictionary />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Instead of trying to import the XAML file as resource via the Source property you now include an instance of the specialised ResourceDictionary.
Is the ProvisiorResourceDictionary class public? If not, maybe you need to specify the x:ClassModifier attribute as well.

Getting a ComponentResourceKey to Work?

I am building a WPF app with several assemblies, and I want to share a resource dictionary among them. That requires a ComponentResourceKey. I have built a small demo to test out the CRK, and I can't seem to get it working.
My demo has two projects, a WPF project called Demo, and a DLL called Common. The Common project has a folder called Themes. It contains my resource dictionary, generic.xaml. Here is the text of the Resource dictionary:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Common" >
<SolidColorBrush
x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:SharedResources}, ResourceId=RedSolidBrush}"
Color="Red"/>
</ResourceDictionary>
Common also contains a class called SharedResources.cs. It contains a property for referencing the Brush resource in the dictionary:
public static ComponentResourceKey RedSolidBrush
{
get { return new ComponentResourceKey(typeof (SharedResources), "RedSolidBrush"); }
}
Finally, the main window in my Demo project references the brush resource to fill a rectangle:
<Window x:Class="ComponentResourceKeyDemo.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:res="clr-namespace:Common;assembly=Common"
Title="Window1" Height="300" Width="300">
<Grid>
<Rectangle Height="100" Width="100" Stroke="Black" Fill="{DynamicResource {ComponentResourceKey TypeInTargetAssembly={x:Type res:SharedResources}, ResourceId=RedSolidBrush}}" />
</Grid>
</Window>
I can't find the reason it's not working. It compiles fine in VS 2008 and Blend, but the resource isn't invoked. The only clue I have is an error message in Blend:
The Resource "{ComponentResourceKey ResourceId=RedSolidBrush, TypeInTargetAssembly={x:Type res:SharedResources}}" could not be resolved.
Any idea why this isn't working? Thanks for your help.
I found my problem. I was confusing the Component Resource Key with the Resource ID inside the resource dictionary. In other words, my Component Resource Key was the same as the Resource ID. I changed my static property to this:
public static ComponentResourceKey RedBrushKey
{
get {return new ComponentResourceKey(typeof(SharedResources), "RedSolidBrush"); }
}
The property name is now RedBrushKey, instead of RedSolidBrush. And the key is now working.

Resources