How can I conditionally style only text in a selected ComboBox element?

I have a situation where I need to style the selected item in the ComboBox in different ways (make the text bold), when it is one of all but one value. For example, in the dropdown "What is your favorite primary color?" I would have four options: No Preference , Red , Green and Blue . ComboBox elements are just text with a default style, images, or something else unusual, as well as C # classes not related to ComboBoxItems.

When the user specifies a preference from the list, I want to highlight this choice by setting the text of the selected item in the collapsed list so that it is bold. If the user selects No Preference , the font weight should remain normal.

I got a 90% solution by setting the FontWeight property of a ComboBox to Bold in a style with a DataTrigger defined as SelectedItem != No Preference . However, it stylizes all the items in the ComboBox item list, including everything in the drop-down list. I would like these elements to always display with normal font weight.

Is it possible?

Edit

I am trying to use the @crazyarabian method to style ComboBoxItem with MultiTrigger. Style Definition:

 <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 applies to ComboBox in the following data table:

 <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, possibly, leads to it, I can not decrypt the output). The only thing I can think of that could be the reason for this is to resolve the property in the binding in my second MultiTrigger state, but I don't get any binding errors at all. Here's the top of the stack trace in case this 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) 
+7
source share
3 answers

There is no need to get involved in something as despicable as the owner - we are talking about WPF here, not WinForms. In WinForms, the only solution was to write more code. In WPF, we can solve this problem with a few simple custom templates. In this example, I used Kaxaml , a free, lightweight XAML editor. No code was required. Kaxaml comes with a bunch of β€œstarter” styles called Simple Styles. I used ComboBox Simple Style and made changes to it. Therefore, although this seems like a lot of XAML, I really just started with a template and added a couple of lines.

You can probably think of more elegant ways to start changing the font weight; 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 ContentTemplate in a ComboBox to add a custom data template ( SelectionBoxTextTemplate ). That TextBlock captures its FontWeight from the ancestor-neighbor field. Then I added a template for individual elements, which makes them have a normal font weight. This led to what you were looking for:

enter image description here

+5
source

You need to apply your trigger to the ComboBoxItem itself. If you have not changed the behavior of ComboBox, all elements are displayed in containers (which are used to apply styles and templates), and the default container used by ComboBox is ComboBoxItem.

 <Style TargetType="ComboBoxItem"> <Style.Triggers> <Trigger Property="IsSelected" Value="True"> <Setter Property="FontWeight" Value="Bold" /> </Trigger> </Style.Triggers> </Style> 

You will need to add existing triggers so as not to highlight the selection, if that is not preference.

+2
source

I'm not a WPF expert, but on Windows Forms the answer is called "OwnerDraw". This means that your code is responsible for drawing the control, rather than relying on the default behavior. When you draw it yourself, you can apply any style you want - including the ability to draw different objects with different styles.

I did a quick web search for "combobox ownerdraw" and got over 1,000,000 views. You are not the only one who has to do this, so you are sure to find a decent textbook pretty quickly.

-4
source

All Articles