WPF, как я могу оптимизировать рисование линий и кругов? - PullRequest
2 голосов
/ 22 апреля 2010

Я разрабатываю приложение, в котором мне нужно нарисовать график на экране. Для этого я использую Canvas и помещаю на него элементы управления.

Пример такого розыгрыша, показанного в приложении, можно найти здесь: http://free0.hiboox.com/images/1610/d82e0b7cc3521071ede601d3542c7bc5.png

Он отлично работает для простых графиков, но я также хочу иметь возможность рисовать очень большие графики (сотни узлов). И когда я пытаюсь нарисовать очень большой график, на рендеринг уходит МНОГО времени.

Моя проблема в том, что код вообще не оптимизирован, я просто хотел, чтобы он работал. До сих пор у меня есть холст с одной стороны и несколько элементов управления с другой. На самом деле круги и линии перечислены в коллекциях, и для каждого элемента этих коллекций я использую ControlTemplate, определяя красный круг, черный круг, линию и т. Д.

Вот пример определения графа круга:

<!--
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>

Кроме того, линии рисуются с помощью элемента управления Line.

<!--
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>

Итак, мне нужны ваши советы. Как я могу значительно улучшить производительность рендеринга? Должен ли я вместо этого определить MultiTrigger возможность рендеринга круга в его собственном ControlTemplate? Есть ли лучшая техника рисования линий?

Должен ли я открыть DrawingContext и нарисовать все в одном элементе управления вместо сотен элементов управления?

1 Ответ

4 голосов
/ 22 апреля 2010

Я часто вижу это, и у меня всегда один и тот же ответ.Вот что происходит, когда вы строите графику.Не строить графику, рисовать графику.Другими словами, не создавайте много структуры данных (много элементов управления) сверх данных приложения.Скорее, есть обработчик событий Paint , который может рисовать с помощью DrawLine , DrawEllipse и т. Д.

Это конкретный экземплярdictum Будьте Ненавистником Данных .

Только если вы знаете, что у вас есть требование иметь возможность реагировать на контакты мыши на нарисованных объектах, имеет смысл создавать некоторые виды объектов,и даже тогда, возможно, нет.

Если рендеринг вызывает много миганий, потому что у вас так много объектов, что их рендеринг занимает заметное время, попробуйте нарисовать в растровое изображение памяти и скопировать его на экран.Убедитесь, что событие перед рисованием не очищает экран.Это то, что я делаю, и это всегда выглядит хорошо.

Добавлено: Я понимаю, что вы не всегда можете избежать управления большим количеством элементов управления, поэтому у меня есть стратегия для этого.Например, мне часто приходится писать диалоги пользовательского интерфейса, в которых содержимое с точки зрения элементов управления динамически изменяется по мере изменения данных базового приложения.Стандартные общеизвестные способы справиться с этим я считаю грубыми, трудными для кодирования и подверженными ошибкам.К счастью, я наткнулся на то, что, на мой взгляд, гораздо лучше, динамические диалоги .Я не утверждаю, что это для слабонервных.Я действительно утверждаю, что это экономит порядок кода, работает как часы и отнимает у меня руки управления элементами управления, связывания и всех этих событий.

...