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
相关产品推荐
相关产品推荐

