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-nam开发者_如何学Cespace: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.
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
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.
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
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.
精彩评论