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>
Related
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.
I want to create custom visual style for my buttons.
I need a simple working example of how to override default visual style of a button. As well as a simple explanation of how to apply it.
I want to get something working, so I can start from there and experiment my way further.
I've tried to add a new recourse dictionary as follows:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="mstyle" TargetType="Button">
<Setter Property="FontWeight" Value="Bold" />
</Style>
</ResourceDictionary>
after that I've created some new button in runtime and tried to apply this style to it:
Dim MyButton As New Button
Dim st As New Style
st = Application.Current.FindResource("mstyle")
MyButton.Style = st
When I try to run this, I get an error that the recourse 'mstyle' could not be found.
You don't in most cases need any code behind to do that all what you need is to define a custom style that target your button in the resource dictionary or in the window resource here an example :
<Style x:Key="DarkStyleButton" TargetType="{x:Type Button}">
<Setter Property="Background" Value="#373737" />
<Setter Property="Foreground" Value="White" />
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontSize" Value="12" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border CornerRadius="4" Background="{TemplateBinding Background}">
<Grid>
<ContentPresenter x:Name="MyContentPresenter" Content="{TemplateBinding Content}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0" />
</Grid>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="#E59400" />
<Setter Property="Foreground" Value="White" />
</Trigger>
<Trigger Property="IsPressed" Value="True">
<Setter Property="Background" Value="White" />
<Setter Property="Foreground" Value="Black" />
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Background" Value="Gray" />
<Setter Property="Foreground" Value="LightGray" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
first set the value for the properties you want to customize,
then set the button template and don't forget to add the
ContentPresenter that will hold the button content
finally define triggers to handle the mouse over, click and what ever
else you want to set a custom look when it triggers (for example the
desable/enabled )
To use that style here how
<Button x:Name="BrowseButton" Margin="5" Style="{StaticResource DarkStyleButton}" ToolTip="tooltip about the button">
<Button.Content>
<StackPanel Orientation="Horizontal">
<Image Source="../BrowseImage.png"/>
<TextBlock Text="Browse" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="5"></TextBlock>
</StackPanel>
</Button.Content>
</Button>
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.
It almost gets me mad in recent days. I have a textbox and the style in xaml file.
But the style without a control template cannot take effect on textbox. Whereas, a control template works, but control template seems to overwrite the textbox totally, the default behaviors loses of textbox such as editing, inputing or selecting...
Here is content of xaml with the control template:
<Style TargetType="{x:Type TextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Name="tbBorder" Background="White" BorderThickness="0.6" BorderBrush="#B9B9B9">
<ContentPresenter/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="BorderBrush" Value="#4D90FE" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="#4D90FE" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And here is the simple style which does not work at all,
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="BorderBrush" Value="#4D90FE" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="#4D90FE" />
</Trigger>
</Style.Triggers>
</Style>
Thanks!
update: the entire textbox's code snipt:
<TextBox Height="23" HorizontalAlignment="Left" Margin="114,53,0,0" Name="textBox1" VerticalAlignment="Top" Width="150" Text="{Binding Path=TraderAccount, Mode=OneWayToSource, NotifyOnValidationError=True}" BorderBrush="#FFB9B9B9" BorderThickness="1" >
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="BorderBrush" Value="Red" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
The style setter is working for me but the issue I see is that the controls animations are animating away the style that's just been set.
You may want to extract the original control template and redefine that rather than completely redefining it. As far as I know The textbox control is more complex than just a border with a content presenter (I've never extracted the control template for it though!) and its likely to have a couple of borders that work to give it all it's states etc
You can use Blend to do this - in the absence of Blend there is the MSDN resource for control templates and styles:
http://msdn.microsoft.com/en-us/library/aa970773.aspx
Edit:
For starters it looks to me like you are missing the content 'PART' in your redefined template
<ScrollViewer Margin="0" x:Name="PART_ContentHost" />
Edit 2:
You are saying it doesn't work... this works for me on WPF using .NET Framework 4.0 - I changed the border colour to 'Red' instead to make sure I could see the effect and it definitely works, aside from the red fading immediately because the controls visual state is changed by the Visual State Manager (which is why you need to edit the control template and change the visual states)
<TextBox>
<TextBox.Style>
<Style TargetType="TextBox">
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="BorderBrush" Value="Red" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
When you hover over the box, you get a red border which immediately fades
Does this XAML not work for you at all??
You did not post TextBox code but I assume (it happened to me too) that you simply forgot to set BorderThickness of your textbox:
<TextBox BorderThickness="4">
<TextBox.Style>
<Style TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="true">
<Setter Property="BorderBrush" Value="#4D90FE" />
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" Value="#4D90FE" />
</Trigger>
</Style.Triggers>
</Style>
</TextBox.Style>
</TextBox>
Your style does work, set a property like Background in your style without the template and you will see that it does get applied. However, like someone else mentioned, the reason you do not see any changes is because of animation in the default WPF control template for TextBox (Animation values always take precedence over local values, setters and triggers). When you redefine the control template, those animations are no longer there and so your example works. What you could do is take the default TextBox template and modify it to suit your purposes (can be found here: http://msdn.microsoft.com/en-us/library/cc645061%28VS.95%29.aspx).