15

我有一个非常简单的可滚动日历 UI:

在此处输入图像描述

但在滚动过程中,日历有时会闪烁。我查看了 WPF Performance Suite 并注意到有大量的脏矩形(大约 400 个):

在此处输入图像描述

日历的标记是 ItemsControl,它绑定了 Days(仅绑定了可见的日期)。看起来 WPF 每天都在重绘(这就是为什么这么简单的 UI 有这么多脏矩形的原因)。我认为可能有一种方法可以告诉 WPF 不要重绘许多小矩形,而是一次重绘整个 ItemsControl(类似于双缓冲在 WinForms 的所有好日子里所做的)。

PS WritableBitmap 解决了这个问题,但我希望有更好的方法

更新。如果我打开“显示脏区更新覆盖”选项,日历的外观如下:

在此处输入图像描述

所以 WPF 正确地找到了脏区。问题是为什么它决定使用这么多脏矩形来重绘它。我的猜测是它的发生是因为天之间的空间(一个或两个白色像素)在滚动期间是相同的。

更新 2。

这是日历的标记:

<ItemsControl  Panel.ZIndex="1" Grid.Column="1" 
       ItemsSource="{Binding Days}" 
       VerticalAlignment="Center" 
       HorizontalAlignment="Stretch">
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal"/>
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <Border Margin="1,0,1,0" Padding="0,0,3,0" 
                  CornerRadius="1" Width="28" Height="28" 
                  VerticalAlignment="Top">
                <Border.Background>
                    <MultiBinding Converter="{StaticResource DayOfWeekToColorConverter}">
                        <Binding Path="IsWeekend"/>
                    </MultiBinding>
                </Border.Background>
                <StackPanel>
                    <TextBlock  Style="{StaticResource TextStyle}" 
                          HorizontalAlignment="Center" 
                          VerticalAlignment="Center"/>
                    <Label  Style="{StaticResource LabelStyle}" 
                          Content="{Binding Date.Day}" 
                          HorizontalAlignment="Center" 
                          VerticalAlignment="Center"/>
                </StackPanel>
            </Border>
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>
4

3 回答 3

1

我会添加到评论列表中,但我没有足够的声誉这样做。您可能会看到闪烁的另一个可能原因是 GC 导致 UI 性能下降。根据您的代码的工作方式(仅发布了部分代码),您可能正在创建和孤立大量对象,这些对象最终会导致 GC 触发,并可能导致 UI 稍微变慢。您应该能够通过运行perfmon并查看每次 UI 闪烁时是否触发 GC 来查看是否是这种情况。

我想我也应该提到这种可能性,因为它似乎没有被考虑过,并且可以通过在应用程序运行时运行跟踪 GC 调用来测试它。

于 2014-02-02T22:35:34.957 回答
1

这里重绘的原因可能是由于Days集合的代码触发了不需要的属性更改通知。这可能会导致重新绑定整个列表,从而导致闪烁。

尝试使用Visual Studio Profiler缩小原因并解决它。Profiler 在 VS studio 的高级版和终极版中可用。

于 2014-07-09T05:25:42.810 回答
1

这不是一个答案,而是一个建议。我想可能是里面的xaml将根据您的Days集合中的项目重复多次,如果我们尽可能少地编码,它会改善渲染。

  1. Border样式移动到静态资源BorderStyle
  2. 将标签更改为TextBlock(标签已完成一些有趣的预处理。例如:删除_下划线)
  3. 由于只绑定了一个属性,我们可以取出MultiBinding,放入简单的绑定
  4. 您已经使用了边框背景转换器。您可以在Date中创建一个属性,该属性根据 WeekDay 返回颜色。

            <DataTemplate>
                <Border Style="{StaticResource BorderStyle}" Background="{Binding Date.DayBackgroundColor}">
                    <StackPanel>
                        <TextBlock  Style="{StaticResource TextStyle}"/>
                        <TextBlock  Style="{StaticResource LabelStyle}" Text="{Binding Date.Day}" />
                    </StackPanel>
                </Border>
            </DataTemplate>
    

就像当您稍微更改 SQL 查询时,查询计划会发生变化和优化,这些更改可能会改进 WPF 的呈现计划 :)

于 2014-05-28T18:29:04.070 回答