问题描述
我正在使用 .NET Core 制作一个小程序,但我不知道如何实现:我想在可能需要很长时间的功能期间更新我的 GUI 中的文本块。它不是异步的,我只有一个线程。我的 XAML 是:
fn f(x: Box<dyn MyTrait>) { ... }
在 UI 中有一个调用函数的按钮,在此函数中,我想向用户显示一些信息,例如“步骤 1 已完成”、“步骤 2 正在进行中”等;我不想也不需要详细的更新,只是提供一些关于正在发生的事情的想法。 XAML.CS 是:
<mah:MetroWindow x:Class="MyProgram.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:MyProgram"
xmlns:mah="clr-namespace:MahApps.Metro.Controls;assembly=MahApps.Metro"
WindowStartupLocation="CenterScreen"
mc:Ignorable="d"
Title="My Title" Height="550" Width="850" FontSize="14" FontFamily="Dosis,Verdana,Comic Sans MS" >
<Grid>
<!-- Grid Rows and Column DeFinitions -->
<!-- Fields in the UI -->
<TextBlock Grid.Row="5" Name="lblStatusTxtBlk" Grid.Column="1" Grid.ColumnSpan="3" Height="auto" textwrapping="Wrap" Margin="10 30" Text="{Binding lblStatus,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}" />
</Grid>
</mah:MetroWindow>
当我启动程序时,我在文本块中读到“start”,所以我可以说绑定有效。但是,当函数运行时,TextBlock 直到最后才会更新,而不是每一步都更新。我在编译时和运行时都没有收到任何错误或警告。怎么解决?
解决方法
您可能应该为长时间运行的任务使用不同的线程(这是它的目的)!我希望如果你这样做,更新会在你期望的时候出现,你的用户界面也应该响应更快。
可以使用实现 Progress<T>
接口的 IProgress<T>
类将进度发送回 UI 线程,两者都可以在 System
中找到。
为了简洁起见,我没有将您的示例修改为工作程序,而是将控制台版本放在一起:
using System;
using System.Threading;
using System.Threading.Tasks;
namespace AsyncTest
{
class Program
{
static async Task Main(string[] args)
{
var progReporter = new Progress<int>(o => Report(o));
await Task.Run(() => LongProcess(progReporter));
Console.WriteLine("Task Complete");
}
private static void Report(int progress)
=> Console.WriteLine($"{progress}% completed");
private static void LongProcess(IProgress<int> progReporter)
{
// Step 1...
Thread.Sleep(300);
progReporter.Report(25);
// Step 2...
Thread.Sleep(300);
progReporter.Report(50);
// Step 3...
Thread.Sleep(300);
progReporter.Report(75);
// Step 4...
Thread.Sleep(300);
progReporter.Report(100);
}
}
}
唯一需要注意的是,传递给 progReporter
的 lambda 捕获了主线程的上下文——GUI 程序中的 UI 线程是什么——所以在主线程上定义它很重要,并且不在 LongProcress()
内或由 Task.Run()
调用的任何其他地方。显然,您的 lambda 将更新 lblStatus
。
另请注意,任务有时会在显示上次更新之前完成 - 确保您的 UI 可以干净地应对这种情况。
MSDN 博客上有一篇很好的技术文章:https://devblogs.microsoft.com/dotnet/async-in-4-5-enabling-progress-and-cancellation-in-async-apis/