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.
Related
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>
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
As far as I understand I should be using Style triggers to update the TextBox's border colour when it is focused. However no matter what I do it always turns to the system default blue, not the black I have specified.
Anyone have any ideas?
Code below:
<UserControl.Resources>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" Value="Black" />
</Trigger>
</Style.Triggers>
</Style>
</UserControl.Resources>
Try set for BorderThickness value more than 1 (by default):
<Window.Resources>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="BorderBrush" Value="Pink" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Grid>
<TextBox Width="100"
Height="30"
Text="Test"
BorderThickness="4" />
</Grid>
Tested on Windows Seven.
Edit: why is this happening?
I looked in the default style for TextBox in Blend under Windows 7, here it is ControlTemplate:
<ControlTemplate x:Key="TextBoxControlTemplate1" TargetType="{x:Type TextBox}">
<Microsoft_Windows_Themes:ListBoxChrome x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" RenderMouseOver="{TemplateBinding IsMouseOver}" RenderFocused="{TemplateBinding IsKeyboardFocusWithin}" SnapsToDevicePixels="true">
<ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Microsoft_Windows_Themes:ListBoxChrome>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.ControlBrushKey}}"/>
<Setter Property="Foreground" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
Here there is two parameters:
RenderMouseOver="{TemplateBinding IsMouseOver}"
RenderFocused="{TemplateBinding IsKeyboardFocusWithin}"
They are responsible for blue gradient Border when states Focus and MouseOver and probably there stands a condition on BorderThickness and BorderBrush. If they remove / reset the blue gradient Border will disappear and will not need to set values for BorderThickness greater than 1.
In ILSpy I found ChangeVisualState(bool) method in TextBoxBase class, here it is:
internal override void ChangeVisualState(bool useTransitions)
{
if (!base.IsEnabled)
{
VisualStateManager.GoToState(this, "Disabled", useTransitions);
}
else
{
if (this.IsReadOnly)
{
VisualStateManager.GoToState(this, "ReadOnly", useTransitions);
}
else
{
if (base.IsMouseOver)
{
VisualStateManager.GoToState(this, "MouseOver", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Normal", useTransitions);
}
}
}
if (base.IsKeyboardFocused)
{
VisualStateManager.GoToState(this, "Focused", useTransitions);
}
else
{
VisualStateManager.GoToState(this, "Unfocused", useTransitions);
}
base.ChangeVisualState(useTransitions);
}
It turns out that these visual states implemented "systematically" and in Styles not present.
Default template of TextBox has trigger which set the border brush of textBox. In case you want to override it, you need to override ControlTemplate of TextBox.
This is how you do that:
<Style TargetType="TextBox">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border BorderThickness="{TemplateBinding Border.BorderThickness}"
BorderBrush="{TemplateBinding Border.BorderBrush}"
Background="{TemplateBinding Panel.Background}"
Name="border"
SnapsToDevicePixels="True">
<ScrollViewer HorizontalScrollBarVisibility="Hidden"
VerticalScrollBarVisibility="Hidden"
Name="PART_ContentHost"
Focusable="False" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="UIElement.IsEnabled" Value="False">
<Setter Property="UIElement.Opacity" TargetName="border"
Value="0.56"/>
</Trigger>
<Trigger Property="UIElement.IsMouseOver" Value="True">
<Setter Property="Border.BorderBrush" TargetName="border"
Value="#FF7EB4EA"/>
</Trigger>
<Trigger Property="UIElement.IsKeyboardFocused" Value="True">
<Setter Property="Border.BorderBrush" TargetName="border"
Value="Black"/> <-- HERE
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
Also you might want to override MouseOver brush to Black in above template. You can do that by supplying color value for UIElement.IsMouseOver trigger.
I have a Button that I want to have an Orange Background with a White Foreground. When I mouse over the Button I would like it to display in DarkOrange. This is the Style I am using at the moment.
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="White"/>
<Setter Property="Background" Value="DarkOrange"/>
</Trigger>
</Style.Triggers>
</Style>
If I don't modify the original Background color of the my Button this style works fine. For example;
<Button Grid.Row="2" Content="SIGN IN" />
has no issues. However, when I want to change the default Background of the Button, i.e
<Button Grid.Row="2" Content="SIGN IN" Background="Orange"/>
the style does not work. I assume this is because I am now overriding the Background property that the IsMouseOver is attempting to change.
Is there a way I can achieve both a modified default Background and a IsMouseOver effect? I have also tried setting <Border Background="Orange"> but to no effect still.
You could just add a setter to your Style instead of adding a Background property to your Button. Like this:
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Orange"/>
<Setter Property="Template">
...
</Setter>
<Style.Triggers>
...
</Style.Triggers>
</Style>
Here is an another example of how to use it.
<Style TargetType="{x:Type Button}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate x:Name="Border" TargetType="{x:Type Button}">
<Border x:Name="Border" Background="{TemplateBinding Background}">
<ContentPresenter x:Name="CP" HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="CP" Property="TextBlock.Foreground" Value="White"/>
<Setter TargetName="Border" Property="Background" Value="DarkOrange"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This working fine with
<Button Grid.Row="2" Content="SIGN IN" Background="Orange"/>
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" />