From 8be0df2baacc98fa7976d282eacd0421f0bdb777 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 19 Nov 2025 21:54:54 +0000 Subject: [PATCH 1/3] Initial plan From 818aa0ad01764b69c556d3a70e9536ab973e12ce Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 19 Nov 2025 22:04:29 +0000 Subject: [PATCH 2/3] Add exception handling in EditorServiceContext.EditValue Co-authored-by: merriemcgaw <20996271+merriemcgaw@users.noreply.github.com> --- .../Forms/Design/EditorServiceContext.cs | 28 ++++++++++++++++- .../Forms/Design/EditorServiceContextTests.cs | 31 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/EditorServiceContext.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/EditorServiceContext.cs index d34507a23a1..d0836c650ec 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/EditorServiceContext.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/EditorServiceContext.cs @@ -50,8 +50,34 @@ internal EditorServiceContext(ComponentDesigner designer, PropertyDescriptor? pr UITypeEditor editor = descriptor.GetEditor()!; // Get value to edit object? value = descriptor.GetValue(objectToChange); + // Edit value - object? newValue = editor.EditValue(context, context, value); + object? newValue; + try + { + newValue = editor.EditValue(context, context, value); + } + catch (Exception ex) when (!ex.IsCriticalException()) + { + // Display the error to the user + IUIService? uiService = context.GetService(); + if (uiService is not null) + { + uiService.ShowError(ex); + } + else + { + string message = ex.Message; + if (string.IsNullOrEmpty(message)) + { + message = ex.ToString(); + } + + MessageBox.Show(message, null, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); + } + + return value; + } if (newValue != value) { diff --git a/src/System.Windows.Forms.Design/tests/UnitTests/System/Windows/Forms/Design/EditorServiceContextTests.cs b/src/System.Windows.Forms.Design/tests/UnitTests/System/Windows/Forms/Design/EditorServiceContextTests.cs index 4b0232f4f27..9819dcc0dcd 100644 --- a/src/System.Windows.Forms.Design/tests/UnitTests/System/Windows/Forms/Design/EditorServiceContextTests.cs +++ b/src/System.Windows.Forms.Design/tests/UnitTests/System/Windows/Forms/Design/EditorServiceContextTests.cs @@ -95,6 +95,37 @@ public void EditValue_ShouldNotUpdatePropertyValue_WhenEditorReturnsSameValue() _mockPropertyDescriptor.Verify(p => p.SetValue(It.IsAny(), It.IsAny()), Times.Never); } + [Fact] + public void EditValue_ShouldShowError_WhenEditorThrowsException() + { + List list = new() { "Test" }; + ArgumentException expectedException = new("DataSource is set"); + + _mockPropertyDescriptor.Setup(p => p.GetValue(_component)).Returns(list); + _mockPropertyDescriptor.Setup(p => p.PropertyType).Returns(typeof(List)); + _mockPropertyDescriptor.Setup(p => p.Name).Returns("Items"); + _mockPropertyDescriptor.Setup(p => p.Attributes).Returns(new AttributeCollection(null)); + + _mockEditor + .Setup(e => e.EditValue(It.IsAny(), It.IsAny(), list)) + .Throws(expectedException); + + _mockPropertyDescriptor + .Setup(p => p.GetEditor(typeof(UITypeEditor))) + .Returns(_mockEditor.Object); + + _mockSite.Setup(s => s.GetService(typeof(IUIService))).Returns(_mockUIService.Object); + _component.Site = _mockSite.Object; + + TypeDescriptor.AddProvider(new TypeDescriptionProviderMock(_mockPropertyDescriptor.Object), _component); + + object? result = EditorServiceContext.EditValue(_designer, _component, "Items"); + + result.Should().BeSameAs(list); + _mockUIService.Verify(s => s.ShowError(expectedException), Times.Once); + _mockPropertyDescriptor.Verify(p => p.SetValue(It.IsAny(), It.IsAny()), Times.Never); + } + [Fact] public void Container_ShouldReturnNull_WhenComponentSiteIsNull() { From 9b0328e0ea30791c07dc4380935c5dbed8b9c63a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 24 Nov 2025 08:39:13 +0000 Subject: [PATCH 3/3] Hide 'Edit Items...' when DataSource is set - Modified ListControlUnboundActionList to conditionally show "Edit Items..." action only when DataSource is null - Added ItemsPropertyDescriptor to ListBoxDesigner to remove editor from Properties window when DataSource is set - Updated tests to verify the new behavior - Reverted previous exception handling approach per feedback Co-authored-by: LeafShi1 <132890443+LeafShi1@users.noreply.github.com> --- .../Forms/Design/EditorServiceContext.cs | 28 +---------- .../Windows/Forms/Design/ListBoxDesigner.cs | 49 +++++++++++++++++++ .../Design/ListControlUnboundActionList.cs | 14 ++++-- .../Forms/Design/EditorServiceContextTests.cs | 31 ------------ .../ListControlUnboundActionListTests.cs | 25 ++++++++-- 5 files changed, 79 insertions(+), 68 deletions(-) diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/EditorServiceContext.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/EditorServiceContext.cs index d0836c650ec..d34507a23a1 100644 --- a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/EditorServiceContext.cs +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/EditorServiceContext.cs @@ -50,34 +50,8 @@ internal EditorServiceContext(ComponentDesigner designer, PropertyDescriptor? pr UITypeEditor editor = descriptor.GetEditor()!; // Get value to edit object? value = descriptor.GetValue(objectToChange); - // Edit value - object? newValue; - try - { - newValue = editor.EditValue(context, context, value); - } - catch (Exception ex) when (!ex.IsCriticalException()) - { - // Display the error to the user - IUIService? uiService = context.GetService(); - if (uiService is not null) - { - uiService.ShowError(ex); - } - else - { - string message = ex.Message; - if (string.IsNullOrEmpty(message)) - { - message = ex.ToString(); - } - - MessageBox.Show(message, null, MessageBoxButtons.OK, MessageBoxIcon.Exclamation); - } - - return value; - } + object? newValue = editor.EditValue(context, context, value); if (newValue != value) { 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/EditorServiceContextTests.cs b/src/System.Windows.Forms.Design/tests/UnitTests/System/Windows/Forms/Design/EditorServiceContextTests.cs index 9819dcc0dcd..4b0232f4f27 100644 --- a/src/System.Windows.Forms.Design/tests/UnitTests/System/Windows/Forms/Design/EditorServiceContextTests.cs +++ b/src/System.Windows.Forms.Design/tests/UnitTests/System/Windows/Forms/Design/EditorServiceContextTests.cs @@ -95,37 +95,6 @@ public void EditValue_ShouldNotUpdatePropertyValue_WhenEditorReturnsSameValue() _mockPropertyDescriptor.Verify(p => p.SetValue(It.IsAny(), It.IsAny()), Times.Never); } - [Fact] - public void EditValue_ShouldShowError_WhenEditorThrowsException() - { - List list = new() { "Test" }; - ArgumentException expectedException = new("DataSource is set"); - - _mockPropertyDescriptor.Setup(p => p.GetValue(_component)).Returns(list); - _mockPropertyDescriptor.Setup(p => p.PropertyType).Returns(typeof(List)); - _mockPropertyDescriptor.Setup(p => p.Name).Returns("Items"); - _mockPropertyDescriptor.Setup(p => p.Attributes).Returns(new AttributeCollection(null)); - - _mockEditor - .Setup(e => e.EditValue(It.IsAny(), It.IsAny(), list)) - .Throws(expectedException); - - _mockPropertyDescriptor - .Setup(p => p.GetEditor(typeof(UITypeEditor))) - .Returns(_mockEditor.Object); - - _mockSite.Setup(s => s.GetService(typeof(IUIService))).Returns(_mockUIService.Object); - _component.Site = _mockSite.Object; - - TypeDescriptor.AddProvider(new TypeDescriptionProviderMock(_mockPropertyDescriptor.Object), _component); - - object? result = EditorServiceContext.EditValue(_designer, _component, "Items"); - - result.Should().BeSameAs(list); - _mockUIService.Verify(s => s.ShowError(expectedException), Times.Once); - _mockPropertyDescriptor.Verify(p => p.SetValue(It.IsAny(), It.IsAny()), Times.Never); - } - [Fact] public void Container_ShouldReturnNull_WhenComponentSiteIsNull() { 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); + } }