WPF, how can I optimize lines and circles drawing ?

Posted by Aurélien Ribon on Stack Overflow See other posts from Stack Overflow or by Aurélien Ribon
Published on 2010-04-22T14:30:50Z Indexed on 2010/04/22 14:33 UTC
Read the original article Hit count: 365

Filed under:
|
|

Hello !

I am developping an application where I need to draw a graph on the screen. For this purpose, I use a Canvas and I put Controls on it.

An example of such a draw as shown in the app can be found here : http://free0.hiboox.com/images/1610/d82e0b7cc3521071ede601d3542c7bc5.png

It works fine for simple graphs, but I also want to be able to draw very large graphs (hundreds of nodes). And when I try to draw a very large graph, it takes a LOT of time to render.

My problem is that the code is not optimized at all, I just wanted it to work. Until now, I have a Canvas on the one hand, and multiple Controls on the other hands. Actually, circles and lines are listed in collections, and for each item of these collections, I use a ControlTemplate, defining a red circle, a black circle, a line, etc.

Here is an example, the definition of a graph circle :

<!--
STYLE : DISPLAY DATA NODE
-->
<Style TargetType="{x:Type flow.elements:DisplayNode}">
    <Setter Property="Canvas.Left" Value="{Binding X, RelativeSource={RelativeSource Self}}" />
    <Setter Property="Canvas.Top" Value="{Binding Y, RelativeSource={RelativeSource Self}}" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type flow.elements:DisplayNode}">

                <!--TEMPLATE-->
                <Grid x:Name="grid" Margin="-30,-30,0,0">
                    <Ellipse x:Name="selectionEllipse" StrokeThickness="0" Width="60"
                            Height="60" Opacity="0" IsHitTestVisible="False">
                        <Ellipse.Fill>
                            <RadialGradientBrush>
                                <GradientStop Color="Black" Offset="0.398" />
                                <GradientStop Offset="1" />
                            </RadialGradientBrush>
                        </Ellipse.Fill>
                    </Ellipse>
                    <Ellipse Stroke="Black" Width="30" Height="30" x:Name="ellipse">
                        <Ellipse.Fill>
                            <LinearGradientBrush EndPoint="0,1">
                                <GradientStop Offset="0" Color="White" />
                                <GradientStop Offset="1.5" Color="LightGray" />
                            </LinearGradientBrush>
                        </Ellipse.Fill>
                    </Ellipse>
                    <TextBlock x:Name="tblock"
                            Text="{Binding NodeName, RelativeSource={RelativeSource Mode=TemplatedParent}}"
                            Foreground="Black" VerticalAlignment="Center"
                            HorizontalAlignment="Center" FontSize="10.667" />
                </Grid>

                <!--TRIGGERS-->
                <ControlTemplate.Triggers>
                    <!--DATAINPUT-->
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="SkinMode" Value="NODETYPE" />
                            <Condition Property="NodeType" Value="DATAINPUT" />
                        </MultiTrigger.Conditions>
                        <Setter TargetName="tblock" Property="Foreground" Value="White" />
                        <Setter TargetName="ellipse" Property="Fill">
                            <Setter.Value>
                                <LinearGradientBrush EndPoint="0,1">
                                    <GradientStop Offset="-0.5" Color="White" />
                                    <GradientStop Offset="1" Color="Black" />
                                </LinearGradientBrush>
                            </Setter.Value>
                        </Setter>
                    </MultiTrigger>

                    <!--DATAOUTPUT-->
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="SkinMode" Value="NODETYPE" />
                            <Condition Property="NodeType" Value="DATAOUTPUT" />
                        </MultiTrigger.Conditions>
                        <Setter TargetName="tblock" Property="Foreground" Value="White" />
                        <Setter TargetName="ellipse" Property="Fill">
                            <Setter.Value>
                                <LinearGradientBrush EndPoint="0,1">
                                    <GradientStop Offset="-0.5" Color="White" />
                                    <GradientStop Offset="1" Color="Black" />
                                </LinearGradientBrush>
                            </Setter.Value>
                        </Setter>
                    </MultiTrigger>

                    ....... THERE IS A TOTAL OF 7 MULTITRIGGERS .......

                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Also, the lines are drawn using the Line Control.

<!--
STYLE : DISPLAY LINK
-->
<Style TargetType="{x:Type flow.elements:DisplayLink}">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="{x:Type flow.elements:DisplayLink}">

                <!--TEMPLATE-->
                <Line X1="{Binding X1, RelativeSource={RelativeSource TemplatedParent}}"
                        X2="{Binding X2, RelativeSource={RelativeSource TemplatedParent}}"
                        Y1="{Binding Y1, RelativeSource={RelativeSource TemplatedParent}}"
                        Y2="{Binding Y2, RelativeSource={RelativeSource TemplatedParent}}"
                        Stroke="Gray" StrokeThickness="2" x:Name="line" />

                <!--TRIGGERS-->
                <ControlTemplate.Triggers>
                    <!--BRANCH : ASSERTION-->
                    <MultiTrigger>
                        <MultiTrigger.Conditions>
                            <Condition Property="SkinMode" Value="BRANCHTYPE" />
                            <Condition Property="BranchType" Value="ASSERTION" />
                        </MultiTrigger.Conditions>
                        <Setter TargetName="line" Property="Stroke" Value="#E0E0E0" />
                    </MultiTrigger>
                </ControlTemplate.Triggers>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

So, I need your advices. How can I drastically improve the rendering performances ? Should I define each MultiTrigger circle rendering possibility in its own ControlTemplate instead ? Is there a better line drawing technique ?

Should I open a DrawingContext and draw everything in one control, instead of having hundreds of controls ?

© Stack Overflow or respective owner

Related posts about wpf

Related posts about drawing