【问题标题】:How to determine when the user control is fully loaded and shown?如何确定用户控件何时完全加载并显示?
【发布时间】:2011-07-11 15:54:56
【问题描述】:

stackoverflow上已经有几个类似的问题了,但是我没有找到答案

我有一个包含多个标签页的应用程序。在其中一个上,我一次加载几十个用户控件的列表。目前我正在 Load 事件中执行此操作,因此在加载此页面之前我有一个小的延迟。我想要做的是使 UI 更具响应性并在页面完全加载后填充列表。有什么方法可以跟踪用户控件何时完全加载其内容?

VisibleChanged 也无济于事,因为它会在显示任何其他子控件之前触发。当我开始加载控件列表时,当某些子控件仍然不可见时,这会导致一些难看的视觉效果。

编辑

为了更清楚。我在页面容器上有一些子控件,并且有一个稍后要加载的自定义控件列表。下面几个答案中描述的两种方法的问题是,当我开始加载控件时,它们不会让容器上的其他子控件显示出来,这就是为什么我有那些丑陋的效果(我正在这样做) BackgroundWorker,但无论如何它必须与主线程交互才能将控件添加到列表中)

【问题讨论】:

  • DRapp's answer,使用正确,应该可以解决这个问题。有关详细信息,请参阅我对该答案的评论。

标签: c# winforms events user-controls load


【解决方案1】:

为了让 UI 响应更快,你应该给自己发一条消息 (Control.BeginInvoke),做一个操作,给自己发另一条消息。然后每次你做任何事情时,下一步都会在所有用户消息之后排队,因此用户操作会得到及时处理。

一个非常好的方法是使用yield return 并让编译器处理所有的闭包逻辑:

IEnumerable AsyncLoadUI()
{
    var p = new Panel();
    Controls.Add(p);
    yield return null;

    for( int i = 0; i < 50; ++i ) {
        var txt = new TextBox();
        p.Controls.Add(txt);
        yield return null;
    }
}

override void OnLoad(EventArgs e)
{
    IEnumerator tasks = AsyncLoadUI().GetEnumerator();
    MethodInvoker msg = null;
    msg = delegate { if (tasks.MoveNext()) BeginInvoke(msg); };
    msg();
}

【讨论】:

  • 哇,我读了几本书才知道这里发生了什么。非常聪明,但也可能让未来的代码维护者感到困惑。像在您的示例中那样仅添加一堆文本框将是矫枉过正 - 我希望 OP 在加载代码中执行一些更耗时的操作。
  • @Mark:这种方法的吸引力在于它也适用于非常复杂的代码,与 DoEvents 一样易于使用,但不像 DoEvents 那样需要嵌套事件循环。跨度>
  • @Mark Heat,是的,我正在加载自定义控件列表。 @Ben Voigt 这个 sn-p 效果很好,但并不能完全解决问题。我在每个任务中输入的代码都需要时间。处理这些任务时,它们不会显示所有其他子控件,因为每个任务都会与 UI 线程进行交互。感谢您的回答,它真的很有创意=)
  • 这也是Form.Shown事件的实现方式。这将是获得这种行为的另一种方式。
  • @username:这为您提供了一种将任务分解为子任务的简单方法,每个子任务都需要很少的时间。如果您有大量不涉及 UI 的处理,例如数据库查询,请使用后台工作程序。
【解决方案2】:

看看my solution offered to another。他们有一个非常相似的问题。等到一切都完成加载后再执行某个操作,但不是每次表单都必须“激活”或“显示”。它涉及附加到您最感兴趣的最外层控件的负载处理程序。在您的情况下,选项卡式页面,但我提供的示例解决方案是在 FORM 级别。

【讨论】:

  • 您的解决方案非常优雅和简单。 +1
  • 在 OP 的情况下,有 两个问题混合在一起第一个问题是提前开始加载。 DRapp 的回答应该可以解决这个问题,如果您将他的代码放在正确的位置。 不要将他的代码放在用户控件本身上。相反,将它放在包含这些用户控件的任何控件上。然后直到该容器的 Load 返回后才会启动 - 届时所有子级也将被加载。
  • 第二个问题是加载用户控件的代码有些“慢”。该逻辑应该从 UI 线程中移出。使用任何技术启动后台任务,但不要等待主线程的结果。而是在包含控件上使用BeginInvoke 来执行最后的显示步骤。 最重要的是,无论是什么速度都很慢(例如,加载图像文件并准备 - 但不添加到显示 - 来自它的位图)必须在 @987654324 之前完成@,当你还在后台线程时。
【解决方案3】:

如果此列表的加载会导致小延迟,那么在 UI 线程上执行此加载将始终使表单无响应,无论您在什么事件中执行此操作 - 将其更改为在表单已加载,那么它只会使表单无响应且可见,而不是在显示表单之前造成延迟。

如果没有办法加快列表的加载,那么您可能需要更改表单加载逻辑,以便在后台线程中完成“繁重的工作”,以便表单在列表时保持响应正在填充。您应该意识到多线程代码更难理解,如果处理不当会产生间歇性且难以调试的错误,因此您绝对应该首先尝试加速现有代码。也就是说,如果您不能加快列表加载速度并且延迟是不可接受的,那么实际上没有其他选择。

如果您确实选择异步加载列表(在后台线程中),那么想法是启动一个后台线程(通常通过BackgroundWorker )来完成准备要添加的项目列表的艰苦工作 -当这完成(或失败)时,表单/列表框将使用提供的项目列表进行更新。

您应该能够在互联网上找到大量有关如何执行此操作的资源,这些资源将对此进行更详细的介绍。

【讨论】:

  • 是的,我正在另一个线程中加载它们,但这仍然需要与 UI 线程同步,因此我在任何其他子控件显示之前加载它们
  • @username 我不确定我是否理解 - 如果在加载方法期间同步此调用,为什么还要在后台线程中加载此列表?也许你的代码的一个小sn-p会有所帮助?
  • 我已经用 BackgroundWorker 加载它们了。问题是什么时候开始。当我开始使用 VisibleChanged 时,仍然有一些不可见的子控件。加载控件需要与 UI 线程同步,并且不允许任何其他子控件立即加载
  • @username 我会在 Load 期间启动 worker 并让列表框显示为“Loading...”(或类似),直到 worker 完成 - 您需要确保您的表单逻辑可以处理这个,但这意味着不需要同步。
  • 你最后的提议并没有解决其他子控件的问题。我可以在我的控件列表上放置一个 Loading.. 图像或类似的东西,并在 backgroundworker 完成后将其隐藏,但是当其他子控件继续一个接一个地改变它们的可见性时,它们的加载仍然会导致这些视觉效果。 (除非这个进度标签也会隐藏它们,但它不是很有帮助)。当然后台工作者需要大量的工作来创建控件,但我仍然需要在 UI 线程中添加它们
猜你喜欢
  • 1970-01-01
  • 2011-08-01
  • 1970-01-01
  • 1970-01-01
  • 2013-06-17
  • 2012-12-31
  • 2016-07-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多