【问题标题】:C# Invoke button control on separate threadC#在单独的线程上调用按钮控件
【发布时间】:2015-03-17 19:56:31
【问题描述】:

我已经看到很多关于如何从不同的线程编辑 c# 表单上的控件的问题,但对我来说没有多大意义。我知道您不能从另一个线程更改任何 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 循环完成时,文件的写入完成,线程也完成了,因此它结束并给出了一个带有“完成”的消息框,现在作为最后一个按钮需要重新启用。

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

        ThreadTick();
    }

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

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

【问题讨论】:

  • 小评论,如果只是while (currValue_ < maxValue_),你的while条件会更有意义
  • @RufusL 实际上它不会那样工作,我真的不明白为什么。该代码是进度条的一部分,它不会“进步”但会立即停止,可能是因为它无法访问当前值和最大值。

标签: c# multithreading winforms controls invoke


【解决方案1】:

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

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

        ThreadTick();
    }

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

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

【讨论】:

  • 无法识别当前部分。 System.Windows.Forms.Application does not contain a definition for Current
  • 这成功了!现在嗯。在这里,BeingInvoke 和委托实际上是什么?
  • 我将用更多细节更新答案,但本质上 beginInvoke 异步调用在定义的匿名方法上创建控件的线程。
【解决方案2】:

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

请注意,此 async 方法与您的 Generate_Click 事件存在于同一类中,并在 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;
}

【讨论】:

  • 我想不出办法来使用它。当写入文件的线程处于活动状态时,必须禁用该按钮。您在这里使用计时器和 thread.sleep 吗?
  • 没有计时器,我只是使用 Thread.Sleep 来表示一个长时间运行的任务。您可以改为调用其他方法。关键是另一个方法的调用是在一个Task中完成的,不会阻塞UI线程。
  • 长时间运行的方法叫什么名字?由于缺少很多代码,我在理解您的示例时遇到了一些麻烦。
【解决方案3】:

不知何故,获取对承载 generate_ 按钮的表单的引用(我们称之为myform)。然后,在 ThreadWork 的底部:

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

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

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

【讨论】:

  • 如何引用表格?将其作为对象发送给班级?
  • @user1859829 随心所欲。将其设为公共静态变量,将其设为私有实例变量并将其传递到各处,等等。我需要查看更多代码才能具体告诉您。 ThreadWork 在哪里?那在你的表单类里面吗?
猜你喜欢
  • 2015-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
  • 2012-11-06
  • 1970-01-01
相关资源
最近更新 更多