Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF drawing performance with large numbers of elements

I'm trying to create a custom control in WPF to display the game tree for a game of go (see here for what it looks like). I've more-or-less got it working laying out the nodes, but one problem I've found is that it begins gets noticeably slow to render/navigate (it's in a scroll viewer) when the number of nodes gets larger than about 30. Since an average game of go consists of around 200 moves (not to mention other branches that the player might fork onto), this is going to be a fairly big problem in any realistic game.

Currently, I'm using individual user controls for the game nodes (each being an ellipse with a shadow bitmap effect on it and some text annotation) and simple lines for the arcs in the tree, all of which are positioned absolutely in a canvas.

The layout algorithm isn't such a problem since this only has to be executed when a new branch is created (otherwise the node can be added directly beneath its parent, so no other nodes need to be relocated). The main problem is simply that any kind of manipulation of this canvas and its elements is very slow, presumably just due to the number of children it has. It obviously gets slower as the width and complexity of the tree increases, since there are more arcs as well as nodes.

My question: What's the proper way to about drawing a large/complex structure like this in such a way that it doesn't render too slowly as it grows?

Edit: This is related to my other question.

Edit: Here's the markup for the user controls I'm using for the nodes:

<UserControl x:Class="Go.UI.GameNodeMarker"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:Go.UI"
             xmlns:wpftools="clr-namespace:WpfTools.Extensions;assembly=WpfTools"
             x:Name="_This">
    <UserControl.Resources>
        <!-- Some brushes, resources, etc. are omitted -->
        <Style x:Key="StoneStyle" TargetType="{x:Type Ellipse}">
            <Setter Property="StrokeThickness" Value="0"/>
            <Setter Property="BitmapEffect" Value="{StaticResource StoneEffect}"/>
            <Style.Triggers>
                <DataTrigger Binding="{Binding ElementName=_This, Path=StoneColour}"  Value="Black">
                    <Setter Property="Fill" Value="{StaticResource BlackStoneBrush}"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding ElementName=_This, Path=StoneColour}" Value="White">
                    <Setter Property="Fill" Value="{StaticResource WhiteStoneBrush}"/>
                </DataTrigger>
                <DataTrigger Binding="{Binding ElementName=_This, Path=IsEmpty}" Value="True">
                    <Setter Property="Fill" Value="{StaticResource EmptyBrush}"/>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </UserControl.Resources>
    <Grid>
        <Ellipse Style="{StaticResource StoneStyle}"/>
        <TextBlock Text="{Binding ElementName=_This, Path=Text}"
                   HorizontalAlignment="Center"
                   VerticalAlignment="Center"
                   FontWeight="Medium"/>
    </Grid>
</UserControl>

These are being added dynamically to a canvas in order to draw the tree.

like image 614
Will Vousden Avatar asked Jan 12 '10 23:01

Will Vousden


3 Answers

Rendering controls is actually quite expensive. If you do custom drawing (draw the items yourself instead of placing controls) in the client area you will see your performance increase dramatically.

This has been my experience time and again when doing this type of work. If you need more than a dozen controls, go for owner drawn.

By the way, don't let custom drawing intimidate you. It's not too much more complex than placing controls dynamically.

Link to custom WPF drawing sample

like image 176
Paul Sasik Avatar answered Sep 25 '22 11:09

Paul Sasik


I'm a 4-D go player myself :) You might want to create a bit-map of the board, and just redraw the part that has changed.

like image 22
Larry Watanabe Avatar answered Sep 25 '22 11:09

Larry Watanabe


I don't know if you will like my suggestion because it will look ugly but you could try to turn off antialiasing by:

Set RenderOptions.EdgeMode for the current Window to "Unspecified" (no antialiasing)

You should get better performance but some kind of coarse drawing.

Good luck ! Eric

like image 20
Eric Ouellet Avatar answered Sep 24 '22 11:09

Eric Ouellet