C# 在单独的线程上调用按钮控件

C# Invoke button control on separate thread

我已经看到很多关于如何从不同线程编辑 C# 窗体上的控件的问题,但 none 对我来说很有意义。我知道你不能从另一个主线程更改任何 UI。要完成这项工作,您必须使用 invoke 并从那里安全地编辑控件?

我有一个开始写入文件的按钮,当您按下按钮时,按钮本身会被禁用,因此您无法启动多个执行完全相同操作的线程。写入完成后,我希望该按钮再次可用,但我无法让它在另一个线程上工作。

我将此作为表单中的 Generate_Click 事件。

private void Generate_Click(object sender, EventArgs e)
{
    Generate.Enabled = false;

    int x = 512;
    int y = 512;

    MBrot mbrot = new MBrot(x, y);

    PB_Update lb = new PB_Update(0, y, Generator_PB, Generate, mbrot, this);
    lb.Start();
}

这是在 PB_Update.cs ThreadWork() 函数中,当 while 循环完成时,文件写入完成,线程也完成,因此它结束并给出一个带有 [=20= 的消息框] 现在作为最后一次需要再次启用该按钮。

public void ThreadWork()
{
    while (true)
    {
        if (currValue_ >= maxValue_)
            break;

        ThreadTick();
    }

    mb_.StopBrot();
    t_.Interrupt();

    MessageBox.Show("Finished!");
    Generate_.Enabled = true;
}

对于 WinForms,您可以直接在通过 Control.BeginInvoke 方法创建控件的线程上执行,您也可以使用 Control.Invoke,但是 Control.BeginInvoke 更适合 UI 操作。

public void ThreadWork()
{
    while (true)
    {
        if (currValue_ >= maxValue_)
            break;

        ThreadTick();
    }

    mb_.StopBrot();
    t_.Interrupt();

    MessageBox.Show("Finished!");
    Generate_.BeginInvoke((Action)delegate()
    {
        Generate_.Enabled = true;
    });
}

以某种方式获取对托管 generate_ 按钮的表单的引用(我们称之为 myform)。然后,在 ThreadWork 的底部:

myform.Invoke(new Action(() => {
    myform.SetGenerateEnabled();
}));

然后在您的表单中创建适当启用按钮的方法。 (我使用了一种方法,而不是直接更新按钮,这样您就不会公开暴露按钮。)

这会在 myform 的线程上执行 { ... } 内的命令,这是一个 UI 线程,因为它是 UI。至少,我是这么理解的。这就是我从其他线程进行所有 UI 更新的方式。

这是一个启动异步任务的简单示例,该任务禁用按钮 5 秒,然后再次启用它。同时,UI 的其余部分可以正常使用。

请注意,此 async 方法与您的 Generate_Click 事件存在于同一 class 中,并在 UI 线程上运行。这意味着它可以启用和禁用按钮。但是长 运行 任务在单独的线程上执行,因此它不会锁定 UI.

希望此示例为您提供修改自己代码的基础:

private void Generate_Click(object sender, EventArgs e)
{
    DisableButton(sender as Button, 5);
}

private async void DisableButton(Button sender, int secondsToDisable)
{
    sender.Enabled = false;

    // In your code, you would kick off your long-running process here as a task
    await Task.Run(()=>Thread.Sleep(TimeSpan.FromSeconds(secondsToDisable)));

    sender.Enabled = true;
}