2010-07-07 70 views
7

在WPF UI我具有由貝塞爾路徑連接的節點,例如:WPF的PathGeometry更新是_SLOW_

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

當用戶拖動周圍的節點,連接路徑需要實被更新時間。但是,我注意到一些減速(特別是如果一個節點連接到其他許多節點,或者多個節點一次被拖動)。我異形它,主要問題似乎是在這裏:

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

這是每個源或目標屬性改變時調用的函數。每當任何控制點發生變化時,組成路徑的幾何圖形似乎都會在內部重新生成。也許如果在所有相關的依賴項屬性設置完成之前,有一種方法可以防止幾何體被重新生成?

編輯:沃爾瑪的解決方案使用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爲好奇。

回答

7

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個frameworkelement。選中此項: Writing More Efficient ItemsControls

+0

謝謝;切換到StreamGeometry似乎解決了問題! – 2010-07-07 15:36:40

0

如果您不需要命中測試,上下文菜單,曲線工具提示,則可以使用簡單的視覺效果而不是框架元素。

+0

謝謝!但它在Canvas上,所以它至少必須是UIElement(在面板上)。並且由於屬性更改會使渲染失效,所以最簡單的方法是通過FrameworkPropertyMetadataOptions.AffectsRender(需要FraworkElement)。無論如何,這將如何幫助解決上述問題? – 2010-07-07 06:07:13

0

我想象你的性能問題來自於FrameworkElement的降序,並且讓WPF佈局引擎在計算曲線時重新計算佈局。

您可以考慮的是從Freezable降序建模曲線,然後使用FrameworkElement(如PathGeometry)顯示實際幾何圖形。

+0

佈局實際上只在源或目標發生更改時才重新計算,但如果用戶正在拖動節點,則可能每次鼠標移動乘以受影響路徑的次數。路徑沒有被凍結,所以它不能從可凍結的地方下降。 – 2010-07-07 07:15:13

+1

Freezable並不意味着您無法更改它 - 凍結後您無法更改它。我可以想象它可以複製,修改曲線,凍結幾何並更新包含它的FrameworkElement,比使FrameworkElement完成所有操作更快。最終,我認爲將無法通過使用FrameworkElement作爲基礎獲得可接受的性能。 – codekaizen 2010-07-07 07:24:13