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.I used the
ContentTemplateproperty of theContentPresenterinComboBoxto add a custom data template (SelectionBoxTextTemplate). ThatTextBlockgrabs itsFontWeightfrom 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: