从线程 C# 更新 GUI,而不绑定到 UI 控件
Updating GUI from thread C#, without binding to UI controls
据了解,当您需要从其他线程更新gui 时,会使用Invoke 方法。但是如何在不将控件绑定到代码的情况下实现它?
这是我的测试 class:
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 而不是将控制硬编码到 class?我试图将委托传递给 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,不如让它 return 的值:
//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 和 non-UI 分开工作,尽管它的功能、通用性和可扩展性远不如新功能。
The question is = what tecnique I must use in order to update gui but not hardcode control into class? I've tried to pass delegate to DoWork Method, but this doesn't work
这确实是可能的技术之一。它不起作用,因为您在 UI 线程中有一个阻塞循环 - button1_Click
处理程序中的大部分代码。产生额外的工作线程并不重要 - 该代码使 UI 线程忙碌,因此 Control.Invoke
/ Control.BeginInvoke
不起作用,因为它们由 [=19= 处理] 线程消息循环,在这种情况下没有机会这样做。最终结果是经典死锁。
因此,您可以使用委托方法,但要使其正常工作,您需要将该代码移到单独的线程中。像这样
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
}
据了解,当您需要从其他线程更新gui 时,会使用Invoke 方法。但是如何在不将控件绑定到代码的情况下实现它?
这是我的测试 class:
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 而不是将控制硬编码到 class?我试图将委托传递给 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,不如让它 return 的值:
//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 和 non-UI 分开工作,尽管它的功能、通用性和可扩展性远不如新功能。
The question is = what tecnique I must use in order to update gui but not hardcode control into class? I've tried to pass delegate to DoWork Method, but this doesn't work
这确实是可能的技术之一。它不起作用,因为您在 UI 线程中有一个阻塞循环 - button1_Click
处理程序中的大部分代码。产生额外的工作线程并不重要 - 该代码使 UI 线程忙碌,因此 Control.Invoke
/ Control.BeginInvoke
不起作用,因为它们由 [=19= 处理] 线程消息循环,在这种情况下没有机会这样做。最终结果是经典死锁。
因此,您可以使用委托方法,但要使其正常工作,您需要将该代码移到单独的线程中。像这样
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
}