从线程 C# 更新 GUI,而不绑定到 UI 控件

本文关键字:绑定 UI 控件 线程 更新 GUI | 更新日期: 2023-09-27 18:29:28

知道当你需要从其他线程更新gui时,会使用Invoke方法。但是,如何在不将控件绑定到代码的情况下实现这一点呢?

这是我的测试类:

class test
{
   public List<Thread> threads = new List<Thread>();
    public int nThreads = 0;
    public int maxThreads = 5;
    public void DoWork(object data)
    {
        string message = (string)data;
        //MessageBox.Show(message);        

    }
    public void CreateThread(object data)
    {
        if (nThreads >= maxThreads)
            return;
        Thread newThread = new Thread(DoWork);
        threads.Add(newThread);
        newThread.IsBackground = true;
        newThread.Start(data);
        nThreads++;

    }
    public void WindUpThreads()
    {
        //MessageBox.Show("count: " + nThreads.ToString());
        for(int i = 0; i < threads.Count; i++)
        {
            if (threads[i].IsAlive == false)
            {
                threads[i].Abort();
                threads.RemoveAt(i);
               //MessageBox.Show("removing at " + i.ToString());
            }
        }
        nThreads = threads.Count;
    }
}

问题是 = 我必须使用什么技术才能将 gui 而不是硬编码控制更新到类中?我试图将委托传递给 DoWork 方法,但这不起作用 (http://pastebin.com/VaSYFxPw(。谢谢!

我正在使用WinForms,.NET 3.5

下面是button_click处理程序:

 private void button1_Click(object sender, EventArgs e)
    {
        button1.Enabled = false;
        test thTest = new test();
        string[] strings;
        try
        {
            strings = File.ReadAllLines("C:''users''alex''desktop''test.txt");
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
            return;
        }
        bool flag = true;
        int counter = 0;
        int dataCount = strings.Length;
        while (flag == true)
        {
            if (counter >= dataCount)
            {
                flag = false;
            }
            while (thTest.nThreads < thTest.maxThreads)
            {
                if (flag == false)
                    break;
                thTest.CreateThread(strings[counter]);
                //Data d = new Data();
                //d.deleg = AddItem;
                //d.mess = strings[counter];
                //thTest.CreateThread((object)d);
                //MessageBox.Show(counter.ToString());
                counter++;
            }
            thTest.WindUpThreads();
            if (flag == false)
            {
                do
                {
                    thTest.WindUpThreads();
                } while (thTest.nThreads != 0);
            }

        }
        listBox1.Items.Add("Done");
    }

这个想法是我正在为我要处理的每个任务启动线程。在我检查是否有已完成的任务之后,它们被关闭并启动新任务,直到没有更多任务。

从线程 C# 更新 GUI,而不绑定到 UI 控件

与其让DoWork负责用它执行的操作的结果更新 UI,不如让它返回值:

//TODO change the type of the result as appropriate
public string DoWork(string message)
{
    string output = "output";
    //TODO do some work to come up with the result;
    return output;
}

然后使用 Task.Run 创建一个Task,该表示线程池线程中正在完成的工作。 然后,您可以从按钮单击处理程序等待该任务。

private async void button1_Click(object sender, EventArgs e)
{
    button1.Enabled = false;
    test thTest = new test();
    //I'd note that you really should pull out reading in this file from your UI code; 
    //it should be in a separate method, and it should also be reading 
    //the file asynchronously.
    string[] strings;
    try
    {
        strings = System.IO.File.ReadAllLines("C:''users''alex''desktop''test.txt");
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
        return;
    }
    foreach (var line in strings)
    {
        var result = await thTest.DoWork(line);
        listBox1.Items.Add(result);
    }
    listBox1.Items.Add("Done");
}

如果你真的想老派,你可以改用BackgroundWorker。 只需在DoWork处理程序中执行工作,在计算结果后设置结果(通过参数(,并使用 RunWorkerCompleted 事件处理程序中的结果更新 UI。 这使你可以将 UI 和非 UI 工作分开,尽管它远不如较新的功能强大、通用和可扩展。

问题是 = 我必须使用什么技术才能将 gui 而不是硬编码控制更新到类中?我试图将委托传递给 DoWork 方法,但这不起作用

这确实是可能的技术之一。它不起作用,因为 UI 线程中有一个阻塞循环 - button1_Click处理程序中的大部分代码。生成其他工作线程并不重要 - 该代码使 UI 线程保持繁忙,因此 Control.Invoke/Control.BeginInvoke不起作用,因为它们由 UI 线程消息循环处理,在这种情况下,它没有机会这样做。最终结果是经典的僵局。

因此,您可以使用委托方法,但要使其正常工作,您需要在单独的线程中移动该代码。像这样的东西

private void button1_Click(object sender, EventArgs e)
{
    button1.Enabled = false;
    var worker = new Thread(DoWork);
    worker.IsBackground = true;
    worker.Start();
}
private void OnWorkComplete(Exception error)
{
    if (error != null)
        MessageBox.Show(error.Message);
    button1.Enabled = true;
}
private void DoWork()
{
    Exception error = null;
    try { DoWorkCore(); }
    catch (Exception ex) { error = ex; }
    Invoke(new Action(OnWorkComplete), error);
}
private void DoWorkCore()
{
    test thTest = new test();
    // NOTE: No try/catch for showing message boxes, this is running on a non UI thread
    string[] strings = File.ReadAllLines("C:''users''alex''desktop''test.txt");
    bool flag = true;
    int counter = 0;
    int dataCount = strings.Length;
    // The rest of the code...
    // Pass a delegate to the other threads.
    // Make sure using Invoke when you need to access/update UI elements
}