从线程 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");
}
这个想法是我正在为我要处理的每个任务启动线程。在我检查是否有已完成的任务之后,它们被关闭并启动新任务,直到没有更多任务。
与其让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
}