【问题标题】:Separate UI thread for details view using WinForms使用 WinForms 为细节视图单独的 UI 线程
【发布时间】:2010-12-08 19:34:37
【问题描述】:

我们应用的主/详细视图使用 datagridview 作为主视图,使用自定义控件作为详细视图。详细信息视图需要很长时间来计算和渲染,这使得在主视图上/下光标非常缓慢。

因此,我们希望详细信息视图通过来自主节点的更改通知异步运行(在单独的 UI 线程中)。

在单独的线程中创建一个表单相对简单,因为 Application.Run 需要一个表单参数。

有没有办法在单独的线程上运行 winforms 控件?我知道不同线程中的本机窗口可以具有父/子关系,只是不确定如何使用 winforms 进行设置。

TIA,

【问题讨论】:

    标签: winforms multithreading user-interface


    【解决方案1】:

    从辅助线程更新 UI
    http://msdn.microsoft.com/en-us/magazine/cc188732.aspx

    直观地说,您也应该能够通过使用 BackgroundWorker 来完成同样的事情。 BackgroundWorker 旨在在后台执行内容时更新 UI 内容,例如进度条,并且可以在操作期间取消。

    【讨论】:

    • 不幸的是,没有。实际的 UI 需要在不同的线程中,因为计算和渲染调用不同的 Winforms 控件,只能在它们的 UI 线程上调用(这是 Winforms 限制。)为了安全地调用它们,我们需要执行 InvokeSync回到主线程,这再次意味着阻塞 ui。假设我们已经尽可能使用单个 UI 线程,原来的问题仍然存在:有没有办法在不同的 UI 线程上设置 winforms 控件?
    【解决方案2】:

    速度变慢是由于加载数据,还是 UI 本身的填充?

    大多数时候是前者,所以如果是这样的话,那么进行数据加载的逻辑应该被抽象到不同的线程中。 UI 代码可以存在于主线程中,因为更新很快。在这种情况下,您可以使用ThreadBackgroundWorker。关键是将数据加载与 GUI 群体分开。

    【讨论】:

    • 在我们的例子中,它肯定会更新和重新计算视图。数据已经在内存中。占用大量 CPU 的是布局和绘图算法。
    • 另外(正如下面另一条评论中提到的)这些算法需要调用子 winforms 控件,这些控件只能在其 UI 线程上调用。
    • 布局和绘图是分开的东西,可能会分开。如果您可以提供有关视图中发生的更多细节(请编辑您的原始问题),我们可以为您提供更具体的问题帮助。
    • 与其追求完全不同的路线,不如将其引回到最初的问题 - winforms 控件可以在单独的 ui 线程上运行吗?我们感谢大家的建议,但到目前为止,还没有人直接解决这个问题,这是独立于应用程序并且纯粹与框架有关的问题。
    • 这是一个很好的问题,但我认为您在这里寻找的答案很有可能只是为了规避真正的问题。
    【解决方案3】:

    如果您在代码中触发了详细视图的更新,您可以通过在用户选择主记录和更新详细视图之间休眠 500 毫秒来大大提高可用性。

    这使用户有 1/2 秒的时间滚动到下一条记录,而完全不更新详细信息视图。

    【讨论】:

    • 我们实际上确实有这样的延迟(并且可能应该在原始问题中提到它,谢谢。)但是,当详细信息视图确实开始更新它时,它会明显阻止用户的滚动,这是为什么我们需要在单独的线程中执行此操作。有没有办法做到这一点?
    • 查看我关于线程的其他答案。如果用户在延迟期间滚动,则需要重新启动计时器。
    【解决方案4】:

    如果您在渲染过程中遇到速度问题,您应该考虑暂停布局,直到表单完成更新,然后在最后刷新一次可见显示。

    this.SuspendLayout();
    
    // Do control stuff here
    
    this.ResumeLayout();
    

    如果这没有帮助,试试这个:

    [DllImport("user32.dll")]
    
    public static extern bool LockWindowUpdate(IntPtr hWndLock);
    //
    LockWindowUpdate(this.Handle);
    
    // Do control stuff here
    
    this.Refresh(); //Forces a synchronous redraw of all controls
    
    LockWindowUpdate(IntPtr.Zero);
    

    http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/8a5e5188-2985-4baf-9a0e-b72064ce5aeb

    【讨论】:

    • 我们已经做到了 :) 为了让您了解这方面的范围,几个月来我们一直致力于在该领域获得最大性能。所以是的,我们使用了延迟更新、backgroundworker,将所有数据保存在内存中,以及 SuspendLayout()。我们尽可能多地记住计算。我们将重绘区域与当前图形上下文相交。我们已经到了真正觉得需要使用单独的线程来进行控制的地步。再说一遍,有没有办法做到这一点?
    • 我还没有找到,但我会继续寻找。问题在于 Winforms 使用的消息泵本质上是从 Windows 事件模型继承的单线程单元 (STA)。即使在 WPF(支持某些多线程屏幕更新)中,消息泵仍然是一个 STA。我认为唯一可能剩下的就是找到一种方法,如果用户移动到不同的记录,以某种方式简单地 ABORT 屏幕更新(因为该用户操作本质上会使任何可能正在完成的屏幕更新无效)。
    • 好的,这里还有一点信息:stackoverflow.com/questions/1566791/run-multiple-ui-threads。它描述了如何同时运行两个消息泵。也许这会以某种方式帮助你。不过,我不能保证它不会杀死一只小猫。
    • 谢谢,但是我们知道这一点 - 上面的原始问题提到在单独的 UI 线程中运行第二个 form 很简单。我们正在寻找一种在单独的 UI 线程中运行 控件 的方法。 Application.Run 采用 Form 参数,而不是 Control。
    猜你喜欢
    • 2011-02-17
    • 1970-01-01
    • 2012-11-06
    • 2011-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多