diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/AdapterHelpers.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/AdapterHelpers.cs new file mode 100644 index 00000000000..157d145d49f --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/AdapterHelpers.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; + +namespace System.Windows.Forms.Design; + +/// +/// Helpers for adapting between generic and non-generic lists. +/// +internal static class AdapterHelpers +{ + /// + /// Provides an extension method to safely extract the underlying non-generic + /// from a wrapped when available. + /// + /// The type of elements in the . + /// The IList<T> to unwrap. + /// The underlying non-generic if available; otherwise, the original . + internal static IList Unwrap(this IList list) => list is IWrapper wrapper ? wrapper.Unwrap() : (IList)list; + + /// + /// Provides an extension method to adapt a non-generic IList to a generic by creating + /// a when needed. + /// + /// The desired type of elements in the resulting . + /// The non-generic to adapt. + /// + /// A generic if the input IList can be cast to otherwise, a new wrapping the input IList. + /// + internal static IList Adapt(this IList list) => list is IList iList ? iList : new ListAdapter(list); +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/BehaviorService.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/BehaviorService.cs index f09a3a01863..dea43a0f423 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/BehaviorService.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/BehaviorService.cs @@ -816,7 +816,7 @@ private void TestHook_GetAllSnapLines(ref Message m) if (host.GetDesigner(comp) is ControlDesigner designer) { - foreach (SnapLine line in designer.SnapLines) + foreach (SnapLine line in designer.SnapLinesInternal) { snapLineInfo.Append($"{line}\tAssociated Control = {designer.Control.Name}:::"); } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DragAssistanceManager.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DragAssistanceManager.cs index 36e0e9aa9b8..b28691cc26c 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DragAssistanceManager.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/Behavior/DragAssistanceManager.cs @@ -130,7 +130,7 @@ internal DragAssistanceManager(IServiceProvider serviceProvider, Graphics graphi /// private void AddSnapLines(ControlDesigner controlDesigner, List horizontalList, List verticalList, bool isTarget, bool validTarget) { - IList snapLines = controlDesigner.SnapLines; + IList snapLines = controlDesigner.SnapLinesInternal; // Used for padding snaplines Rectangle controlRect = controlDesigner.Control.ClientRectangle; // Used for all others diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ButtonBaseDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ButtonBaseDesigner.cs index 335bed4f3d6..2036f051535 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ButtonBaseDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ButtonBaseDesigner.cs @@ -49,7 +49,7 @@ public override IList SnapLines { get { - ArrayList snapLines = base.SnapLines as ArrayList; + IList snapLines = SnapLinesInternal; FlatStyle flatStyle = FlatStyle.Standard; ContentAlignment alignment = ContentAlignment.MiddleCenter; @@ -103,7 +103,7 @@ public override IList SnapLines snapLines.Add(new SnapLine(SnapLineType.Baseline, baseline, SnapLinePriority.Medium)); - return snapLines; + return snapLines.Unwrap(); } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ComboBoxDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ComboBoxDesigner.cs index 029f259611e..635dca98068 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ComboBoxDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ComboBoxDesigner.cs @@ -27,14 +27,14 @@ public override IList SnapLines { get { - ArrayList snapLines = base.SnapLines as ArrayList; + IList snapLines = SnapLinesInternal; // a single text-baseline for the label (and linklabel) control int baseline = DesignerUtils.GetTextBaseline(Control, Drawing.ContentAlignment.TopLeft); baseline += 3; snapLines.Add(new SnapLine(SnapLineType.Baseline, baseline, SnapLinePriority.Medium)); - return snapLines; + return snapLines.Unwrap(); } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ControlDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ControlDesigner.cs index 5c2007a773b..ea40f967834 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ControlDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ControlDesigner.cs @@ -356,13 +356,15 @@ private bool IsResizableConsiderAutoSize(PropertyDescriptor autoSizeProp, Proper /// Returns a list of SnapLine objects representing interesting alignment points for this control. /// These SnapLines are used to assist in the positioning of the control on a parent's surface. /// - public virtual IList SnapLines => SnapLinesInternal(); + public virtual IList SnapLines => EdgeAndMarginSnapLines().Unwrap(); - internal IList SnapLinesInternal() => SnapLinesInternal(Control.Margin); + internal IList SnapLinesInternal => SnapLines.Adapt(); - internal IList SnapLinesInternal(Padding margin) + internal IList EdgeAndMarginSnapLines() => EdgeAndMarginSnapLines(Control.Margin); + + internal IList EdgeAndMarginSnapLines(Padding margin) { - ArrayList snapLines = new(4); + List snapLines = new(8); int width = Control.Width; int height = Control.Height; diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DateTimePickerDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DateTimePickerDesigner.cs index 1fbaccd03a7..3b09ddc0ea8 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DateTimePickerDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/DateTimePickerDesigner.cs @@ -21,7 +21,7 @@ public override IList SnapLines { get { - IList snapLines = base.SnapLines; + IList snapLines = SnapLinesInternal; // A single text-baseline for the label (and linklabel) control. int baseline = DesignerUtils.GetTextBaseline(Control, ContentAlignment.MiddleLeft); @@ -30,7 +30,7 @@ public override IList SnapLines baseline += 2; snapLines.Add(new SnapLine(SnapLineType.Baseline, baseline, SnapLinePriority.Medium)); - return snapLines; + return snapLines.Unwrap(); } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/FlowPanelDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/FlowPanelDesigner.cs index c6d80b1f7f6..4e7bc403737 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/FlowPanelDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/FlowPanelDesigner.cs @@ -1,8 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -#nullable disable - using System.Collections; using System.Windows.Forms.Design.Behavior; @@ -16,25 +14,19 @@ public override IList SnapLines { get { - ArrayList snapLines = (ArrayList)base.SnapLines; + IList snapLines = SnapLinesInternal; - // identify all the paddings to remove - ArrayList paddingsToRemove = new(4); - foreach (SnapLine line in snapLines) + // identify and remove all paddings + for (int i = snapLines.Count - 1; i >= 0; i--) { - if (line.Filter is not null && line.Filter.Contains(SnapLine.Padding)) + if (snapLines[i] is SnapLine line + && line.Filter?.Contains(SnapLine.Padding) == true) { - paddingsToRemove.Add(line); + snapLines.RemoveAt(i); } } - // remove all padding - foreach (SnapLine line in paddingsToRemove) - { - snapLines.Remove(line); - } - - return snapLines; + return snapLines.Unwrap(); } } @@ -48,7 +40,7 @@ protected override void OnDragDrop(DragEventArgs de) { base.OnDragDrop(de); - SelectionManager sm = GetService(typeof(SelectionManager)) as SelectionManager; + SelectionManager? sm = GetService(typeof(SelectionManager)) as SelectionManager; sm?.Refresh(); } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/FormDocumentDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/FormDocumentDesigner.cs index 7aa0047b996..5a01206b02b 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/FormDocumentDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/FormDocumentDesigner.cs @@ -181,12 +181,12 @@ public override IList SnapLines { get { - ArrayList snapLines = null; + IList snapLines = null; AddPaddingSnapLines(ref snapLines); if (snapLines is null) { Debug.Fail("why did base.AddPaddingSnapLines return null?"); - snapLines = new ArrayList(4); + snapLines = new List(4); } // if the padding has not been set - then we'll auto-add padding to form - this is a Usability request @@ -218,7 +218,7 @@ public override IList SnapLines } } - return snapLines; + return (IList)snapLines; } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IWrapper.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IWrapper.cs new file mode 100644 index 00000000000..2de0e3067b1 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/IWrapper.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace System.Windows.Forms.Design; + +/// +/// Interface to provide a contract to provide access to the underlying type. +/// +/// The type of object being wrapped. +internal interface IWrapper +{ + T Unwrap(); +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/LabelDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/LabelDesigner.cs index 3b8d6a3a99c..1fa0602e7c5 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/LabelDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/LabelDesigner.cs @@ -31,7 +31,7 @@ public override IList SnapLines { get { - ArrayList snapLines = base.SnapLines as ArrayList; + IList snapLines = SnapLinesInternal; ContentAlignment alignment = ContentAlignment.TopLeft; PropertyDescriptor prop; @@ -93,7 +93,7 @@ public override IList SnapLines } } - return snapLines; + return snapLines.Unwrap(); } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ListAdapter.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ListAdapter.cs new file mode 100644 index 00000000000..676b2fc23e0 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ListAdapter.cs @@ -0,0 +1,63 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections; + +namespace System.Windows.Forms.Design; + +/// +/// Adapter for bridging between generic and non-generic lists. +/// It implements both the generic and the non-generic +/// interfaces +/// to provide a unified interface for working with IList collections. +/// +/// The type of elements in the list. +internal sealed class ListAdapter : IList, IWrapper +{ + private readonly IList _list; + internal ListAdapter(IList list) => _list = list.OrThrowIfNull(); + + T IList.this[int index] + { + get => (T?)_list[index] ?? throw new InvalidOperationException(); + set => _list[index] = value.OrThrowIfNull(); + } + + int ICollection.Count => _list.Count; + + bool ICollection.IsReadOnly => _list.IsReadOnly; + + void ICollection.Add(T item) => _list.Add(item.OrThrowIfNull()); + void ICollection.Clear() => _list.Clear(); + bool ICollection.Contains(T item) => _list.Contains(item); + void ICollection.CopyTo(T[] array, int arrayIndex) => _list.CopyTo(array, arrayIndex); + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + int IList.IndexOf(T item) => _list.IndexOf(item); + void IList.Insert(int index, T item) => _list.Insert(index, item.OrThrowIfNull()); + void IList.RemoveAt(int index) => _list.RemoveAt(index); + + bool ICollection.Remove(T item) + { + if (_list.IsReadOnly || !_list.Contains(item)) + { + return false; + } + + _list.Remove(item); + return true; + } + + IEnumerator IEnumerable.GetEnumerator() => new Enumerator(_list.GetEnumerator()); + + private sealed class Enumerator(IEnumerator _enumerator) : IEnumerator + { + T IEnumerator.Current => (T)_enumerator.Current; + object IEnumerator.Current => _enumerator.Current; + + void IDisposable.Dispose() { } + bool IEnumerator.MoveNext() => _enumerator.MoveNext(); + void IEnumerator.Reset() => _enumerator.Reset(); + } + + public IList Unwrap() => _list; +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ParentControlDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ParentControlDesigner.cs index 00f268f82e5..31d31a2793e 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ParentControlDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ParentControlDesigner.cs @@ -295,9 +295,13 @@ protected Size GridSize // We need to allocation new ArrayList and pass it to the caller.. // So its ok to Suppress this. protected void AddPaddingSnapLines(ref ArrayList snapLines) - { - snapLines ??= new ArrayList(4); + => AddPaddingSnapLinesCommon((snapLines ??= new(4)).Adapt()); + + internal void AddPaddingSnapLines(ref IList snapLines) + => AddPaddingSnapLinesCommon(snapLines ??= new List(4)); + private void AddPaddingSnapLinesCommon(IList snapLines) + { // In order to add padding, we need to get the offset from the usable client area of our control // and the actual origin of our control. In other words: how big is the non-client area here? // Ex: we want to add padding on a form to the insides of the borders and below the titlebar. @@ -328,16 +332,16 @@ public override IList SnapLines { get { - ArrayList snapLines = base.SnapLines as ArrayList; + IList snapLines = SnapLinesInternal; if (snapLines is null) { Debug.Fail("why did base.SnapLines return null?"); - snapLines = new ArrayList(4); + snapLines = new List(4); } AddPaddingSnapLines(ref snapLines); - return snapLines; + return snapLines.Unwrap(); } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SplitContainerDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SplitContainerDesigner.cs index 2dfd2c9e174..10bbba5ab25 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SplitContainerDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/SplitContainerDesigner.cs @@ -61,7 +61,7 @@ internal partial class SplitContainerDesigner : ParentControlDesigner /// public override IList SnapLines => // We don't want padding snaplines, so call directly to the internal method. - SnapLinesInternal(); + EdgeAndMarginSnapLines().Unwrap(); /// /// Returns the number of internal control designers in the . An internal control is a control that is not in the diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/TextBoxBaseDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/TextBoxBaseDesigner.cs index 26efc159cee..59908835e4a 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/TextBoxBaseDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/TextBoxBaseDesigner.cs @@ -28,12 +28,10 @@ public override IList SnapLines { int baseline = DesignerUtils.GetTextBaseline(Control, Drawing.ContentAlignment.TopLeft); - BorderStyle borderStyle = BorderStyle.Fixed3D; PropertyDescriptor? prop = TypeDescriptor.GetProperties(Component)["BorderStyle"]; - if (prop is not null) - { - borderStyle = (BorderStyle)prop.GetValue(Component)!; - } + BorderStyle borderStyle = prop is not null + ? (BorderStyle)prop.GetValue(Component)! + : BorderStyle.Fixed3D; if (borderStyle == BorderStyle.None) { @@ -53,11 +51,9 @@ public override IList SnapLines baseline += 0; } - IList snapLines = base.SnapLines; - + IList snapLines = SnapLinesInternal; snapLines.Add(new SnapLine(SnapLineType.Baseline, baseline, SnapLinePriority.Medium)); - - return snapLines; + return snapLines.Unwrap(); } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripContainerDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripContainerDesigner.cs index 1edc9de18e1..e20b278da1e 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripContainerDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ToolStripContainerDesigner.cs @@ -117,7 +117,7 @@ public override DesignerActionListCollection ActionLists public override IList SnapLines // We don't want padding SnapLines, so call directly to the internal method. - => SnapLinesInternal(); + => EdgeAndMarginSnapLines().Unwrap(); /// /// Returns the internal control designer with the specified index in the ControlDesigner. diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/UpDownBaseDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/UpDownBaseDesigner.cs index b5a4ea24101..b38761f845c 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/UpDownBaseDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/UpDownBaseDesigner.cs @@ -43,29 +43,17 @@ public override IList SnapLines { get { - ArrayList snapLines = base.SnapLines as ArrayList; - + IList snapLines = SnapLinesInternal; int baseline = DesignerUtils.GetTextBaseline(Control, Drawing.ContentAlignment.TopLeft); - BorderStyle borderStyle = BorderStyle.Fixed3D; PropertyDescriptor prop = TypeDescriptor.GetProperties(Component)["BorderStyle"]; - if (prop is not null) - { - borderStyle = (BorderStyle)prop.GetValue(Component); - } - - if (borderStyle == BorderStyle.None) - { - baseline -= 1; - } - else - { - baseline += 2; - } + BorderStyle borderStyle = prop is not null + ? (BorderStyle)prop.GetValue(Component) + : BorderStyle.Fixed3D; + baseline += borderStyle == BorderStyle.None ? -1 : 2; snapLines.Add(new SnapLine(SnapLineType.Baseline, baseline, SnapLinePriority.Medium)); - - return snapLines; + return snapLines.Unwrap(); } } }