Not able to Style my WPF Controls - wpf

I am going crazy here! What am I missing and why it is not styling anything:
<Style x:Key="textBoxStyle" TargetType="{x:Type TextBox}">
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
<TextBox Width="100" Style="{StaticResource textBoxStyle}" Height="20" Background="Yellow" ></TextBox>
The above code does not do anything. It does not highlight the TextBox control!

This occurs because local values override style values. (Properties set directly on an element have very high precedence.) You are setting Background directly on the TextBox, so WPF is going, "Well, he normally wants textBoxStyle backgrounds to be Red when focused, but for this particular TextBox, he's said he specifically wants Background to be Yellow, so Yellow it is."
So the fix is to move the Yellow background to be part of the Style:
<Style x:Key="textBoxStyle" TargetType="{x:Type TextBox}">
<Setter Property="Background" Value="Yellow" />
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
and remove it from the TextBox:
<TextBox Width="100" Style="{StaticResource textBoxStyle}" Height="20" />

Define your Style before the TextBox or use DynamicResource instead of StaticResource

Related

Change style of a child control on MouseOver in WPF

I have a custom control that holds two Rectangles and several TextBoxes. I wish to change the background color of the Rectangle on MouseOver.
I add trigger as following:
<Rectangle
Grid.Column="1"
Fill="#FF383838"
Grid.ColumnSpan="3"
Margin="0,4,4,4">
<Rectangle.Style>
<Style TargetType="{x:Type Rectangle}">
<Setter Property="Fill" Value="#FF383838" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Fill" Value="#FF575757" />
</Trigger>
</Style.Triggers>
</Style>
</Rectangle.Style>
</Rectangle>
But since rectangle is part of my control, I assume the event is not firing.
Setting a property via XAML will be applied over the style properties that you try to set. To fix this, remove Fill=#FF383838 so you should have:
<Rectangle Grid.Column="1"
Grid.ColumnSpan="3"
Margin="0,4,4,4">
//... rest of code here
Try this code:
<Window.Resources>
<Style TargetType="Rectangle" x:Key="test">
<Setter Property="Fill" Value="#FF383838" />
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Fill" Value="#FF575757" />
</Trigger>
</Style.Triggers>
</Style>
</Window.Resources>
<Rectangle Style="{StaticResource test}" />

WPF: the style of control does not work unless the control template created for style

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).

Styling DataGridCell correctly

This is a question following my previous problem, you can find it right there
So. Now I defined a DataGrid with a specific ElementStyle for each column (which just defines the TextBlocks inside in bold & white -- will come over this problem later)
So now I have two questions
First question (solved)
When I happen to set a background to my cell, it overrides the default style, and the background stays the same when the cell is highlighted.
One example of a style:
<!-- Green template for market-related -->
<ControlTemplate x:Key="Green" TargetType="{x:Type tk:DataGridCell}">
<Grid Background="Green">
<ContentPresenter
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
I'd naturally say that this is "normal" because I set the Grid's background to Green. I therefore tried it this way:
<!-- Light green template for sophis-related -->
<ControlTemplate x:Key="LightGreen" TargetType="{x:Type tk:DataGridCell}">
<Grid Background="LightGreen">
<Grid.Resources>
<Style TargetType="{x:Type Grid}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type tk:DataGridCell}},
Converter={StaticResource DebugConverter}}" Value="True">
<Setter Property="Grid.Background" Value="#FF3774FF" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
This won't work either. As you can see I put a DebugConverter so I can check that the trigger is actually called, which is the case, but... Background does not change (and Snoop confirms this...)
Third try:
<!-- Light green template for sophis-related -->
<ControlTemplate x:Key="LightGreen" TargetType="{x:Type tk:DataGridCell}">
<ControlTemplate.Resources>
<Style TargetType="{x:Type tk:DataGridCell}">
<Setter Property="Background" Value="LightGreen" />
</Style>
</ControlTemplate.Resources>
<Grid>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
And... No background will be displayed (stays transparent)
So I think I am working in the wrong way here and I was wondering what should I do to JUST define the "not selected" template.
I would say that I may need to define a style BasedOn the "classic" style but, how would I do that? I tried to add TemplateBindings with no success
** EDIT: Solution**
As H B suggested in his answer, problem was coming from DependencyProperty Precedence, here's the solution:
<!-- Light green template for sophis-related -->
<ControlTemplate x:Key="LightGreen" TargetType="{x:Type tk:DataGridCell}">
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type Grid}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type tk:DataGridCell}},
Converter={StaticResource DebugConverter}}" Value="True">
<Setter Property="Grid.Background" Value="#FF316AC5" />
</DataTrigger>
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type tk:DataGridCell}},
Converter={StaticResource DebugConverter}}" Value="False">
<Setter Property="Grid.Background" Value="LightGreen" />
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Resources>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
Second question
Now, let's speak Triggers.
Basically, what I want to do is to define specific Triggers to my ElementStyle so the font color is white if the cell's background is Red or Green (the only aim of this is to have a better readability as Red and Green are kinda dark, black font on dark background results in a nice fail :p )
Edit Seems like I'm not clear enough: the following style is the style applied to each item of the datagrid, through the property DataGridTextColumn.ElementStyle. Here is the code handling that:
void VolatilityDataGrid_AutoGeneratingColumn(object sender, DataGridAutoGeneratingColumnEventArgs e)
{
DataGridTextColumn column = e.Column as DataGridTextColumn;
column.ElementStyle = s_boldCellStyle;
// Other stuff here...
}
Here is what I do:
<!-- Cell style for colored matrix-->
<Style x:Key="BoldCellStyle" TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding Background, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type tk:DataGridCell}}}"
Value="Red">
<Setter Property="Foreground" Value="White" />
</DataTrigger>
<DataTrigger Binding="{Binding Background, RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type tk:DataGridCell}},
Converter={StaticResource DebugConverter}}"
Value="Green">
<Setter Property="Foreground" Value="White" />
</DataTrigger>
</Style.Triggers>
<Setter Property="FontWeight" Value="Bold"/>
</Style>
And... It doesn't work. Strangely, what goes through converter is ONLY transparent background colors. I am definitely missing something here!
BTW, I also tried with classic triggers, no success either, I use DataTriggers here so I can debug the binding values!
Now I've been stuck for more than three days on this and I'm starting to freak out... Hopefully the Stackoverflow community will save me :)
Thanks!
Edit
Okay, update.
I understood why my Trigger does not work. The Background actually set is on the Grid and NOT on the DataGridCell. It is therefore normal that I don't get any color set there.
However, I ran some tests and found out that when the binding is set, the TextBlock does not have any parent yet (Parent = null). Binding to a RelativeSource of type Grid will bind me to... The whole DataGrid items presenter.
I'm not sure what to do now, since it seems like that from the actual TextBlock style I can't reach the parent Grid and therefore cannot resolve what color should I display according to the background.
Also, I can't change the Font color in my ControlTemplate because the DataGrid wants a Style for each column, which overrides the template's style by default (see my previous question and its answer)
So... Stuck again I am!
Dependency Property Value Precedence
This:
<Grid Background="LightGreen">
<Grid.Resources>
<Style TargetType="{x:Type Grid}">
<!-- Trigger Stuff -->
</Style>
</Grid.Resources>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
Needs to be:
<Grid>
<Grid.Resources>
<Style TargetType="{x:Type Grid}">
<Setter Property="Background" Value="LightGreen"/>
<!-- Trigger Stuff -->
</Style>
</Grid.Resources>
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
Not sure about your second question as of now, possibly a related problem, i would suggest setting TextElement.Foreground instead of Foreground for starters. Getting Transparent as value is not very helpful, what control template do you use for the DataGridCell? If it is custom, is the Background hooked up properly via a TemplateBinding?
This works as long as the Background property is used, so if you have a ControlTemplate which sets things internally you need to externalize that. A normal DataGrid example:
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Background" Value="LightGreen"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Content}" Value="Apple">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding Content}" Value="Tomato">
<Setter Property="Background" Value="Green"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger Binding="{Binding Background, RelativeSource={RelativeSource AncestorType={x:Type DataGridCell}}}" Value="Red">
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
<DataTrigger Binding="{Binding Background, RelativeSource={RelativeSource AncestorType={x:Type DataGridCell}}}" Value="Green">
<Setter Property="Foreground" Value="White"/>
</DataTrigger>
</Style.Triggers>
<Setter Property="FontWeight" Value="Bold"/>
</Style>
So if the CellStyle sets the ControlTemplate the properties need to be hooked up via TemplateBinding. e.g.
<DataGrid.CellStyle>
<Style TargetType="{x:Type DataGridCell}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type DataGridCell}">
<Grid Background="{TemplateBinding Background}">
<ContentPresenter />
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="LightGreen"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Content}" Value="Apple">
<Setter Property="Background" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding Content}" Value="Tomato">
<Setter Property="Background" Value="Green"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.CellStyle>
Do not do the triggering inside the template or it will get messy.

WPF Combobox Validation.ErrorTemplate error

I have a combobox that i need to edit its error template to show a red border when there is a validation error.
I am using the following style
<Style TargetType="{x:Type ComboBox}" >
<Setter Property="Validation.ErrorTemplate">
<Setter.Value>
<ControlTemplate>
<DockPanel>
<Border BorderBrush="Red" BorderThickness="3">
<AdornedElementPlaceholder />
</Border>
</DockPanel>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="FontFamily" Value="Segoe UI" />
<Setter Property="FontSize" Value="12" />
<Setter Property="VerticalAlignment" Value="Center" />
</Style>
The border never shows up when validation errors occur. Any tips what is going wrong?
The Style you posted works. You should check your binding, did you add ValidatesOnDataErrors=True and ValidatesOnExceptions=True to the binding of SelectedValue?
enter code heretry without the dock panel, that is uneuseful since it wraps jus one element. However, sicnecerely I don't wnow if it makes sense to wrap a textbox with a border, since it has already a border! You should try to change directly the colour of its border. You could try to use again the panel but then put the border around the panel ie:
Border BorderBrush="Red" BorderThickness="3"
DockPanel
AdornedElement
This makes more sense because the wrap panel has not its own border.
Use This.
<Style x:Key="textBoxStyle" TargetType="{x:Type telerik:RadMaskedTextBox}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="ToolTip" Value="{Binding RelativeSource={x:Static RelativeSource.Self}, Path=(Validation.Errors)[0].ErrorContent}"/>
<Setter Property="Control.BorderBrush" Value="Red" />
</Trigger>
</Style.Triggers>
</Style>
I don't like any of the responses here. Simply put, how do you change the border color for the error template for a ComboBox using Blend or not? It shouldn't be acceptable to draw another border around the existing border of the ComboBox. I've figured out how to creat a ControlTemplate in Blend but not a Validation Template.
I've come close with trying to make it appear like I've changed the actual border color, but that's not what I actually want. Suggestions? To complicate it a bit, I'd like to display a red asterisk outside of the right border of the control.
The following code is a close attempt, but it is actually drawing a border inside the ComboBox and if you look close, you can see that the border is 2 pixels wide when combined with the ComboBox border:
<DockPanel Name="myDockPanel">
<AdornedElementPlaceholder>
<Border BorderBrush="Blue" BorderThickness="1" CornerRadius="2" />
</AdornedElementPlaceholder>
<TextBlock Text="*" FontWeight="Bold" FontSize="14" Foreground="Red" DockPanel.Dock="Left" ToolTip="{Binding .CurrentItem}" />
</DockPanel>
I searched around some more and came up with a solution based on another article here: WPF - How to apply style to AdornedElementPlaceholder's AdornedElement?
<!-- This works -->
<ComboBox Name="comboBox1" Style="{StaticResource NewComboBoxStyle}" Validation.ErrorTemplate="{StaticResource comboBoxValidationTemplate}" />
<SolidColorBrush x:Key="MainBorderBrush">#FF91B3FF</SolidColorBrush>
<Style x:Key="NewComboBoxStyle" TargetType="{x:Type ComboBox}" BasedOn="{StaticResource myErrorTemplate}">
<Setter Property="BorderBrush" Value="{DynamicResource MainBorderBrush}" />
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="True">
<Setter Property="BorderBrush" Value="Blue" />
</Trigger>
</Style.Triggers>
</Style>
<!-- Sets ToolTip when Validation.HasError is True. -->
<Style TargetType="Control" x:Key="myErrorTemplate">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={x:Static RelativeSource.Self},
Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
<ControlTemplate x:Key="comboBoxValidationTemplate">
<DockPanel Name="myDockPanel">
<AdornedElementPlaceholder/>
<TextBlock Text="*" FontWeight="Bold" FontSize="14" Foreground="Red" DockPanel.Dock="Left" ToolTip="{Binding .CurrentItem}" />
</DockPanel>
</ControlTemplate>

How to parametrize WPF Style?

I'm looking for a simplest way to remove duplication in my WPF code.
Code below is a simple traffic light with 3 lights - Red, Amber, Green. It is bound to a ViewModel that has one enum property State taking one of those 3 values.
Code declaring 3 ellipses is very duplicative. Now I want to add animation so that each light fades in and out - styles will become even bigger and duplication will worsen.
Is it possible to parametrize style with State and Color arguments so that I can have a single style in resources describing behavior of a light and then use it 3 times - for 'Red', 'Amber' and 'Green' lights?
<UserControl.Resources>
<l:TrafficLightViewModel x:Key="ViewModel" />
</UserControl.Resources>
<StackPanel Orientation="Vertical" DataContext="{StaticResource ViewModel}">
<StackPanel.Resources>
<Style x:Key="singleLightStyle" TargetType="{x:Type Ellipse}">
<Setter Property="StrokeThickness" Value="2" />
<Setter Property="Stroke" Value="Black" />
<Setter Property="Height" Value="{Binding Width, RelativeSource={RelativeSource Self}}" />
<Setter Property="Width" Value="60" />
<Setter Property="Fill" Value="LightGray" />
</Style>
</StackPanel.Resources>
<Ellipse>
<Ellipse.Style>
<Style TargetType="{x:Type Ellipse}" BasedOn="{StaticResource singleLightStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding State}" Value="Red">
<Setter Property="Fill" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
<Ellipse>
<Ellipse.Style>
<Style TargetType="{x:Type Ellipse}" BasedOn="{StaticResource singleLightStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding State}" Value="Amber">
<Setter Property="Fill" Value="Red" />
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
<Ellipse>
<Ellipse.Style>
<Style TargetType="{x:Type Ellipse}" BasedOn="{StaticResource singleLightStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding State}" Value="Green">
<Setter Property="Fill" Value="Green" />
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
</StackPanel>
As long as your "Traffic Light" is wrapped up inside a control, which it appears it is, I don't think this is horrible. Each ellipse is well defined and has different triggers, each indicating its own state. You've already factored the common parts out into the base style, which is good.
You could wrap the individual ellipses inside another user control (which wouldn't need a backing ViewModel) that had an ActiveState property and an ActiveFill property. Then your TrafficLight looks something like:
<StackPanel Orientation="Vertical" DataContext="{StaticResource ViewModel}">
<my:Indicator State="{Binding State}" ActiveState="Red" ActiveFill="Red" />
<my:Indicator State="{Binding State}" ActiveState="Amber" ActiveFill="Red" />
<my:Indicator State="{Binding State}" ActiveState="Green" ActiveFill="Green" />
</StackPanel>
This lets you wrap up all your Ellipse styling inside your Indicator control and the only thing that control needs to worry about is comparing the State to the ActiveState to determine if it should fill itself with the ActiveFill brush.
As to if this is worth the effort or not, that depends on how many of these you have floating around and if you use them outside of your Traffic Light user control. Remember: You Ain't Gonna Need It.

Resources