Defining WPF Window style in class library - wpf

Currently I am developing some WPF class library, which will have several WPF Windows and trying to create my own window ControlTemplate for these Windows to make more nice design of these windows (inspired by this article: Reusing Control Templates in Resource Dictionaries).
Problem is, then it's a WPF class library not an application assembly, where i can use app.xaml and to define my resource dictionary reference & etc...
Using code below i getting an error: StaticResource reference 'MyWindowStyle' was not found
<Window x:Class="SomeERP.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300"
Style="{StaticResource MyWindowStyle}">
<Window.Resources>
<!-- My Window Style -->
<Style x:Key="MyWindowStyle" TargetType="Window">
<Setter Property="Background" Value="Transparent" />
<Setter Property="WindowStyle" Value="None" />
<Setter Property="AllowsTransparency" Value="True" />
<Setter Property="Opacity" Value="0.95" />
<Setter Property="Template" Value="{StaticResource MyWindowTemplate}" />
</Style>
<!-- Window Template -->
<ControlTemplate x:Key="MyWindowTemplate" TargetType="{x:Type Window}">
<Grid>
</Grid>
</ControlTemplate>
</Window.Resources>
</Window>
I suspect i get this error because in my case it's not predeclared before Window declaration as in application's case in app.xaml which i don't have in class libary. I am pretty new in WPF and just starting to use WPF design possibilities.

If you need the style only once, the solution is pretty simple: Just define the style in-place
<Window.Style>
<!-- My Window Style -->
<Style TargetType="Window">
...
</Style>
</Window.Style>
However, if you need the style in more than one window, it is reasonable to define the style in a resource dictionary. Then you can integrate the resource dictionary in the window's resources and set the style accordingly:
<Window.Resources>
<!-- My Window Style -->
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml"/>
<!-- path to the resource dictionary -->
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resource>
<Window.Style>
<StaticResource ResourceKey="MyWindowStyle"/>
</Window.Style>

Looks like i've found solution to my problem in this post: Assembly-wide / root-level styles in WPF class library and according to this post What's the difference between StaticResource and DynamicResource in WPF?
A StaticResource will be resolved and assigned to the property during the loading of the XAML which occurs before the application is actually run. It will only be assigned once and any changes to resource dictionary ignored.
A DynamicResource assigns an Expression object to the property during loading but does not actually lookup the resource until runtime when the Expression object is asked for the value. This defers looking up the resource until it is needed at runtime. A good example would be a forward reference to a resource defined later on in the XAML. Another example is a resource that will not even exist until runtime. It will update the target if the source resource dictionary is changed.
That was what i needed, as i have class library, i don't have app.xaml and can't predeclare resources for Window. I just need to use DynamicResource rather than StaticResource to get it working.
Thanks for attention :)

Just add the external assembly:
<Application.Resources>
<ResourceDictionary>
Source="/MyAssemblyName;component/MyResources.xaml"
</ResourceDictionary>
</Application.Resources>

Related

XAML designer with UserControls in DLL doesn't resolve resource

I have a solution in Visual Studio 2019, made of some projects built as DLL using WPF .NET 5.
Now I need to create some UserControls inside one of this projects, that I will load in a future WPF .NET 5 application, but the problem is that the XAML designer is not able to preview the UserControl with StaticResource that are in an external ResourceDictionary.
I simplify the code:
<UserControl.Resources>
<Style x:Key="sButton" TargetType="Button">
<Setter Property="BorderBrush" Value="{StaticResource color_UC_BorderBrush}"/>
</Style>
</UserControl.Resources>
Having only DLL I don't have an Application with shared ResourceDictionary and color_UC_BorderBrush could not be resolved for preview.
In the following way instead the XAML designer works properly, but I don't want that every single UserControl loads his own ResourceDictionary everytime:
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/MyLib;component/ResourceDictionaries/myRD.xaml"/>
</ResourceDictionary.MergedDictionaries>
<Style x:Key="sButton" TargetType="Button">
<Setter Property="BorderBrush" Value="{StaticResource color_UC_BorderBrush}"/>
</Style>
</ResourceDictionary>
</UserControl.Resources>
In my future WPF .NET 5 application I will use shared ResourceDictionary but for now how can I manage to let the XAML designer works properly inside a DLL or how can I make the MergedDictionary defined in the XAML ignorable?
Maybe there are other ways I don't have in mind. Thank you.
You can't omit merging the resource into a ResourceDictionary within the scope of the control that depends on it.
But instead of merging the same resource into each control's ResourceDictionary, you can choose a ResourceDictionary of a common parent. The XAML resource lookup strategy will traverse the logical tree to search for the required resource, starting at the local ResourceDictionary of the control and ending at the root, before checking App.xaml and Generic.xaml.
In case of the UserControl, you would have to use the DynamicResource markup extension. This is, because the content will only be part of of the logical tree during runtime. In design time, the root from the user control's content point of view is the containing UserControl.
You should know that DynamicResource has a performance penalty. So you typically would try to avoid it.
Also defining the resource in each control's ResourceDictionary will introduce a performance penalty.
The best solution is to define the resource in the application's root control like Window.Resources or even better in the ResourceDictionary inside App.xaml.
Using App.xaml to host the merged ResourceDictionary would also allow to use the cheaper StaticResource from inside the UserControl to lookup the merged resources.
Please read Static and dynamic resources to learn more about the lookup behavior of the XAML engine.
The following example chooses the MainWindow as the host for the merged resources. It is the root of all it's contained elements and therefore reachable from any location in its logical tree.
MainWindow.xaml
Make the resource available for any element that is a child of MainWindow:
<Window>
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- The resource dictionary that contains the shared 'color_UC_BorderBrush' resource -->
<ResourceDictionary Source="pack://application:,,,/MyLib;component/ResourceDictionaries/myRD.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<MyUserControl />
</Window>
MyUserControl.xaml
<UserControl>
<UserControl.Resources>
<Style x:Key="sButton" TargetType="Button">
<Setter Property="BorderBrush"
Value="{DynamicResource color_UC_BorderBrush}" />
</Style>
</UserControl.Resources>
<Button Style="{StaticResource sButton}" />
</UserControl>

Using Resource Dictionaries in a Class Library

I'm using this (excellent) flowchart diagram designer in my application, but I'd like to use it as a UserControl.
To convert the Application into a UserControl I've changed the only window the application had:
<Window x:Class="DiagramDesigner.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:DiagramDesigner"
xmlns:c="clr-namespace:DiagramDesigner.Controls"
WindowStartupLocation="CenterScreen"
Title="Diagram Designer"
Height="850" Width="1000">
<Window.Resources>
<ContextMenu x:Key="DesignerCanvasContextMenu">
...
</ContextMenu>
</Window.Resources>
...
</Window>
into an user control:
<UserControl x:Class="DiagramDesigner.DiagramDesignerWPFControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:DiagramDesigner"
xmlns:c="clr-namespace:DiagramDesigner.Controls"
Height="850" Width="1000">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
...
</ResourceDictionary.MergedDictionaries>
<ContextMenu x:Key="DesignerCanvasContextMenu">
...
</ContextMenu>
</ResourceDictionary>
</UserControl.Resources>
...
</UserControl>
I took the ResourceDicctionary from the contents of App.xaml and added it to the control. Then I deleted the App.xaml file, as it can't be used in Class Library compilations.
My problem is:
When I add that new User Control to a WPF Form in another project, I can run the new application, I can add chart components and move them, but when I join/link the following exception raises:
Cannot find resource named '{SolidBorderBrush}'. Resource names are case sensitive.
What am I doing wrong with the resources or their location in my User Control?
Edition after accepting the answer:
The risen exception also pointed to a line where the '{SolidBorderBrush}' was called. I didn't initially put it in this question, as it was a call and not a declaration. This is the piece of code which linked the exception:
<Trigger Property="IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background" Value="{DynamicResource ToolbarSelectedBackgroundBrush}" />
<Setter TargetName="Border" Property="BorderBrush" Value="{DynamicResource SolidBorderBrush}" />
</Trigger>
Im guessing here, because your question doesn't actually show any of the code which seems to be causing the issue, but you probably need to use DynamicResource.
{DynamicResource SolidBorderBrush}
You can only use StaticResource under very particular circumstances. You get a big performance boost most of the time, but it's very easy to end up in a situation where it can't be used (which may be what has happened).

DynamicResource Cannot be resolved

I have been trying to learn about resources and styles, I want to create a chromeless window.
I have an example that acheives what I want via the following simple extracts of xaml.
I have a Resource set in Themes/Generic.xaml
<Style x:Key="BorderlessWindowStyle" TargetType="{x:Type Window}">
<Setter Property="AllowsTransparency" Value="true" />
<Setter Property="WindowStyle" Value="None" />
<Setter Property="ResizeMode" Value="CanResizeWithGrip" />
<Setter Property="Background" Value="Transparent" />
</Style>
I have a main window:
<Window x:Class="Project1.Shell"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Project1"
x:Name="Window"
Title="Shell" Height="576" Width="1024" Style="{DynamicResource BorderlessWindowStyle}">
<Grid></Grid>
But the style is not being applied and VS designer states it cannot resolve the resource.
The example I have been looking at does things this way and I cannot discover the difference between what I have seen done and what I am trying to do.
I thought that Genric.xaml was a 'special' resource dictionary that should be discoverable by my Window control - and I am guessing this assumption is my error.
What do I need to do to make this work? (Now I understand I can set these properties in the Window xaml directly, and I have done so and get the effect I want. BUT I really want to undersatnd using the Generic.xaml resource dictionary way as I have presented here)
best regards
John.
Themes/generic.xaml file is automatically used to find default styles for Custom Controls. In your case you have an ordinary Window with custom style. You cannot define this style in Window.Resources section, because the style should be defined at a higher level. The only higher level of Window is App.xaml, because the Window is in fact its child. That's why the solution for your question is to place the style into App.Resources section.
Thought I would add the following example in case it helps some others out. To add a resource dictionary to your app.xaml file you can add the following xaml code to the app.xaml file.
<Application x:Class="ProjectX.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ProjectX"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Use the Black skin by default -->
<ResourceDictionary Source="Resources\ResourceFile.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Where 'Resources' coudl be a folder in your project that contains the Resource Dictionary file (ResourceFile.xaml).
The you can add code to your resource dictionary like such:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ProjectX.Resources">
<!-- The Background Brush is used as the background for the Main Window -->
<SolidColorBrush x:Key="MainBackgroundBrush" Color="#FF202020" />
</ResourceDictionary>
And then finally, dynamically bind to your resource dictionary doing something like:
<Window
x:Class="ProjectX.MainWindow"
Title="Family.Show" Height="728" Width="960"
Background="{DynamicResource MainBackgroundBrush}"
ResizeMode="CanResizeWithGrip">
</Window>

DataTemplates in resource dictionaries don't inherit from app.xaml styles?

I added custom named styles to the app.xaml.
I created an external resource dictionary (which I attach in the merged dictionaries of the app.xaml) and when I try to use one of the above named styles in the rcource dictionary, it says that there is no such style.
Also the default styles (i.e. unnamed styles that apply for the entire application) don't apply on the template elements.
Note: The Build Action of the templates is 'Page'.
Here is an example of how my code is written:
<Application x:Class="Application"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
ShutdownMode="OnExplicitShutdown">
<Application.Resources>
<ResourceDictionary>
<Style
x:Key="StackPanelStyle"
TargetType="StackPanel"
BasedOn="{StaticResource {x:Type StackPanel}}">
<Setter Property="Margin" Value="5"/>
<Setter Property="Orientation" Value="Horizontal" />
<Setter Property="Height" Value="40"/>
</Style>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Templates/DataTemplate1.xaml"/>
<ResourceDictionary Source="/Templates/DataTemplate2.xaml"/>
<ResourceDictionary Source="/Templates/DataTemplate3.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
This an example of the data template:
<DataTemplate DataType="{x:Type Entity}" x:Key="NameDataTemplate">
<Expander>
<StackPanel>
<--The following line produces: StackPanelStyle was not found.-->
<StackPanel Style="{StaticResource StackPanelStyle}">
<Label Content="Name:"/>
<TextBox Text="{Binding Name}"/>
</StackPanel>
</StackPanel>
</Expander>
</DataTemplate>
Any ideas?
Do I have to merge the dictionaries in a different way?
The code won't work well because the DataTemplate in the resource dictionary doesn't know which one is using it, it just been used. Like Hollywood mode. They compiled separately.
To make this work, you can put your style which in app.xaml in the same resource dictionary of the DataTemplate or if you don't like this coupling, you can put it in a different resource dictionary, and merge it into the DataTemplate's resource dictionary.
The Build Action for your App.xaml should be ApplicationDefinition, and the build action for your Resource Dictionary files should be Page. I'm guessing you have both of those correct because they are default (and I see that you mentioned about Page already).
I can't think of any other problem with your situation. As long as your static resources are defined in the correct order, which they appear to be, it should be able to find them when you run your application.
Edit
Debugging idea: Create a fresh resource dictionary called "TestDictionary.xaml" with a simple button style. Make sure this dictionary is in the same folder as your other dictionaries (DataTemplate1.xaml, etc.). Put a link to only TestDictionary in MergedDictionaries (comment out the others). Put a button on your startup window and apply the style. See if just that works. If it fails, you know you have an issue with your merge. If it succeeds, something about your DataTemplate might be the problem.

Themable User Controls in WPF

How can I create a UserControl in WPF that has a basic default style but can also easily themed when needed?
Do you have some good guidelines, blog entries or example that explain this specific topic?
Thank you in advance,
Marco
In WPF themes are simply a set of XAML files each containing a ResourceDictionary which holds the Style and Template definitions that apply to the controls used in the application. A theme file could look like this:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:MyApp.UserControls">
<!-- Standard look for MyUserControl -->
<Style x:Key="Standard" TargetType="{x:Type uc:MyUserControl}">
<Setter Property="Width" Value="22" />
<Setter Property="Height" Value="10" />
</Style>
</ResourceDictionary>
Support for themes in a WPF application must be explicitly enabled by adding the following attribute to the assembly:
[assembly: ThemeInfo(
ResourceDictionary.None,
ResourceDictionaryLocation.SourceAssembly
)]
This will instruct WPF to look for an embedded resource file called themes\generic.xaml to determine the default look of the application's controls.
Note that when the theme-specific dictionaries are contained separate files than the application's assembly, style and template resources must use a composite key, which tells WPF which assembly contains the control that the style/template applies to. So the previous example should be modified to:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:uc="clr-namespace:MyApp.UserControls;assembly=MyApp">
<!-- Standard look for MyUserControl in the MyApp assembly -->
<Style x:Key="{ComponentResourceKey {x:Type uc:MyUserControl}, Standard}">
<Setter Property="Width" Value="22" />
<Setter Property="Height" Value="10" />
</Style>
</ResourceDictionary>
Look at this article: http://msdn.microsoft.com/en-us/magazine/cc135986.aspx
It talks about how to write a control that you can change with a ControlTemplate, like the built in controls.

Resources