循环通过 IP 地址时 UI 未更新
Posted
技术标签:
【中文标题】循环通过 IP 地址时 UI 未更新【英文标题】:UI not updating while looping through IP Addresses 【发布时间】:2020-08-10 14:26:12 【问题描述】:我有一个简单的工作要做,点击按钮它将从一个范围内获取所有网络 IP 地址,循环通过它们并将活动放在一个列表中。在执行该过程时,将显示一个面板,其中将显示正在检查的 IP 地址。代码运行良好,但表单挂起,应用程序没有响应,即使面板没有显示,IP 地址也没有显示。怎么做? 我的代码是:
private void btnAutoSearch_Click(object sender, EventArgs e)
panel_Search.Visible = true; // Not Working
Cursor.Current = Cursors.WaitCursor;
string ipBase = getIPAddress();
string[] ipParts = ipBase.Split('.');
ipBase = ipParts[0] + "." + ipParts[1] + "." + ipParts[2] + ".";
for (int i = 1; i < 255; i++)
string ip = ipBase + i.ToString();
Ping p = new Ping();
PingReply pingresult = p.Send(ip, 100);
if (pingresult.Status == IPStatus.Success)
lstIPs.Add(ip);
lblConnecting.Text = ip; // Not Working
GridConn.Rows.Clear();
foreach (string s in lstIPs)
Device obj = new Device();
obj.IPAddress = s;
lblConnecting.Text = s;
int vSMSLimit = 0;
int.TryParse(txtSMSLimit.Text, out vSMSLimit);
obj.SMSLimit = 0;
if (obj.ConnectToHostServer())
obj.SendConnectionMessage();
obj.ReceiveConnectionMessage();
MyDevices.lst.Add(obj);
GridConn.Rows.Add(true, obj.IPAddress, obj.PhoneModel, obj.PhoneStatus, obj.SoftwareVersion);
Cursor.Current = Cursors.Default;
panel_Search.Visible = false;
【问题讨论】:
使用BackgroundWorker
将网络通信移出 UI 线程
感谢您的帮助。
【参考方案1】:
问题是 UI 必须从“主”线程(通常称为 UI 线程)更新,但如果您在此线程上执行其他处理,则 UI 本身将锁定。所以你应该把长时间运行的进程放到另一个线程上。
这将导致问题,但是该线程无法更新 UI,因为它不是 Main/UI 线程。所以你必须自己调用 UI 线程来更新它。
幸运的是,有一种简单的方法可以使用 C# 中的 BackgroundWorker 类来执行此操作,这对您有很大帮助。但是您需要将 UI 和后台任务分开。
//Define worker
BackgroundWorker myBGWorker;
//Initalise worker and start it from your button
private void btnAutoSearch_Click(object sender, EventArgs e)
panel_Search.Visible = true; // Not Working
Cursor.Current = Cursors.WaitCursor;
myBGWorker = new BackgroundWorker()
//This method will execute your processing on a background thread
myBGWorker.DoWork += new DoWorkEventHandler(bgw_DoWork);
//This method will execute when your background worker finishes
//It runs on the Main/UI thread
myBGWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgw_RunWorkerCompleted);
//This method will execute when the background thread wants to update the progress
//It runs on the Main/UI Thread
myBGWorker.ProgressChanged += new ProgressChangedEventHandler(bgw_ProgressChanged);
//Tell it we will be reporting progress
myBGWorker.WorkerReportsProgress = true;
//Start!
myBGWorker.RunWorkerAsync()
private void bgw_DoWork(object sender, DoWorkEventArgs e)
string ipBase = getIPAddress();
string[] ipParts = ipBase.Split('.');
ipBase = ipParts[0] + "." + ipParts[1] + "." + ipParts[2] + ".";
for (int i = 1; i < 255; i++)
string ip = ipBase + i.ToString();
Ping p = new Ping();
PingReply pingresult = p.Send(ip, 100);
if (pingresult.Status == IPStatus.Success)
lstIPs.Add(ip);
//Below reports the progress. The number shouuld represent the percentage of process, the object can be anything you want
double percentDone = (100.0 / 255.0) * i;
e.ReportProgress(Convert.ToInt32(percentDone), ip);
private void bgw_ProgressChanged(object sender, ProgressChangedEventArgs e)
lblConnecting.Text = e.UserState as string;
private void bgw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
if (e.Cancelled)
MessageBox.Show("Operation was canceled");
else if (e.Error != null)
MessageBox.Show(e.Error.Message);
else
GridConn.Rows.Clear();
foreach (string s in lstIPs)
Device obj = new Device();
obj.IPAddress = s;
lblConnecting.Text = s;
int vSMSLimit = 0;
int.TryParse(txtSMSLimit.Text, out vSMSLimit);
obj.SMSLimit = 0;
if (obj.ConnectToHostServer())
obj.SendConnectionMessage();
obj.ReceiveConnectionMessage();
MyDevices.lst.Add(obj);
GridConn.Rows.Add(true, obj.IPAddress, obj.PhoneModel, obj.PhoneStatus, obj.SoftwareVersion);
Cursor.Current = Cursors.Default;
panel_Search.Visible = false;
【讨论】:
【参考方案2】:桌面应用程序使用单个线程来处理 UI,并且事件处理程序在 UI 线程上同步运行,这就是表单挂起的原因,因为它无法在事件处理程序运行时处理其他 UI 交互。
最佳实践是事件处理程序占用 UI 线程的时间很短。
当您因为用户交互需要执行长时间运行的任务时,您应该在另一个线程中执行该任务,您不应该使用 UI 线程来执行长时间运行的任务。
您可以使用 BackgroundWorker 将任务移动到另一个线程,但最好使用异步事件处理程序。
例如:
private async void myButton_Click(object sender, EventArgs e)
await PerformLongRunningTaskAsync();
//TODO: update UI after completing task
await Task.Run(() => PerformLongRunningTaskSynchronously());
//TODO: update UI after completing task;
private async Task PerformLongRunnigTaskAsync()
//TODO: implement this async method
private void PerformLongRunningTaskSynchronously()
//TODO: implement this synchronus method
你的代码应该是这样的:
private async void btnAutoSearch_Click(object sender, EventArgs e)
panel_Search.Visible = true; // Not Working
Cursor.Current = Cursors.WaitCursor;
string ipBase = getIPAddress();
string[] ipParts = ipBase.Split('.');
ipBase = ipParts[0] + "." + ipParts[1] + "." + ipParts[2] + ".";
for (int i = 1; i < 255; i++)
string ip = ipBase + i.ToString();
Ping p = new Ping();
PingReply pingresult = await p.SendPingAsync(ip, 100);
if (pingresult.Status == IPStatus.Success)
lstIPs.Add(ip);
lblConnecting.Text = ip; // Not Working
GridConn.Rows.Clear();
foreach (string s in lstIPs)
Device obj = new Device();
obj.IPAddress = s;
lblConnecting.Text = s;
int vSMSLimit = 0;
int.TryParse(txtSMSLimit.Text, out vSMSLimit);
obj.SMSLimit = 0;
if (await Task.Run(() => obj.ConnectToHostServer())
await Task.Run(() => obj.SendConnectionMessage());
await Task.Run(() => obj.ReceiveConnectionMessage());
MyDevices.lst.Add(obj);
GridConn.Rows.Add(true, obj.IPAddress, obj.PhoneModel, obj.PhoneStatus, obj.SoftwareVersion);
Cursor.Current = Cursors.Default;
panel_Search.Visible = false;
【讨论】:
【参考方案3】:感谢 Brian Rogers,这是我的工作方式,
private void btnAutoSearch_Click(object sender, EventArgs e)
backgroundWorker1.WorkerSupportsCancellation = true;
if (backgroundWorker1.IsBusy != true)
// Start the asynchronous operation.
backgroundWorker1.RunWorkerAsync();
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
BackgroundWorker worker = sender as BackgroundWorker;
for (int j = 1; j <= 10; j++)
if (worker.CancellationPending == true)
e.Cancel = true;
break;
else
Cursor.Current = Cursors.WaitCursor;
panel_Search.Location = new Point(380, 72);
// Perform a time consuming operation and report progress.
string ipBase = getIPAddress();
string[] ipParts = ipBase.Split('.');
ipBase = ipParts[0] + "." + ipParts[1] + "." + ipParts[2] + ".";
for (int i = 1; i < 255; i++)
string ip = ipBase + i.ToString();
Ping p = new Ping();
PingReply pingresult = p.Send(ip, 100);
if (pingresult.Status == IPStatus.Success)
lstIPs.Add(ip);
lblConnecting.Text = ip;
//listBox1.Items.Add(ip);
GridConn.Rows.Clear();
foreach (string s in lstIPs)
Device obj = new Device();
obj.IPAddress = s;
lblConnecting.Text = s;
int vSMSLimit = 0;
int.TryParse(txtSMSLimit.Text, out vSMSLimit);
obj.SMSLimit = 0;
if (obj.ConnectToHostServer())
obj.SendConnectionMessage();
obj.ReceiveConnectionMessage();
MyDevices.lst.Add(obj);
GridConn.Rows.Add(true, obj.IPAddress, obj.PhoneModel, obj.PhoneStatus, obj.SoftwareVersion);
Cursor.Current = Cursors.Default;
panel_Search.Location = new Point(333, 252);
【讨论】:
以上是关于循环通过 IP 地址时 UI 未更新的主要内容,如果未能解决你的问题,请参考以下文章
如何在不冻结 UI 的情况下使用 QProcess 循环的输出更新 UI?
如何在不冻结 UI 的情况下使用 QProcess 循环的输出更新 UI?