I am new to WPF and I am trying to do a rounded corner textbox. There are a lot of examples I gather from here. However I cannot seems to make it work. Below are the two ways I tried and the results I obtained.
First Way:
<SolidColorBrush x:Key="SubTransparentTextBoxBG" Color="#ffffff" Opacity="0.12"/>
<Style TargetType="TextBox">
<Style.Setters>
<Setter Property="Background" Value="{StaticResource SubTransparentTextBoxBG}" />
<Setter Property="FontSize" Value="24px" />
<Setter Property="FontFamily" Value="Segoe UI Semibold"/>
<Setter Property="Foreground" Value="White" />
<Setter Property="Padding" Value="10" />
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="2"/>
</Style.Setters>
<Style.Resources>
<Style TargetType="Border">
<Setter Property="CornerRadius" Value="10"/>
</Style>
</Style.Resources>
</Style>
Result:
Apparently all my Setters took effect but not the Corner Radius
Second Way:
<SolidColorBrush x:Key="SubTransparentTextBoxBG" Color="#ffffff" Opacity="0.12"/>
<Style TargetType="TextBox">
<Style.Setters>
<Setter Property="Background" Value="{StaticResource SubTransparentTextBoxBG}" />
<Setter Property="FontSize" Value="24px" />
<Setter Property="FontFamily" Value="Segoe UI Semibold"/>
<Setter Property="Foreground" Value="White" />
<Setter Property="Padding" Value="10" />
<Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="2"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border x:Name="border" CornerRadius="10" BorderBrush="#000" BorderThickness="2" Background="#fff"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
Result:
This time, only the Round Border takes place and the rest of the Setters' property are overwritten.
Can Someone Please Help to point out what are the mistakes in these two ways?
The best way to give the TextBox rounded corners is to overwrite the template.
The following is your Style, but fixed. It now contains the mandatory parts with their mandatory naming: a content host named PART_ContentHost. In order to make the style setters work, you need to bind the template's controls (in this case the Border properties) to the appropriate properties of the templated parent (the TextBox) using TemplateBinding.
<Style TargetType="TextBox">
<Style.Setters>
<Setter Property="Background"
Value="{StaticResource SubTransparentTextBoxBG}" />
<Setter Property="FontSize"
Value="24px" />
<Setter Property="FontFamily"
Value="Segoe UI Semibold" />
<Setter Property="Foreground"
Value="White" />
<Setter Property="Padding"
Value="10" />
<Setter Property="BorderBrush"
Value="Red" />
<Setter Property="BorderThickness"
Value="2" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="TextBox">
<Border CornerRadius="10"
Margin="{TemplateBinding Margin}"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
Padding="{TemplateBinding Padding}">
<!-- The required part with the required name -->
<ScrollViewer x:Name="PART_ContentHost"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style.Setters>
</Style>
Wrapping the TextBox in a Border with rounded corners will still leave the TexBox with sharp corners that will overlap the rounded corners of the surrounding Border.
Some controls have mandatory template elements (parts) which must be part of the ContorlTemplate and have a special name. When those parts are missing or the name doesn't match the required name, then the functionality of the templated control might get broken. To know the parts and their names of TextBox visit TextBox Parts. To know the named parts of all WPF controls visit Control Styles and Templates. This links also contains an example of the actual Style and Template.
An alternative approach to get the required template parts is to select the control you wish to template and then open the XAML designer view. Right click on the selected control and select Edit Template. In the popup select Edit a Copy. A dialog opens. Here you can give the extracted template a name and set the location where the extracted template will be moved to. Now you can edit this template which already has all the required parts.
Related
I have a TextBox in WPF and I'm trying to make the border color change when the mouse hovers over the TextBox. Based on my experience with other elements in WPF, I need to insert a ControlTemplate value with TemplateBinding to the values I am trying to dynamically change. However, when I apply this, the box becomes uneditable (and the text disappears). If I remove the Template setter, the box becomes editable again, but the custom BorderBrush triggers do not work.
Here is the Style:
<Style x:Key="TextBoxBase" TargetType="TextBox">
<Setter Property="FontSize" Value="30"/>
<Setter Property="Background" Value="{StaticResource BrushLightGrey}"/>
<Setter Property="Foreground" Value="{StaticResource BrushNormalText}"/>
<Setter Property="IsReadOnly" Value="False"/>
<Setter Property="Height" Value="40"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Border BorderBrush="{TemplateBinding BorderBrush}">
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="BorderBrush" Value="{StaticResource BrushBlue}"/>
</Trigger>
</Style.Triggers>
<Style.Resources>
<Style TargetType="{x:Type Border}">
<Setter Property="CornerRadius" Value="5"/>
</Style>
</Style.Resources>
</Style>
Any suggestions or help is appreciated. Thanks.
You missed out the critical part:
<ScrollViewer Margin="0"
x:Name="PART_ContentHost" />
This is what hosts the text.
See
https://learn.microsoft.com/en-us/dotnet/framework/wpf/controls/textbox-styles-and-templates
TextBox Parts
The following table lists the named parts for the TextBox control.
TEXTBOX PARTS
Part Type Description
PART_ContentHost FrameworkElement A visual element that can contain a FrameworkElement. The text of the TextBox is displayed in this element.
I start to learn WPF and there is something that is still unclear for me:
i created new Style for button:
<!-- no border button style -->
<Style x:Key="NoBorderButton" TargetType="Button">
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Style.Triggers>
<Trigger Property="Control.IsMouseOver" Value="true">
<Setter Property="Control.FontSize" Value="18" />
</Trigger>
<Trigger Property="Control.IsMouseOver" Value="true" >
<Setter Property="Foreground" Value="LightSkyBlue" />
</Trigger>
<Trigger Property="Control.IsMouseOver" Value="false" >
<Setter Property="Foreground" Value="White" />
</Trigger>
</Style.Triggers>
</Style>
So this is my button:
<Button Content="Button" Style="{StaticResource NoBorderButton}">
</Button>
Now after search for solution to remove all the border i found this template that need to be add to the button:
<Button.Template>
<ControlTemplate TargetType="{x:Type Button}">
<ContentPresenter Content="{TemplateBinding Content}"/>
</ControlTemplate>
</Button.Template>
So i have several questions:
What this template doing ?
Why i cannot add it the the style i created inside my Windows.Resources ?
I'm going to attempt to answer your questions directly, however there is much that can be discussed here.
What this template doing?
All controls have some kind of default template, a pre-defined look and feel of what the control looks like.
The Template is overriding the default look and feel for your Button. What you are effectively doing is completely starting afresh a new template for a button.
So for example, you can define a new template for what a button would look like. It can be a TextBlock inside an Ellipse for example. Instead of the default button template.
It's hard to put into words, but I think I explained that well enough.
Why i cannot add it the the style i created inside my Windows.Resources?
You can:
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
...
</ControlTemplate>
</Setter.Value>
</Setter>
Problem domain: In my WPF application, I change background of lot of UI controls like Button or ListItems dynamically based on data they contain. The background is changed either when the control is loaded or based on use action/data received.
Problem statement: If the background is too dark (Green/Blue) I want to set the foreground to white else black.
Constraints: I have a big application and performance is a major concern. That's why I am hesitant to use converters and am looking for some xaml/style trigger based solutions as this is just a condition based issue.
Solutions tried: To keep it simple, I am explaining what I tried for a simple wpf button:
<UserControl.Resources>
<Style x:Key="NoChromeButton" TargetType="{x:Type Button}">
<Setter Property="Background" Value="{Binding Background}"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="BorderBrush" Value="White"/>
<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}">
<Grid x:Name="Chrome"
Background="{TemplateBinding Background}"
SnapsToDevicePixels="true"
HorizontalAlignment="Stretch">
<TextBlock
Text="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Content}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
Background="{Binding RelativeSource={RelativeSource TemplatedParent},Path=Background}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
Style="{StaticResource MyTextBlockStyle}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="Black"/>
</Trigger>
<Trigger Property="Background" Value="White">
<Setter Property="Foreground" Value="Aqua"/>
</Trigger>
<Trigger Property="Background" Value="Transparent">
<Setter Property="Foreground" Value="BlueViolet"/>
</Trigger>
<Trigger Property="Background" Value="Green">
<Setter Property="Foreground" Value="Blue"/>
</Trigger>
<Trigger Property="Background" Value="Yellow">
<Setter Property="Foreground" Value="Red"/>
</Trigger>
<Trigger Property="Background" Value="Red">
<Setter Property="Foreground" Value="Yellow"/>
</Trigger>
<Trigger Property="Background" Value="Black">
<Setter Property="Foreground" Value="DarkSeaGreen"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style x:key="MyTextBlockStyle" TargetType="TextBlock">
<Setter Property="FontSize" Value="12"/>
<Setter Property="FontStyle" Value="Italic"/>
<Style.Triggers>
<Trigger Property="Background" Value="White">
<Setter Property="Foreground" Value="Aqua"/>
</Trigger>
<Trigger Property="Background" Value="Transparent">
<Setter Property="Foreground" Value="BlueViolet"/>
</Trigger>
<Trigger Property="Background" Value="Green">
<Setter Property="Foreground" Value="Blue"/>
</Trigger>
<Trigger Property="Background" Value="Yellow">
<Setter Property="Foreground" Value="Red"/>
</Trigger>
<Trigger Property="Background" Value="Red">
<Setter Property="Foreground" Value="Yellow"/>
</Trigger>
<Trigger Property="Background" Value="Black">
<Setter Property="Foreground" Value="DarkSeaGreen"/>
</Trigger>
</Style>
</UserControl.Resources>
When button is created in the XAML:
<Button Content="{Binding Name}" Style="{StaticResource NoChromeButton}"/>
Also, I would like to point out a couple of things in the above style:
If I would have used ContentPresenter instead of TextBlock inside the Grid named Chrome, background property was not set on the ContentPresenter and when I snooped (http://snoopwpf.codeplex.com/) the UI, I found that the ContentPresenter has TextBlock whose Background was always set to Default and hence no styletriggers were applied to the TextBlock. Also, this TextBlock's background valuesource is Default.
On the other hand, when I use TextBlock directly inside the Grid named Chrome, I can set its background explicitly to Grid's Background which is set to Button's Background. Snooping reveals that now TextBlock's Background ValueSource is ParentTemplate.
Button picks up MyTextBlockStyle while displaying its content.
Style triggers for Button or TextBlock were never triggered unless I did mouse over the button which changes the button's background to Black and propagates this value down to TextBlock background changing the TextBlock's foreground color to DarkSeaGreen.
Also, changing the button's background in snoop utility while application is running, triggers the Style Triggers.
Questions:
Why none of the Style triggers work for Background property whereas they work for IsMouseOver property?
What I am doing wrong?
Any solution for this?
I found the solution to my problem.
TextBlock does not derive from Control. Any text shown on UI by any control internally uses TextBlock to represent the textual content. If TextBlock style is set using the following in ResourceDictionary:
<Style TargetType="TextBlock">
<Setter Property="FontFamily" Value="Arial" />
<Setter Property="FontSize" Value="12" />
<Setter Property="FontStyle" Value="Normal" />
</Style>
Any control that represents text will have this style (since no key is assigned to this style which implies that all TextBlock will get it by default) unless the control's template override the TextBlock's default style which can be done as follows:
<Button Grid.Column="1" Style="{StaticResource NoChromeButton}">
<TextBlock Style="{x:Null}" Text="abc" FontFamily="Segoe UI Symbol"/>
</Button>
This simple setting has resolved most of the issues we have with dynamic foreground color changing.
I have a style for button as follow:
<Style TargetType="Button" x:Key="BlackButton">
<Setter Property="Background" Value="Black"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="red" />
</Trigger>
</ControlTemplate.Triggers>
<ContentPresenter />
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
and a button on which is defined as follow:
<Button Canvas.Left="19" Canvas.Top="520" Height="34" Width="107"
Style="{StaticResource BlackButton}" />
But when I run application, I can not see the button. Its background set to none.
If I change the style as follow:
<Style TargetType="Button" x:Key="BlackButton">
<Setter Property="Background" Value="Black"/>
</Style>
(Removing the template) then the button is shown but its background is not changing.
What is wrong with this xaml code?
You've overridden the template of your control in order to set the MouseOver trigger. That means your control template is otherwise empty - and so nothing is drawn for your button.
You can fix that by moving your triggers to the style itself, like this:
<Style TargetType="Button" x:Key="BlackButton">
<Setter Property="Background" Value="Black"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="red" />
</Trigger>
</Style.Triggers>
</Style>
However, what you're likely to run into now is that the button's built-in MouseOver animation will override your red background. You'll see a flash of red, followed by a transition to the default Windows colour. One way to fix that thoroughly is to take a full copy of the default Button template (using Expression Blend is the easiest way to do this) and remove the animations from it.
Well your ControlTemplate is simply empty, although you have a ControlPresenter in it. But since its Content property is not set, it's also empty. To have a Background you will have to add a Border.
<Style TargetType="Button" x:Key="BlackButton">
<Setter Property="Background" Value="Black"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="Button">
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="red" />
</Trigger>
</ControlTemplate.Triggers>
<Border Background="{TemplateBinding Background}">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
This should show you something.
I want to override the default TextBox borders in WPF. I have this style that applies on all TextBoxes.
<!-- StyleTextBox-->
<Style x:Key="StyleTextBox" TargetType="{x:Type TextBox}">
<Setter Property="MinHeight" Value="20" />
<Setter Property="HorizontalAlignment" Value="Left"/>
<Setter Property="Margin" Value="3"/>
<Setter Property="IsEnabled" Value="{DynamicResource WriteAble}"/>
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="VerticalContentAlignment" Value="Top" />
<Setter Property="BorderThickness" Value="1" />
<Setter Property="BorderBrush" Value="{StaticResource ButtonFont_DarkGray}" />
<Style.Triggers>
<!--Resolves multiline textbox vertical alignment problem-->
<Trigger Property="TextWrapping" Value="NoWrap">
<Setter Property="VerticalContentAlignment" Value="Center" />
</Trigger>
</Style.Triggers>
</Style>
I added SnapsToDevicePixels="True" to display borders correctly on LCD monitors.
But, every TextBox seems to be different. Some borders are missing, or gray..
Does anyone know why?
You could try editing the template for the textboxes and changing the border name Bd to a "real" border instead of the chrome one. Like this:
<ControlTemplate x:Key="TextBoxBaseControlTemplate1"
TargetType="{x:Type TextBoxBase}">
<Border x:Name="Bd" SnapsToDevicePixels="True"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}" >
<ScrollViewer x:Name="PART_ContentHost"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Border>
<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>
add this setter to your style to enable the template:
<Setter Property="Template"
Value="{DynamicResource TextBoxBaseControlTemplate1}"/>
WPF tries to be device independent when rendering UI to the monitor, and won't draw things "pixel perfect" unless you tell it to. Try adding this to your style:
<Setter Property="SnapsToDevicePixels" Value="True" />
That should tell WPF to render each 1-pixel-thick border along a single pixel line.
You need do the followings to solve that problem...
SnapsToDevicePixels="True"
Dont specify width, do it with the margins
<Setter Property="BorderBrush" HorizontalAlignment="Left" Margin="2,2,2,2"
Value="{StaticResource DarkGray}" />
Hope this helps :)
I think that the left border and upper border is styled, but the right and buttom border stays gray, although I explicitly said in Style that BorderBrush=MyBrush.
What do you think? What about to try remove the 3D effect from TextBox?