在WPF ListBox中,如何将有焦点的选中项样式“复制”到非有焦点的选中项样式?

5
首先,这不是 设置非活动状态下ListBox的高亮颜色与活动状态下相同 的重复问题。请参考以下解释。
设置:
我有一个WPF ListBox在UserControl中,稍后将放入使用大量主题的应用程序中。从UserControl的角度来看,我事先不知道主题会是什么样子。
期望的行为:
如果ListBox在某个时刻没有焦点,我仍希望所选的ListBoxItems具有与ListBox具有焦点时相同的外观。
附加信息:
请注意,仅将颜色设置为一些系统默认值将不起作用。这样做将覆盖包含应用程序的主题。 (这就是为什么此问题不是上述链接问题的原因。)
是否有一种方法实现这一点,例如使用XAML?
编辑:经过一番研究,我认为我想创建“默认”ListBoxItem样式的副本(至少在UserControl级别上是默认的),其中所有属性为“Button.IsFocused” Value = “False”的Trigger都不会被触发,并且所有属性为“ Button.IsFocused”Value =“True”的Trigger将始终被触发。
不幸的是,我甚至不知道从哪里开始进行研究以完成这项工作。因此,任何提示我可以开始研究的地方都将不胜感激。

你正在尝试使用自己控制的主题还是第三方主题? - zastrowm
抱歉没能及时回复你。回想起来,在我度假前直接开始悬赏并不是一个好主意。;) 这个用户控件的想法是它应该可以放入任何类型的应用程序中。然而在实践中,它很可能只会在单个应用程序中使用 - 在这个应用程序中,我可以修改主题。显然,我更感兴趣的是一个可以独立于主题工作的答案......但由于似乎没有一种简单的方法来实现这样的东西(根据你的回答),我想一个更专业的方法(像你的)也是一个解决方案。 - Hauke P.
2个回答

4

摘要

看起来你想要实现聚焦样式与非聚焦样式相同,而不是编辑主题并以主题独立的方式完成。据我所知,这是不可能的,主要是因为每个主题在技术上都可以以不同的方式实现ListBoxItem的聚焦行为。事实上,我曾经见过一个主题,其中您期望的行为就是ListBoxItem的行为!

如何修改主题

现在,如果你愿意修改每个主题以适应你的需求,请继续阅读。

如果你要全局修改主题,你可以直接编辑ListBoxItem的样式(找到它存在的位置后)。如果你想更具体地应用更改,那么你将需要复制当前主题中的ListBoxItem样式,并对其进行更改。

默认ListBoxItem主题的副本如下(我使用Visual Studio制作了副本)。你需要更改的内容对于每个主题略有不同,但总体思路是相同的。

<Style x:Key="FocusVisual">
  <Setter Property="Control.Template">
    <Setter.Value>
      <ControlTemplate>
        <Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>
<SolidColorBrush x:Key="Item.MouseOver.Background" Color="#1F26A0DA"/>
<SolidColorBrush x:Key="Item.MouseOver.Border" Color="#a826A0Da"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Background" Color="#3DDADADA"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Border" Color="#FFDADADA"/>
<SolidColorBrush x:Key="Item.SelectedActive.Background" Color="#3D26A0DA"/>
<SolidColorBrush x:Key="Item.SelectedActive.Border" Color="#FF26A0DA"/>
<Style TargetType="{x:Type ListBoxItem}">
  <Setter Property="SnapsToDevicePixels" Value="True"/>
  <Setter Property="Padding" Value="4,1"/>
  <Setter Property="HorizontalContentAlignment" Value="{Binding HorizontalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
  <Setter Property="VerticalContentAlignment" Value="{Binding VerticalContentAlignment, RelativeSource={RelativeSource AncestorType={x:Type ItemsControl}}}"/>
  <Setter Property="Background" Value="Transparent"/>
  <Setter Property="BorderBrush" Value="Transparent"/>
  <Setter Property="BorderThickness" Value="1"/>
  <Setter Property="FocusVisualStyle" Value="{StaticResource FocusVisual}"/>
  <Setter Property="Template">
    <Setter.Value>
      <ControlTemplate TargetType="{x:Type ListBoxItem}">
        <Border x:Name="Bd" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
          <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
        </Border>
        <ControlTemplate.Triggers>
          <MultiTrigger>
            <MultiTrigger.Conditions>
              <Condition Property="IsMouseOver" Value="True"/>
            </MultiTrigger.Conditions>
            <Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.MouseOver.Background}"/>
            <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.MouseOver.Border}"/>
          </MultiTrigger>
          <MultiTrigger>
            <MultiTrigger.Conditions>
              <Condition Property="Selector.IsSelectionActive" Value="False"/>
              <Condition Property="IsSelected" Value="True"/>
            </MultiTrigger.Conditions>
            <Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Background}"/>
            <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Border}"/>
          </MultiTrigger>
          <MultiTrigger>
            <MultiTrigger.Conditions>
              <Condition Property="Selector.IsSelectionActive" Value="True"/>
              <Condition Property="IsSelected" Value="True"/>
            </MultiTrigger.Conditions>
            <Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Background}"/>
            <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Border}"/>
          </MultiTrigger>
          <Trigger Property="IsEnabled" Value="False">
            <Setter Property="TextElement.Foreground" TargetName="Bd" Value="{DynamicResource {x:Static SystemColors.GrayTextBrushKey}}"/>
          </Trigger>
        </ControlTemplate.Triggers>
      </ControlTemplate>
    </Setter.Value>
  </Setter>
</Style>

重点部分在中间:
<MultiTrigger>
  <MultiTrigger.Conditions>
    <Condition Property="Selector.IsSelectionActive" Value="False"/>
    <Condition Property="IsSelected" Value="True"/>
  </MultiTrigger.Conditions>
  <Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Background}"/>
  <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedInactive.Border}"/>
</MultiTrigger>
<MultiTrigger>
  <MultiTrigger.Conditions>
    <Condition Property="Selector.IsSelectionActive" Value="True"/>
    <Condition Property="IsSelected" Value="True"/>
  </MultiTrigger.Conditions>
  <Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Background}"/>
  <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Border}"/>
</MultiTrigger>

这是在有选中状态和未选中状态的情况下设置两种不同的样式。为了获得你所期望的行为,你有两个选择:要么只需将其转换为一个简单的触发器,只在IsSelected上触发;要么替换上面的块,如下所示:
<Trigger Property="IsSelected" Value="True">
  <Setter Property="Background" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Background}"/>
  <Setter Property="BorderBrush" TargetName="Bd" Value="{StaticResource Item.SelectedActive.Border}"/>
</Trigger>

或者您可以更改Item.SelectedInactive.BackgroundItem.SelectedInactive.Border属性,以匹配活动颜色(这在ListBox样式之前):

<SolidColorBrush x:Key="Item.SelectedInactive.Background" Color="#3D26A0DA"/>
<SolidColorBrush x:Key="Item.SelectedInactive.Border" Color="#FF26A0DA"/>
<SolidColorBrush x:Key="Item.SelectedActive.Background" Color="#3D26A0DA"/>
<SolidColorBrush x:Key="Item.SelectedActive.Border" Color="#FF26A0DA"/>

通常首选第一种方法,因为更清楚正在发生什么。

额外限制

现在,上述默认主题的 ListBoxItem 副本将更改所有 ListBoxItem。如果您只想更改其中一些,则需要向“复制的样式”添加密钥,如下所示:

<Style x:Key="InactiveLikeActive" TargetType="{x:Type ListBoxItem}">

然后在您想要应用样式的级别上方(甚至只是单个ListBox本身),添加以下样式定义:

<Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource InactiveLikeActive}" />

例如:

<ListBox>
  <ListBox.Resources>
    <Style TargetType="{x:Type ListBoxItem}" BasedOn="{StaticResource InactiveLikeActive}" />
  </ListBox.Resources>
  <ListBoxItem>One</ListBoxItem>
  <ListBoxItem>Two</ListBoxItem>
</ListBox>

结束语

虽然WPF使得几乎所有默认外观都能被覆盖,但这并不一定意味着这很容易或简单。


明晚我会仔细看一下这个!非常感谢。 - Hauke P.
我已经为你的答案颁发了悬赏。我会等到度假后再接受它,一旦我试过它。 - Hauke P.

1
已达到的最短变体
<ListBox ItemsSource="{Binding ElectrEquipAll}"
                <ListBox.ItemContainerStyle>
                    <Style TargetType="ListBoxItem" >
                        <Setter Property="IsSelected" Value="{Binding IsSelected}"/>
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="ListBoxItem">
                                    <Border x:Name="Bd"  BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Padding="{TemplateBinding Padding}" SnapsToDevicePixels="true">
                                        <ContentPresenter HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
                                    </Border>
                                    <ControlTemplate.Triggers>
                                        <Trigger Property="IsMouseOver" Value="True">
                                            <Setter Property="Background"  Value="#1F26A0DA"/>
                                            <Setter Property="BorderBrush"  Value="#a826A0Da"/>
                                        </Trigger>
                                        <Trigger Property="IsSelected" Value="True">
                                            <Setter Property="Background"  Value="#3D26A0DA"/>
                                            <Setter Property="BorderBrush"  Value="#FF26A0DA"/>
                                        </Trigger>
                                    </ControlTemplate.Triggers>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Style>
                </ListBox.ItemContainerStyle>
            </ListBox>

这很好,但可能需要更多的解释。 - ouflak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接