2010-07-07 5 views
7

Dans une interface WPF J'ai des noeuds connectés par des chemins de Bézier, comme ceci:WPF PathGeometry mise à jour est _SLOW_

It might be... atomic http://nv3wrg.blu.livefilestore.com/y1pIGBd33lCC6lF-9H0MqgnL40BdNEoEemZDENzgpEI1IL2j4B-qb3qS3WlxMSys28IjqNngR7mdfvQBnPzerf4cFJQj9VqHBh4/acurve.png?psid=1

Lorsque l'utilisateur fait glisser un nœud autour, les chemins de connexion doivent être mis à jour en réel temps. Cependant, j'ai remarqué un certain ralentissement (en particulier si un nœud est connecté à beaucoup d'autres ou si plusieurs nœuds sont déplacés en même temps). Je PROFILES, et le principal problème semble être ici:

Proof I actually used a profiler, so please don't be all like "OMG, premature opiumzation; you are DEMON!!" http://nv3wrg.blu.livefilestore.com/y1pjRfQYuN57yei5qdUxW4Dlh4vVCzPy8TcfEzlw_8cUicfOR6BwHCTntcQbQUspRAgBdKcItC0ZcEJbIWMKaYrCtDMOtCBKB4g/profile.png?psid=1

C'est la fonction qui est appelée à chaque fois soit la propriété source ou la destination est modifiée. La géométrie qui compose le chemin semble être régénérée en interne chaque fois que l'un des points de contrôle change. Peut-être s'il y avait un moyen d'empêcher la régénération de la géométrie jusqu'à ce que toutes les propriétés de dépendance pertinentes aient été définies?

EDIT: solution Mart à utiliser StreamGeometry accéléré vers le haut de façon exponentielle; la fonction n'est nulle part proche d'un goulot d'étranglement. Un peu de réflexion suggère que PathGeometry utilise StreamGeometry en interne, et chaque fois que l'une des propriétés de dépendance est modifiée, le StreamGeometry est recalculé. Donc, cette façon coupe juste l'intermédiaire. Le résultat final est:

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); 
    } 
} 

Le code source du projet est disponible à http://zeal.codeplex.com pour les curieux.

Répondre

7

1- Je voudrais essayer d'utiliser 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 

Il semble beaucoup plus rapide que PathGeometry + PathFigure.

Lorsque vous définissez le point pour le QuadraticBezierSegment, il recalcule tout. C'est pourquoi c'est lent. Et plus lent quand il est déjà ajouté à une géométrie.

2- Essayez d'utiliser un seul élément de structure pour toutes vos courbes. Vérifiez ceci: Writing More Efficient ItemsControls

+0

Merci; passer à StreamGeometry semblait résoudre le problème! –

0

Si vous n'avez pas besoin de tests d'impact, de menus contextuels et d'infobulles pour vos courbes, vous pouvez utiliser des visuels simples plutôt que des éléments de cadre.

+0

Merci! Mais il est sur un canevas, il doit donc au moins être un UIElement (pour être sur un panneau). Et puisque les modifications de propriétés invalident le rendu, la méthode la plus simple consiste à utiliser FrameworkPropertyMetadataOptions.AffectsRender, qui nécessite un objet FraworkElement. Quoi qu'il en soit, comment cela pourrait-il aider à résoudre le problème ci-dessus? –

0

J'imagine que vos problèmes de performances proviennent de la descente de FrameworkElement et que le moteur de disposition WPF recalcule la disposition lorsque la courbe est calculée. Ce que vous pouvez envisager est de modéliser la courbe en descendant de Freezable puis d'utiliser un FrameworkElement (comme PathGeometry) pour afficher la géométrie réelle.

+0

La mise en page est en réalité recalculée uniquement lorsque la source ou la destination change, mais si l'utilisateur fait glisser un nœud, cela peut être chaque fois que la souris se déplace multipliée par le nombre de chemins affectés. Le chemin n'est pas gelé, il ne peut donc pas descendre de freezable. –

+1

Freezable ne signifie pas que vous ne pouvez pas le changer - vous ne pouvez pas le changer après qu'il soit gelé. J'imagine qu'il est possible de copier, de muter la courbe, de geler la géométrie et de mettre à jour le FrameworkElement qui le contient beaucoup plus rapidement que si vous faites un FrameworkElement. En fin de compte, je pense juste qu'il n'y aura aucun moyen d'obtenir des performances acceptables en utilisant FrameworkElement comme base. – codekaizen

Questions connexes