0

我有一个绘制指南针的 WPF 应用程序。有一个带有刻度线和标签的大环。我有一个复选框,可以打开和关闭罗盘图形。当我第一次启动应用程序时,指南针会立即打开和关闭。

同时,我有一个组合框,它从本地数据库中获取一些数据并使用它来呈现一些叠加图形。使用此组合框后,指南针图形不再快速切换。事实上,每当我单击复选框时,UI 都会完全冻结大约 4 秒。

我尝试使用 WPF 的 Window Performance Profiling Tool 来分析我的应用程序。当我激活该复选框时,不仅我的应用程序冻结了,分析器也冻结了。之后图表“赶上了”,但这告诉我一定有严重错误。

我已经设法确定问题图形是刻度线(而不是数字标签)。如果我消除它们,冻结问题就会停止。如果我将它们从 360 减少到 36,应用程序仍然会冻结,但时间会更短。同样,无论我有多少刻度线,它们都会在应用程序首次启动时立即切换。

我的问题是,我如何弄清楚为什么我的指南针图形的切换从瞬间变为非常缓慢?我已经尝试了广泛的分析和调试,但我想不出任何理由为什么在某些刻度线上设置 Visibility 会导致应用程序冻结。

编辑

好的,我已经从我的应用程序中剥离了所有东西,只剩下最基本的东西,把它压缩起来,然后上传到 Sendspace。这是链接(大约143K):

http://www.sendspace.com/file/n1u3yg

[注意:不要误点击横幅广告,真正的下载链接在页面上要小得多,低得多。]

两个请求:

  1. 您是否在机器上遇到问题?尝试打开 Compass.exe(在 bin\Release 中)并快速单击复选框。指南针刻度线应无延迟地打开和关闭。然后,从组合框中选择一个项目并尝试再次快速单击该复选框。在我的机器上,它非常滞后,在我停止快速点击后,图形需要几秒钟才能赶上。

  2. 如果您确实遇到了延迟,您是否在代码中看到任何可能导致这种奇怪行为的内容?组合框没有连接任何东西,那么为什么从中选择一个项目会影响窗口上其他图形的未来性能呢?

4

1 回答 1

3

尽管 ANTS 没有指出特定的性能“热点”,但我认为您的技术存在轻微缺陷,因为似乎每个刻度都有一个ViewModel负责处理单个刻度的,并且您将这些刻度单独绑定到视图。您最终会为这些刻度创建 720 个视图模型,每次显示或隐藏整个指南针时都会触发类似的事件。每次访问此字段时,您还会创建一个新的 LineGeometry。

在这样的自定义绘制情况下,WPF 的推荐方法是使用DrawingVisual并包含 WPF 呈现系统的保留模式方面。有几个 googleable 资源讨论了这种技术,但要点是声明一个指南针类继承自FrameworkElement,以及一些较小的类继承自DrawingVisual并使用它来呈现指南针。使用这种技术,您仍然可以让 ViewModel 驱动指南针行为,但您不会为指南针的每个部分提供单独的视图模型。我倾向于将指南针分解为挡板,箭头,瞄准器等部分......但您的问题可能需要不同的方法。

class Compass : FrameworkElement
{
    private readonly List<ICompassPart> _children = new List<ICompassPart>();

    public void AddVisualChild(ICompassPart currentObject)
    {
        _children.Add(currentObject);
        AddVisualChild((Visual)currentObject);
    }

    override protected int VisualChildrenCount { get { return _children.Count; } }

    override protected Visual GetVisualChild(int index)
    {
        if (index < 0 || index >= _children.Count) throw new ArgumentOutOfRangeException();

        return _children[index] as Visual;
    }

    override protected void OnRender(DrawingContext dc)
    {
        //The control automatically renders its children based on their RenderContext.
        //There's really nothing to do here.
        dc.DrawRectangle(Background, null, new Rect(RenderSize));
    }
}

class Bezel : DrawingVisual
{
   private bool _visible;

   public bool Visible {
   {
     get { return _visible; }
     set
     {
        _visible = value;
        Update();
     }
   }

   private void Update()
   {
       var dc = this.RenderOpen().DrawingContext;
       dc.DrawLine(/*blah*/);
       dc.Close();
   }
}
于 2012-04-12T22:05:06.983 回答