More flexible setters in ControlTemplate.Triggers - c#

I'm trying to style a button in XAML. Below you can see what I created so far.
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="#f2f2f7"/>
<Setter Property="Padding" Value="6,4"/>
<Setter Property="Foreground" Value="#222222" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border CornerRadius="1" Background="{TemplateBinding Background}" BorderThickness="1" BorderBrush="#b1b1c0">
<Border CornerRadius="1" Background="{TemplateBinding Background}" BorderThickness="1,1,1,0" BorderBrush="#f8f8fa" Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center">
</ContentPresenter>
</Border>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#8e8eba" />
<Setter Property="Foreground" Value="#f2f291" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
The problem is, that this button has two <Border> elements nested one in another. I'd like to have different BorderBrush="" attribute when IsMouseOver trigger is activated. For example, when I put mouse over button, the inner border would red, and outer border would be blue.
Can you help me with that?

Try set the Name for Borders and use TargetName in Trigger like this:
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="#f2f2f7"/>
<Setter Property="Padding" Value="6,4"/>
<Setter Property="Foreground" Value="#222222" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Name="OuterBorder" CornerRadius="1" Background="{TemplateBinding Background}" BorderThickness="1" BorderBrush="#b1b1c0">
<Border Name="InnerBorder" CornerRadius="1" Background="{TemplateBinding Background}" BorderThickness="1,1,1,0" BorderBrush="#f8f8fa" Padding="{TemplateBinding Padding}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Border>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#8e8eba" />
<Setter Property="Foreground" Value="#f2f291" />
<!-- Here use TargetName -->
<Setter TargetName="OuterBorder" Property="BorderBrush" Value="Red" />
<Setter TargetName="InnerBorder" Property="BorderBrush" Value="Blue" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Some notes
TargetName can be set in only <ControlTemplate.Triggers>, not in <Style.Triggers>
TargetName operates only within the one ControlTemplate section

Give the Border a name and use the TargetName property on the trigger setter.
<Setter TargetName="MyBorderName" Property="BorderBrush" Value="Red" />

Related

Textbox style setter breaks functionality

for some reason in my code behind (WPF), the "setter" section makes the text box not function anymore. I can't type in it or add any functionality to it. When I remove the setter section, the text box works. I've seen other people online do it this exact way and it works for them but I'm not sure what I'm doing wrong.
Here's what I have in the main window.
<TextBox Style="{DynamicResource inputBox}" Margin="0,50,125,0"/>
And here's what I have in the code behind
<Style x:Key="inputBox" TargetType="TextBox">
<Setter Property="Padding" Value="3"/>
<Setter Property="Height" Value="35"/>
<Setter Property="Width" Value="65"/>
<Setter Property="FontSize" Value="10"/>
<Setter Property="TextWrapping" Value="Wrap"/>
<Setter Property="Text" Value=""/>
<Setter Property="HorizontalAlignment" Value="Center"/>
<Setter Property="VerticalAlignment" Value="Top"/>
<!--This setter somehow breaks the text box-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border Background="Transparent" CornerRadius="5" BorderThickness="1" BorderBrush="Black">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Thanks
Your template only shows the border. Not the content. It needs a PART_ContentHost Do yourself a favor and copy the existing template and edit that. You can do this on the XAML designer. For example, here is what it looks like when I copy it on mine:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border x:Name="border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="True">
<ScrollViewer x:Name="PART_ContentHost" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.MouseOver.Border}"/>
</Trigger>
<Trigger Property="IsKeyboardFocused" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource TextBox.Focus.Border}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>

Override property of custom control style in XAML

I borrowed some code from some other guy here on Stack Overflow. I have two PasswordBoxes. I want the first one to show "Password" and the second one to show "Re-enter Password". I don't want to rewrite the complete style all over again if the only difference is the text in the TextBlock. How can I override and change the value of the TextBlock, if the TargetType has to be PasswordBox? I'm trying to create a second style that is based on the first one, and then change it from there, but I'm not sure about the syntax.
This one works fine:
<Style x:Name="customPWBStyle" x:Key="customPasswordBox"
TargetType="{x:Type PasswordBox}">
<Setter Property="helper:PasswordBoxMonitor.IsMonitoring"
Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type PasswordBox}">
<Border Name="Bd"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
SnapsToDevicePixels="true">
<Grid>
<ScrollViewer x:Name="PART_ContentHost"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<TextBlock Text="Password"
Margin="4, 2, 0, 0"
Foreground="Gray"
Visibility="Collapsed"
Name="txtPrompt" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled"
Value="false">
<Setter TargetName="Bd"
Property="Background"
Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
<Trigger Property="helper:PasswordBoxMonitor.PasswordLength" Value="0">
<Setter Property="Visibility" TargetName="txtPrompt" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
But I want to create another style identical, but the only difference has to be the TextBlock that has to say "Re-enter password"
This is what I got so far:
<Style x:Key="reEnterPasswordBox" BasedOn="{StaticResource customPasswordBox}" TargetType="{x:Type PasswordBox}">
<Style.Resources>
<Style TargetType="TextBlock">
<Setter Property="Text" Value="Re-enter Password"></Setter>
</Style>
</Style.Resources>
</Style>
However it does not work. I can see that theres a name for the TextBlock, which is txtPrompt, but I'm not sure if its possible to use that as a reference for chaning the value of the TextBlock.
I would recommend to create a special dependency property in customPasswordBox, e.g. InputHint. (If you can't change customPasswordBox code, make a custom attached dependency property - like helper:PasswordBoxMonitor.IsMonitoring. Attached DPs are great for parametrizing templates)
When you have a property, set default value via Setter, and then bind TextBlock to it via TemplateBinding.
<Style x:Name="customPWBStyle" x:Key="customPasswordBox"
TargetType="{x:Type PasswordBox}">
<Setter Property="helper:PasswordBoxMonitor.IsMonitoring" Value="True"/>
<Setter Property="InputHint" Value="Password"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type PasswordBox}">
<Border Name="Bd"
Background="{TemplateBinding Background}"
BorderThickness="{TemplateBinding BorderThickness}"
BorderBrush="{TemplateBinding BorderBrush}"
SnapsToDevicePixels="true">
<Grid>
<ScrollViewer x:Name="PART_ContentHost"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<TextBlock Text="{TemplateBinding InputHint}"
Margin="4, 2, 0, 0"
Foreground="Gray"
Visibility="Collapsed"
Name="txtPrompt" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled"
Value="false">
<Setter TargetName="Bd"
Property="Background"
Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
<Trigger Property="helper:PasswordBoxMonitor.PasswordLength" Value="0">
<Setter Property="Visibility" TargetName="txtPrompt" Value="Visible"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
to make another style, change only Setter for InputHint:
<Style x:Key="reEnterPasswordBox" BasedOn="{StaticResource customPasswordBox}" TargetType="{x:Type PasswordBox}">
<Setter Property="InputHint" Value="Re-enter Password"/>
</Style>
Parts of template are not easily reachable for modification, even with implicit styles

WPF Content Presenter weird horizontal alignment behaviour

I'm trying to center a content a Content Presenter in a button, It centers perfectly in the designer But when ran it will either go off center by a bit or will get cut off. What's confusing me is that I have another style that does relatively the same thing but works perfectly.
Here's the style;
<Style x:Key="MainButtonStyle" TargetType="{x:Type Button}">
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="0,1"/>
<Setter Property="RenderOptions.BitmapScalingMode" Value="NearestNeighbor"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid x:Name="Chrome" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
<Grid.BindingGroup>
<BindingGroup/>
</Grid.BindingGroup>
<Border x:Name="border" CornerRadius="3,3,3,3" BorderBrush="LightGray" BorderThickness="1" Background="#FFF3F3F3" Padding="0">
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Stretch"
TextElement.FontWeight="Light" Height="Auto" Margin="153.636,0.52,156,1.52" Width="Auto" UseLayoutRounding="False" SnapsToDevicePixels="False" >
</ContentPresenter>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" TargetName="border" Value="#330CB3EE"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" TargetName="border" Value="#FF0CB3EE"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Opacity" TargetName="Chrome" Value="0.25"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Remove the Margin from the Content Presenter:
<ContentPresenter HorizontalAlignment="Center"
VerticalAlignment="Stretch"
TextElement.FontWeight="Light"
Height="Auto"
Width="Auto"
UseLayoutRounding="False"
SnapsToDevicePixels="False" >
This Margin was probably added by the designer, matching the layout of the control in the designer.

Setting BorderBrush of Button with a custom template and triggers

I'm trying to change the borderbrush property of a button in a custom template. Changing the background on mouseover using this method works just fine, but the BorderBrush? Nada.
Here's my code:
<Style x:Key="ImageButtonStyle" TargetType="Button">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Margin" Value="2"/>
<Setter Property="Background" Value="LightGray"/>
<Setter Property="BorderBrush" Value="Blue"/>
<Setter Property="BorderThickness" Value="3"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<Border Name="border" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" Margin="1,0,0,-9">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0,1,1,0" Height="94" Width="101"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="border" Property="BorderBrush" Value="Red"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Edit: Actually not even the blue borderbrush set at the beggining shows up so that may be of relevance.
Here's where the style is used if of any importance.
<Button Margin="191,10,138,109" Style="{StaticResource ImageButtonStyle}">
<Image/>
</Button>
You create style setting BorderThickness to 3 but then override default visual tree so this thicknes is not taken into acount. Add this
BorderThickness="{TemplateBinding BorderThickness}"
in order to see it.

WPF: Cant find correct BorderBrush after setting Style

I have a button which I apply a style too. After the style is applied I have a test that checks the BorderBrush of the button to make sure the style was correctly applied. This test always fails, because the BorderBrush never gets updated in the Buttons properties.
Is there a way for me to get the actual BorderBrush being displayed? All i can get is the default BorderBrush, not the applied styles value.
The style I am using is this:
<Style x:Key="ActiveModuleBtnStyleOn" TargetType="Button">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Border Margin="0"
BorderThickness="3"
BorderBrush="Green"
Background="White"
VerticalAlignment="Stretch"
CornerRadius="4"
HorizontalAlignment="Stretch">
<Border.Effect>
<DropShadowEffect BlurRadius="10" Color="Black" Direction="325" Opacity=".5" RenderingBias="Quality" ShadowDepth="5" />
</Border.Effect>
</Border>
<TextBlock Margin="2" Text="{TemplateBinding Content}" TextWrapping="Wrap" TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="IsEnabled" Value="true"/>
</Style>
Then I apply the style like this:
if(var1 == 1)
TopologyNvTxBtn.Style = this.FindResource("ActiveModuleBtnStyleOn") as Style;
else
TopologyNvTxBtn.Style = this.FindResource("ActiveModuleBtnStyleOff") as Style;
In my test app, I want to check which style has been applied. The difference between the two styles is the border brush. I would like to do something like this:
if(TopologyNvTxBtn.BorderBrush == Brushes.Green)
return PASS;
But the BorderBrush never changes (e.g. in a watch window, it stays the same as the default buttons style BorderBrush), but its definitely applied correctly as in the GUI it turns green.
How can i access this property in my test?
Many thanks in advance
The BorderBrush property of the Button is not bound to anything in your template. You need to do this in the control template:
<Style x:Key="ActiveModuleBtnStyleOn" TargetType="Button">
<Setter Property="BorderBrush" Value="Green" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid>
<Border Margin="0"
BorderThickness="3"
BorderBrush="{TemplateBinding BorderBrush}"
Background="White"
VerticalAlignment="Stretch"
CornerRadius="4"
HorizontalAlignment="Stretch">
<Border.Effect>
<DropShadowEffect BlurRadius="10" Color="Black" Direction="325" Opacity=".5" RenderingBias="Quality" ShadowDepth="5" />
</Border.Effect>
</Border>
<TextBlock Margin="2" Text="{TemplateBinding Content}" TextWrapping="Wrap" TextAlignment="Center" VerticalAlignment="Center" HorizontalAlignment="Center"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="IsEnabled" Value="true"/>
</Style>
Notice how the BorderBrush is now bound to the BorderBrush property of the Button. Then you you can set the BorderBrush in the style and should be able to access it in this way:
if(TopologyNvTxBtn.BorderBrush == Brushes.Green)
Let me know if that works.
Cheers,
Eric
As I stated above, the Border is part of the Button ControlTemplate. I just launched Blend and got a copy of the "Factory" template. With this, you can do pretty much everything with it (assuming you know about Stylying.):
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="Button.Static.Background" Color="#FFDDDDDD"/>
<SolidColorBrush x:Key="Button.Static.Border" Color="#FF707070"/>
<SolidColorBrush x:Key="Button.MouseOver.Background" Color="#FFBEE6FD"/>
<SolidColorBrush x:Key="Button.MouseOver.Border" Color="#FF3C7FB1"/>
<SolidColorBrush x:Key="Button.Pressed.Background" Color="#FFC4E5F6"/>
<SolidColorBrush x:Key="Button.Pressed.Border" Color="#FF2C628B"/>
<SolidColorBrush x:Key="Button.Disabled.Background" Color="#FFF4F4F4"/>
<SolidColorBrush x:Key="Button.Disabled.Border" Color="#FFADB2B5"/>
<SolidColorBrush x:Key="Button.Disabled.Foreground" Color="#FF838383"/>
<Style x:Key="ButtonStyle1" TargetType="{x:Type Button}">
<Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
<Setter Property="Background" Value="{StaticResource Button.Static.Background}"/>
<Setter Property="BorderBrush" Value="{StaticResource Button.Static.Border}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="HorizontalContentAlignment" Value="Center"/>
<Setter Property="VerticalContentAlignment" Value="Center"/>
<Setter Property="Padding" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
<ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsDefaulted" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/>
<Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
MSDN has a good introductory tutorial about Style and Templates. Besides, you can find tons of articles around the web!
The Button itself is pretty much this:
<Border
x:Name="border"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
SnapsToDevicePixels="true">
<ContentPresenter
x:Name="contentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Focusable="False"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
So, what your looking for is Triggers, you'll have to add and change as you wish these tags:
<ControlTemplate.Triggers>
<Trigger Property="IsDefaulted" Value="true">
<Setter Property="BorderBrush" TargetName="border" Value="{DynamicResource {x:Static SystemColors.HighlightBrushKey}}"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.MouseOver.Border}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.Pressed.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Pressed.Border}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="border" Value="{StaticResource Button.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="border" Value="{StaticResource Button.Disabled.Border}"/>
<Setter Property="TextElement.Foreground" TargetName="contentPresenter" Value="{StaticResource Button.Disabled.Foreground}"/>
</Trigger>
</ControlTemplate.Triggers>
Things to pay attention: TargetName and every <Trigger Property="IsDefaulted" Value="true"> alike!
Good luck!

Categories

Resources