gpt4 book ai didi

c# - 从不同窗口的构造函数更新 UI 不起作用

转载 作者:行者123 更新时间:2023-12-03 12:58:50 25 4
gpt4 key购买 nike

我目前正在尝试实现启动画面。我已采取this tutorial作为起点。

我的 App.xaml.cs 中的 OnStartup 如下所示:

protected override void OnStartup(StartupEventArgs e)
{
//initialize the splash screen and set it as the application main window
splashScreen = new MySplashScreen();
this.MainWindow = splashScreen;
splashScreen.Show();

//in order to ensure the UI stays responsive, we need to
//do the work on a different thread
Task.Factory.StartNew(() =>
{
//we need to do the work in batches so that we can report progress
for (int i = 1; i <= 100; i++)
{
//simulate a part of work being done
System.Threading.Thread.Sleep(30);

//because we're not on the UI thread, we need to use the Dispatcher
//associated with the splash screen to update the progress bar
splashScreen.Dispatcher.Invoke(() => splashScreen.Progress = i);
splashScreen.Dispatcher.Invoke(() => splashScreen.MyText = i.ToString());
}

//once we're done we need to use the Dispatcher
//to create and show the main window
this.Dispatcher.Invoke(() =>
{
//initialize the main window, set it as the application main window
//and close the splash screen
var mainWindow = new MainWindow();
this.MainWindow = mainWindow;
mainWindow.Show();
splashScreen.Close();
});
});
}

这完美地工作。启动画面被调用,进度 (ProgressBar) 增加到 100。

现在,我不仅要从 OnStartup 还要从 MainWindow 的构造函数将进度写入启动画面。

我的 MainWindow 构造函数:
 public MainWindow()
{
InitializeComponent();

((App)Application.Current).splashScreen.Dispatcher.Invoke(() => ((App)Application.Current).splashScreen.MyText = "From MainWindow");

// do some stuff that takes a few seconds...

}

这没有按预期工作。只有在完全调用构造函数后,才会在初始屏幕的文本框中更新文本“From MainWindow”。在执行“做一些需要几秒钟的事情......”之前并不像预期的那样。

我的错误是什么?这甚至像我想的那样可能吗?

最佳答案

Dispatcher已经忙于创建 MainWindow当您使用 Dispatcher.Invoke 调用构造函数时.然后在MainWindow的构造函数中你调用了Dispatcher再次。 Dispatcher.Invoke有效地将委托(delegate)排入调度程序队列。一旦第一个委托(delegate)运行完成,下一个委托(delegate)(在这种情况下是来自 MainWindow 的构造函数内部的委托(delegate))被出列并执行(总是相对于给定的 DispatcherPriority )。这就是为什么你必须等到构造函数完成,即第一个委托(delegate)已经完成。

我强烈推荐使用 Progress<T> 这是从 .NET 4.5 ( Async in 4.5: Enabling Progress and Cancellation in Async APIs ) 开始的推荐进度报告方式。它的构造函数捕获当前 SynchronizationContext并对其执行报告回调。由于 Progress<T> 的实例在 UI 线程上创建回调将在正确的线程上自动执行,因此没有 Dispatcher不再需要。这将解决您的问题。此外,当在异步上下文中使用时,进度报告也可以使用取消。

我也推荐使用async/ await来控制流量。目标是创建 MainWindow 的实例在 UI 线程上。
还要始终避免使用 Thread.Sleep因为它会阻塞线程。在这种情况下,UI 线程将因此变得无响应并卡住。使用异步(非阻塞)await Task.Delay反而。根据经验,替换所有对 Thread 的引用与 Task ,即任务并行库是首选方式 (Task-based asynchronous programming)。

我相应地重构了您的代码:

应用程序.xaml.cs

private SplashScreen { get; set; }

protected override async void OnStartup(StartupEventArgs e)
{
// Initialize the splash screen.
// The first Window shown becomes automatically the Application.Current.MainWindow
this.SplashScreen = new MySplashScreen();
this.SplashScreen.Show();

// Create a Progress<T> instance which automatically
// captures the current SynchronizationContext (UI thread)
// which makes the Dispatcher obsolete for reporting the progress to the UI.
// Pass a report (UI update) callback to the Progress<T> constructor,
// which will execute automatically on the UI thread.
// Because of the generic parameter which is in this case of type ValueTuple (C# 7),
// 'System.ValueTuple' is required to be referenced (use NuGet Package Manager to install).
// Alternatively replace the tuple with an arg class.
var progressReporter = new Progress<(int Value, string Message)>(ReportProgress);

// Wait asynchronously for the background task to complete
await DoWorkAsync(progressReporter);

// Override the Application.Current.MainWindow instance.
this.MainWindow = new MainWindow();

// Asynchronously wait until MainWindow is initialized
// Pass the Progress<T> instance to the method,
// so that MainWindow can report progress too
await this.MainWindow.InitializeAsync(progressReporter);

this.SplashScreen.Close();
this.MainWindow.Show();
}

private async Task DoWorkAsync(IProgress<(int Value, string Message)> progressReporter)
{
// In order to ensure the UI stays responsive, we need to
// do the work on a different thread
await Task.Run(
async () =>
{
// We need to do the work in batches so that we can report progress
for (int i = 1; i <= 100; i++)
{
// Simulate a part of work being done
await Task.Delay(30);

progressReporter.Report((i, i.ToString()));
}
});
}

// The progress report callback which is automatically invoked on the UI thread.
// Requires 'System.ValueTuple' to be referenced (see NuGet)
private void ReportProgress((int Value, string Message) progress)
{
this.SplashScreen.Progress = progress.Value;
this.SplashScreen.MyText = progress.Message;
}

MainWindow.xaml.cs
public partial class MainWindow
{
public MainWindow()
{
InitializeComponent();
}


public async Task InitializeAsync(IProgress<(int Value, string Message)> progressReporter)
{
await Task.Run(
() =>
{
progressReporter.Report((100, "From MainWindow"));

// Run the initialization routine that takes a few seconds
}
}
}

关于c# - 从不同窗口的构造函数更新 UI 不起作用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59448414/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com