Using one class library (and theme) as basis to another class library - wpf

Still learning this stuff on WPF, themes, derivations, etc.
I understand basics on the "Themes\Generic.xaml", and then setting your application's app.xaml file to include the resource dictionary pointing to the theme in question.
So, thats fine from an application project perspective. Now, how about from a class library/dll file. I have a DLL project which I want to use as the basis of all controls in my project. In that, I have the Themes/Generic.xaml and have it coded up with some basics to confirm visual design implementation (originally confirmed ok, when tested under an App/exe project).
Now, I want this theme at a level BEFORE the actual application. Again, this is the baseline. Now, I add a second library of custom grouped controls (for example, a user control for address information... multiple address lines, city, state, zip, labels, etc). I want this second library to reference the first library with the themes, so I can see visually at design time what it will look like (alignments, colors, fonts, etc).
What / where should I be looking to let one DLL know about the merge dictionaries that are the basis in the first DLL. Hope this makes sense.
-- EDIT -- for clarification
First Class Library... "MyThemeLibrary" compiles into a .dll
In this dll is path/file of "/Themes/MyTheme.xaml"
As suggested by first answer, if I have a Resource Dictionary in the first library, I can reference it in anything else that I will derive from it. So, I have
<ResourceDictionary x:Name="MyGenericTheme"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Themes/MyTheme.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
Second Class Library... "SecondLevel" compiles into a .dll
In this, I have a user control that I want to put a grid for columns/rows, labels and textbox controls into. I want the controls to respect the colors, fonts, sizes, alignments as defined in the "MyTheme.xaml" of the first dll.
<UserControl x:Class="SecondLevel.multipleControlContainer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Label Content="Something"
Grid.Row="0" Grid.Column="0" />
<TextBox Text="Testing" Grid.Row="1" Grid.Column="1" />
</Grid>
</UserControl>
So, how / what should I do the necessary reference, declaration, inclusion of resource dictionary from the first library into the second.

make a reference to your dll and if you know where your theme is located you can do this one
<Application x:Class="My.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Common base theme -->
<ResourceDictionary Source="pack://application:,,,/Your.Base.Dll;component/YourResDictionary/YourTheme.xaml" />
<!-- here comes your custom theme -->
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
do this in the App.xaml
EDIT after clarification (look at the comments)
<UserControl x:Class="SecondLevel.multipleControlContainer"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!-- Common base theme -->
<ResourceDictionary Source="pack://application:,,,/Your.Base.Dll;component/YourResDictionaryFolder/MyGenericTheme.xaml" />
<!-- Custom theme -->
<ResourceDictionary Source="pack://application:,,,/Another.Dll;component/AnotherResDictionaryFolder/MyCustomTheme.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<Grid>
<!-- all controls in this usercontrol respect the styles in MyGenericTheme.xaml"
if you use implicite styles-->
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<Label Content="Something"
Grid.Row="0"
Grid.Column="0" />
<TextBox Text="Testing"
Grid.Row="1"
Grid.Column="1" />
<!-- if you use explicit styles then you must do this -->
<TextBox Style="{StaticResource myTextBoxStyle}"
Text="Testing"
Grid.Row="1"
Grid.Column="1" />
</Grid>
</UserControl>

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>

Embedding one WPF app into another - Resource file not working

I have a requirement to embed one of our existing applications in our new one. I figured it would be quite easy, so I set made all the content of the origional application inside a UserControl "EmbeddedApp", with the theme applied in it's resources:
<UserControl x:Class="App.GUI.Window.EmbeddedApp"
...>
<UserControl.Resources>
<ResourceDictionary Source="/App;component/GUI/Themes/Theme.xaml" />
</UserControl.Resources>
...all the app stuff...
</UserControl>
And the window for this as a stand alone app then just looked like this:
<Controls:MetroWindow x:Class="App.MainWindow"
...>
<Window:EmbeddedApp/>
</Controls:MetroWindow>
Nice and easy. I tested the "embedding" function in a test application in that project and it works fine.
However, I'm getting resource issues when I embed it into a "full" application like this. There's an exception "Exception: Cannot find resource named 'AccentSelectedColorBrush'. Resource names are case sensitive." on line:
<Rectangle Name ="container" Grid.Row="0" Height="50" Fill="Black" Stroke="{StaticResource AccentSelectedColorBrush}"/>
in the depths of the embedded App. How could this be happening if this is defined in a static resource in Theme.xaml? Why would it work fine in an "empty" application?
I tried adding the theme manually:
Now, Here AccentSelectedColorBrush is found for the rectangle. However, I'm still getting the error AccentSelectedColorBrush not found within EmbeddedApp, it's also White - which isn't as it's defined in Theme.xaml.I can inspect it and the rectangle is resolving it fine:
If I remove the Theme.xaml, AccentSelectedColorBrush is not found.
Edit: I now have this:
<UserControl x:Class="MyNewApp.Pages.Views.EmbeddedAppView"
InheritanceBehavior="SkipAllNow"
...>
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/App;component/GUI/Themes/Theme.xaml" />
<!--<ResourceDictionary Source="pack://application:,,,/App;component/GUI/Themes/Theme2.xaml" />-->
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</UserControl.Resources>
<StackPanel Height="447" Width="539">
<Rectangle Width="100" Height="100" Fill="{StaticResource AccentSelectedColorBrush}"/>
<!--<App:EmbeddedApp Width="300" Height="300"/>-->
<Label Content="Texty Text"/>
<TextBox Text="Some Text"/>
<CheckBox IsChecked="True" Content="Test"/>
</StackPanel>
</UserControl>
Now, I'd expect the label and TextBox to be styled acording to Theme.xaml, but it's not doing that it's just using the default themes:
Yet in my "test" project, the Theme.xaml is applied fine:
So my conclusion is that:
<ResourceDictionary Source="/App;component/GUI/Themes/Theme.xaml" />
is not correct for an externally referenced project. I tried:
<ResourceDictionary Source="pack://application:,,,/App;component/GUI/Themes/Theme.xaml" />
but that has similar failure. It's definately finding it though, if I misspell and say put "Theme2.xaml" it comes up with an error. But for some reason it's not being applied properly.
Edit2 I've tracked down the problem to how resources are referenced between user controls. I've decided to create a new question that focuses on the actual problem at hand:
"Sub" UserControl cannot find resource - how to get resources to "flow" down the visual tree (without placing it in App resources)

How to use style defined in another xaml file inside a style

I had MultiSelComboBoxStyle defined in a xaml resource file, and in another xaml resource file, I have
<Style x:Key="DataGridDemoStyle" TargetType="{x:Type DataGrid}">
....
<ComboBox x:Name="ccBox" Grid.Row="0" Grid.Column="1" Width="9" Height="18" VerticalAlignment="Top" Margin="0" Style="{StaticResource MultiSelComboBoxStyle}" Panel.ZIndex="1"/>
....
</Style>
I put every xaml in App.xaml and the compiler did find this MultiSelComboBoxStyle style , but debugging the program says MultiSelComboBoxStyle was not found. In App.xaml
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="DataGridCustomizations.xaml" />
<ResourceDictionary Source="MultiSelComboBoxStyle.xaml"></ResourceDictionary>
It turns out I need to do
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="MultiSelComboBoxStyle.xaml"></ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
If needs to use styles from different resource files.

Reference custom resource defined in another xaml file

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

How can I reference a resource in a separate ResourceDictionary in WPF?

In my project (ABC), I have MainWindow.xaml where I have defined a toolbar for it and this toolbar is defined in
project under Resource/Views/ApplicationToolbar.xmal as following:
I have referenced it in my MainWindow.xaml as xmlns:local="clr-namespace:ABC" and as soon as I run it I get an
error indicating View123 not found.
More information: (EDIT)
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ABC">
<StackPanel x:Key="View1">
<Button Margin="3" Content="Test1"></Button>
<Button Margin="3" Content="Test2"></Button>
</StackPanel>
</ResourceDictionary>
Now in MainWindow.xmal have:
<Window x:Class="ABC.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ABC"
Title="MainWindow" Height="350" Width="525">
<Grid Margin="10">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="10"/>
<RowDefinition Height="*"/>
<RowDefinition Height="10"/>
</Grid.RowDefinitions>
<!-- FOLLOWING LINE CAUSING ERROR-->
<ContentControl Name="Toolbar" Content="{StaticResource View1 }"></ContentControl>
</Grid>
</Window>
What am I missing?
Thanks,
Amit
Since View1 is referenced in a separate ResourceDictionary you need to merge it into the ResourceDictionary for this Window.
Try adding this to the code just inside your Window declaration:
<Window.Resources>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resource/Views/ApplicationToolbar.xaml"/>
</ResourceDictionary.MergedDictionaries>
</Window.Resources>
At this point your Window should be able to reference View1.
Note: I haven't tested this fully in an IDE, so it's possible there could be a slight syntax error or issue with the path. You may have to format your dictionary URL with a Pack URI in order for the reference to resolve correctly. Resource paths in WPF tend to be a little tricky.
Thanks to all. OK, I tried what was suggested here and I appreciate Mike and it was close to the solution. What worked for me was adding following tag in App.xaml:
<Application.Resources>
<ResourceDictionary Source="Resources/views/ApplicationToolbar.xaml"/>
</Application.Resources>
Thanks.
Amit

Resources