Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dirty Rects grouping

Tags:

.net

wpf

I have a pretty simple scrollable calendar UI:

enter image description here

But from time to time calendar blinks during scrolling. I have looked at WPF Performance Suite and noticed there is a significant amount of Dirty Rects (about 400):

enter image description here

The markup of the calendar is ItemsControl which binds Days (only visible days are bound). Looks like WPF redraws day by day (so that's why there are so many dirty rects for such a simple UI). I thought may be there is a way to tell WPF not to redraw many small rectangles but redraw the whole ItemsControl at once (similar to what Double Buffering did in the all good days of WinForms).

P. S. WritableBitmap fixes the problem but I hope there is a nicer way

Update. Here is how Calendar looks if I switch the "Show dirty-region update overlay" option on:

enter image description here

So WPF correctly finds the dirty region. The question is why it decides to use so many dirty-rects to redraw it. My guess is that it happens because of the space between days (one or two pixels of white) which is the same during scrolling.

Update 2.

Here is the markup of the Calender:

<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>
like image 693
SiberianGuy Avatar asked Oct 28 '13 07:10

SiberianGuy


3 Answers

I would have added to the comment list but I don't have enough reputation to do so. Another possible reason why you may be seeing the blinking is due to a slow down in UI performance caused by a GC. Depending on how your code is working (only part of it is posted) you may be creating and orphaning a lot of objects that will eventually cause a GC to fire and could potentially cause a slight slowdown in the UI. You should be able to see if this is the case by running perfmon and seeing if a GC is fired every time the UI blinks.

I figured I should mention this possibility as well since it doesn't seem to have been considered and is something that can be tested with running a trace of GC calls while your application is running.

like image 104
Chiune Sugihara Avatar answered Nov 07 '22 18:11

Chiune Sugihara


It is not an answer, but it is a suggestion. I thought may be as the xaml inside will be repeated as many times as there are items in your Days collection, it improves the rendering if we have the coding as minimal as possible.

  1. Move styles for Border to static resource BorderStyle
  2. Change Label to TextBlock (Label has some funny preprocessing done. ex: Remove _ underscores)
  3. As only one property is bound, we can take out MultiBinding and put simple binding
  4. You've used a converter for border Background. You can create a property in Date which returns color based on 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>
    

Like when you change an SQL query slightly the query plan changes and optimization occurs, these changes may improve WPF's rendering plan :)

like image 38
Vibgy Avatar answered Nov 07 '22 19:11

Vibgy


Here the cause of redrawing could be due to unwanted property changed notification fired from the code for the Days collection. This could cause rebinding of the whole list resulting in blinking.

Try using visual studio profiler to narrow down the cause and hence resolve it. Profiler is available in premium and ultimate versions of VS studio.

like image 1
DiAgo Avatar answered Nov 07 '22 17:11

DiAgo