英文:
Better way to output stdout to WPF UI
问题
以下是您要翻译的代码部分:
I need to run and watch some slow bat-files in our system from a WPF/C# user interface.
The user interface should not wait for the process to end but display it whenever something happens in the bat file.
The following code works for now but introduced a thread to handle it.
private void OnClick(object sender, RoutedEventArgs e)
{
Thread thread1 = new Thread(() =>
{
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.WorkingDirectory = "C:\my\folder\with\batfile";
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/C run.bat";
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.EnableRaisingEvents = true;
p.OutputDataReceived += P_OutputDataReceived;
p.Start();
p.BeginOutputReadLine();
p.WaitForExit(20000);
});
thread1.Start();
}
private void P_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (!string.IsNullOrEmpty(e.Data))
{
lvConsoleOutput.Dispatcher.Invoke(() =>
{
m_ConsoleRows.Add(e.Data);
});
}
}
A few notes:
```lvConsoleOutput``` is a regular listview that is associated with ```m_ConsoleRows``` which is an ObservableCollection<string>.
The contents of a sample run.bat file here:
echo 1
echo 2
timeout 2
echo more text 3
timeout 1
echo almost the end
timeout 3
echo DONE!
By not running this in a thread, the entire UI freezes until the entire bat file has run until completion. Then I get the output in the UI and it unfreezes again.
With ```thread1``` above I get the achieved result but...
My questions are:
Can I do this without introducing a thread?
Are there cleaner ways to achieve this?
英文:
I need to run and watch some slow bat-files in our system from a WPF/C# user interface.
The user interface should not wait for the process to end but display it whenever something happens in the bat file.
The following code works for now but introduced a thread to handle it.
private void OnClick(object sender, RoutedEventArgs e)
{
Thread thread1 = new Thread(() =>
{
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.WorkingDirectory = "C:\my\folder\with\batfile";
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/C run.bat";
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.EnableRaisingEvents = true;
p.OutputDataReceived += P_OutputDataReceived;
p.Start();
p.BeginOutputReadLine();
p.WaitForExit(20000);
}
);
thread1.Start();
}
private void P_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (!string.IsNullOrEmpty(e.Data))
{
lvConsoleOutput.Dispatcher.Invoke(() =>
{
m_ConsoleRows.Add(e.Data);
});
}
}
A few notes:
lvConsoleOutput
is a regular listview that is associated with m_ConsoleRows
which is an ObservableCollection<string>.
The contents of a sample run.bat file here:
echo 1
echo 2
timeout 2
echo more text 3
timeout 1
echo almost the end
timeout 3
echo DONE!
By not running this in a thread, the entire UI freezes until the entire bat file has run until completion. Then I get the output in the UI and it unfreezes again.
With thread1
above I get the achieved result but...
My questions are:
Can I do this without introducing a thread?
Are there cleaner ways to achieve this?
答案1
得分: 1
不在线程中运行此操作会导致整个用户界面冻结,直到整个批处理文件运行完成。
这是因为WaitForExit
调用。只需将其删除:
private void OnClick(object sender, RoutedEventArgs e)
{
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.WorkingDirectory = @"C:\my\folder\with\batfile";
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/C run.bat";
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.EnableRaisingEvents = true;
p.OutputDataReceived += P_OutputDataReceived;
p.Start();
p.BeginOutputReadLine();
}
顺便提一下,由于您已经直接访问了ListBox
,所以不一定需要ObservableCollection
作为其ItemsSource
。以下代码同样有效,而且代码更少:
private void P_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (!string.IsNullOrEmpty(e.Data))
{
lvConsoleOutput.Dispatcher.Invoke(() => lvConsoleOutput.Items.Add(e.Data));
}
}
英文:
> By not running this in a thread, the entire UI freezes until the entire bat file has run until completion.
That is because of the WaitForExit
call. Just remove it:
private void OnClick(object sender, RoutedEventArgs e)
{
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.WorkingDirectory = @"C:\my\folder\with\batfile";
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/C run.bat";
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.EnableRaisingEvents = true;
p.OutputDataReceived += P_OutputDataReceived;
p.Start();
p.BeginOutputReadLine();
}
As a note, since you are directly accessing the ListBox anyway, you do not strictly need the ObservableCollection as its ItemsSource. This works as well with less code:
private void P_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (!string.IsNullOrEmpty(e.Data))
{
lvConsoleOutput.Dispatcher.Invoke(() => lvConsoleOutput.Items.Add(e.Data));
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论