diff --git a/src/System.Design/src/System.Design.Forwards.cs b/src/System.Design/src/System.Design.Forwards.cs index a4f9fb50bbf..70a56c37881 100644 --- a/src/System.Design/src/System.Design.Forwards.cs +++ b/src/System.Design/src/System.Design.Forwards.cs @@ -4,6 +4,8 @@ using System.Runtime.CompilerServices; // internal UITypeEditors +[assembly: TypeForwardedTo(typeof(System.Windows.Forms.Design.BindingNavigatorDesigner))] +[assembly: TypeForwardedTo(typeof(System.Windows.Forms.Design.BindingSourceDesigner))] [assembly: TypeForwardedTo(typeof(System.Windows.Forms.Design.ColumnHeaderCollectionEditor))] [assembly: TypeForwardedTo(typeof(System.Windows.Forms.Design.DataMemberFieldConverter))] [assembly: TypeForwardedTo(typeof(System.Windows.Forms.Design.DataGridViewCellStyleEditor))] diff --git a/src/System.Windows.Forms.Design/src/PublicAPI.Unshipped.txt b/src/System.Windows.Forms.Design/src/PublicAPI.Unshipped.txt index 7990ca9f94e..e3349deca6a 100644 --- a/src/System.Windows.Forms.Design/src/PublicAPI.Unshipped.txt +++ b/src/System.Windows.Forms.Design/src/PublicAPI.Unshipped.txt @@ -4,4 +4,45 @@ virtual System.ComponentModel.Design.ComponentDesigner.SetTextualDefaultProperty *REMOVED*~System.Windows.Forms.Design.Behavior.GlyphCollection.AddRange(System.Windows.Forms.Design.Behavior.Glyph[] value) -> void System.ComponentModel.Design.DesignerActionListCollection.AddRange(params System.ComponentModel.Design.DesignerActionList?[]! value) -> void ~System.Windows.Forms.Design.Behavior.BehaviorServiceAdornerCollection.AddRange(params System.Windows.Forms.Design.Behavior.Adorner[] value) -> void -~System.Windows.Forms.Design.Behavior.GlyphCollection.AddRange(params System.Windows.Forms.Design.Behavior.Glyph[] value) -> void \ No newline at end of file +~System.Windows.Forms.Design.Behavior.GlyphCollection.AddRange(params System.Windows.Forms.Design.Behavior.Glyph[] value) -> void +abstract System.ComponentModel.Design.DataSourceDescriptor.Image.get -> System.Drawing.Bitmap! +abstract System.ComponentModel.Design.DataSourceDescriptor.IsDesignable.get -> bool +abstract System.ComponentModel.Design.DataSourceDescriptor.Name.get -> string! +abstract System.ComponentModel.Design.DataSourceDescriptor.TypeName.get -> string! +abstract System.ComponentModel.Design.DataSourceGroup.DataSources.get -> System.ComponentModel.Design.DataSourceDescriptorCollection! +abstract System.ComponentModel.Design.DataSourceGroup.Image.get -> System.Drawing.Bitmap! +abstract System.ComponentModel.Design.DataSourceGroup.IsDefault.get -> bool +abstract System.ComponentModel.Design.DataSourceGroup.Name.get -> string! +abstract System.ComponentModel.Design.DataSourceProviderService.AddDataSourceInstance(System.ComponentModel.Design.IDesignerHost! host, System.ComponentModel.Design.DataSourceDescriptor! dataSourceDescriptor) -> object! +abstract System.ComponentModel.Design.DataSourceProviderService.GetDataSources() -> System.ComponentModel.Design.DataSourceGroupCollection! +abstract System.ComponentModel.Design.DataSourceProviderService.InvokeAddNewDataSource(System.Windows.Forms.IWin32Window! parentWindow, System.Windows.Forms.FormStartPosition startPosition) -> System.ComponentModel.Design.DataSourceGroup! +abstract System.ComponentModel.Design.DataSourceProviderService.InvokeConfigureDataSource(System.Windows.Forms.IWin32Window! parentWindow, System.Windows.Forms.FormStartPosition startPosition, System.ComponentModel.Design.DataSourceDescriptor! dataSourceDescriptor) -> bool +abstract System.ComponentModel.Design.DataSourceProviderService.NotifyDataSourceComponentAdded(object! dsc) -> void +abstract System.ComponentModel.Design.DataSourceProviderService.SupportsAddNewDataSource.get -> bool +abstract System.ComponentModel.Design.DataSourceProviderService.SupportsConfigureDataSource.get -> bool +System.ComponentModel.Design.DataSourceDescriptor +System.ComponentModel.Design.DataSourceDescriptor.DataSourceDescriptor() -> void +System.ComponentModel.Design.DataSourceDescriptorCollection +System.ComponentModel.Design.DataSourceDescriptorCollection.Add(System.ComponentModel.Design.DataSourceDescriptor! value) -> int +System.ComponentModel.Design.DataSourceDescriptorCollection.Contains(System.ComponentModel.Design.DataSourceDescriptor! value) -> bool +System.ComponentModel.Design.DataSourceDescriptorCollection.CopyTo(System.ComponentModel.Design.DataSourceDescriptor![]! array, int index) -> void +System.ComponentModel.Design.DataSourceDescriptorCollection.DataSourceDescriptorCollection() -> void +System.ComponentModel.Design.DataSourceDescriptorCollection.IndexOf(System.ComponentModel.Design.DataSourceDescriptor! value) -> int +System.ComponentModel.Design.DataSourceDescriptorCollection.Insert(int index, System.ComponentModel.Design.DataSourceDescriptor! value) -> void +System.ComponentModel.Design.DataSourceDescriptorCollection.Remove(System.ComponentModel.Design.DataSourceDescriptor! value) -> void +System.ComponentModel.Design.DataSourceDescriptorCollection.this[int index].get -> System.ComponentModel.Design.DataSourceDescriptor? +System.ComponentModel.Design.DataSourceDescriptorCollection.this[int index].set -> void +System.ComponentModel.Design.DataSourceGroup +System.ComponentModel.Design.DataSourceGroup.DataSourceGroup() -> void +System.ComponentModel.Design.DataSourceGroupCollection +System.ComponentModel.Design.DataSourceGroupCollection.Add(System.ComponentModel.Design.DataSourceGroup! value) -> int +System.ComponentModel.Design.DataSourceGroupCollection.Contains(System.ComponentModel.Design.DataSourceGroup! value) -> bool +System.ComponentModel.Design.DataSourceGroupCollection.CopyTo(System.ComponentModel.Design.DataSourceGroup![]! array, int index) -> void +System.ComponentModel.Design.DataSourceGroupCollection.DataSourceGroupCollection() -> void +System.ComponentModel.Design.DataSourceGroupCollection.IndexOf(System.ComponentModel.Design.DataSourceGroup! value) -> int +System.ComponentModel.Design.DataSourceGroupCollection.Insert(int index, System.ComponentModel.Design.DataSourceGroup! value) -> void +System.ComponentModel.Design.DataSourceGroupCollection.Remove(System.ComponentModel.Design.DataSourceGroup! value) -> void +System.ComponentModel.Design.DataSourceGroupCollection.this[int index].get -> System.ComponentModel.Design.DataSourceGroup? +System.ComponentModel.Design.DataSourceGroupCollection.this[int index].set -> void +System.ComponentModel.Design.DataSourceProviderService +System.ComponentModel.Design.DataSourceProviderService.DataSourceProviderService() -> void \ No newline at end of file diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DataSourceDescriptor.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DataSourceDescriptor.cs new file mode 100644 index 00000000000..9e8e6891776 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DataSourceDescriptor.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing; + +namespace System.ComponentModel.Design; + +public abstract class DataSourceDescriptor +{ + public abstract string Name { get; } + + public abstract Bitmap Image { get; } + + public abstract string TypeName { get; } + + public abstract bool IsDesignable { get; } +} diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DataSourceDescriptorCollection.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DataSourceDescriptorCollection.cs new file mode 100644 index 00000000000..e91af842e5b --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DataSourceDescriptorCollection.cs @@ -0,0 +1,31 @@ +// 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.ComponentModel.Design; + +public class DataSourceDescriptorCollection : CollectionBase +{ + public DataSourceDescriptorCollection() : base() + { + } + + public int Add(DataSourceDescriptor value) => List.Add(value); + + public int IndexOf(DataSourceDescriptor value) => List.IndexOf(value); + + public void Insert(int index, DataSourceDescriptor value) => List.Insert(index, value); + + public bool Contains(DataSourceDescriptor value) => List.Contains(value); + + public void CopyTo(DataSourceDescriptor[] array, int index) => List.CopyTo(array, index); + + public void Remove(DataSourceDescriptor value) => List.Remove(value); + + public DataSourceDescriptor? this[int index] + { + get => List[index] as DataSourceDescriptor; + set => List[index] = value; + } +} diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DataSourceGroup.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DataSourceGroup.cs new file mode 100644 index 00000000000..050148344fa --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DataSourceGroup.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Drawing; + +namespace System.ComponentModel.Design; + +public abstract class DataSourceGroup +{ + public abstract string Name { get; } + + public abstract Bitmap Image { get; } + + public abstract DataSourceDescriptorCollection DataSources { get; } + + public abstract bool IsDefault { get; } +} diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DataSourceGroupCollection.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DataSourceGroupCollection.cs new file mode 100644 index 00000000000..356c73025b3 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DataSourceGroupCollection.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.ComponentModel.Design; + +public class DataSourceGroupCollection : CollectionBase +{ + public DataSourceGroupCollection() : base() + { + } + + public int Add(DataSourceGroup value) => List.Add(value); + + public int IndexOf(DataSourceGroup value) => List.IndexOf(value); + + public void Insert(int index, DataSourceGroup value) => List.Insert(index, value); + + public bool Contains(DataSourceGroup value) => List.Contains(value); + + public void CopyTo(DataSourceGroup[] array, int index) => List.CopyTo(array, index); + + public void Remove(DataSourceGroup value) => List.Remove(value); + + public DataSourceGroup? this[int index] + { + get => List[index] as DataSourceGroup; + + set => List[index] = value; + } +} diff --git a/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DataSourceProviderService.cs b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DataSourceProviderService.cs new file mode 100644 index 00000000000..e20c2373b00 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/ComponentModel/Design/DataSourceProviderService.cs @@ -0,0 +1,25 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Runtime.InteropServices; +using System.Windows.Forms; + +namespace System.ComponentModel.Design; + +[Guid("ABE5C1F0-C96E-40c4-A22D-4A5CEC899BDC")] +public abstract class DataSourceProviderService +{ + public abstract bool SupportsAddNewDataSource { get; } + + public abstract bool SupportsConfigureDataSource { get; } + + public abstract DataSourceGroupCollection GetDataSources(); + + public abstract DataSourceGroup InvokeAddNewDataSource(IWin32Window parentWindow, FormStartPosition startPosition); + + public abstract bool InvokeConfigureDataSource(IWin32Window parentWindow, FormStartPosition startPosition, DataSourceDescriptor dataSourceDescriptor); + + public abstract object AddDataSourceInstance(IDesignerHost host, DataSourceDescriptor dataSourceDescriptor); + + public abstract void NotifyDataSourceComponentAdded(object dsc); +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/BindingNavigatorDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/BindingNavigatorDesigner.cs new file mode 100644 index 00000000000..1ed14582044 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/BindingNavigatorDesigner.cs @@ -0,0 +1,180 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel.Design; +using System.ComponentModel; +using System.Collections; + +namespace System.Windows.Forms.Design; + +internal class BindingNavigatorDesigner : ToolStripDesigner +{ + private static readonly string[] s_itemNames = + [ + "MovePreviousItem", + "MoveFirstItem", + "MoveNextItem", + "MoveLastItem", + "AddNewItem", + "DeleteItem", + "PositionItem", + "CountItem" + ]; + + public override void Initialize(IComponent component) + { + base.Initialize(component); + + IComponentChangeService componentChangeService = GetService(); + if (componentChangeService is not null) + { + componentChangeService.ComponentRemoved += ComponentChangeService_ComponentRemoved; + componentChangeService.ComponentChanged += ComponentChangeService_ComponentChanged; + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + IComponentChangeService componentChangeService = GetService(); + if (componentChangeService is not null) + { + componentChangeService.ComponentRemoved -= ComponentChangeService_ComponentRemoved; + componentChangeService.ComponentChanged -= ComponentChangeService_ComponentChanged; + } + } + + base.Dispose(disposing); + } + + public override void InitializeNewComponent(IDictionary defaultValues) + { + base.InitializeNewComponent(defaultValues); + + BindingNavigator navigator = (BindingNavigator)Component; + IDesignerHost? host = Component?.Site?.GetService(); + + try + { + s_autoAddNewItems = false; // Temporarily suppress "new items go to the selected strip" behavior + navigator.SuspendLayout(); // Turn off layout while adding items + navigator.AddStandardItems(); // Let the control add its standard items (user overridable) + SiteItems(host: host, items: navigator.Items); // Recursively site and name all the items on the strip + RaiseItemsChanged(); // Make designer Undo engine aware of the newly added and sited items + navigator.ResumeLayout(); // Allow strip to lay out now + navigator.ShowItemToolTips = true; // Non-default property setting for ShowToolTips + } + finally + { + s_autoAddNewItems = true; + } + } + + private void RaiseItemsChanged() + { + BindingNavigator navigator = (BindingNavigator)Component; + IComponentChangeService componentChangeService = GetService(); + + if (componentChangeService is not null) + { + MemberDescriptor? memberDescriptor = TypeDescriptor.GetProperties(navigator)["Items"]; + componentChangeService.OnComponentChanging(component: navigator, member: memberDescriptor); + componentChangeService.OnComponentChanged(component: navigator, member: memberDescriptor, oldValue: null, newValue: null); + + foreach (string itemName in s_itemNames) + { + PropertyDescriptor? propertyDescriptor = TypeDescriptor.GetProperties(navigator)[itemName]; + + if (propertyDescriptor is not null) + { + componentChangeService.OnComponentChanging(component: navigator, member: propertyDescriptor); + componentChangeService.OnComponentChanged(component: navigator, member: propertyDescriptor, oldValue: null, newValue: null); + } + } + } + } + + private void SiteItem(IDesignerHost? host, ToolStripItem item) + { + // Skip any controls added for design-time use only + if (item is DesignerToolStripControlHost) + { + return; + } + + // Site the item in the container, giving it a unique site name based on its initial Name property + host?.Container.Add(item, DesignerUtils.GetUniqueSiteName(host, item.Name)); + + // Update the item's Name property to reflect the unique site name that it was actually given + item.Name = item?.Site?.Name; + + // Site any sub-items of this item + ToolStripDropDownItem? dropDownItem = item as ToolStripDropDownItem; + if (dropDownItem is not null && dropDownItem.HasDropDownItems) + { + SiteItems(host: host, items: dropDownItem.DropDownItems); + } + } + + private void SiteItems(IDesignerHost? host, ToolStripItemCollection items) + { + foreach (ToolStripItem item in items) + { + SiteItem(host, item); + } + } + + private void ComponentChangeService_ComponentRemoved(object? sender, ComponentEventArgs e) + { + ToolStripItem? item = e.Component as ToolStripItem; + + if (item is not null) + { + BindingNavigator navigator = (BindingNavigator)Component; + + if (item == navigator.MoveFirstItem) + { + navigator.MoveFirstItem = null; + } + else if (item == navigator.MovePreviousItem) + { + navigator.MovePreviousItem = null; + } + else if (item == navigator.MoveNextItem) + { + navigator.MoveNextItem = null; + } + else if (item == navigator.MoveLastItem) + { + navigator.MoveLastItem = null; + } + else if (item == navigator.PositionItem) + { + navigator.PositionItem = null; + } + else if (item == navigator.CountItem) + { + navigator.CountItem = null; + } + else if (item == navigator.AddNewItem) + { + navigator.AddNewItem = null; + } + else if (item == navigator.DeleteItem) + { + navigator.DeleteItem = null; + } + } + } + + private void ComponentChangeService_ComponentChanged(object? sender, ComponentChangedEventArgs e) + { + BindingNavigator navigator = (BindingNavigator)Component; + + if (e.Component is not null && e.Component == navigator.CountItem && e.Member is not null && e.Member.Name == "Text") + { + navigator.CountItemFormat = navigator.CountItem.Text ?? string.Empty; + } + } +} diff --git a/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/BindingSourceDesigner.cs b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/BindingSourceDesigner.cs new file mode 100644 index 00000000000..d46ba7a5302 --- /dev/null +++ b/src/System.Windows.Forms.Design/src/System/Windows/Forms/Design/BindingSourceDesigner.cs @@ -0,0 +1,84 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.ComponentModel; +using System.ComponentModel.Design; + +namespace System.Windows.Forms.Design; + +internal class BindingSourceDesigner : ComponentDesigner +{ + private bool _bindingUpdatedByUser; + + public bool BindingUpdatedByUser + { + set + { + _bindingUpdatedByUser = value; + } + } + + public override void Initialize(IComponent component) + { + base.Initialize(component); + + IComponentChangeService componentChangeService = GetService(); + if (componentChangeService is not null) + { + componentChangeService.ComponentChanged += OnComponentChanged; + componentChangeService.ComponentRemoving += OnComponentRemoving; + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + IComponentChangeService componentChangeService = GetService(); + if (componentChangeService is not null) + { + componentChangeService.ComponentChanged -= OnComponentChanged; + componentChangeService.ComponentRemoving -= OnComponentRemoving; + } + } + + base.Dispose(disposing); + } + + private void OnComponentChanged(object? sender, ComponentChangedEventArgs e) + { + if (_bindingUpdatedByUser && e.Component == Component && + e.Member is not null && (e.Member.Name == "DataSource" || e.Member.Name == "DataMember")) + { + _bindingUpdatedByUser = false; + + DataSourceProviderService dataSourceProviderService = GetService(); + dataSourceProviderService?.NotifyDataSourceComponentAdded(Component); + } + } + + private void OnComponentRemoving(object? sender, ComponentEventArgs e) + { + BindingSource? bingSource = Component as BindingSource; + if (bingSource is not null && bingSource.DataSource == e.Component) + { + IComponentChangeService componentChangeService = GetService(); + string previousDataMember = bingSource.DataMember; + + PropertyDescriptorCollection propertyDescriptorCollection = TypeDescriptor.GetProperties(bingSource); + PropertyDescriptor? propertyDescriptor = propertyDescriptorCollection?["DataMember"]; + + if (componentChangeService is not null && propertyDescriptor is not null) + { + componentChangeService.OnComponentChanging(bingSource, propertyDescriptor); + } + + bingSource.DataSource = null; + + if (componentChangeService is not null && propertyDescriptor is not null) + { + componentChangeService.OnComponentChanged(bingSource, propertyDescriptor, previousDataMember, string.Empty); + } + } + } +} diff --git a/src/System.Windows.Forms/tests/IntegrationTests/DesignSurface/DemoConsole/MainForm.cs b/src/System.Windows.Forms/tests/IntegrationTests/DesignSurface/DemoConsole/MainForm.cs index 0265fa88f73..247a40ddd13 100644 --- a/src/System.Windows.Forms/tests/IntegrationTests/DesignSurface/DemoConsole/MainForm.cs +++ b/src/System.Windows.Forms/tests/IntegrationTests/DesignSurface/DemoConsole/MainForm.cs @@ -249,6 +249,11 @@ private void CreateDesignSurface(int n) surface.CreateControl(new Size(290, 160), new Point(20, 150)); surface.CreateControl(new Size(200, 150), new Point(430, 23)); surface.CreateComponent(); + + ListBox listBox = surface.CreateControl(new Size(120, 94), new Point(337, 217)); + BindingSource bindingSource = surface.CreateComponent(); + bindingSource.DataSource = new List { "a1", "b2", "c3", "d4", "e5", "f6" }; + listBox.DataSource = bindingSource; } break; diff --git a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/Design/DesignerAttributeTests.cs b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/Design/DesignerAttributeTests.cs index 6985ddb70d6..56013493bb7 100644 --- a/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/Design/DesignerAttributeTests.cs +++ b/src/System.Windows.Forms/tests/UnitTests/System/Windows/Forms/Design/DesignerAttributeTests.cs @@ -24,8 +24,6 @@ public class DesignerAttributeTests // https://github.com/dotnet/winforms/issues/2411 "System.Windows.Forms.Design.AxDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Windows.Forms.Design.AxHostDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "System.Windows.Forms.Design.BindingNavigatorDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", - "System.Windows.Forms.Design.BindingSourceDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Windows.Forms.Design.DataGridViewColumnDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Windows.Forms.Design.DataGridViewComboBoxColumnDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", "System.Windows.Forms.Design.DataGridViewDesigner, System.Design, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",