diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ListBoxDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ListBoxDesigner.cs index 19e7fe9be7a..147bffdd59f 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ListBoxDesigner.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ListBoxDesigner.cs @@ -72,6 +72,12 @@ protected override void PreFilterProperties(IDictionary properties) properties[nameof(Dock)] = TypeDescriptor.CreateProperty(typeof(ListBoxDesigner), dockProp, []); } + // Wrap Items property to control editor availability based on DataSource + if (properties["Items"] is PropertyDescriptor itemsProp && Component is ListBox listBox) + { + properties["Items"] = new ItemsPropertyDescriptor(itemsProp, listBox); + } + base.PreFilterProperties(properties); } @@ -218,4 +224,47 @@ public override DesignerActionListCollection ActionLists return _actionLists; } } + + /// + /// Custom property descriptor that removes the editor when DataSource is set. + /// + private sealed class ItemsPropertyDescriptor : PropertyDescriptor + { + private readonly PropertyDescriptor _baseDescriptor; + private readonly ListBox _listBox; + + public ItemsPropertyDescriptor(PropertyDescriptor baseDescriptor, ListBox listBox) + : base(baseDescriptor) + { + _baseDescriptor = baseDescriptor; + _listBox = listBox; + } + + public override Type ComponentType => _baseDescriptor.ComponentType; + + public override bool IsReadOnly => _baseDescriptor.IsReadOnly; + + public override Type PropertyType => _baseDescriptor.PropertyType; + + public override bool CanResetValue(object component) => _baseDescriptor.CanResetValue(component); + + public override object? GetValue(object? component) => _baseDescriptor.GetValue(component); + + public override void ResetValue(object component) => _baseDescriptor.ResetValue(component); + + public override void SetValue(object? component, object? value) => _baseDescriptor.SetValue(component, value); + + public override bool ShouldSerializeValue(object component) => _baseDescriptor.ShouldSerializeValue(component); + + public override object? GetEditor(Type editorBaseType) + { + // Don't provide an editor if DataSource is set + if (_listBox.DataSource is not null) + { + return null; + } + + return _baseDescriptor.GetEditor(editorBaseType); + } + } } diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ListControlUnboundActionList.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ListControlUnboundActionList.cs index 187bf8c994e..4cf595c3584 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ListControlUnboundActionList.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/ListControlUnboundActionList.cs @@ -22,13 +22,17 @@ public void InvokeItemsDialog() public override DesignerActionItemCollection GetSortedActionItems() { - DesignerActionItemCollection returnItems = - [ - new DesignerActionMethodItem(this, "InvokeItemsDialog", + DesignerActionItemCollection returnItems = []; + + // Only show "Edit Items..." if DataSource is not set + if (Component is ListControl control && control.DataSource is null) + { + returnItems.Add(new DesignerActionMethodItem(this, "InvokeItemsDialog", SR.ListControlUnboundActionListEditItemsDisplayName, SR.ItemsCategoryName, - SR.ListControlUnboundActionListEditItemsDescription, true), - ]; + SR.ListControlUnboundActionListEditItemsDescription, true)); + } + return returnItems; } } diff --git a/src/System.Windows.Forms.Design/tests/UnitTests/System/Windows/Forms/Design/ListControlUnboundActionListTests.cs b/src/System.Windows.Forms.Design/tests/UnitTests/System/Windows/Forms/Design/ListControlUnboundActionListTests.cs index e9503f46ca8..0b0243ec153 100644 --- a/src/System.Windows.Forms.Design/tests/UnitTests/System/Windows/Forms/Design/ListControlUnboundActionListTests.cs +++ b/src/System.Windows.Forms.Design/tests/UnitTests/System/Windows/Forms/Design/ListControlUnboundActionListTests.cs @@ -10,24 +10,28 @@ namespace System.Windows.Forms.Design.Tests; public sealed class ListControlUnboundActionListTests : IDisposable { private readonly ComponentDesigner _designer; - private readonly Mock _componentMock; + private readonly ListBox _listBox; private readonly ListControlUnboundActionList _actionList; public ListControlUnboundActionListTests() { _designer = new(); - _componentMock = new(); - _designer.Initialize(_componentMock.Object); + _listBox = new(); + _designer.Initialize(_listBox); _actionList = new(_designer); } - public void Dispose() => _designer.Dispose(); + public void Dispose() + { + _designer.Dispose(); + _listBox.Dispose(); + } [Fact] public void Constructor_ShouldInitializeDesigner() => _actionList.Should().NotBeNull(); [Fact] - public void GetSortedActionItems_ShouldReturnCorrectItems() + public void GetSortedActionItems_ShouldReturnCorrectItems_WhenDataSourceIsNull() { DesignerActionItemCollection items = _actionList.GetSortedActionItems(); @@ -39,4 +43,15 @@ public void GetSortedActionItems_ShouldReturnCorrectItems() methodItem.Category.Should().Be(SR.ItemsCategoryName); methodItem.Description.Should().Be(SR.ListControlUnboundActionListEditItemsDescription); } + + [Fact] + public void GetSortedActionItems_ShouldReturnEmpty_WhenDataSourceIsSet() + { + _listBox.DataSource = new List { "Item1", "Item2" }; + + DesignerActionItemCollection items = _actionList.GetSortedActionItems(); + + items.Should().NotBeNull(); + items.Count.Should().Be(0); + } }