Wpf ListBox – 在 ContentPresenter *内部*更改默认选定项样式

Wpf ListBox – change default selected-item style *inside* the ContentPresenter

我有一个 ListBox,其中每个项目都是一个 StackPanel。 StackPanel 由一个图像及其下方的一个 TextBlock 组成:

<ListBox.ItemTemplate>

    <DataTemplate>

        <StackPanel Margin="10">

            <Image>
                <Image.Source>
                    <BitmapImage UriSource="{Binding Path=ImageFilePath}"/>
                </Image.Source>
            </Image>

            <TextBlock Text="Title" TextAlignment="Center"/>

        </StackPanel>

   </DataTemplate>

</ListBox.ItemTemplate>

看起来像这样:

当用户 select 一个项目时,我得到围绕 StackPanel 的默认蓝色矩形:

现在,我想为 selected-item 制作不同的边框,但我希望它 仅围绕图像

我知道如何制作控件模板并在 ContentPresenter 周围放置自定义边框,但这当然会围绕整个 StackPanel,而不仅仅是图像。

我不知道是否可以更改 ContentPresenter,是否是个好主意。如果有其他方法可以达到我想要的效果,也可以。

是的,ListBox 自己的 ContentPresenter 对您正在做的事情没有帮助。您想要 a) 消除 ListBox 自己的选择视觉效果,以及 b) 用 DataTemplate 中更适合您项目的内容替换它们。

默认选择视觉效果由 ListBoxItem 的默认模板应用。所以更换那个模板。在 ListBox 的资源中使用样式,将您自己的控件模板应用于 ListBoxItem。没什么大不了的,只是展示内容,不提供选择背景。然后,您使用数据模板中的触发器处理选择视觉效果,您的图像和标签在其中定义,您可以将更改应用到一个而不是另一个。下面的例子对我有用。

请注意,对 Border 元素上的 Horizo​​ntalAlignment 进行了一些调整,使其紧贴其中的 Image 元素。另外,我写了一个快速测试视图模型,其项目 属性 被称为 Items;我假设这不是您用来填充自己的 ListBox 的集合成员的名称。

<ListBox
    Margin="8"
    ItemsSource="{Binding Items}"
    >
    <ListBox.Resources>
        <Style TargetType="{x:Type ListBoxItem}">
            <Setter Property="HorizontalContentAlignment" Value="Stretch" />
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ListBoxItem}">
                        <Grid>
                            <ContentPresenter />
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </ListBox.Resources>
    <ListBox.ItemTemplate>
        <DataTemplate>
            <Grid>
                <Border
                    x:Name="HighlightBorder"
                    BorderThickness="4"
                    HorizontalAlignment="Left"
                    VerticalAlignment="Top"
                    Margin="10"
                    >
                    <Border.Style>
                        <Style TargetType="Border">
                            <!-- MUST set default BorderBrush via a style, if you set it at all. 
                            As an attribute on the Border tag, it would override the effects of 
                            the trigger below. 
                            -->
                            <Setter Property="BorderBrush" Value="Transparent" />
                        </Style>
                    </Border.Style>
                    <Image Source="{Binding ImageFilePath}" />
                </Border>
            </Grid>
            <DataTemplate.Triggers>
                <DataTrigger 
                    Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=ListBoxItem}}" 
                    Value="True">
                    <Setter TargetName="HighlightBorder" Property="BorderBrush" Value="Orange" />
                </DataTrigger>
            </DataTemplate.Triggers>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>