Skip to content

Commit 1d93e9e

Browse files
Add ShowAsync unit tests.
1 parent ffeaae5 commit 1d93e9e

File tree

3 files changed

+121
-22
lines changed

3 files changed

+121
-22
lines changed

src/System.Windows.Forms/System/Windows/Forms/Form.cs

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5574,18 +5574,21 @@ public void Show(IWin32Window? owner)
55745574
/// <para>Thrown if the owner window is trying to set itself as its own owner.</para>
55755575
/// </exception>
55765576
[Experimental(DiagnosticIDs.ExperimentalAsync, UrlFormat = DiagnosticIDs.UrlFormat)]
5577-
public async Task ShowAsync(IWin32Window? owner = null)
5577+
public Task ShowAsync(IWin32Window? owner = null)
55785578
{
55795579
// We lock the access to the task completion source to prevent
55805580
// multiple calls to ShowAsync from interfering with each other.
55815581
lock (_lock)
55825582
{
5583-
if (_nonModalFormCompletion is not null || _modalFormCompletion is not null)
5583+
if (_nonModalFormCompletion is not null
5584+
|| _modalFormCompletion is not null)
55845585
{
55855586
throw new InvalidOperationException(SR.Form_HasAlreadyBeenShownAsync);
55865587
}
55875588

5588-
_nonModalFormCompletion = new(TaskCreationOptions.RunContinuationsAsynchronously);
5589+
_nonModalFormCompletion = new(
5590+
new WeakReference<Form>(this),
5591+
TaskCreationOptions.RunContinuationsAsynchronously);
55895592
}
55905593

55915594
if (SynchronizationContext.Current is null)
@@ -5598,20 +5601,7 @@ public async Task ShowAsync(IWin32Window? owner = null)
55985601

55995602
syncContext.Post((state) => ShowFormInternally(owner), null);
56005603

5601-
// Wait until the form is closed or disposed.
5602-
try
5603-
{
5604-
await _nonModalFormCompletion.Task.ConfigureAwait(true);
5605-
}
5606-
catch (Exception ex)
5607-
{
5608-
// We need to rethrow the exception on the caller's context.
5609-
Application.OnThreadException(ex);
5610-
}
5611-
finally
5612-
{
5613-
_nonModalFormCompletion = null;
5614-
}
5604+
return _nonModalFormCompletion.Task;
56155605

56165606
void ShowFormInternally(IWin32Window? owner)
56175607
{
@@ -5623,6 +5613,7 @@ void ShowFormInternally(IWin32Window? owner)
56235613
catch (Exception ex)
56245614
{
56255615
_nonModalFormCompletion.TrySetException(ex);
5616+
_nonModalFormCompletion = null;
56265617
}
56275618
}
56285619
}
@@ -5891,12 +5882,15 @@ private Task<DialogResult> ShowDialogAsyncInternal(IWin32Window? owner)
58915882
{
58925883
lock (_lock)
58935884
{
5894-
if (_nonModalFormCompletion is not null || _modalFormCompletion is not null)
5885+
if (_nonModalFormCompletion is not null
5886+
|| _modalFormCompletion is not null)
58955887
{
58965888
throw new InvalidOperationException(SR.Form_HasAlreadyBeenShownAsync);
58975889
}
58985890

5899-
_modalFormCompletion = new TaskCompletionSource<DialogResult>(TaskCreationOptions.RunContinuationsAsynchronously);
5891+
_modalFormCompletion = new TaskCompletionSource<DialogResult>(
5892+
state: new WeakReference<Form>(this),
5893+
creationOptions: TaskCreationOptions.RunContinuationsAsynchronously);
59005894
}
59015895

59025896
if (SynchronizationContext.Current is null)
@@ -5918,11 +5912,11 @@ void ShowDialogProc(ref TaskCompletionSource<DialogResult> modalFormCompletion,
59185912
try
59195913
{
59205914
DialogResult result = ShowDialog(owner);
5921-
modalFormCompletion.SetResult(result);
5915+
modalFormCompletion.TrySetResult(result);
59225916
}
59235917
catch (Exception ex)
59245918
{
5925-
modalFormCompletion.SetException(ex);
5919+
modalFormCompletion.TrySetException(ex);
59265920
}
59275921
finally
59285922
{
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
#nullable disable
5+
6+
namespace System.Windows.Forms.Tests;
7+
8+
internal static class FormAsyncExtensions
9+
{
10+
public static Task WaitForHandleCreatedAsync(this Form form)
11+
{
12+
ArgumentNullException.ThrowIfNull(form, nameof(form));
13+
14+
TaskCompletionSource tcs = new TaskCompletionSource(new WeakReference(form));
15+
form.HandleCreated += Form_HandleCreated;
16+
17+
return tcs.Task;
18+
19+
void Form_HandleCreated(object sender, EventArgs e)
20+
{
21+
((Form)sender).HandleCreated -= Form_HandleCreated;
22+
tcs.TrySetResult();
23+
}
24+
}
25+
26+
public static Form ToForm(this Task task)
27+
{
28+
ArgumentNullException.ThrowIfNull(task, nameof(task));
29+
30+
if (task.AsyncState is WeakReference<Form> weakRefToForm)
31+
{
32+
if (weakRefToForm.TryGetTarget(out Form form))
33+
{
34+
return form;
35+
}
36+
}
37+
38+
return null;
39+
}
40+
}

src/test/unit/System.Windows.Forms/System/Windows/Forms/FormTests.cs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
namespace System.Windows.Forms.Tests;
1111

12-
public class FormTests
12+
public partial class FormTests
1313
{
1414
[WinFormsFact]
1515
public void Form_Ctor_Default()
@@ -2656,6 +2656,71 @@ public void Form_Show_SetsOwnerToTopLevelForm_WhenShownWithOwner(Action<Form, Co
26562656
Assert.False(child.IsHandleCreated);
26572657
}
26582658

2659+
#pragma warning disable WFO5002 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
2660+
[WinFormsFact]
2661+
public async Task Form_ShowAsync_GeneralTaskTests()
2662+
{
2663+
using Form mainForm = new();
2664+
2665+
Task frmTask = mainForm.ShowAsync();
2666+
2667+
Task closerTask = Task.Run(async () =>
2668+
{
2669+
await Task.Delay(100).ConfigureAwait(false);
2670+
mainForm.Close();
2671+
});
2672+
2673+
await Task.WhenAll(frmTask, closerTask)
2674+
.ConfigureAwait(false);
2675+
}
2676+
2677+
[WinFormsFact]
2678+
public async Task Form_ShowAsync_GetFormFromReturnedTask()
2679+
{
2680+
Dictionary<Task, Form> formControlTasks = [];
2681+
Random random = new();
2682+
2683+
for (int i = 0; i < 5; i++)
2684+
{
2685+
Form form = new()
2686+
{
2687+
Text = $"Form {i}",
2688+
Size = new Size(200, 200),
2689+
};
2690+
2691+
form.Load += async (sender, e) =>
2692+
{
2693+
await Task.Delay(random.Next(20, 100))
2694+
.ConfigureAwait(true);
2695+
2696+
((Form)sender).Close();
2697+
};
2698+
2699+
formControlTasks.Add(
2700+
key: form.ShowAsync(),
2701+
value: form);
2702+
}
2703+
2704+
while (formControlTasks.Count > 0)
2705+
{
2706+
Task finishedTask = await Task
2707+
.WhenAny(formControlTasks.Keys)
2708+
.ConfigureAwait(false);
2709+
2710+
if (finishedTask.ToForm() is Form form)
2711+
{
2712+
Assert.Same(
2713+
// The form, which we got through the task's AsyncState.
2714+
form,
2715+
// The form, which got through the lookup.
2716+
formControlTasks[finishedTask]);
2717+
}
2718+
2719+
formControlTasks.Remove(finishedTask);
2720+
}
2721+
}
2722+
#pragma warning restore WFO5002 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
2723+
26592724
[WinFormsFact]
26602725
public void Form_ParentThenSetShowInTaskbarToFalse()
26612726
{

0 commit comments

Comments
 (0)