From cca55606e2bfd4c719ab03b167770b187636783d Mon Sep 17 00:00:00 2001 From: Igor Velikorossov Date: Wed, 3 Nov 2021 17:14:55 +1100 Subject: [PATCH] Resume invocation of OnParentHandleRecreated for child control when form recreates handle Fixes #6093 Fix the regression introduced in #2262. --- .../src/System/Windows/Forms/Control.cs | 13 +- .../System/Windows/Forms/ControlTests.cs | 136 +++++++++++++++++- 2 files changed, 143 insertions(+), 6 deletions(-) diff --git a/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs index 5ece110ce61..87fdc8c8973 100644 --- a/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs +++ b/src/System.Windows.Forms/src/System/Windows/Forms/Control.cs @@ -1483,7 +1483,7 @@ public ControlCollection Controls [EditorBrowsable(EditorBrowsableState.Advanced)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [SRDescription(nameof(SR.ControlCreatedDescr))] - public bool Created => (_state & States.Created) != 0; + public bool Created => GetState(States.Created); /// /// Returns the CreateParams used to create the handle for this control. @@ -3032,7 +3032,7 @@ internal virtual Control ParentInternal [EditorBrowsable(EditorBrowsableState.Advanced)] [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)] [SRDescription(nameof(SR.ControlRecreatingHandleDescr))] - public bool RecreatingHandle => (_state & States.Recreate) != 0; + public bool RecreatingHandle => GetState(States.Recreate); internal virtual void AddReflectChild() { @@ -4989,7 +4989,7 @@ public void CreateControl() /// internal void CreateControl(bool fIgnoreVisible) { - bool ready = (_state & States.Created) == 0; + bool ready = !GetState(States.Created); // PERF: Only "create" the control if it is // : visible. This has the effect of delayed handle creation of @@ -4998,7 +4998,7 @@ internal void CreateControl(bool fIgnoreVisible) if (ready || fIgnoreVisible) { - _state |= States.Created; + SetState(States.Created, true); bool createdOK = false; try { @@ -5033,7 +5033,7 @@ internal void CreateControl(bool fIgnoreVisible) { if (!createdOK) { - _state &= (~States.Created); + SetState(States.Created, false); } } @@ -9827,6 +9827,9 @@ internal virtual void RecreateHandleCore() // the call shouldn't fail. // However, it could fail if this.CreateParams.Parent is changed outside our control. CreateHandle(); + + // DestroyHandle resets the state, but CreateHandle doesn't set the state back. + SetState(States.Created, true); } catch (Exception) { diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ControlTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ControlTests.cs index 21b550c3ac6..df42fc07221 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ControlTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/ControlTests.cs @@ -730,7 +730,7 @@ public void Control_GetNextItem_Buttons_CycleForwardExpected(RightToLeft rightTo using Button button1 = new(); using Button button2 = new(); using Button button3 = new(); - control.Controls.AddRange(new Button[] { button1, button2, button3}); + control.Controls.AddRange(new Button[] { button1, button2, button3 }); Control nextControl1 = control.GetNextControl(button1, forward: true); Control nextControl2 = control.GetNextControl(button2, forward: true); Control nextControl3 = control.GetNextControl(button3, forward: true); @@ -974,6 +974,62 @@ public void Control_SelectNextControl_ToolStrips_CycleBackwardExpected(RightToLe Assert.True(toolStrip2.IsHandleCreated); } + [WinFormsFact] + public void Control_RecreateHandleCore_invokes_OnParentHandleRecreated_for_children() + { + using Form form = new(); + SubCheckedListBox checkedListBox1 = new(); + SubButton button1 = new(); + SubListBox listBox1 = new(); + SubListView listView1 = new(); + + checkedListBox1.Items.AddRange(new object[] { "Foo", "Foo", "Foo" }); + checkedListBox1.Location = new Point(10, 10); + checkedListBox1.Size = new Size(103, 64); + + button1.Location = new Point(12, 166); + button1.Size = new Size(213, 20); + + listBox1.Items.AddRange(new object[] { "Foo", "Foo", "Foo" }); + listBox1.Location = new Point(12, 80); + listBox1.Size = new Size(101, 69); + + listView1.Items.AddRange(new ListViewItem[] { new("Foo"), new("Foo"), new("Foo") }); + listView1.Location = new Point(130, 10); + listView1.Size = new Size(121, 64); + listView1.View = View.List; + + form.Controls.Add(checkedListBox1); + form.Controls.Add(button1); + form.Controls.Add(listBox1); + form.Controls.Add(listView1); + + form.Show(); + + // This will recreate the handle. + form.ShowInTaskbar = false; + + try + { + AssertHandler(button1); + AssertHandler(listView1); + AssertHandler(checkedListBox1); + AssertHandler(listBox1); + } + finally + { + form.Close(); + } + + return; + + static void AssertHandler(IParentHandleRecreationHandler handler) + { + Assert.Equal(1, handler.OnParentHandleRecreatedCalled); + Assert.Equal(1, handler.OnParentHandleRecreatingCalled); + } + } + private class SubControl : Control { public SubControl() : base() @@ -1301,5 +1357,83 @@ public Control GetNextSelectableControl(Control ctl, bool forward, bool tabStopO public new void WndProc(ref Message m) => base.WndProc(ref m); } + + private class SubCheckedListBox : CheckedListBox, IParentHandleRecreationHandler + { + public int OnParentHandleRecreatedCalled { get; private set; } + public int OnParentHandleRecreatingCalled { get; private set; } + + internal override void OnParentHandleRecreated() + { + OnParentHandleRecreatedCalled++; + base.OnParentHandleRecreated(); + } + + internal override void OnParentHandleRecreating() + { + OnParentHandleRecreatingCalled++; + base.OnParentHandleRecreating(); + } + } + + private class SubListBox : ListBox, IParentHandleRecreationHandler + { + public int OnParentHandleRecreatedCalled { get; private set; } + public int OnParentHandleRecreatingCalled { get; private set; } + + internal override void OnParentHandleRecreated() + { + OnParentHandleRecreatedCalled++; + base.OnParentHandleRecreated(); + } + + internal override void OnParentHandleRecreating() + { + OnParentHandleRecreatingCalled++; + base.OnParentHandleRecreating(); + } + } + + private class SubButton : Button, IParentHandleRecreationHandler + { + public int OnParentHandleRecreatedCalled { get; private set; } + public int OnParentHandleRecreatingCalled { get; private set; } + + internal override void OnParentHandleRecreated() + { + OnParentHandleRecreatedCalled++; + base.OnParentHandleRecreated(); + } + + internal override void OnParentHandleRecreating() + { + OnParentHandleRecreatingCalled++; + base.OnParentHandleRecreating(); + } + } + + private class SubListView : ListView, IParentHandleRecreationHandler + { + public int OnParentHandleRecreatedCalled { get; private set; } + public int OnParentHandleRecreatingCalled { get; private set; } + + internal override void OnParentHandleRecreated() + { + OnParentHandleRecreatedCalled++; + base.OnParentHandleRecreated(); + } + + internal override void OnParentHandleRecreating() + { + OnParentHandleRecreatingCalled++; + base.OnParentHandleRecreating(); + } + } + + private interface IParentHandleRecreationHandler + { + int OnParentHandleRecreatedCalled { get; } + int OnParentHandleRecreatingCalled { get; } + } } }