WPF折叠加载指示器后动画仍高占CPU的性能问题排查
WPF隐藏加载指示器后CPU占用仍高的原因及解决方法
我用Storyboard实现了旋转加载指示器,当把包含5000个该指示器的ItemsControl设为Visibility.Collapsed后,CPU占用率依然高达10%,无法下降。
原因分析
- 设置
Visibility.Collapsed仅会让元素脱离视觉渲染流程,但逻辑树中的5000个LoadingIndicator实例并未被销毁,它们的循环动画仍在后台持续运行。 - WPF的Storyboard动画默认不会随元素可见性变化自动停止,只要元素存在于逻辑树中,动画就会一直占用CPU计算帧更新,哪怕没有实际渲染。
解决方案
方案1:清除ItemsSource,销毁逻辑树中的元素
直接移除ItemsControl的数据源,让所有LoadingIndicator实例被销毁,动画自然终止:
private void Button_Click(object sender, RoutedEventArgs e) { Spinner.ItemsSource = null; // 若后续需要恢复显示,可提前保存原集合: // var vm = (Vm)DataContext; // _savedItems = vm.Items; // Spinner.ItemsSource = null; // 恢复时:Spinner.ItemsSource = _savedItems; }
方案2:在LoadingIndicator模板中监听可见性,启停动画
修改LoadingIndicator的ControlTemplate,添加触发器,根据元素可见性自动停止/启动动画:
<ControlTemplate TargetType="{x:Type local:LoadingIndicator}"> <Grid Width="20" Height="20" x:Name="rootGrid"> <Canvas x:Name="icon"> <Ellipse Canvas.Left="0" Canvas.Top="0" Width="20" Height="20" StrokeThickness="2" Stroke="{StaticResource THEME_LoadingSpinnerBackgroundBrush}" StrokeMiterLimit="4"/> <Ellipse Canvas.Left="0" Canvas.Top="0" Width="20" Height="20" StrokeThickness="2" Stroke="{StaticResource THEME_LoadingSpinnerForegroundBrush}" StrokeMiterLimit="4" StrokeDashArray="17 25"/> <Canvas.RenderTransform> <RotateTransform CenterX="10" CenterY="10"/> </Canvas.RenderTransform> </Canvas> <Grid.Triggers> <EventTrigger RoutedEvent="Loaded"> <BeginStoryboard x:Name="spinStoryboard" Storyboard="{StaticResource LoadingIndicatorStoryBoard}"/> </EventTrigger> <!-- 元素隐藏时停止动画 --> <Trigger Property="Visibility" Value="Collapsed"> <StopStoryboard BeginStoryboardName="spinStoryboard"/> </Trigger> <!-- 元素显示时重启动画 --> <Trigger Property="Visibility" Value="Visible"> <BeginStoryboard Storyboard="{StaticResource LoadingIndicatorStoryBoard}"/> </Trigger> </Grid.Triggers> </Grid> </ControlTemplate>
方案3:配合FillBehavior优化动画状态
如果希望动画在停止后重置初始状态,可以给DoubleAnimation添加FillBehavior="Stop",但需配合上述可见性触发器使用:
<DoubleAnimation Storyboard.TargetName="icon" Storyboard.TargetProperty="(Canvas.RenderTransform).(RotateTransform.Angle)" From="0" To="360" Duration="00:00:1.3" RepeatBehavior="Forever" FillBehavior="Stop"/>
内容的提问来源于stack exchange,提问作者DerApe
相关产品推荐
相关产品推荐

