【问题标题】:Winforms: How to display a "loading" form?Winforms:如何显示“加载”表单?
【发布时间】:2011-02-16 20:09:21
【问题描述】:

我有一个网格,当双击一行时会加载一个表单。但是必须加载大量数据,所以我想显示一个带有文本“加载中,请稍候..”的简单表单。当所有加载完成后,表单必须消失。

这是我现在拥有的,但它不起作用:

调用包含大量数据的表单的代码:

FormWithLotData form = new FormWithLotData();
form.ShowDialog(this);

FormWithLotData 的构造函数:

// Show load form
FormIsLoading frm = new FormIsLoading();
_CloseLoadForm closeForm = new _CloseLoadForm(frm.Close);
System.Threading.Thread thread = new System.Threading.Thread(frm.Show);

thread.Start();

InitializeComponent();

this.Visible = false;

LoadAllData();

this.Visible = true;

// Close load form
Invoke(closeForm);

希望你能帮帮我。

编辑: 我想在加载表单上显示一个动画 gif。

解决方案: 我创建了一个后台工作者。 DoWork 事件处理所有加载,并通过使用 invoke() 方法将节点添加到树视图。现在,GUI 没有挂起,用户也不知道应用程序正在挂起。

【问题讨论】:

    标签: c# winforms


    【解决方案1】:

    您需要反转您的代码。

    FormWithLotData 的构造函数在 UI 线程中运行。这是必须显示您的 FormIsLoading 表单的线程。因此,不要尝试使用新线程显示此表单,而是使用它加载数据。

    其他人建议的 DoEvents 方式是最容易实现的,并且(可能?我自己从未做过)可能效果很好。

    更好的模式是在工作线程上加载数据。在显示 FormWithLotData 之前,开始在后台线程上加载数据并显示加载对话框。加载数据的方法应该有一个回调方法到加载对话框中,以指示何时应该关闭()。一旦它关闭,您就可以构建一个新的 FWLD,将已加载的数据传递给它,然后显示它。

    在表单已被调用后尝试加载数据会将您的 UI 与数据操作混合在一起,迫使您的表单不仅负责 UI,还负责数据检索。不利于 KISS 和单一职责,恕我直言。


    在您更新之后,DoEvents 似乎将成为您问题的唯一真正答案,但有一些警告。

    在您构建树时,您将无法以模态方式显示另一种形式。您仍然需要在表单的构造函数中完成繁重的工作。您仍然必须隐藏您的主表单和 Show()(不是 ShowDialog)您的加载表单。您还必须在构建树时在每一个可能的时刻调用 DoEvents。它并不完全是一个优雅的解决方案,但它可能是您目前最好的选择。

    【讨论】:

    • +1。比我的更好的主意,并且应该解决 GIF 不被动画化的问题......它做起来更复杂,但不是很多,我认为值得付出努力。
    • 在我的案例中,繁重的工作是构建一个大的树形视图。如果我将此方法放在一个线程中,则它无法访问树视图,因为树视图位于另一个线程中。
    • @Martijn:您可以在该线程上调用该对象的事件。如果您想更新 GUI 对象,这就是您需要做的。
    • @Martijn 很不幸。在 WPF 中它会相对容易。你可能仍然可以这样做。您需要做的是构造一个真正的Thread(即不使用线程池)并将其单元状态设置为 STA。然后在您的工作线程中构造它,完成后将其传回。然后,您必须将这个预先构建的树添加到 UI,希望它不会因为线程关联性问题而爆炸。这似乎比您在应用中需要的复杂得多。
    • @Martijn 更新了答案。在您的 LoadAllData 方法的每一行中添加对 DoEvents 的调用,看看它是否适合您。
    【解决方案2】:

    怎么样...

    FormIsLoading frm = new FormIsLoading();
    frm.Show();
    Application.DoEvents();
    
    // ... load data ...
    
    frm.Close();
    

    【讨论】:

    • 这行得通,但是...我的 gif 静止不动。这个问题的解决方案?
    • @Martijn DoEvents(我相信)只泵送一次消息队列,因此您必须在 LoadAllData 中的每一个机会调用它。
    【解决方案3】:

    在 Form_Load 事件中,添加以下内容:

    this.Visible = true;
    Application.DoEvents();
    

    在任何其他处理发生之前。 Application.DoEvents 导致 UI 以当前状态显示表单,通常在您进行其他处理时 UI 线程被锁定。

    【讨论】:

    • 我创建了一个加载事件并将那段代码放入事件中。现在我得到了这个例外:Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
    【解决方案4】:

    不要直接在 UI 线程中执行 LoadAllData(),而是启动后台线程来执行此操作。在您的 Form_Loaded 事件处理程序中,使用 AutoResetEvent 并等待它被后台数据检索线程发出信号。一旦收到信号,您就可以继续对数据执行任何您需要执行的操作,例如将其绑定到 UI。

    由于各种原因,这种方法仍然有点笨拙,但它可以帮助您入门。

    编辑:我对上面的回答很懒惰......更好的选择是在调用委托时(在数据完成后)将委托(回调)传递给后台线程检索)它将自己编组回 UI 线程,并开始对数据进行所需的工作。

    【讨论】:

    • 实际上,我打算这样做。但是...当您阻塞在 ARE 上等待的 UI 线程时,您就是在阻塞 ui 线程。因此,它使整个后台工作人员变得毫无意义。您需要的是一个回调,LoadAllData 方法可以调用该回调来向 UI 线程发出数据已加载的信号。
    • 请注意我的评论是“笨拙”。它仍然可以工作,因为后台线程没有被阻塞,它是一个发出 ARE 信号的线程,这意味着 UI 线程可以继续。无论如何,我做了一个更好的建议的编辑。
    • 如果他的后台线程工作了十分钟,你的UI线程就阻塞了十分钟。用户会认为应用程序被锁定,这是@Mart 试图避免的。您的更新是正确的。
    【解决方案5】:
    1. 在您的表单中,您可以使用 Timer 控件来模拟加载 当 ProgressBar 达到 100 时使用它会卸载表单或任何 一种动画。

      对于进度条代码,只需从工具箱中添加控件,然后 编写如下代码:

    ProgressBar1.Value = ProgressBar1.Value+1;
    if(ProgressBar1.Value == 100)
    { 
      timer1.Enabled = false; this.Hide();
      MessageBox.Show("Complete Loading...");
    }
    

    【讨论】:

      猜你喜欢
      • 2010-12-27
      • 2019-06-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多