I want to create a Splash Screen on my WinForms application. I create the splash screen form and run the initialization operation.
public async Task RunApplication()
{
splash = new SplashWindow();
splash.Show();
await Task.Run(InitializeAsync);
Application.Run(new frmMain());
}
//the InitializeAsync function
private async Task InitializeAsync()
{
splash.Status = "Test";
}
//the status property
public string Status
{
get { return status; }
set {
if (status != value)
{
status = value;
lblStatus.InvokeIfRequired(() => lblStatus.Text = value);
}
}
}
public static void InvokeIfRequired(this ISynchronizeInvoke snc,
MethodInvoker action)
{
if (snc.InvokeRequired) {
snc.Invoke(action, null);
} else {
action();
}
}
As you can see, I want to be able to change the label to the current status. However, when I run this code, as soon as I hit the snc.Invoke(action, null);
line, the program hangs. I did some research/debugging and it seems that when I do Task.Run
, it will block the UI thread and since the splash has been created on that thread, the program is never able to run that action on the UI thread.
What I ended up doing was to wait until the task is finished and constantly do the application events.
var task = Task.Run(InitializeAsync);
while(!task.IsCompleted)
{
Application.DoEvents();
Thread.Sleep(10);
}
Applicaiton.Run(new frmMain());
This works. However, I was wondering if there's a more elegant solution for this.
First, get rid of InvokeIfRequired
. InvokeRequired
- regardless of how common it is - is a serious code smell.
Instead, you can get rid of Task.Run
and just call InitializeAsync
directly:
public async Task RunApplication()
{
splash = new SplashWindow();
splash.Show();
await InitializeAsync();
Applicaiton.Run(new frmMain());
}
private async Task InitializeAsync()
{
splash.Status = "Test";
}
public string Status
{
get => lblStatus.Text;
set => lblStatus.Text = value;
}
If you do need to use Task.Run
for some reason (ie, if InitializeAsync
has CPU-bound or blocking work to do), then you can use Progress<T>
:
public async Task RunApplication()
{
splash = new SplashWindow();
splash.Show();
var progress = new Progress<string>(update => splash.Status = update);
await Task.Run(() => InitializeAsync(progress));
Applicaiton.Run(new frmMain());
}
private async Task InitializeAsync(IProgress<string>? progress)
{
progress?.Report("Test");
}
public string Status
{
get => lblStatus.Text;
set => lblStatus.Text = value;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.