diff --git a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListBoxes/ListBox.cs b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListBoxes/ListBox.cs index ef7c6daab90..130c1487f39 100644 --- a/src/System.Windows.Forms/System/Windows/Forms/Controls/ListBoxes/ListBox.cs +++ b/src/System.Windows.Forms/System/Windows/Forms/Controls/ListBoxes/ListBox.cs @@ -2071,6 +2071,15 @@ protected override void ScaleControl(SizeF factor, BoundsSpecified specified) UpdateFontCache(); } + // In OwnerDrawFixed the native list box doesn't rescale the fixed item height when the control + // autoscales, so scale it here (clamped to [1, 255]; height-axis guard avoids a double-apply). #6382 + // Round away from zero: banker's rounding would round .5 midpoints down (15 * 1.5 -> 22), which + // re-introduces the cramped rows this fixes. + if (_drawMode == DrawMode.OwnerDrawFixed && factor.Height != 1F && (specified & BoundsSpecified.Height) != 0) + { + ItemHeight = Math.Clamp((int)Math.Round(_itemHeight * factor.Height, MidpointRounding.AwayFromZero), 1, 255); + } + base.ScaleControl(factor, specified); } diff --git a/src/test/unit/System.Windows.Forms/System/Windows/Forms/ListBoxTests.cs b/src/test/unit/System.Windows.Forms/System/Windows/Forms/ListBoxTests.cs index 1ad48b85b77..1307f7eee7d 100644 --- a/src/test/unit/System.Windows.Forms/System/Windows/Forms/ListBoxTests.cs +++ b/src/test/unit/System.Windows.Forms/System/Windows/Forms/ListBoxTests.cs @@ -1845,6 +1845,76 @@ public void ListBox_ItemHeight_ShouldSerializeValue_Success() Assert.False(property.ShouldSerializeValue(control)); } + [WinFormsFact] + public void ListBox_ScaleControl_OwnerDrawFixed_ScalesItemHeight() + { + // #6382: an OwnerDrawFixed item height must scale with the control during autoscaling. + using SubListBox control = new() { DrawMode = DrawMode.OwnerDrawFixed, ItemHeight = 20 }; + + control.ScaleControl(new SizeF(1.5f, 1.5f), BoundsSpecified.All); + + Assert.Equal(30, control.ItemHeight); + } + + [WinFormsFact] + public void ListBox_ScaleControl_OwnerDrawFixed_WithHandle_UpdatesNativeItemHeight() + { + using SubListBox control = new() { DrawMode = DrawMode.OwnerDrawFixed, ItemHeight = 20 }; + Assert.NotEqual(IntPtr.Zero, control.Handle); + Assert.Equal(20, (int)PInvokeCore.SendMessage(control, PInvoke.LB_GETITEMHEIGHT)); + + control.ScaleControl(new SizeF(2f, 2f), BoundsSpecified.All); + + Assert.Equal(40, control.ItemHeight); + Assert.Equal(40, (int)PInvokeCore.SendMessage(control, PInvoke.LB_GETITEMHEIGHT)); + } + + [WinFormsTheory] + [InlineData(25, 38)] // 37.5 rounds up + [InlineData(15, 23)] // 22.5 rounds up, not down to 22 as banker's rounding would + public void ListBox_ScaleControl_OwnerDrawFixed_RoundsMidpointsAwayFromZero(int itemHeight, int expected) + { + using SubListBox control = new() { DrawMode = DrawMode.OwnerDrawFixed, ItemHeight = itemHeight }; + + control.ScaleControl(new SizeF(1.5f, 1.5f), BoundsSpecified.All); + + Assert.Equal(expected, control.ItemHeight); + } + + [WinFormsFact] + public void ListBox_ScaleControl_OwnerDrawFixed_HeightExcluded_DoesNotScaleItemHeight() + { + // A scaling pass that excludes the height axis must leave the item height alone, otherwise the + // included/excluded pass pair in ContainerControl.PerformAutoScale would apply the factor twice. + using SubListBox control = new() { DrawMode = DrawMode.OwnerDrawFixed, ItemHeight = 25 }; + + control.ScaleControl(new SizeF(1.5f, 1.5f), BoundsSpecified.Width); + + Assert.Equal(25, control.ItemHeight); + } + + [WinFormsFact] + public void ListBox_ScaleControl_OwnerDrawFixed_ClampsItemHeightToMax() + { + // Scaling must not throw when the result exceeds the 255 maximum; it clamps instead. + using SubListBox control = new() { DrawMode = DrawMode.OwnerDrawFixed, ItemHeight = 200 }; + + control.ScaleControl(new SizeF(2f, 2f), BoundsSpecified.All); + + Assert.Equal(255, control.ItemHeight); + } + + [WinFormsFact] + public void ListBox_ScaleControl_Normal_DoesNotChangeItemHeight() + { + using SubListBox control = new() { DrawMode = DrawMode.Normal }; + int before = control.ItemHeight; + + control.ScaleControl(new SizeF(1.5f, 1.5f), BoundsSpecified.All); + + Assert.Equal(before, control.ItemHeight); + } + public static IEnumerable Items_CustomCreateItemCollection_TestData() { yield return new object[] { null };