如何有条件地仅设置ComboBox所选项目中的文本样式?

时间:2011-07-21 21:31:17

标签: wpf

我有一种情况,我需要以不同的方式对ComboBox中的所选项进行样式设置(使文本变为粗体),除非是一个值。例如,在标有“您最喜欢的原色是什么?”的下拉框中。我有四个选项:No PreferenceRedGreenBlue。 ComboBox项只是具有默认样式的文本,没有图像或其他任何花哨的东西,而且是C#类,不包含在ComboBoxItems中。

当用户从列表中指定首选项时,我想通过将折叠列表中所选项目的文本设置为粗体来突出显示该选项。如果用户选择No Preference,则字体粗细应该保持正常。

通过将ComboBox上的FontWeight属性设置为Bold,并将DataTrigger定义为SelectedItem != No Preference,我已经实现了90%的解决方案。但是,这会对ComboBox项目列表中的所有项目进行样式设置,包括下拉列表中的所有项目。我希望总是的那些项目以正常的字体粗细显示。

这可能吗?

修改

我一直在尝试@ crazyarabian使用MultiTrigger设置ComboBoxItem样式的方法。样式定义是:

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

并将其应用于以下DataTemplate中的ComboBox:

<DataTemplate x:Key="PrimaryColoursTemplate" DataType="{x:Type ViewModels:PrimaryColoursViewModel}">
    <ComboBox ItemsSource="{Binding PrimaryColours}" SelectedItem="{Binding SelectedPrimaryColour}"
              ItemContainerStyle="{StaticResource SelectedItemStyle}" />
</DataTemplate>

不幸的是,这会杀死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'.

应用程序死于NullReferenceException,它在上面的InvalidOperationException之后抛出(或者可能导致它,我无法解密输出)。我唯一能想到的可能是导致这是在我的第二个MultiTrigger条件中解析绑定中的属性,但我根本没有得到任何绑定错误。这是堆栈跟踪的顶部,如果有帮助的话:

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)

3 个答案:

答案 0 :(得分:5)

没有必要像所有者那样进入任何卑鄙的东西 - 我们在这里讨论的是 WPF ,而不是WinForms。在WinForms中,您唯一的解决方案是编写更多代码。在WPF中,我们可以使用一些非常简单的自定义模板来解决这个问题。在这个例子中,我使用了Kaxaml,一个免费的轻量级XAML编辑器。不需要代码隐藏。 Kaxaml 包含一堆名为 Simple Styles 的“初学者”样式。我使用了 ComboBox Simple Style 并对其进行了修改。所以虽然这看起来像很多XAML,但我真的只是从样板文件开始,并添加了几行。

你可能会想到更优雅的方式来触发字体重量变化;我使用了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>

我使用ContentTemplateContentPresenter的{​​{1}}属性添加自定义数据模板(ComboBox)。 SelectionBoxTextTemplate从祖先的组合框中抓取TextBlock。然后我为各个项目添加了一个模板,强制它们达到正常的字体粗细。这得到了你想要的结果:

enter image description here

答案 1 :(得分:2)

您需要将触发器应用于ComboBoxItem本身。除非您更改了ComboBox的行为,否则所有项都显示在容器中(用于应用样式和模板),ComboBox使用的默认容器是ComboBoxItem。

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

您需要添加现有的触发器,以补偿在没有偏好时选择不加粗的选项。

答案 2 :(得分:-4)

我不是WPF的专家,但在Windows Forms中,答案称为“OwnerDraw”。这意味着您的代码负责绘制控件,而不是依赖于默认行为。当您自己绘制时,可以应用任何您想要的样式 - 包括绘制不同样式的不同项目的能力。

我对“combobox ownerdraw”进行了快速网络搜索,并获得了超过1,000,000次点击。你并不是唯一一个需要这样做的人,所以你肯定能很快找到一份体面的教程。