WPF ControlTemplate Style GradientStop in Trigger - wpf

Here is my XAML for a TabItem. I want to be able to set the Color of a single gradient stop in a trigger. I know that I can re-define the gradient completely in the trigger's setter, but I want to access a specific property on the background so I can animate it in the future.
I have tried every variation of everything in the trigger's setter and googled for a long time - but I still can't get it to compile. I have also tried class.property syntax, but still nothing. The current error this code raises is:
"Type 'Background.GradientStops[0]' was not found."
I am pretty sure I know what is going on here - and perhaps what I want is impossible. But there has to be a way to animate a control's gradient in a control template...
Can anyone help me?
thanks
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<TextBlock Padding="6 2 6 2" Name="TheHeader">
<TextBlock.Background>
<LinearGradientBrush StartPoint="0, 0" EndPoint="0, 1">
<GradientStop Offset="0" Color="#f4fafd" />
<GradientStop Offset="1" Color="#ceedfa" />
</LinearGradientBrush>
</TextBlock.Background>
<ContentPresenter ContentSource="Header" Margin="0" />
</TextBlock>
<ControlTemplate.Triggers >
<Trigger Property="IsSelected" Value="True">
<Setter TargetName="TheHeader" Property="Background.GradientStops[0].Color" Value="White" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

You can animate it, like in the example here.
You also could use a slight hack to set it, though I always prefer creating multiple brushes as resources and swapping them or re-creating a brush in the as you mentioned.
<Style TargetType="{x:Type TabItem}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TabItem}">
<TextBlock Padding="6 2 6 2"
Name="TheHeader" Tag="#f4fafd">
<TextBlock.Background>
<LinearGradientBrush StartPoint="0, 0"
EndPoint="0, 1">
<GradientStop Offset="0"
Color="{Binding ElementName=TheHeader, Path=Tag}"/>
<GradientStop Offset="1"
Color="#ceedfa" />
</LinearGradientBrush>
</TextBlock.Background>
<ContentPresenter ContentSource="Header"
Margin="0" />
</TextBlock>
<ControlTemplate.Triggers>
<Trigger Property="IsSelected"
Value="True">
<Setter TargetName="TheHeader"
Property="Tag"
Value="Red" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>

Related

WPF: Change Style of an element in an Inherited Theme

I want to change the style of a theme an inherited style (inherited through based on). Have any idea? This is basically to define multiple styles for multi-series charts in wpf toolkit. Code looks as follows:
<Style x:Key="A" TargetType="DVC:ColumnDataPoint">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="IsTabStop" Value="False" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="DVC:ColumnDataPoint">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Opacity="0" x:Name="Root">
<Grid Background="{TemplateBinding Background}" Name="columngrid">
<Grid.Resources>
<Style x:Key="aquaboarder" TargetType="Border">
<Style.Resources>
<LinearGradientBrush x:Key="BackBrush" StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#B211B9D8" Offset="0.1" />
<GradientStop Color="#FF0F56C7" Offset="0.9" />
</LinearGradientBrush>
</Style.Resources>
<Setter Property="Background" Value="{StaticResource BackBrush}"/>
</Style>
</Grid.Resources>
<Border Name="columnBorder" BorderBrush="Transparent" BorderThickness="1" CornerRadius="20,20,0,0" Style="{StaticResource aquaboarder}">
</Border>
</Grid>
<ToolTipService.ToolTip>
<ContentControl Content="{TemplateBinding FormattedDependentValue}" />
</ToolTipService.ToolTip>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And the inherited style goes as follows:
<Style x:Key="B" BasedOn="{StaticResource A}" TargetType="DVC:ColumnDataPoint">
<Style.Resources>
<LinearGradientBrush x:Key="BackBrush" StartPoint="0.5,0" EndPoint="0.5,1">
<GradientStop Color="#B24DE509" Offset="0.1" />
<GradientStop Color="#FF238910" Offset="0.9" />
</LinearGradientBrush>
</Style.Resources>
</Style>
I want to set the columngrid in "style A" to use the backbrush defined in "style B". I do not like to do more stuff in style B as I will have many of inherited styles be defined just changing this style afterwards.
You are wasting the perfectly useful Background property that is already available and trying to create a new one that serves the same purpose. Since you are overriding the control template, just use the Background for the purpose that charting intended. Instead of setting it to transparent in your style, let your derived style set or override Background and then use {TemplateBinding Background} in the control template where are you current using {StaticResource Backbrush}. Your other use of {TemplateBinding Background} on the Grid element you can remove since it seems clear that your intention is that the grid background will be transparent.

Inheriting/overriding WPF styles

I created style for a toggle button, defined below:
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border HorizontalAlignment="Center" VerticalAlignment="Center" x:Name="border" Padding="5,5,5,5" CornerRadius="5" Background="#FFBFACAC" BorderBrush="#FF000000" BorderThickness="1,1,1,1" SnapsToDevicePixels="True">
<ContentPresenter x:Name="contentPresenter"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsChecked" Value="true">
<Setter Property="Foreground" Value="White"/>
<Setter Property="Background" TargetName="border">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF36587C" Offset="0.5"/>
<GradientStop Color="#FF122F53" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsChecked" Value="false">
<Setter Property="Foreground" Value="Black"/>
<Setter Property="Background" TargetName="border">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="LightGray" Offset="0.5"/>
<GradientStop Color="White" Offset="1"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
I have multiple toggle buttons but I would like a way to change their border corner radius. For example, I would like buttons with only their right border corners to be rounded, or some with no corner rounding.
Do I have to recreate the entire style for each type of rounding I need, where the only difference in each style would be the following line?
<Border HorizontalAlignment="Center" VerticalAlignment="Center" x:Name="border" Padding="5,5,5,5" CornerRadius="5" Background="#FFBFACAC" BorderBrush="#FF000000" BorderThickness="1,1,1,1" SnapsToDevicePixels="True">
Since the corner rounding is part of the control template, I don't think I can somehow change just a part of the template in a new style without including all of it but I am not sure.
Thanks.
You will need to create the entire style to change part of the control template. I suggest putting this into a resource dictionary that you can merge as part of your window to keep the window (or control) XAML cleaner.
An easy way to do this is with Blend - add the control you want and right click it, then choose "edit template", to edit a copy of the template.
The alternative is to create a Custom Control, inheriting from ToggleButton.
You could then have a property called CornerRadius, and in the generic template for the class, do a TemplateBinding to this property.
If this is something you plan to use "a lot" in your project, with a bunch of variations, then a Custom Control may be the way to go. It will also allow you to expand the number of properties later, in case you want to do further customizations.

How do you style a WPF GridView Header?

I went from this: WPF GridViewHeader styling questions
to this:
Now I just need to get rid of the white space to the right of the "Size" header. I basically have a template for the GridViewColumnHeader that makes it a TextBlock. Is there any way I can set the background for that header area so that it spans the entire width of the GridView?
ADDED CODE:
This is my right-most column. The grid does not span 100% of available window area. In the header I need everything to the right of this column to have the same background as the column headers themselves.
<Style x:Key="GridHeaderRight" TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GridViewColumnHeader}">
<TextBlock Text="{TemplateBinding Content}" Padding="5" Width="{TemplateBinding Width}" TextAlignment="Right">
<TextBlock.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0.0" Color="#373638" />
<GradientStop Offset="1.0" Color="#77797B" />
</LinearGradientBrush>
</TextBlock.Background>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Background" Value="Green" />
<Setter Property="Foreground" Value="White" />
<Setter Property="FontSize" Value="12" />
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0.0" Color="#373638" />
<GradientStop Offset="1.0" Color="#77797B" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
<GridViewColumn Width="200" HeaderContainerStyle="{ StaticResource GridHeaderRight}" Header="Size">
<GridViewColumn.CellTemplate>
<DataTemplate>
<TextBlock Text="{Binding Path=EmployeeNumber}" HorizontalAlignment="Right"></TextBlock>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
UPDATE
I am one step closer (I think) to solving this.
I added the following code inside the GridView tag:
<GridView.ColumnHeaderContainerStyle>
<Style TargetType="GridViewColumnHeader">
<Setter Property="BorderThickness" Value="1"></Setter>
<Setter Property="BorderBrush" Value="Green"></Setter>
<Setter Property="Height" Value="Auto"></Setter>
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0.0" Color="#373638" />
<GradientStop Offset="1.0" Color="#77797B" />
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
</GridView.ColumnHeaderContainerStyle>
The border is there just so you can see the boundary of what this style covers. This is an enlarged image of what this does. It seems to be what I want if I can get rid of the little white border on the bottom.
So I guess removing that tiny white bottom border would also be an accepted answer for this one.
This is a simple style that will accomplish what you are looking for. Just change the Transparent background on the Border to be your desired gradient.
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GridViewColumnHeader}">
<Border BorderThickness="0,0,0,1" BorderBrush="Black" Background="Transparent">
<TextBlock x:Name="ContentHeader" Text="{TemplateBinding Content}" Padding="5,5,5,0" Width="{TemplateBinding Width}" TextAlignment="Center" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Foreground" Value="Black" />
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontSize" Value="12" />
</Style>
sometimes the simplest way is the best. All you need to do it to change the TextBlock attached properties of GridViewColumnHeader
Define something like this in Resources:
<Style TargetType="{x:Type GridViewColumnHeader}" BasedOn="{StaticResource {x:Type GridViewColumnHeader}}">
<Setter Property="TextBlock.TextWrapping" Value="Wrap"/>
<Setter Property="TextBlock.Foreground" Value="Black"/>
</Style>
Have a look at the GridViewColumnHeader.Role property. The sample in the documentation for the GridViewColumnHeaderRole enumeration might give you some ideas...
EDIT: Have you considered using the GridView.HeaderStyle property ?
I solved this issue but I think there should be a better way of doing it. The problem was that I had TextBlocks on the header of each column. The unused area didn't have anything on the header row. I just added a TextBlock with the same background in the GridView.ColumnHeaderContainerStyle and it happened to span the rest of the unused width of the grid.
<GridView.ColumnHeaderContainerStyle>
<Style TargetType="GridViewColumnHeader">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GridViewColumnHeader}">
<TextBlock Text="" Padding="5">
<TextBlock.Background>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0.0" Color="#373638" />
<GradientStop Offset="1.0" Color="#77797B" />
</LinearGradientBrush>
</TextBlock.Background>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</GridView.ColumnHeaderContainerStyle>

Is it possible to extend a ControlTemplate the same way you extend a Style in WPF?

So the thing is that I have a main ControlTemplate which defines the most basic stuff for the new button look we're designing. But I want to do 3 other control templates for this button so we can set different colors in those; but I don't want to copy paste the main ControlTemplate and change the color there, instead I want to "inherit" from that (like with the BasedOn property in Style) and change the color in the inherited ControlTemplate.
Is this possible?
Thanks!
Found the solution. You don't extend ControlTemplates, instead you define all the basic behavior you want, and then you let either a style or the control itself modify it. Take the example below for instance. The ControlTemplate sets the OpacityMask and the round corners for my rectangle, the Styles sets the color of the background for each button (with help of a TemplateBinding), and there's my solution:
<Window.Resources>
<ControlTemplate x:Key="BaseMainButtonTemplate" TargetType="{x:Type Button}">
<Grid TextBlock.Foreground="White" TextBlock.FontFamily="Calibri">
<Rectangle Stroke="#FFE8E6E6" x:Name="rectangle" RadiusX="14.5" RadiusY="14.5" Fill="{TemplateBinding Property=Background}"> <!-- This TemplateBinding takes the color set by the style and applies it to the rectangle. Doing it this way, allows the style to modify the background color -->
<Rectangle.OpacityMask>
<LinearGradientBrush EndPoint="0,1" SpreadMethod="Reflect">
<GradientStop Offset="0" Color="Transparent"></GradientStop>
<GradientStop Offset="1" Color="Gray"></GradientStop>
</LinearGradientBrush>
</Rectangle.OpacityMask>
</Rectangle>
<ContentPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True"/>
</Grid>
<ControlTemplate.Triggers>
<!-- OpacityMask when it's Focused, Defaulted and Mouse is over -->
<Trigger Property="IsFocused" Value="True"/>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="OpacityMask" TargetName="rectangle">
<Setter.Value>
<LinearGradientBrush EndPoint="0,1" SpreadMethod="Repeat">
<GradientStop Offset="1" Color="Transparent"></GradientStop>
<GradientStop Offset="0" Color="Gray"></GradientStop>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
<!-- OpacityMask when it's pressed -->
<Trigger Property="IsPressed" Value="True">
<Setter Property="Stroke" TargetName="rectangle">
<Setter.Value>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF223472" Offset="0"/>
<GradientStop Color="#FFF2F0F0" Offset="0.911"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="StrokeThickness" TargetName="rectangle" Value="3"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="BlueButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Blue" />
<Setter Property="Template" Value="{StaticResource BaseMainButtonTemplate}">
</Setter>
</Style>
<Style x:Key="RedButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Red" />
<Setter Property="Template" Value="{StaticResource BaseMainButtonTemplate}">
</Setter>
</Style>
<Style x:Key="GreenButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Green" />
<Setter Property="Template" Value="{StaticResource BaseMainButtonTemplate}">
</Setter>
</Style>
</Window.Resources>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<StackPanel>
<Button Style="{StaticResource BlueButtonStyle}" Height="30" Content="Test">
</Button>
<Button Style="{StaticResource RedButtonStyle}" Height="30" Content="Test">
</Button>
<Button Style="{StaticResource GreenButtonStyle}" Height="30" Content="Test">
</Button>
</StackPanel>
</Grid>
Alternately you can define a "DynamicResource" reference to any dependency property in your Control Template and have it go resolve it's value given the presence of available resources.
For example, you can set Background="{DynamicResource SomeBrushColorVariable}"
Then SomeBrushColorVariable can change given different ResourceDictionaries that are merged into your App.xaml file or even set by the user given some user preferences display setting or color scheme.

Using WPF, what is the best method to update the background for a custom button control?

In WPF, we are creating custom controls that inherit from button with completely drawn-from-scratch xaml graphics. We have a border around the entire button xaml and we'd like to use that as the location for updating the background when MouseOver=True in a trigger. What we need to know is how do we update the background of the border in this button with a gradient when the mouse hovers over it?
In your ControlTemplate, give the Border a Name and you can then reference that part of its visual tree in the triggers. Here's a very brief example of restyling a normal Button:
<Style
TargetType="{x:Type Button}">
<Setter
Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Name="customBorder"
CornerRadius="5"
BorderThickness="1"
BorderBrush="Black"
Background="{StaticResource normalButtonBG}">
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Border>
<ControlTemplate.Triggers>
<Trigger
Property="IsMouseOver"
Value="True">
<Setter
TargetName="customBorder"
Property="Background"
Value="{StaticResource hoverButtonBG}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
If that doesn't help, we'll need to know more, probably seeing your own XAML. Your description doesn't make it very clear to me what your actual visual tree is.
You would want to add a trigger like this...
Make a style like this:
<Style x:Key="ButtonTemplate"
TargetType="{x:Type Button}">
<Setter Property="Foreground"
Value="{StaticResource ButtonForeground}" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate
TargetType="{x:Type Button}">
<Grid
SnapsToDevicePixels="True"
Margin="0,0,0,0">
<Border Height="20"
x:Name="ButtonBorder"
BorderBrush="{DynamicResource BlackBorderBrush}">
<TextBlock x:Name="button"
TextWrapping="Wrap"
Text="{Binding Path=Content, RelativeSource={RelativeSource TemplatedParent}}"
SnapsToDevicePixels="True"
Foreground="#FFFFFFFF"
Margin="6,0,0,0"
VerticalAlignment="Center"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<!-- Disabled -->
<Trigger Property="IsMouseOver"
Value="True">
<Setter TargetName="ButtonBorder"
Property="Background"
Value="{DynamicResource ButtonBackgroundMouseOver}" />
<Setter TargetName="ButtonBorder"
Property="BorderBrush"
Value="{DynamicResource ButtonBorderMouseOver}" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Then add some resources for the gradients, like this:
<LinearGradientBrush x:Key="ButtonBackgroundMouseOver"
EndPoint="0.5,1"
StartPoint="0.5,0">
<GradientStop Color="#FF000000"
Offset="0.432"/>
<GradientStop Color="#FF808080"
Offset="0.9"/>
<GradientStop Color="#FF848484"
Offset="0.044"/>
<GradientStop Color="#FF787878"
Offset="0.308"/>
<GradientStop Color="#FF212121"
Offset="0.676"/>
</LinearGradientBrush>
Please let me know if you need more help with this.

Resources