C# WPF PathGeometry更新缓慢_

C# WPF PathGeometry更新缓慢_,c#,.net,wpf,performance,pathgeometry,C#,.net,Wpf,Performance,Pathgeometry,在WPF UI中,我使用贝塞尔路径连接节点,如下所示: 当用户拖动节点时,需要实时更新连接路径。然而,我注意到一些减速(特别是当一个节点连接到多个其他节点,或者同时拖动多个节点时)。我分析了它,主要的问题似乎在这里: 这是每次更改源或目标属性时调用的函数。每次更改任何控制点时,构成路径的几何体似乎都会在内部重新生成。也许有一种方法可以防止在设置所有相关依赖属性之前重新生成几何体 编辑:Mart使用StreamGeometry的解决方案以指数方式加快了速度;这一功能远未达到瓶颈。稍微反映一下,

在WPF UI中,我使用贝塞尔路径连接节点,如下所示:

当用户拖动节点时,需要实时更新连接路径。然而,我注意到一些减速(特别是当一个节点连接到多个其他节点,或者同时拖动多个节点时)。我分析了它,主要的问题似乎在这里:

这是每次更改源或目标属性时调用的函数。每次更改任何控制点时,构成路径的几何体似乎都会在内部重新生成。也许有一种方法可以防止在设置所有相关依赖属性之前重新生成几何体

编辑:Mart使用StreamGeometry的解决方案以指数方式加快了速度;这一功能远未达到瓶颈。稍微反映一下,PathGeometry在内部使用StreamGeometry,每次更改任何依赖项属性时,都会重新计算StreamGeometry。这样就省去了中间人。最终结果是:

private void onRouteChanged()
{
    Point src = Source;
    Point dst = Destination;
    if (!src.X.isValid() || !src.Y.isValid() || !dst.X.isValid() || !dst.Y.isValid())
    {
        _shouldDraw = false;
        return;
    }

    /*
        * The control points are all laid out along midpoint lines, something like this:
        * 
        *   -------------------------------- 
        *  |          |          |          |
        *  |   SRC    |    CP1   |          |
        *  |          |          |          |
        *   -------------------------------- 
        *  |          |          |          |
        *  |          |    MID   |          |
        *  |          |          |          |
        *   ------------------------------- 
        *  |          |          |          |
        *  |          |    CP2   |    DST   |
        *  |          |          |          |
        *   -------------------------------- 
        *   
        * This causes it to be horizontal at the endpoints and vertical
        * at the midpoint.
        */

    double mx = (src.X + dst.X) / 2;
    double my = (src.Y + dst.Y) / 2;
    Point mid = new Point(mx, my);
    Point cp1 = new Point(mx, src.Y);
    Point cp2 = new Point(mx, dst.Y);

    _geometry.Clear();
    _shouldDraw = true;
    using(StreamGeometryContext ctx = _geometry.Open())
    {
        ctx.BeginFigure(src, false, false);
        ctx.QuadraticBezierTo(cp1, mid, true, false);
        ctx.QuadraticBezierTo(cp2, dst, true, false);
    }
}

该项目的完整源代码可在上获得,以供好奇者使用。

如果您不需要曲线的点击测试、上下文菜单和工具提示,您可以使用简单的视觉效果而不是框架元素。

1-我会尝试使用StreamGeometry:

        StreamGeometry streamGeo = new StreamGeometry();
        Stopwatch sw = new Stopwatch();
        sw.Start();
        for (int i = 0; i < 10000; i++)
        {
            streamGeo.Clear();
            var ctx = streamGeo.Open();
            ctx.BeginFigure(new Point(0, 0), false, false);
            ctx.QuadraticBezierTo(new Point(10, 10), new Point(10, i), true, true);
            ctx.Close();
        }
        sw.Stop();
        Console.WriteLine(sw.ElapsedMilliseconds); // For 10k it took 30 ms
StreamGeometry streamGeo=新的StreamGeometry();
秒表sw=新秒表();
sw.Start();
对于(int i=0;i<10000;i++)
{
streamGeo.Clear();
var ctx=streamGeo.Open();
ctx.beginigure(新点(0,0),false,false);
ctx.二次Bezierto(新点(10,10),新点(10,i),真,真);
ctx.Close();
}
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);//10公里需要30毫秒
它看起来比PathGeometry+PathFigure快得多

当您为方形BezierSegment设置点时,它会重新计算所有内容。这就是它慢的原因。当它已经添加到几何体时,速度会更慢

2-尝试对所有曲线仅使用一个frameworkelement。选中此项:

我可以想象,性能问题来自于从
FrameworkElement
下降,并让WPF布局引擎在计算曲线时重新计算布局


你可以考虑通过从 FrutZaby中降阶来建模曲线,然后使用<代码>框架元素(如PathGeometry)来显示实际的几何图形。但它位于画布上,所以它至少必须是UIElement(位于面板上)。由于属性更改会使渲染无效,因此最简单的方法是通过FrameworkPropertyMetadataOptions.AffectsRender,它需要FraworkElement。无论如何,这将如何帮助解决上述问题?布局实际上仅在源或目标更改时重新计算,但如果用户拖动节点,则每次鼠标移动都可能是受影响路径数的倍。路径没有冻结,因此它不能从freezable下降。freezable并不意味着你不能更改它-你只是不能在冻结后更改它。我认为复制、变异曲线、冻结几何体和更新包含它的FrameworkElement可能比让FrameworkElement完成所有操作要快得多。最终,我只是认为使用FrameworkElement作为基础无法获得可接受的性能;切换到StreamGeometry似乎解决了这个问题!