You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

WPF中ListBox切换ContentControl时动画仅首次生效(最后项除外)

WPF导航栏内容切换动画重复触发问题修复

问题描述

用WPF的ListBox实现自定义导航栏,选中ListBox项时ContentControl渲染对应页面。通过DataTrigger.EnterActions添加了内容切换动画,但只有最后一个ListBoxItem的动画能重复触发,其他项的动画仅在首次选中时生效。

简化代码

<Window.Resources>
    <DataTemplate x:Key="action_page">
        <Grid>
            <TextBlock Style="{StaticResource textBlockText}" Text="ACTION PAGE" />
        </Grid>
    </DataTemplate>
    <DataTemplate x:Key="entry_page">
        <Grid>
            <TextBlock Style="{StaticResource textBlockText}" Text="ENTRY PAGE" />
        </Grid>
    </DataTemplate>
    <DataTemplate x:Key="alarms_page">
        <Grid>
            <TextBlock Style="{StaticResource textBlockText}" Text="ALARMS PAGE" />
        </Grid>
    </DataTemplate>
    <DataTemplate x:Key="warnings_page">
        <Grid>
            <TextBlock Style="{StaticResource textBlockText}" Text="WARNINGS PAGE" />
        </Grid>
    </DataTemplate>

    <Storyboard x:Key="ActionPageEnterAnimation">
        <DoubleAnimation
            Storyboard.TargetProperty="Opacity"
            From="0.0"
            To="1.0"
            Duration="0:0:0.5" />
    </Storyboard>
</Window.Resources>

<Grid Margin="30">
    <Grid.RowDefinitions>
        <RowDefinition Height="auto" />
        <RowDefinition Height="*" />
    </Grid.RowDefinitions>
    <Grid
        Grid.Row="0"
        Width="600"
        Height="30"
        HorizontalAlignment="Left"
        VerticalAlignment="Top">
        <navi:NaviListBox x:Name="bar">
            <ListBoxItem Content="ACTION" />
            <ListBoxItem Content="ENTRY" />
            <ListBoxItem Content="ALARMS" />
            <ListBoxItem Content="WARNINGS" />
        </navi:NaviListBox>
    </Grid>
    <Grid Grid.Row="1">
        <ContentControl x:Name="contentControl">
            <ContentControl.Style>
                <Style TargetType="ContentControl">
                    <Style.Triggers>
                        <!--  ACTION PAGE  -->
                        <DataTrigger Binding="{Binding Path=SelectedIndex, ElementName=bar}" Value="0">
                            <Setter Property="ContentTemplate" Value="{StaticResource action_page}" />
                            <DataTrigger.EnterActions>
                                <BeginStoryboard Storyboard="{StaticResource ActionPageEnterAnimation}" />
                            </DataTrigger.EnterActions>
                        </DataTrigger>
                        <!--  ENTRY PAGE  -->
                        <DataTrigger Binding="{Binding Path=SelectedIndex, ElementName=bar}" Value="1">
                            <Setter Property="ContentTemplate" Value="{StaticResource entry_page}" />
                            <DataTrigger.EnterActions>
                                <BeginStoryboard Storyboard="{StaticResource ActionPageEnterAnimation}" />
                            </DataTrigger.EnterActions>
                        </DataTrigger>
                        <!--  ALARMS PAGE  -->
                        <DataTrigger Binding="{Binding Path=SelectedIndex, ElementName=bar}" Value="2">
                            <Setter Property="ContentTemplate" Value="{StaticResource alarms_page}" />
                            <DataTrigger.EnterActions>
                                <BeginStoryboard Storyboard="{StaticResource ActionPageEnterAnimation}" />
                            </DataTrigger.EnterActions>
                        </DataTrigger>
                        <!--  WARNINGS PAGE  -->
                        <DataTrigger Binding="{Binding Path=SelectedIndex, ElementName=bar}" Value="3">
                            <Setter Property="ContentTemplate" Value="{StaticResource warnings_page}" />
                            <DataTrigger.EnterActions>
                                <BeginStoryboard Storyboard="{StaticResource ActionPageEnterAnimation}" />
                            </DataTrigger.EnterActions>
                        </DataTrigger>
                    </Style.Triggers>
                </Style>
            </ContentControl.Style>
        </ContentControl>
    </Grid>
</Grid>

问题原因

WPF Style的DataTrigger.EnterActions仅在触发条件从不满足变为满足时执行一次。当切换到其他页面再切回原页面时,虽然SelectedIndex回到对应值,但ContentTemplate已经被之前的Setter设置为目标模板,Trigger没有检测到“从false到true”的状态变化,因此EnterActions不会再次触发。最后一个项能重复触发是因为初始状态下ContentTemplate未设置,每次切回时都是从其他状态变为满足条件。

修复方案

方案1:为每个Trigger添加ExitActions重置动画

给每个DataTrigger添加ExitActions停止对应的Storyboard,确保下次进入Trigger时动画能重新执行,同时每个Trigger使用内联Storyboard避免资源冲突:

<Style TargetType="ContentControl">
    <Style.Triggers>
        <DataTrigger Binding="{Binding Path=SelectedIndex, ElementName=bar}" Value="0">
            <Setter Property="ContentTemplate" Value="{StaticResource action_page}" />
            <DataTrigger.EnterActions>
                <BeginStoryboard x:Name="ActionStoryboard">
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.0" To="1.0" Duration="0:0:0.5" />
                    </Storyboard>
                </BeginStoryboard>
            </DataTrigger.EnterActions>
            <DataTrigger.ExitActions>
                <StopStoryboard BeginStoryboardName="ActionStoryboard" />
            </DataTrigger.ExitActions>
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=SelectedIndex, ElementName=bar}" Value="1">
            <Setter Property="ContentTemplate" Value="{StaticResource entry_page}" />
            <DataTrigger.EnterActions>
                <BeginStoryboard x:Name="EntryStoryboard">
                    <Storyboard>
                        <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.0" To="1.0" Duration="0:0:0.5" />
                    </Storyboard>
                </BeginStoryboard>
            </DataTrigger.EnterActions>
            <DataTrigger.ExitActions>
                <StopStoryboard BeginStoryboardName="EntryStoryboard" />
            </DataTrigger.ExitActions>
        </DataTrigger>
        <!-- 其他两个DataTrigger按相同方式修改 -->
    </Style.Triggers>
</Style>

方案2:监听ContentTemplateChanged事件触发动画

直接监听ContentControl的ContentTemplateChanged事件,只要模板切换就执行动画,逻辑更简洁:

<ContentControl x:Name="contentControl">
    <ContentControl.Triggers>
        <EventTrigger RoutedEvent="ContentTemplateChanged">
            <BeginStoryboard>
                <Storyboard>
                    <DoubleAnimation Storyboard.TargetProperty="Opacity" From="0.0" To="1.0" Duration="0:0:0.5" />
                </Storyboard>
            </BeginStoryboard>
        </EventTrigger>
    </ContentControl.Triggers>
    <ContentControl.Style>
        <Style TargetType="ContentControl">
            <Style.Triggers>
                <DataTrigger Binding="{Binding Path=SelectedIndex, ElementName=bar}" Value="0">
                    <Setter Property="ContentTemplate" Value="{StaticResource action_page}" />
                </DataTrigger>
                <DataTrigger Binding="{Binding Path=SelectedIndex, ElementName=bar}" Value="1">
                    <Setter Property="ContentTemplate" Value="{StaticResource entry_page}" />
                </DataTrigger>
                <DataTrigger Binding="{Binding Path=SelectedIndex, ElementName=bar}" Value="2">
                    <Setter Property="ContentTemplate" Value="{StaticResource alarms_page}" />
                </DataTrigger>
                <DataTrigger Binding="{Binding Path=SelectedIndex, ElementName=bar}" Value="3">
                    <Setter Property="ContentTemplate" Value="{StaticResource warnings_page}" />
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </ContentControl.Style>
</ContentControl>

内容的提问来源于stack exchange,提问作者madhawa polkotuwa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 20:35:58