If I have two 200 lines long control templates which differ only in a few words (a few colors), how can I make the xaml reusable ? That is, to don't have to copy-paste the template and change 3 words in 200 lines.
Here is a simplified example. The only difference between the two styles is the border color. So can I somehow define a ButtonStyle, with a parameterized color, and inherit BlackButtonStyle and GrayButtonStyle from it, and specify only that color in BlackButtonStyle and GrayButtonStyle ?
<Window x:Class="WpfApplication33.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">
<Window.Resources>
<Style x:Key="BlackButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="Black" BorderThickness="3">
<ContentControl Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="GrayButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="Gray" BorderThickness="3">
<ContentControl Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<StackPanel>
<Button Content="Black Button"
Style="{StaticResource BlackButtonStyle}"/>
<Button Content="Gray Button"
Style="{StaticResource GrayButtonStyle}"/>
</StackPanel>
</Window>
Here is the code based on the 2 answers. Only a style needs to be set on the control, but unfortunately it still messes up the Tag of the control:
<Window x:Class="WpfApplication33.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">
<Window.Resources>
<Style x:Key="ButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Name="border"
BorderBrush="Black"
BorderThickness="3">
<ContentControl Content="{TemplateBinding Content}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="Tag" Value="Gray">
<Setter TargetName="border"
Property="BorderBrush"
Value="Gray"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="BlackButtonStyle"
TargetType="{x:Type Button}"
BasedOn="{StaticResource ButtonStyle}"/>
<Style x:Key="GrayButtonStyle"
TargetType="{x:Type Button}"
BasedOn="{StaticResource ButtonStyle}">
<Setter Property="Tag" Value="Gray"/>
</Style>
</Window.Resources>
<StackPanel>
<Button Content="Black Button"
Style="{StaticResource BlackButtonStyle}"/>
<Button Content="Gray Button"
Style="{StaticResource GrayButtonStyle}"/>
</StackPanel>
</Window>
While Charlie's right about his example, for your specific case I'd just use the BorderThickness and BorderBrush properties that a button already exposes: you can use {TemplateBinding BorderBrush} instead of creating your own property.
Edit: sample xaml... note that my style defaults the color & thickness, but these could be overridden inline...
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Page.Resources>
<SolidColorBrush x:Key="BlackBrush" Color="Black"/>
<SolidColorBrush x:Key="GrayBrush" Color="Gray"/>
<Style x:Key="CustomButtonStyle" TargetType="{x:Type Button}">
<Setter Property="BorderThickness" Value="3" />
<Setter Property="BorderBrush" Value="{StaticResource BlackBrush}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderColor="{TemplateBinding BorderThickness}">
<ContentControl Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<StackPanel>
<Button Content="Black Button" BorderBrush="{StaticResource BlackBrush}"
Style="{StaticResource CustomButtonStyle}"/>
<Button Content="Gray Button" BorderBrush="{StaticResource GrayBrush}"
Style="{StaticResource CustomButtonStyle}"/>
</StackPanel>
The right way to go about this is usually to create a DependencyProperty on the class that can hold the parametrized data, and then bind to that property in your template. For the sake of creating a quick example, I'm going to use the Button.Tag property, which works perfectly well for storing something as simple as a brush:
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1" Height="300" Width="300">
<Page.Resources>
<SolidColorBrush x:Key="BlackBrush" Color="Black"/>
<SolidColorBrush x:Key="GrayBrush" Color="Gray"/>
<Style x:Key="CustomButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="{TemplateBinding Tag}" BorderThickness="3">
<ContentControl Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<StackPanel>
<Button Content="Black Button" Tag="{StaticResource BlackBrush}"
Style="{StaticResource CustomButtonStyle}"/>
<Button Content="Gray Button" Tag="{StaticResource GrayBrush}"
Style="{StaticResource CustomButtonStyle}"/>
</StackPanel>
Have a look at this solution, it solves the exact issue you're having.
Related
I am new to WPF and I created the following simple style example. But it doesn't work properly and button's content doesn't show although I can still click on it. Can anyone tell me why it is broken?
<Window.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border BorderBrush="Blue"
BorderThickness="5"
Background="Aqua"
Width="80"
Height="40">
<ContentPresenter></ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="Grid" x:Name="GridWithMarginStyle">
<Setter Property="Margin" Value="12"></Setter>
</Style>
</Window.Resources>
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type Button}}">
<EventSetter Event="Button.Click" Handler="ButtonHandler" />
<Setter Property="Background" Value="Red"></Setter>
<Setter Property="Foreground" Value="White"></Setter>
</Style>
</StackPanel.Resources>
<Button Name="OkBtn">OK</Button>
<Button Name="CancelBtn" Click="CancelBtn_Click">Cancel</Button>
</StackPanel>
You are using the BasedOn property in the correct way. The problem is that your ContentPresenter is not binded to the control it renders (i.e. the button).
Just try to replace your ControlTemplate XAML with this one:
<ControlTemplate TargetType="{x:Type Button}">
<Border BorderBrush="Blue"
BorderThickness="5"
Background="Aqua"
Width="80"
Height="40">
<ContentPresenter Content="{TemplateBinding Content}" />
</Border>
</ControlTemplate>
By using TemplateBinding you can bind the ContentPresenter to the Content property of your templated control.
I have style for Button
<Style x:Key="menu_button" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Label Content="{TemplateBinding Content}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
it must be applied to the buttons
<StackPanel>
<Button Content="Создать тест" Style="{DynamicResource menu_button}"/>
<Button Content="Тесты" Style="{DynamicResource menu_button}"/>
</StackPanel>
How to make that do not prescribe style to each button, and enter it in the StackPanel. After all, it will be very tiring
In case you can move the style and you do not use the style at another place you can do it this way:
<StackPanel>
<StackPanel.Resources>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Label Content="{TemplateBinding Content}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</StackPanel.Resources>
<Button Content="Создать тест"/>
<Button Content="Тесты"/>
</StackPanel>
Alright, so I have a window with the following resources
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<TextBlock Text="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
I am getting an error saying that, "The member "Content" is not recognized or is not accessible."
What am I doing wrong?
You will have to define TargetType on ControlTemplate
<ControlTemplate TargetType="Button">
<Grid>
<TextBlock Text="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Grid>
<TextBlock Text="{TemplateBinding Button.Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
or
<Window.Resources>
<Style TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Grid>
<TextBlock Text="{TemplateBinding Content}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I want to shape my WPF tooltip like the image below:
How do I achieve this?
Use this Code:
<Window x:Class="WpfApplication2.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:ed="http://schemas.microsoft.com/expression/2010/drawing"
x:Name="Window"
Title="MainWindow"
Width="640"
Height="480">
<Window.Resources>
<Style x:Key="{x:Type ToolTip}" TargetType="ToolTip">
<Setter Property="OverridesDefaultStyle" Value="true" />
<Setter Property="HasDropShadow" Value="True" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
<ed:Callout Name="Border"
Width="{TemplateBinding Width}"
Height="{TemplateBinding Height}"
MinWidth="100"
MinHeight="30"
Margin="0,0,0,50"
AnchorPoint="0,1.5"
Background="{StaticResource LightBrush}"
BorderBrush="{StaticResource SolidBorderBrush}"
BorderThickness="1"
CalloutStyle="RoundedRectangle"
Fill="#FFF4F4F5"
FontSize="14.667"
Stroke="Black">
<ContentPresenter Margin="4"
HorizontalAlignment="Left"
VerticalAlignment="Top" />
</ed:Callout>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Window.Resources>
<Grid>
<Button ToolTip="Hello" />
</Grid>
this is the begining, now you have to play with it... enjoy!
Given this piece of XAML
<DockPanel>
<DockPanel.Resources>
<Style TargetType="{x:Type GroupBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupBox}">
<DockPanel>
<Border DockPanel.Dock="Top">
<Border.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground"
Value="Red" />
</Style>
</Border.Resources>
<ContentPresenter ContentSource="Header" />
</Border>
<ContentPresenter />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DockPanel.Resources>
<GroupBox VerticalAlignment="Top"
Header="GroupBox header"
DockPanel.Dock="Top">
...
...
I would like to know why the group box header is not displayed in red letters.
I've already tried styling the Label type with no success either.
(sorry about the overly generic post title... I wasn't able to think of something more meaninful)
This code solved the problem:
<DockPanel>
<DockPanel.Resources>
<Style TargetType="{x:Type GroupBox}">
<Setter Property="HeaderTemplate">
<Setter.Value>
<DataTemplate>
<DataTemplate.Resources>
<Style TargetType="Label">
<Style.Setters>
<Setter Property="Foreground" Value="Red" />
</Style.Setters>
</Style>
</DataTemplate.Resources>
<Label Content="{Binding}" />
</DataTemplate>
</Setter.Value>
</Setter>
</Style>
</DockPanel.Resources>
<GroupBox VerticalAlignment="Top" Header="GroupBox header" DockPanel.Dock="Top">
...
...
However, I still don't know why the proposed code didn't worked.
It seems that the ContentPresenter doesn't use TextBlock to show the string you provide as header or explicitly sets its style, so the style you defined cannot be applied.
If you are certain that you will only use text as group box header, you can remove the ContentPresenter and use a TextBlock on your own.
<DockPanel>
<DockPanel.Resources>
<Style TargetType="{x:Type GroupBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupBox}">
<DockPanel>
<Border DockPanel.Dock="Top">
<Border.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Red" />
</Style>
</Border.Resources>
<TextBlock Text="{TemplateBinding Header}"></TextBlock>
</Border>
<ContentPresenter />
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</DockPanel.Resources>
<GroupBox VerticalAlignment="Top"
Header="GroupBox header"
DockPanel.Dock="Top"/>
</DockPanel>
try this:
<DockPanel.Resources>
<Style TargetType="{x:Type GroupBox}" >
<Setter Property="Foreground" Value="Red" />
</Style>
</DockPanel.Resources>
You don't need a templet for this. But if you demand on using a Templete, you probably have to set the Groupbox.HeaderTemplet not the GroupBox.Templet.
Edit:
This is what i got so far, but i keep getting an XamlPraseException.
<Style TargetType="{x:Type GroupBox}" >
<Setter Property="HeaderTemplate">
<Setter.Value>
<ControlTemplate>
<StackPanel>
<StackPanel.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Red"/>
</Style>
</StackPanel.Resources>
<TextBlock Text="{TemplateBinding GroupBox.Header}" />
</StackPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>