【问题标题】:Form Show not working with threads表单显示不适用于线程
【发布时间】:2016-09-22 08:05:57
【问题描述】:

我正在创建一个应用程序,它启动一个 FormSelect(),要求用户在列表中选择一个项目,当用户按下“选择”按钮时,它将创建一个 MainForm() 的新实例,隐藏 FormSelect 和显示 MainForm,但它不起作用,我所做的所有测试都出现异常。

这里有一些代码:

static class Program {
        public static MainForm mainForm;
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main() {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new FormSelect());
        }
    }

我的 FormSelect 没有什么特别之处,所以这里是单击它启动 MainForm 的按钮:

private async void btnSelect_Click(object sender, EventArgs e) {

            loadingSpiner.Visible = true;
            btnSelect.Enabled = false;

            int index = listProcess.SelectedIndex;

            await startMainForm(index);

            this.Hide();
            Program.mainForm.Show();
        }

private async Task startMainForm(int index) {

            await Task.Run(() => {
                 Program.mainForm = new MainForm(runningProcess[index]);
            });
        }

正如您在上面看到的,我正在使用一个任务来启动 MainForm,因此它不会冻结我的 UI 和我的“加载微调器”

但是当它尝试使用 .Show() 时,我得到了一个跨线程异常,所以我尝试使用以下方法调用该操作:

this.Invoke(new MethodInvoker(delegate () { Program.mainForm.Show(); }));

但是使用上面的方法我得到一个异常说:

System.Windows.Forms.dll 中出现“System.ComponentModel.Win32Exception”类型的异常,但未在用户代码中处理

附加信息:创建窗口句柄时出错。

如果我在 startMainForm 方法中删除“await Task.Run()...”,一切正常,但它完全阻止了 FormSelect UI。

如何避免这个问题?谢谢!

【问题讨论】:

  • 你不应该从 uithread 打开任何 ui。
  • 除了用于调用 Application.Run 的线程(通常称为 UI 线程)之外,您不能在任何线程上创建 UI。
  • 我认为运行应用程序时启动的“默认表单”是在 Program.cs 中指定的。在 Program.cs 中,您应该能够找到启动该表单的调用,它应该是阻塞的(因为那时我们是单线程的),然后添加代码以启动下一个表单?

标签: c# .net winforms


【解决方案1】:

你不能那样做。

如果没有可靠地重现问题的良好Minimal, Complete, and Verifiable code example,就不可能确定最适合您的方案的解决方案是什么。但是 UI 对象只能在 UI 线程中创建和使用。您不能在工作线程中创建 MainForm 实例。

由于您的代码示例不完整,不清楚您为什么首先尝试使用Task.Run() 创建MainForm 实例。代码所做的只是调用构造函数,而构造函数无论如何都不应该是耗时的操作。

如果您要执行一些耗时的初始化,您应该将其从 MainForm 类中抽象出来,将其放入其他类中,一旦完全初始化,就可以将其传递给 MainForm。然后您可以使用例如初始化该其他类对Task.Run() 的调用(但仍然不在构造函数中……创建对象,然后如果您需要异步调用初始化它的方法,请执行此操作)。

例如:

private async void btnSelect_Click(object sender, EventArgs e) {
    loadingSpiner.Visible = true;
    btnSelect.Enabled = false;

    int index = listProcess.SelectedIndex;

    MainFormData data = await startMainForm(index);

    Program.mainForm = new MainForm(data);
    this.Hide();
    Program.mainForm.Show();
}

private async Task<MainFormData> startMainForm(int index) {
    await Task.Run(() => {
         MainFormData data = new MainFormData();
         data.Initialize(runningProcess[index]);
         return data;
    });
}

class MainFormData
{
    public MainFormData() { ... }

    // You don't say what "runningProcess[index]" is, so placeholder here...
    public void Initialize(object o) { ... }
}

类似的东西。当然可以根据您的需要进行调整。

当然,其他类必须只初始化非 UI 方面。 IE。 UI 底层的数据。由于创建 UI 对象通常并不耗时,因此我认为这在您的情况下是可行的。如果您觉得 UI 本身的初始化很耗时,那么您应该寻求帮助以弄清楚是什么情况并解决该问题。当然,同样没有好的 MCVE,我对此没有什么特别的评论。

【讨论】:

  • 谢谢,我将 MainForm 构造函数中的所有逻辑(除了 InitializeComponent())移到返回“MainFormData”的任务中,所以我只创建了一个新的 MainForm(FormData),它运行良好.再次感谢
猜你喜欢
  • 1970-01-01
  • 2011-10-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-08
  • 2023-03-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多