WPF PathGeometry обновление _SLOW_ - PullRequest
7 голосов
/ 07 июля 2010

В пользовательском интерфейсе WPF у меня есть узлы, связанные путями Безье, например:

Это может быть ... атомное http://nv3wrg.blu.livefilestore.com/y1pIGBd33lCC6lF-9H0MqgnL40BdNEoEemZDENzgpEI1IL2j4B-qb3qS3WlxMSys28IjqNngR7mdfvQBnPzerf4cFJQj9VqHBh4/acurve.png?psid=1

Когда пользователь перетаскивает узел, соединительные пути должны обновляться в режиме реального времени. Тем не менее, я заметил некоторое замедление (особенно если один узел подключен ко многим другим или несколько узлов перетаскиваются одновременно). Я профилировал это, и главная проблема, кажется, здесь:

Доказательство того, что я действительно использовал профилировщик, так что, пожалуйста, не будьте все как "OMG, преждевременная опификация; вы ДЕМОН !!" http://nv3wrg.blu.livefilestore.com/y1pjRfQYuN57yei5qdUxW4Dlh4vVCzPy8TcfEzlw_8cUicfOR6BwHCTntcQbQUspRAgBdKcItC0ZcEJbIWMKaYrCtDMOtCBKB4g/profile.png?psid=1

Эта функция вызывается каждый раз при изменении свойства источника или назначения. Кажется, что геометрия, составляющая путь, восстанавливается изнутри каждый раз, когда изменяется любая из контрольных точек. Возможно, если бы был способ предотвратить восстановление геометрии до тех пор, пока не будут установлены все соответствующие свойства зависимостей?

РЕДАКТИРОВАТЬ: Решение Марта использовать 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);
    }
}

Полный исходный код проекта доступен по адресу http://zeal.codeplex.com для любопытных.

Ответы [ 3 ]

7 голосов
/ 07 июля 2010

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

Это выглядит намного быстрее, чем PathGeometry + PathFigure.

Когда вы устанавливаете точку для QuadraticBezierSegment , он пересчитывает все. Вот почему это медленно. И более медленный, когда он уже добавлен в геометрию.

2 - Старайтесь использовать только 1 элемент кадра для всех ваших кривых. Проверь это: Написание более эффективных элементов управления

0 голосов
/ 07 июля 2010

Я полагаю, что ваши проблемы с производительностью связаны с понижением с FrameworkElement и наличием механизма разметки WPF, который пересчитывает разметку по мере вычисления кривой.

То, что вы можете рассмотреть, это смоделировать кривую, опустившись с Freezable, а затем использовать FrameworkElement (например, PathGeometry) для отображения фактической геометрии.

0 голосов
/ 07 июля 2010

Если вам не нужно тестировать нажатия, контекстные меню, всплывающие подсказки для кривых, вы можете использовать простые визуальные элементы вместо элементов каркаса.

...