我正在使用 Blazor Server (.net 8),我想在输出中显示命令的执行结果。
这是有效但不实时的(所有结果在执行完成后显示)。
@page "/"
@using System.Diagnostics
<Button @onclick="ProcessSomething">execute...</Button>
<InputTextArea @bind-Value="output" />
@code{
string output = "";
async Task ProcessSomething()
{
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/c ping 8.8.8.8";
output = "";
p.OutputDataReceived += async (sender, args) =>
{
Console.WriteLine(args.Data);
output += $"{args.Data}\n";
await InvokeAsync(StateHasChanged);
};
p.Start();
p.BeginOutputReadLine();
p.WaitForExit();
}
}
Console.WriteLine(args.Data);
实际上会打印结果,但 InputTextArea
仅在执行完全完成后才会更新。
主要问题是命令代码不是异步的。您在 UI 线程上运行它,因此它会阻止该线程直到完成:事件没有任何效果,因为线程被阻止。
当您在服务器上运行时,您可以将命令分派到线程池线程。这使 UI 可以自由地完成其工作:响应事件并更新 UI。
@page "/"
@using System.Diagnostics
@using System.Text
<button class="btn btn-primary" disabled="@_processing" @onclick="ProcessSomething">execute...</button>
<div class="bg-dark text-white m-2 p-2">
<pre>@_output</pre>
</div>
@code {
private StringBuilder _output = new();
private volatile bool _processing;
private Task ProcessSomething()
{
_output.Clear();
// The command is blocking
// so spin the commandoff to a threadpool thread to free up the UI Thread
Task.Run(RunPingAsync);
return Task.CompletedTask;
}
private async Task RunPingAsync()
{
_processing = true;
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/c ping 8.8.8.8";
p.OutputDataReceived += OnDataReceived;
p.Start();
p.BeginOutputReadLine();
p.WaitForExit();
_processing = false;
// Need to call StateHasChanged to update th button state
await InvokeAsync(StateHasChanged);
}
private async void OnDataReceived( object? sender, DataReceivedEventArgs args)
{
Console.WriteLine(args.Data);
_output.AppendLine(args.Data);
await InvokeAsync(StateHasChanged);
}
}
输出现在看起来像这样: