商業智能的人在這裏有足夠的C#在我的腰帶下是危險的。更新UI線程(文本框)通過C#
我已經構建了一個自制的winforms應用程序,實際上是在一個循環中執行一個命令行工具來「執行」。說的東西可能在幾秒鐘或幾分鐘內完成。通常我需要爲坐在DataTable中的每一行執行一次該工具。
我需要重定向命令行工具的輸出並將其顯示在「我的」應用程序中。我試圖通過一個文本框來做到這一點。我遇到了關於更新UI線程的問題,我無法自行理清。
要執行我的命令行工具,我已經借了代碼從這裏:How to parse command line output from c#?
這裏是我的等價:
private void btnImport_Click(object sender, EventArgs e)
{
txtOutput.Clear();
ImportWorkbooks(dtable);
}
public void ImportWorkbooks(DataTable dt)
{
ProcessStartInfo cmdStartInfo = new ProcessStartInfo();
cmdStartInfo.FileName = @"C:\Windows\System32\cmd.exe";
cmdStartInfo.RedirectStandardOutput = true;
cmdStartInfo.RedirectStandardError = true;
cmdStartInfo.RedirectStandardInput = true;
cmdStartInfo.UseShellExecute = false;
cmdStartInfo.CreateNoWindow = false;
Process cmdProcess = new Process();
cmdProcess.StartInfo = cmdStartInfo;
cmdProcess.ErrorDataReceived += cmd_Error;
cmdProcess.OutputDataReceived += cmd_DataReceived;
cmdProcess.EnableRaisingEvents = true;
cmdProcess.Start();
cmdProcess.BeginOutputReadLine();
cmdProcess.BeginErrorReadLine();
//Login
cmdProcess.StandardInput.WriteLine(BuildLoginString(txtTabCmd.Text, txtImportUserName.Text, txtImportPassword.Text, txtImportToServer.Text));
foreach (DataRow dr in dt.Rows)
{
cmdProcess.StandardInput.WriteLine(CreateServerProjectsString(dr["Project"].ToString(), txtTabCmd.Text));
//Import Workbook
cmdProcess.StandardInput.WriteLine(BuildPublishString(txtTabCmd.Text, dr["Name"].ToString(), dr["UID"].ToString(),dr["Password"].ToString(), dr["Project"].ToString()));
}
cmdProcess.StandardInput.WriteLine("exit"); //Execute exit.
cmdProcess.EnableRaisingEvents = false;
cmdProcess.WaitForExit();
}
private void cmd_DataReceived(object sender, DataReceivedEventArgs e)
{
//MessageBox.Show("Output from other process");
try
{
// I want to update my textbox here, and then position the cursor
// at the bottom ala:
StringBuilder sb = new StringBuilder(txtOutput.Text);
sb.AppendLine(e.Data.ToString());
txtOutput.Text = sb.ToString();
this.txtOutput.SelectionStart = txtOutput.Text.Length;
this.txtOutput.ScrollToCaret();
}
catch (Exception ex)
{
Console.WriteLine("{0} Exception caught.", ex);
}
}
引用txtOuput.text當我實例化我的cmd_DataReceived的StringBuilder()整潔地導致應用程序掛起:我猜測某種跨線程問題。
如果我刪除的StringBuilder的參考txtOuput.text並繼續調試,我得到一個跨線程違反此:
txtOutput.Text = sb.ToString();
Cross-thread operation not valid: Control 'txtOutput' accessed from a thread other than the thread it was created on.
OK,並不感到意外。我認爲cmd_DataReceived在另一個線程上運行,因爲我在Process.Start()之後執行某些操作,並且如果我刪除了對cmd_DataReceived()中的txtOuput.Text的所有引用,並只是轉儲命令行文本通過Console.Write()輸出到控制檯,一切工作正常。
所以,下次我會試着在http://msdn.microsoft.com/en-us/library/ms171728.aspx
使用更新的信息的UI線程上我的文本框的標準技術,我添加了一個委託和線程上我的課:
delegate void SetTextCallback(string text);
// This thread is used to demonstrate both thread-safe and
// unsafe ways to call a Windows Forms control.
private Thread demoThread = null;
我添加一個程序來更新文本框:
private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.txtOutput.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.txtOutput.Text = text;
}
}
我再添PROC它調用線程安全的一個:
private void ThreadProcSafe()
{
// this.SetText(sb3.ToString());
this.SetText("foo");
}
...最後我把這樣的內cmd_DataReceived這個爛攤子:
private void cmd_DataReceived(object sender, DataReceivedEventArgs e)
{
//MessageBox.Show("Output from other process");
try
{
sb3.AppendLine(e.Data.ToString());
//this.backgroundWorker2.RunWorkerAsync();
this.demoThread = new Thread(new ThreadStart(this.ThreadProcSafe));
this.demoThread.Start();
Console.WriteLine(e.Data.ToString());
}
catch (Exception ex)
{
Console.WriteLine("{0} Exception caught.", ex);
}
}
...當我運行這個文本,該文本框坐在那裏確實死了,不會得到更新。我的控制檯窗口繼續更新。正如你所看到的,我試着簡化了一些東西,只是讓文本框顯示「foo」與工具的實際輸出結果 - 但沒有什麼樂趣。我的用戶界面已死亡。
那麼是什麼原因?無法弄清楚我做錯了什麼。我根本不會結婚在文本框中顯示結果,順便說一句 - 我只需要能夠看到應用程序內部發生了什麼,我不想彈出另一個窗口來這樣做。
非常感謝。
但是文本框仍然不會被更新,因爲UI線程被'cmdProcess.WaitForExit();'行阻止。 – Fischermaen
謝謝,我會嘗試將您的建議與Fisherman和KooKiz的建議結合起來。 –