Static Resource Style
<Style TargetType="{x:Type ToggleButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<Border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="1" Background="{TemplateBinding Background}">
<ContentPresenter HorizontalAlignment="Center" VerticalAlignment="Center"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="Background" Value="Red"></Setter>
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Background" Value="Green" />
</Trigger>
</Style.Triggers>
</Style>
Toggle Button Code
<ToggleButton Grid.Row="3" Grid.Column="1" ToolTip="Toggle to Show and Hide Date" IsChecked="True" Cursor="Hand">
<ToggleButton.Style>
<Style TargetType="{StaticResource ToggleButton}">
<Setter Property="Content" Value="No Date" />
<Style.Triggers>
<Trigger Property="IsChecked" Value="True">
<Setter Property="Content" Value="Date" />
</Trigger>
</Style.Triggers>
</Style>
</ToggleButton.Style>
</ToggleButton>
But I'm unable to set the content getting error as Content is not recognized or inaccessible.
I'm not a frequent user of WPF.
Thanks
In your Toggle Button Code, Change
<Style TargetType="{StaticResource ToggleButton}">
to
<Style TargetType="{x:Type ToggleButton}" BasedOn="{StaticResource {x:Type ToggleButton}}">
You can have a style defined for a control globally that is/may apply to all controls of that type, but when you have to give individual control some extra styling you can do that by creating a style within the control and base that style on the global style.
This basedOn can be done on style x:Type (as in my answer), or can be based on x:Name as well if you want to base it on a specific style.
I have a button template, where ContentPresenter is actually a Path.
On MouseOver, I want to change the Path.Fill.
See the template:
<Style x:Key="spinButtonStyle" TargetType="{x:Type RepeatButton}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type RepeatButton}">
<Border x:Name="Bd" BorderThickness="0" Background="Transparent" CornerRadius="2">
<ContentPresenter HorizontalAlignment="Stretch" VerticalAlignment="Stretch" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" TargetName="Bd" Value="#666666"/>
<Setter Property="Control.Foreground" Value="#999999"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
And the code that uses this template:
<RepeatButton Style="{StaticResource spinButtonStyle}">
<Path blah blah blah/>
</RepeatButton>
Now, in this case, that ContentPresenter is a Path. Is there any way to change the Path.Fill color in the XAML?
All you need to do now is bind the Path Fill property to your RepeatButton Foreground property:
Fill="{Binding Path=Foreground, RelativeSource={RelativeSource AncestorType={x:Type RepeatButton}}}"
WPF /MVVM Pattern
User control with multiple textboxes using validation attributes.
In the following style, everything works as intended- EXCEPT for those with a validation error, the Focused backcolor is not being set, due to the method used in the control template to set the error image.
If I remove the control template, the backcolor is set properly when focused if the validation error is set. With the template, backcolor is always white/default.
Any suggestions on the XAML required to have both - different backcolor when focused and the error image when validation fails?
<Style TargetType="{x:Type TextBox}">
<Setter Property="Margin" Value="1" />
<Setter Property="ToolTip" Value="{Binding Description}"/>
<Style.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="LightYellow"/>
</Trigger>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
<!--adds the error image and border, but also prevents background color change OnFocus-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<Border
BorderBrush="#d99" x:Name="textBorder" CornerRadius="4"
BorderThickness="2" >
<ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
</Border>
<Image Name="ErrorImage" Width="24" Height="24" Margin="0,0,4,0"
Source="/Images/error.png" HorizontalAlignment="Right">
</Image>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
</Style>
You should check out the DependencyProperty precedence rules
What happens here is that the ControlTemplate you define overrides the Style.Triggers defined just before.
What you can do to make it work is to set the actual Style with appropriate Triggers directly inside of the ControlTemplate
Otherwise, as you've seen, WPF will just use the default template for a Grid.
The code should look like that:
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self}, Path=(Validation.Errors).CurrentItem.ErrorContent}"/>
<!--adds the error image and border, but also prevents background color change OnFocus-->
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<Grid.Style>
<Style TargetType="{x:Type Grid}">
<Style.Triggers>
<!-- The trigger about IsFocused should be here!
<Trigger Property="IsFocused" Value="True">
<Setter Property="Background" Value="LightYellow"/>
</Trigger>
</Style.Triggers>
</Style>
</Grid.Style>
<Border
BorderBrush="#d99" x:Name="textBorder" CornerRadius="4"
BorderThickness="2" >
<ScrollViewer Margin="0" x:Name="PART_ContentHost"/>
</Border>
<Image Name="ErrorImage" Width="24" Height="24" Margin="0,0,4,0"
Source="/Images/error.png" HorizontalAlignment="Right">
</Image>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Trigger>
</Style.Triggers>
I have a situation where I need to style the selected item in a ComboBox differently (make the text bold) when it is one of all except one value. For example, in the drop-down box labelled "What is your favourite primary colour?" I would have four options: No Preference, Red, Green, and Blue. The ComboBox items are just text with default styling, no images or anything else fancy, and are C# classes, not wrapped in ComboBoxItems.
When the user specifies a preference from the list, I want to highlight that choice by setting the text of the selected item in the collapsed list to be bold. If the user chooses No Preference, the font weight should remain normal.
I have achieved a 90% solution by setting the FontWeight property on the ComboBox to Bold in a Style with a DataTrigger defined as SelectedItem != No Preference. However, this styles all items in the ComboBox's list of items, including all those in the drop-down list. I would like those items to always be displayed with a normal font weight.
Is this possible?
Edit
I have been trying #crazyarabian's method of styling the ComboBoxItem with a MultiTrigger. The style definition is:
<Style x:Key="SelectedItemStyle">
<Setter Property="ComboBoxItem.FontWeight" Value="Normal" />
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="ComboBoxItem.IsSelected" Value="True" />
<Condition Binding="{Binding IsNoPreferenceSelected,Mode=OneWay}" Value="False" />
</MultiTrigger.Conditions>
<Setter Property="ComboBoxItem.FontWeight" Value="Bold" />
</MultiTrigger>
</Style.Triggers>
</Style>
and it is applied to a ComboBox in the following DataTemplate:
<DataTemplate x:Key="PrimaryColoursTemplate" DataType="{x:Type ViewModels:PrimaryColoursViewModel}">
<ComboBox ItemsSource="{Binding PrimaryColours}" SelectedItem="{Binding SelectedPrimaryColour}"
ItemContainerStyle="{StaticResource SelectedItemStyle}" />
</DataTemplate>
Unfortunately, this kills WPF:
System.Windows.Data Error: 8 : Cannot save value from target back to source. BindingExpression:Path=IsDropDownOpen; DataItem='ComboBox' (Name=''); target element is 'ToggleButton' (Name=''); target property is 'IsChecked' (type 'Nullable`1') InvalidOperationException:'System.InvalidOperationException: Must have non-null value for 'Property'.
The application dies with a NullReferenceException, which is thrown after the InvalidOperationException above (or perhaps leads to it, I can't decipher the output). The only thing I can think of that might be causing this is resolving the property in the binding in my second MultiTrigger condition, but I don't get any binding errors at all. Here's the top of the stack trace in case that helps too:
InvalidOperationException:'System.InvalidOperationException: Must have non-null value for 'Property'.
at System.Windows.Condition.Seal(ValueLookupType type)
at System.Windows.ConditionCollection.Seal(ValueLookupType type)
at System.Windows.MultiTrigger.Seal()
at System.Windows.TriggerCollection.Seal()
at System.Windows.Style.Seal()
at System.Windows.StyleHelper.UpdateStyleCache(FrameworkElement fe, FrameworkContentElement fce, Style oldStyle, Style newStyle, Style& styleCache)
at System.Windows.FrameworkElement.OnStyleChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
at System.Windows.DependencyObject.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
at System.Windows.FrameworkElement.OnPropertyChanged(DependencyPropertyChangedEventArgs e)
at System.Windows.DependencyObject.NotifyPropertyChange(DependencyPropertyChangedEventArgs args)
at System.Windows.DependencyObject.UpdateEffectiveValue(EntryIndex entryIndex, DependencyProperty dp, PropertyMetadata metadata, EffectiveValueEntry oldEntry, EffectiveValueEntry& newEntry, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType)
at System.Windows.DependencyObject.SetValueCommon(DependencyProperty dp, Object value, PropertyMetadata metadata, Boolean coerceWithDeferredReference, Boolean coerceWithCurrentValue, OperationType operationType, Boolean isInternal)
at System.Windows.DependencyObject.SetValue(DependencyProperty dp, Object value)
at System.Windows.Controls.ItemsControl.ApplyItemContainerStyle(DependencyObject container, Object item)
There is no need to get into anything as despicable as owner-draw-- we are talking about WPF here, not WinForms. In WinForms, your only solution was to write more code. In WPF, we can solve this problem with a few very simple custom templates. For this example, I used Kaxaml, a free light-weight XAML editor. No code-behind was required. Kaxaml comes packed with a bunch of "starter" styles called Simple Styles. I used the ComboBox Simple Style and made modifications from that. So although this looks like a lot of XAML, I really just started with the boilerplate one and added a couple lines.
You can probably think of more elegant ways of triggering the font weight change; I used SelectedIndex.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<DataTemplate x:Key="SelectionBoxTextTemplate">
<TextBlock FontWeight="{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type ComboBox}}, Path=FontWeight}" Text="{Binding}"/>
</DataTemplate>
<ControlTemplate x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="20"/>
</Grid.ColumnDefinitions>
<Border
x:Name="Border"
Grid.ColumnSpan="2"
Background="#C0C0C0"
BorderBrush="#404040"
BorderThickness="1"
CornerRadius="2"/>
<Border
Grid.Column="0"
Margin="1"
Background="#FFFFFF"
BorderBrush="#404040"
BorderThickness="0,0,1,0"
CornerRadius="2,0,0,2"/>
<Path
x:Name="Arrow"
Grid.Column="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Data="M 0 0 L 4 4 L 8 0 Z"
Fill="#404040"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="ToggleButton.IsMouseOver" Value="true">
<Setter TargetName="Border" Property="Background" Value="#808080"/>
</Trigger>
<Trigger Property="ToggleButton.IsChecked" Value="true">
<Setter TargetName="Border" Property="Background" Value="#E0E0E0"/>
</Trigger>
<Trigger Property="IsEnabled" Value="False">
<Setter TargetName="Border" Property="Background" Value="#EEEEEE"/>
<Setter TargetName="Border" Property="BorderBrush" Value="#AAAAAA"/>
<Setter Property="Foreground" Value="#888888"/>
<Setter TargetName="Arrow" Property="Fill" Value="#888888"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="{x:Type ComboBox}" TargetType="{x:Type ComboBox}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="MinWidth" Value="120"/>
<Setter Property="MinHeight" Value="20"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBox}">
<Grid>
<ToggleButton
Name="ToggleButton"
Grid.Column="2"
ClickMode="Press"
Focusable="false"
IsChecked="{Binding Path=IsDropDownOpen,Mode=TwoWay,RelativeSource={RelativeSource TemplatedParent}}"
Template="{StaticResource ComboBoxToggleButton}">
</ToggleButton>
<ContentPresenter
Name="ContentSite"
HorizontalAlignment="Left"
Margin="3,3,23,3"
VerticalAlignment="Center"
Content="{TemplateBinding SelectionBoxItem}"
ContentTemplate="{StaticResource SelectionBoxTextTemplate}"
ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}"
IsHitTestVisible="False"/>
<TextBox
x:Name="PART_EditableTextBox"
HorizontalAlignment="Left"
Margin="3,3,23,3"
VerticalAlignment="Center"
Background="Transparent"
Focusable="False"
IsReadOnly="{TemplateBinding IsReadOnly}"
Style="{x:Null}"
Visibility="Hidden"/>
<Popup
Name="Popup"
AllowsTransparency="True"
Focusable="False"
IsOpen="{TemplateBinding IsDropDownOpen}"
Placement="Bottom"
PopupAnimation="Slide">
<Grid
Name="DropDown"
MaxHeight="{TemplateBinding MaxDropDownHeight}"
MinWidth="{TemplateBinding ActualWidth}"
SnapsToDevicePixels="True">
<Border
x:Name="DropDownBorder"
Background="#FFFFFF"
BorderBrush="#888888"
BorderThickness="1"/>
<ScrollViewer Margin="4,6,4,6" SnapsToDevicePixels="True">
<StackPanel IsItemsHost="True" KeyboardNavigation.DirectionalNavigation="Contained"/>
</ScrollViewer>
</Grid>
</Popup>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasItems" Value="false">
<Setter TargetName="DropDownBorder" Property="MinHeight" Value="95"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#888888"/>
</Trigger>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
<Trigger Property="Popup.AllowsTransparency" SourceName="Popup" Value="true">
<Setter TargetName="DropDownBorder" Property="CornerRadius" Value="4"/>
<Setter TargetName="DropDownBorder" Property="Margin" Value="0,2,0,0"/>
</Trigger>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter TargetName="PART_EditableTextBox" Property="Visibility" Value="Visible"/>
<Setter TargetName="ContentSite" Property="Visibility" Value="Hidden"/>
</Trigger>
<Trigger Property="SelectedIndex" Value="1">
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
<Trigger Property="SelectedIndex" Value="2">
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
<Trigger Property="SelectedIndex" Value="3">
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="{x:Type ComboBoxItem}" TargetType="{x:Type ComboBoxItem}">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="FontWeight" Value="Normal"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ComboBoxItem}">
<Border Name="Border" Padding="2" SnapsToDevicePixels="true">
<ContentPresenter/>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsHighlighted" Value="true">
<Setter TargetName="Border" Property="Background" Value="#DDDDDD"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Foreground" Value="#888888"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Page.Resources>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Margin="5" Text="What is your favorite primary colour?"/>
<ComboBox Width="150" SelectedIndex="0">
<ComboBoxItem>No Preference</ComboBoxItem>
<ComboBoxItem>Red</ComboBoxItem>
<ComboBoxItem>Green</ComboBoxItem>
<ComboBoxItem>Blue</ComboBoxItem>
</ComboBox>
</StackPanel>
</Page>
I used the ContentTemplate property of the ContentPresenter in ComboBox to add a custom data template (SelectionBoxTextTemplate). That TextBlock grabs its FontWeight from an ancestor combo-box. I then added a template for the individual items, that forces them to normal font weight. This got the result you were looking for:
You need to apply your trigger to the ComboBoxItem itself. Unless you've changed the behavior of the ComboBox, all items are displayed within containers (which are used to apply styles and templates) and the default container used by a ComboBox is a ComboBoxItem.
<Style TargetType="ComboBoxItem">
<Style.Triggers>
<Trigger Property="IsSelected" Value="True">
<Setter Property="FontWeight" Value="Bold" />
</Trigger>
</Style.Triggers>
</Style>
You'll need to add your existing triggers to compensate for not bolding the selection when it is No Preference.
I'm not an expert on WPF, but in Windows Forms the answer is called "OwnerDraw." This means that your code is responsible for drawing the control, instead of relying on the default behavior. When you're drawing it yourself, you can apply any style you want - including the ability to draw different items with different styles.
I did a quick web search for "combobox ownerdraw" and got over 1,000,000 hits. You're not the only one that needs to do this, so you're sure to find a decent tutorial pretty quickly.
I have a textbox that I want to be watermarked. In my window.resources section I added the style included in its entirety below.
When I set the style on the textbox, Blend 3 Beta displays the following message:
'The TypeConverter for "Style" does not support converting from a string'
What is going on and how do I fix this?
<Style x:Key="WaterMarkTextBoxStyle" BasedOn="{StaticResource {x:Type TextBox}}" TargetType="{x:Type TextBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<Grid>
<ScrollViewer x:Name="PART_ContentHost" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<TextBlock x:Name="textBlock" Opacity="0.345" Text="Enter Text Here" TextWrapping="Wrap" Visibility="Hidden" />
</Grid>
<ControlTemplate.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsFocused" Value="False" />
<Condition Property="Text" Value="" />
</MultiTrigger.Conditions>
<Setter Property="Visibility" TargetName="textBlock" Value="Visible" />
</MultiTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
You should be writing,
<TextBox Style="{StaticResource WaterMarkTextBoxStyle}" />
Assuming your Style is in Resources.