views:

818

answers:

3

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.

+4  A: 

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

Paul Sasik
By custom drawing do you mean placing the relevant shapes (ellipses etc.) in the `GameTree` control's codebehind rather than in a separate user control, or doing something lower level? Could you give me a quick example or a link explaining it? Thanks!
Will Vousden
++ Yeah. Draw it yourself, and if there's flicker, draw to a memory bitmap and block-transfer it to the window.
Mike Dunlavey
Here's a link that explains how basic WPF shapes can be drawn: http://msdn.microsoft.com/en-us/library/ms747393.aspx In your case you need to combine the drawing of shadow circles, then the lines, then the filled-in circles with a border, the the text. It'll seem daunting at first but play with it. You'll discover how much fun drawing with .Net can be!
Paul Sasik
So is the overhead just related to the creation/rendering of a user control? I've essentially used lines and ellipses in the same way as the link you posted to create my game node user controls (see my update). Would it be that much faster to have this all done manually by the tree control itself?
Will Vousden
+1  A: 

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.

Larry Watanabe
The problem is that I want the stones and the nodes in the game tree to be interactive, so the automatic hit testing, etc. provided by user controls is useful.
Will Vousden
And yes, Go is very interesting when you extend it into different board geometries! I've written the game engine to allow for boards with arbitrary geometries and for an arbitrary number of players. How do you play it in 4D? Visual projection onto 3D/2D space?
Will Vousden
You can draw a 4D space in 2D the same way you can draw a 3D space in 2D. Try drawing a 4D cube sometime - just use the same generalization that you do for drawing a 3D cube from a 2D cube (a square).
Larry Watanabe
+1  A: 

I know this may not really answer the question, but I think it is worth pointing out.

WPF4 will be coming in the near future, and it will support caching of the element's graphics, making redraws unnecessary in many cases. Add a couple attributes on the elements you want to take advantage of this, and you're good to go. If you do end up using this, try to avoid unnecessary animations. There's no point in caching things if every element on the screen requires redraws on every frame. Rollover animations would be fine.

YotaXP
That's useful to know, thanks. Is this coming with .NET 4?
Will Vousden
Yep. I can't imagine the release date being long from now either. Don't go by me though. =p
YotaXP