How to create parametrized style in xaml without custom control - wpf

I have two styles, which are almost identical:
<Style x:Key="CancelButtonStyle" TargetType="{x:Type Button}" >
<!-- gigantic common code -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<!-- more common code -->
<Path Data="DIFFERENT_VALUE_A"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="OkButtonStyle" TargetType="{x:Type Button}" >
<!-- gigantic common code -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<!-- more common code -->
<Path Data="DIFFERENT_VALUE_B"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I want to extract the common code ("style template"), and then use it like that:
<Style x:Key="OkButtonStyle" Base="PathButtonStyle" PathData="DIFFERENT_VALUE_B" />
EDIT:
As I specified in title, I don't want to create my own control

Just define your ControlTemplate objects separately in Resources:
<ControlTemplate x:Key="ControlTemplate1">
<!-- different part -->
</ControlTemplate>
<ControlTemplate x:Key="ControlTemplate2">
<!-- different part -->
</ControlTemplate>
Create one Style for the common parts:
<Style x:Key="BaseStyle" TargetType="{x:Type Button}" >
<!-- gigantic common code -->
</Style>
Then base your new styles on that one, referencing your new ControlTemplates:
<Style x:Key="CancelButtonStyle" TargetType="{x:Type Button}"
BasedOn="{StaticResource BaseStyle}">
<Setter Property="Template" Value="{StaticResource ControlTemplate1}" />
</Style>
<Style x:Key="OkButtonStyle" TargetType="{x:Type Button}"
BasedOn="{StaticResource BaseStyle}">
<Setter Property="Template" Value="{StaticResource ControlTemplate2}" />
</Style>

You could use Tag to pass the PathData:
<Style x:Key="BaseStyle" TargetType="{x:Type Button}" >
<!-- gigantic common code -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<!-- more common code -->
<Path Data="{TemplateBinding Tag}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and then set the Tag in your individual Style:
<Style x:Key="CancelButtonStyle" TargetType="{x:Type Button}"
BasedOn="{StaticResource BaseStyle}">
<Setter Property="Tag" Value="MY PAth A" />
</Style>
<Style x:Key="OkButtonStyle" TargetType="{x:Type Button}"
BasedOn="{StaticResource BaseStyle}">
<Setter Property="Tag" Value="MY PAth B" />
</Style>

You can create your own Button, which derives from the Wpf-Button and has the property PathData:
public class PathButton:Button
{
public string PathData {get;set;}
}
The your style could be like:
<Style x:Key="PathButtonStyle" TargetType="{x:Type ns:PathButton}" >
<!-- gigantic common code -->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<!-- more common code -->
<Path Data="{TemplateBinding PathData}"/>
</ControlTemplate>
</Setter.Value>
</Setter>
To use it you have to do this:
<ns:PathButton Style="{StaticResource PathButtonStyle}" PathData="M200 200 100 100 L0 0"/>
Or you create your own Custom Control, the way would be the same.
The adventage here is no matter how many different buttons you want to use, there always will be just one Style in your resources and one class (PathButton).
You just have to change the PathData-property of your PathButton to change it's appereance.

Related

How to set style and template for DataGridRow on custom DataGrid

I have a custom DataGrid element. Stle and template of this element described in Theme.xml:
<Style TargetType="{x:Type local:MyDataGrid}" BasedOn="{StaticResource {x:Type DataGrid}}">
<Setter Property="RowHeaderWidth" Value="0"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGrid}">
<!-- ... -->
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
How do i specify template for DataGridRow specially for my custom DataGrid.
Something like this:
<Style TargetType="{x:Type DataGridRow}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridRow}">
<Border BorderThickness="3">
<DataGridCellsPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
litterary changing nothing.
Setting x:Key value for DataGridRow style and then usining it in my custom datagrid style with Setter Property changing nothing too.
You could set the RowStyle property, either in the Style:
<Style TargetType="{x:Type local:MyDataGrid}" BasedOn="{StaticResource {x:Type DataGrid}}">
<Setter Property="RowHeaderWidth" Value="0"/>
<Setter Property="Template">
...
</Setter>
<Setter Property="RowStyle">
<Setter.Value>
<Style TargetType="{x:Type DataGridRow}">
...
</Style>
</Setter.Value>
</Setter>
</Style>
...or on the MyDataGrid element:
<local:MyDataGrid>
<DataGrid.RowStyle>
<Style TargetType="DataGridRow">
...
</Style>
</DataGrid.RowStyle>
</local:MyDataGrid>

How to override Foreground for selected ListBoxItem

I have a global style (Application.Resources) to set the Foreground of all TextBlocks.
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="Brown"/>
</Style>
This works fine.
Now I try to override the Foreground of the TextBlock inside of a selected ListBoxItem, which is part of default ContentPresenter content.
I created a new global style for the ListBoxItem:
<Style TargetType="ListBoxItem">
<Setter Property="Foreground" Value="Orange" />
<Setter Property="Background" Value="Aqua"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Background="{TemplateBinding Background}">
<ContentPresenter/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="Brown" />
<Setter Property="Foreground" Value="Aqua" />
</Trigger>
</Style.Triggers>
</Style>
The background works fine.
But Foreground has still the brush form de global style from the TextBlock.
Which is the best way to set the Foreground in a solution that works with Binding?
This is an example of why defining an implicit application-wide TextBlock style is usually a bad idea.
But you should be able to override it by adding a default style to to <ContentPresenter.Resources>:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListBoxItem">
<Border Background="{TemplateBinding Background}">
<ContentPresenter>
<ContentPresenter.Resources>
<Style TargetType="{x:Type TextBlock}" BasedOn="{x:Null}" />
</ContentPresenter.Resources>
</ContentPresenter>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>

Using the same ControlTemplate in different styles and override a property

For two TabItems I have two Styles, both use the same ControlTemplate. Now I want styleTabB to show a yellow underline instead of green, but still using the ControlTemplate. How can I modify the Style to accomplish this?
<Window x:Class="WpfApplication1.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:WpfApplication1"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" FontSize="16">
<Window.Resources>
<ControlTemplate x:Key="ctrlTemplate" TargetType="{x:Type TabItem}">
<Grid>
<TextBlock Name="tbTabItemHeaderText"
Text="{TemplateBinding Header}"
Grid.Column="0"
Background="Thistle"
HorizontalAlignment="Left"
VerticalAlignment="Center"
Margin="3,0,0,3">
<TextBlock.TextDecorations>
<TextDecorationCollection>
<TextDecoration Location="Underline"
PenThicknessUnit="Pixel"
PenOffsetUnit="Pixel"
PenOffset="2">
<TextDecoration.Pen>
<Pen Brush="Green" Thickness="4" />
</TextDecoration.Pen>
</TextDecoration>
</TextDecorationCollection>
</TextBlock.TextDecorations>
</TextBlock>
</Grid>
</ControlTemplate>
<!-- Style Tab A -->
<Style x:Key="styleTabA" TargetType="{x:Type TabItem}">
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>
<!-- Style Tab B -->
<Style x:Key="styleTabB" TargetType="{x:Type TabItem}">
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>
</Window.Resources>
<Grid>
<TabControl Name="tabControl">
<TabItem Name="tabItem_1" Header="--- Tab A ---" Style="{StaticResource styleTabA}"/>
<TabItem Name="tabItem_2" Header="--- Tab B ---" Style="{StaticResource styleTabB}" />
</TabControl>
</Grid>
</Window>
UPDATE
I tried the proposal of Chris W., but no underline at all is shown:
<ControlTemplate x:Key="ctrlTemplate" TargetType="{x:Type TabItem}">
...
<TextDecoration.Pen>
<Pen Brush="{TemplateBinding Tag}" Thickness="4" />
</TextDecoration.Pen>
...
</ControlTemplate>
<!--Style Tab A-->
<Style x:Key="styleTabA" TargetType="{x:Type TabItem}">
<Setter Property="Tag" Value="Green" />
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>
<!--Style Tab B-->
<Style x:Key="styleTabB" TargetType="{x:Type TabItem}">
<Setter Property="Tag" Value="Yellow" />
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>
You can set the style of the children within the child by targetting particular types. Here all Pen are updated with Yellow color.
<Style x:Key="styleTabB" TargetType="{x:Type TabItem}">
<Style.Resources>
<Style TargetType="{x:Type Pen}">
<Setter Property="Brush" Value="Yellow"></Setter>
</Style>
</Style.Resources>
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>
#CarbineCoder is correct for most instances but for your instance you're right and the error you received from his would be expected since Pen isn't a TargetType. However if we tweak it just a little to hit the actual FrameworkElement of which TextDecorations is a property let's try this...and read the whole thing since the first snippet is just an example explanation.
<Style x:Key="styleTabB" TargetType="{x:Type TabItem}">
<Style.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="TextDecorations">
<Setter.Value>
<TextDecorationCollection>
<TextDecoration>
<TextDecoration.Pen>
<Pen Brush="Yellow"/>
</TextDecoration.Pen>
</TextDecoration>
</TextDecorationCollection>
</Setter.Value>
</Setter>
</Style>
</Style.Resources>
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>
** *Except that's not going to work because you're hoping for something that you've already provided an explicit property value for to inherit from parent, and it doesn't work that way. How about instead we bring in the handy Tag property (which I abuse all the time) to piggy back in our value and allow a way to talk to our buddy on the inside of that bugger by making some quick edits to your ControlTemplate like;
<!-- In your STYLE Template you would want to add a default setter of;
<Setter Property="Tag" Value="Green"/>
-->
<ControlTemplate x:Key="ctrlTemplate" TargetType="{x:Type TabItem}">
<Grid Margin="0,0,0,0">
<TextBlock Name="_tbTabItemHeaderText"
Text="{TemplateBinding Header}"
Grid.Column="0"
Background="Thistle"
VerticalAlignment="Center"
Margin="3,0,0,3">
<TextBlock.TextDecorations>
<TextDecorationCollection>
<TextDecoration Location="Underline"
PenThicknessUnit="Pixel"
PenOffsetUnit="Pixel"
PenOffset="2">
<TextDecoration.Pen>
<Pen Brush="{TemplateBinding Tag}" Thickness="4" />
</TextDecoration.Pen>
</TextDecoration>
</TextDecorationCollection>
</TextBlock.TextDecorations>
</TextBlock>
</Grid>
</ControlTemplate>
Now we should be able to hit like;
<!-- Style Tab A
: This guy should just keep it green IF you applied the default setter mentioned above to the STYLE template -->
<Style x:Key="styleTabA" TargetType="{x:Type TabItem}">
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>
<!-- Style Tab B
: This guy should turn it Yellow -->
<Style x:Key="styleTabB" TargetType="{x:Type TabItem}">
<Setter Property="Tag" Value="Yellow"/>
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>
I didn't have time to test, but it seems like this should work fine for your scenario. Hope it helps.
I made a little change on Chris W.'s solution and now it's working:
<ControlTemplate x:Key="ctrlTemplate" TargetType="{x:Type TabItem}">
...
<TextDecoration.Pen>
<!--Changed next line from <Pen Brush="{TemplateBinding Tag}" Thickness="4" /> to:-->
<Pen Brush="{Binding Path=Tag, RelativeSource={RelativeSource TemplatedParent}}" Thickness="4" />
</TextDecoration.Pen>
...
</ControlTemplate>
<!--Style Tab A-->
<Style x:Key="styleTabA" TargetType="{x:Type TabItem}">
<Setter Property="Tag" Value="Green" />
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>
<!--Style Tab B-->
<Style x:Key="styleTabB" TargetType="{x:Type TabItem}">
<Setter Property="Tag" Value="Yellow" />
<Setter Property="Template" Value="{StaticResource ctrlTemplate}" />
</Style>

WPF style like css classes

I have a tooltip style like below:
<Style TargetType="{x:Type ToolTip}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="HasDropShadow" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToolTip">
...
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have two button with each with the defined tooltip but I want only one of them has my tooltip style.
How can I achieve this?
Add the following to your Tooltip style:
<Style TargetType="{x:Type ToolTip}" x:Key="myTooltipStyle">
Now, only the button which will specifically ask for the myTooltipStyle will get it:
<Button ToolTip="{StaticResource myTooltipStyle}">

wpf ToolTip and Style

How I can add ToolTip to this code ?
<Style TargetType="{x:Type ListBoxItem}">
</Style>
You can set ToolTip property in a style the same way you set any other property.
Sample code as below
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="ToolTip" Value="ToolTip Value" />
</Style>
For a more complex ToolTip use
<Style TargetType="{x:Type ListBoxItem}">
<Setter Property="ToolTip">
<Setter.Value>
<!--Content Here-->
<!--<Grid> or <StackPanel> or <ContentPresenter>...-->
</Setter.Value>
</Setter>
</Style>

Resources