当创建一个新线程时,不会对GUI进行更改(c#)
本文关键字:GUI 创建 一个 新线程 线程 | 更新日期: 2023-09-27 18:18:21
在一些帮助下,我设法创建了一个新线程,虽然方法似乎要执行,方法的条件要么使绿色或红灯出现,虽然在没有新线程的情况下运行方法(Check1..等),这些变化反映在GUI上(例如红/绿灯出现),但是当创建一个新线程并运行方法时,这些变化不反映在Form/GUI上。
// Method / Action to start the checks private void StartChecks_Click(object sender, EventArgs e)
{
Thread t = new Thread(
o =>
{
InitChecks();
});
t.Start();
}
// Check1 public void Check1()
{
// lets grabs the info from the config!
var lines = File.ReadAllLines("probe_settings.ini");
var dictionary = lines.Zip(lines.Skip(1), (a, b) => new { Key = a, Value = b })
.Where(l => l.Key.StartsWith("#"))
.ToDictionary(l => l.Key, l => l.Value);
// lets set the appropriate value for this check field
label1.Text = dictionary["#CheckName1"];
// lets define variables and convert the string in the dictionary to int for the sock.connection method!
int portno1;
int.TryParse(dictionary["#PortNo1"], out portno1);
// Convert hostname to IP, performance issue when using an invalid port on a hostname using the TcpClient class!
IPAddress[] addresslist = Dns.GetHostAddresses(hostname2);
foreach (IPAddress theaddress in addresslist)
{
// Attempt to create socket and connect to specified port on host
TcpClient tcP = new System.Net.Sockets.TcpClient();
try
{
tcP.ReceiveTimeout = 1;
tcP.SendTimeout = 1;
tcP.Connect(theaddress, portno1);
displayGreen1();
tcP.Close();
}
catch
{
displayRed1();
}
}
}
// Change the lights when the condition is met
public void displayGreen1()
{
pictureBox2.Visible = false;
pictureBox1.Visible = true;
}
private void displayRed1()
{
pictureBox2.Visible = true;
pictureBox1.Visible = false;
}
这就是WinForms的设计方式。您不能从另一个线程进行更改。解决方案通常是使用异步委托。
首先添加这个声明
public delegate void MyDelegate1 ();
public delegate void MyDelegate2 ();
然后当你在另一个线程中,你应该做:
MyDelegate1 d1= new MyDelegate1 (displayGreen1);
this.BeginInvoke(d1);
MyDelegate2 d2= new MyDelegate2 (displayRed1);
this.BeginInvoke(d2);
这是因为UI控件只能从UI线程更新。当你没有创建一个新线程时,更新控件的代码在UI线程上运行,所以它像你期望的那样工作。当你做创建一个新线程时,因为这个线程不是 UI线程,应该更新控件的代码失败了。
您可以通过更改方法调用来确保更新控件的代码在UI线程上运行:-
this.BeginInvoke(new Action(() => displayRed1()));
和
this.BeginInvoke(new Action(() => displayGreen1()));
顺便说一句(与你当前的问题无关):-
尽量避免创建显式线程。相反,使用线程池为您管理线程,例如ThreadPool.QueueUserWorkItem(x => InitChecks())
。(请注意,这仍然将在非ui线程上运行,因此您仍然需要使用上面详细介绍的BeginInvoke()
。)线程池最清楚何时创建和执行线程,使用它最终会使你的代码更高效。
尽量避免捕获所有异常类型在您的try{...}catch{...}
。这表明当抛出任何类型的异常时,您的代码知道该做什么。相反,只捕获您确切知道如何处理的异常,
。
try
{
...
}
catch(TcpException)
{
...
}
catch(AnotherKnownException)
{
...
}
...
注意,对于所有异常类型也可以使用catch
块,只要在退出块时重新抛出异常,
。
try
{
...
}
catch(KnownException)
{
...
}
catch(Exception)
{
// perform some logging, rollback, etc.
throw;
}
以你的技能水平,最好是:
- 在表单中使用计时器来检查一些状态变量(例如
bool _pb1Visible
和bool _pb2Visible
) - 在timer事件中更新picturebox可见性
- 在线程中,只更新上述bool成员变量。
它会像一个魅力!
简单的例子:
在Check1()
方法中,而不是:
displayGreen1();
把
_pb1Visible=true;
_pb1Visible=false;
而不是
displayRed1();
把
_pb1Visible=false;
_pb1Visible=true;
将计时器放在表单上。在计时器事件中,执行:
pictureBox2.Visible = _pb2Visible;
pictureBox1.Visible = _pb1Visible;