WPF,我该如何优化线条和圆形绘图?

时间:2010-04-22 14:30:50

标签: c# .net wpf optimization drawing

我正在开发一个应用程序,我需要在屏幕上绘制图形。 为此,我使用Canvas并将控件放在上面。

可在此处找到应用程序中显示的此类绘图示例: http://free0.hiboox.com/images/1610/d82e0b7cc3521071ede601d3542c7bc5.png

它适用于简单的图形,但我也希望能够绘制非常大的图形(数百个节点)。当我尝试绘制一个非常大的图形时,渲染需要很多时间。

我的问题是代码根本没有优化,我只是想让它起作用。到现在为止,我一方面有Canvas,另一方面有多个控件。实际上,圆圈和线条列在集合中,对于这些集合中的每个项目,我使用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>

所以,我需要你的建议。如何大幅提升渲染性能?我应该在自己的ControlTemplate中定义每个MultiTrigger圆圈渲染的可能性吗?是否有更好的画线技术?

我应该打开DrawingContext并在一个控件中绘制所有内容,而不是拥有数百个控件吗?

1 个答案:

答案 0 :(得分:4)

我看到了很多,我总是有同样的答案。当你构建图形时会发生这种情况。不要构建图形,绘制图形。换句话说,不要在应用程序数据之上创建大量数据结构(许多控件)。相反,有一个 Paint 事件处理程序,可以通过 DrawLine DrawEllipse 等绘制。

这是格言是数据持有者的特定实例。

只有当你知道你需要能够对绘制的对象上的鼠标接触做出响应时,制作某些类型的对象才有意义,即使这样,也可能没有。

如果渲染导致大量闪烁,因为你有太多的对象需要花费相当长的时间来渲染它们,请尝试绘制到内存位图,然后将其复制到屏幕上。确保预绘制事件不会清除屏幕。这就是我的工作,它看起来总是很好。

补充:我意识到你不能总是避免管理很多控件,所以我有一个策略。例如,我经常不得不编写UI对话框,其中控件方面的内容会随着底层应用程序数据的变化而动态变化。处理这个标准的众所周知的方法我发现粗糙,难以编码,并且容易出错。幸运的是,我偶然发现了我认为更好的方式,dynamic dialogs。我并不认为这是为了胆小的人。我声称它可以保存一个数量级的代码,像时钟一样工作,并且完成管理控制,绑定和所有事件的工作。