WPF Custom Control Template Not Applied - wpf

I'm sure this question or derivatives of it have been asked a bazillion times, but I couldn't find anything that helped me solve the problem, so I'm asking. Please feel free to direct me to the duplicate that I'm sure exists but I can't find. Apparently I'm not so great with keywords.
I have a Custom Control, it has it's own Resource Dictionary used only to define the control template. This dictionary is then merged into Generic.xaml.
The problem is that when this control shows up in the UI, it has nothing inside of it. I used Snoop to find this out. The control is in the UI, but it is completely empty.
Below you'll find the items that I think are responsible for the problem. Any help or advice you can offer is greatly appreciated.
The relevant parts of my folder structure are like this:
BasicTemplate.xaml:
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WPFSpecBuilder.Layouts.Templates">
<Style TargetType="{x:Type local:BasicTemplate}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:BasicTemplate}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<TextBlock Text="This is a basic template." />
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Generic.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Layouts/Templates/XAML/BasicTemplate.xaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>

Try this:
Set Build Action to BasicTemplate.xaml to Page.
Add reference to BasicTemplate.xaml in Generic.xaml:
ResourceDictionary Source="/WPDSpecBuilder;component/Layouts/Templates/Xaml/BasicTemplate.xaml"
It should works.

I think this may be as simple as changing the relative path of the merged dictionary. Try adding a / to the start of the folder path:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/Layouts/Templates/XAML/BasicTemplate.xaml" />
</ResourceDictionary.MergedDictionaries>

Try:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/WPDSpecBuilder;component/Layouts/Templates/XAML/BasicTemplate.xaml" />
</ResourceDictionary.MergedDictionaries>
See here for more details on Pack Uri's

Related

Wpf ResourceDictionary for Style and Brushes and how to MergedDictionaries

I hope someone can clarify the situation I have below.
I have a simple CustomControl style based on a Button in a ResourceDictionary named SHButtonStyle.xaml
<Style TargetType="{x:Type local:SHButton}">
<Setter Property="Background" Value="{StaticResource ResourceKey= Background}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:SHButton}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I also have a ResourceDictionary named Brushes as below:
<SolidColorBrush x:Key="Background" Color="Red"/>
I also have a Themes folder with Generic.xaml which has the MergedDictionaries as below:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/TestCustomControl;component/SHButtonStyle.xaml"/>
</ResourceDictionary.MergedDictionaries>
I have tried merging the ResourceDictionary for the Brushes within the Generic.xaml as I understood it is best to merge all ResourceDictionary's within Generic.xaml?
But the only way I can get this to work is with an additional MergedDictionaries for the Brushes within the SHButtonStyle.xaml. Is this correct or what am I missing when merging the ResourceDictionary's within the Generic.xaml.
Thank you in advance for your assistance
Either merge the brushes resource dictionary in theme/generic.xaml directly, or merge them both at global level in App.xaml:
<Application x:Class="WpfApp1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="MainWindow.xaml">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/TestCustomControl;component/Brushes.xaml"/>
<ResourceDictionary Source="/TestCustomControl;component/themes/generic.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>
The MergedDictionaries of themes/generic.xaml usually contains "standalone" resource dictionaries for custom controls without any dependencies to resources in other resource dictionaries.

How to build a custom component based on MaterialDesign?

I'm trying to understand how to build a custom component based on material design, in order to understand how exactly the procedure to achieve that is working I thought to build a simple button that includes text and an icon (remember is just for exercise), so I tried to write both a UserControl and a ResurceDictionary, but so far no luck. My question is, how can I build a custom button based on material design? I want it to maintain all effects and shadows that are shipped with material design. I'll post also what I have in terms of ResurceDictionary.
ResourceDictionary
<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"
xmlns:local="clr-namespace:KESS3Mockup">
<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.MergedDictionaries>
<Style TargetType="{x:Type local:VerticalButton}" BasedOn="{StaticResource ResourceKey={x:Type Button}}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:VerticalButton}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
CornerRadius="2"
BorderThickness="{TemplateBinding BorderThickness}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="0.5*"/>
<RowDefinition Height="8*"/>
<RowDefinition Height="8*"/>
<RowDefinition Height="0.5*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="20*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Viewbox HorizontalAlignment="Stretch" Grid.Column="1" Grid.Row="1">
<materialDesign:PackIcon Kind="{TemplateBinding Kind}" Foreground="White" />
</Viewbox>
<Viewbox Grid.Column="1" Grid.Row="2">
<TextBox Text="{TemplateBinding Text}" Foreground="White" SelectionBrush="#000078D7" BorderBrush="#00000000" Focusable="False"/>
</Viewbox>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
You can base a new style on an existing style using BasedOn.
<Style TargetType="{x:Type local:VerticalButton}" BasedOn="{StaticResource {x:Type Button}}">
Instead of the type, you can also refer to a different, specific style.
<Style TargetType="{x:Type local:VerticalButton}" BasedOn="{StaticResource MaterialDesignFlatButton}"/>
However, for this to work, the target types must be compatible (either the same type or a base type, e.g. ButtonBase for a Button). You cannot simply create a UserControl and create a style based on a style for a Button. In order to customize a Button, you would have to create a custom control VerticalButton that inherits from Button, which implements the dependency properties and specifics you want.
public class VerticalButton : Button
{
static VerticalButton()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(VerticalButton), new FrameworkPropertyMetadata(typeof(VerticalButton)));
}
// ...your custom code.
}
Next you would create a Generic.xaml file in the Themes folder in your project (this path and file are named by convention). There you define the default style, which can be based on Material Design.
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:YourProject">
<Style TargetType="{x:Type local:VerticalButton}" BasedOn="{StaticResource {x:Type Button}}">
<!-- ...your style definitions. -->
</Style>
</ResourceDictionary>
This resource dicionary has to be included after the Material Design theme dictionaries in App.xaml. For more information on how to override default Material Design themes, you can refer to the Wiki.
<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="Themes/Generic.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
Please not that although you can base a style on another style, you cannot base one control template on another. That means, if you assign a ControlTemplate in your style it will override the one of the base style. Therefore, if you want to adapt the control template while preserving most of its default visuals and effects, you have to copy it from the Material Design GitHub repository and adapt it to your needs.
For more information on developing custom controls in general see Control authoring overview. Material Design uses the established concepts for themes, styles and templates, so it does not really differ from styling standard WPF controls or building custom controls.
For simple cases of customizing the content of the button, it might be an easier alternative to create a DataTemplate that you assign as a ContentTemplate of the Button, see Data Templating Overview.
When you set the control template, it overrides all the visual styles. It merely carries only the dependency properties. So, it will not carry any shadow or other effects (at least to your new style). however, if such property exists in the VerticalButton class (that you are targeting), then you can reuse such properties and define your own style.

Type converter in WPF Custom Control

I am creating a custom control in WPF and would like to have my converters in a separate resource dictionary to make things cleaner. I have a ControlStyling.xaml resource dictionary for the styling of my controls
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DialIndicatorControl">
<Style TargetType="{x:Type local:MyDialIndicator}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:MyDialIndicator}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}">
<Ellipse Width="{TemplateBinding BackgroundSizeRadius}"
</Ellipse>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
A Converters.xaml resource dictionary where I would like to keep my converteres
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DialIndicatorControl">
<local:RadiusDiameterConverter x:Key="RadiusConvert"/>
And the Themes/Generic.xaml where I am pointing to both of these dictionaries.
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:DialIndicatorControl">
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/DialIndicatorControl;component/Themes/Generic/Converters.xaml"/>
<ResourceDictionary Source="/DialIndicatorControl;component/Themes/Generic/ControlStyling.xaml"/>
</ResourceDictionary.MergedDictionaries>
The problem I am having is when I place my converters in a separate resource dictionary my ControlStyling.xaml dictionary doesn't have a reference to the converters (which seems obvious now that I think about it). I thought that I would be able to use these converters since I merged both dictionaries in my Themes/Generic.xaml dictionary but that didn't work. Is there a good way to have all my converters in a separate dictionary and still be able to reference them in my ControlStyling.xaml for this custom control?
Reference the converter resource dictionary from the styling dictionary:
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="/DialIndicatorControl;component/Themes/Generic/Converters.xaml"/>
</ResourceDictionary.MergedDictionaries>

My Two DataGrid XAML Resources are Mutually Exclusive --- Why?

In order to have a nicer look, I am trying to add to my DataGrid 2 features that I found in separate places, but for some reason they just can't get along.
I can either have the section inside the blue rectangle or the one in the red one.
TIA
Put the <ControlTemplate> tag inside the <ResourceDictionary> tag, underneath the closing </ResourceDictionary.MergedDictionaries> tag.
<UserControl.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Themes\DataGrid.Generic.xaml" />
</ResourceDictionary.MergedDictionaries>
<ControlTemplate x:Key="SelectAllButtonTemplate" TargetType="{x:Type Button}">
<Grid>
<Rectangle x:Name="Border" Fill="LightBlue" SnapsToDevicePixels="True" />
</Grid>
</ControlTemplate>
</ResourceDictionary>
</UserControl.Resources>

Strange Style Behaviour in wpf?

Ok, I was programming an app that loaded merged dictionaries on runtime to change appearance and behaviour when I got stuck : some of the controls on my forms just weren't reacting to the styles I thought they had to react to.
I have tried to simplify the problem as much as I could and came up with something so simple that I'm afraid I am overlooking the bleeding obvious, but anyway here it goes :
<Window x:Class="Example.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="100" Width="50">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary>
<Style TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="Green"></Setter>
</Style>
</ResourceDictionary>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<StackPanel>
<TextBox Text="1" Name="box1"/>
<TextBox Text="2" Name="box2"/>
<TextBox Text="3" Name="box3"/>
</StackPanel>
</Window>
The question is : why is the first textbox not green?
==> that is, the designer shows it in green, but when running the app, it is no longer...
I know the solotion to this particular problem is to remove the merged dicitonary tags, but I need to know how to solve this using merged dictionaries.
Thanks!
MergedDictionaries have always been quirky, you can set any resources you want in them, but they only process outside resource dictionary references at runtime.
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="StylesDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
The only requirement is that the resources be set in an other dictionary.
Try this...
<Style TargetType="{x:Type TextBox}">
I'm not sure if this will work, but when I was having styling problems, I fixed them by using
TargetType="{x:Type TextBox}"

Resources