From e59898ac8a57b112b174ed5dc4ab18721cc70d99 Mon Sep 17 00:00:00 2001 From: Zachary Danz Date: Thu, 17 Oct 2019 15:42:33 -0700 Subject: [PATCH] port ImageIndexEditor --- .../Windows/Forms/Design/ImageIndexEditor.cs | 220 ++++++++++++++++++ .../tests/UnitTests/EnsureEditorsTests.cs | 46 ++-- 2 files changed, 243 insertions(+), 23 deletions(-) create mode 100644 src/System.Windows.Forms.Design.Editors/src/System/Windows/Forms/Design/ImageIndexEditor.cs diff --git a/src/System.Windows.Forms.Design.Editors/src/System/Windows/Forms/Design/ImageIndexEditor.cs b/src/System.Windows.Forms.Design.Editors/src/System/Windows/Forms/Design/ImageIndexEditor.cs new file mode 100644 index 00000000000..14a5d814b5c --- /dev/null +++ b/src/System.Windows.Forms.Design.Editors/src/System/Windows/Forms/Design/ImageIndexEditor.cs @@ -0,0 +1,220 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.Drawing.Design; + +namespace System.Windows.Forms.Design +{ + /// + /// Provides an editor for visually picking an image index. + /// + internal class ImageIndexEditor : UITypeEditor + { + protected ImageList currentImageList; + protected WeakReference currentImageListPropRef; + protected object currentInstance; + protected UITypeEditor imageEditor; + protected string parentImageListProperty = "Parent"; + // disable csharp compiler warning #0414: field assigned unused value + protected string imageListPropertyName = null; + + /// + /// Initializes a new instance of the class. + /// + public ImageIndexEditor() + { + // Get the type editor for images. We use the properties on + // this to determine if we support value painting, etc. + imageEditor = (UITypeEditor)TypeDescriptor.GetEditor(typeof(Image), typeof(UITypeEditor)); + } + + internal UITypeEditor ImageEditor + { + get => imageEditor; + } + + internal string ParentImageListProperty + { + get => parentImageListProperty; + } + + /// + /// Retrieves an image for the current context at current index. + /// + protected virtual Image GetImage(ITypeDescriptorContext context, int index, string key, bool useIntIndex) + { + Image image = null; + object instance = context.Instance; + + // we would not know what to do in this case anyway (i.e. multiple selection of objects) + if (instance is object[]) + { + return null; + } + + // If the instances are different, then we need to re-aquire our image list. + if ((index >= 0) || (key != null)) + { + PropertyDescriptor currentImageListProp = null; + if (currentImageListPropRef != null) + { + currentImageListProp = currentImageListPropRef.Target as PropertyDescriptor; + } + if (currentImageList == null || + instance != currentInstance || + (currentImageListProp != null && (ImageList)currentImageListProp.GetValue(currentInstance) != currentImageList)) + { + + currentInstance = instance; + // first look for an attribute + PropertyDescriptor imageListProp = GetImageListProperty(context.PropertyDescriptor, ref instance); + + // not found as an attribute, do the old behavior + while (instance != null && imageListProp == null) + { + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(instance); + + foreach (PropertyDescriptor prop in props) + { + if (typeof(ImageList).IsAssignableFrom(prop.PropertyType)) + { + imageListProp = prop; + break; + } + } + + if (imageListProp == null) + { + + // We didn't find the image list in this component. See if the + // component has a "parent" property. If so, walk the tree... + PropertyDescriptor parentProp = props[ParentImageListProperty]; + if (parentProp != null) + { + instance = parentProp.GetValue(instance); + } + else + { + // Stick a fork in us, we're done. + instance = null; + } + } + } + + if (imageListProp != null) + { + currentImageList = (ImageList)imageListProp.GetValue(instance); + currentImageListPropRef = new WeakReference(imageListProp); + currentInstance = instance; + } + } + + if (currentImageList != null) + { + if (useIntIndex) + { + if (currentImageList != null && index < currentImageList.Images.Count) + { + index = (index > 0) ? index : 0; + image = currentImageList.Images[index]; + } + } + else + { + image = currentImageList.Images[key]; + } + } + else + { + // no image list, no image + image = null; + } + } + + return image; + } + + /// + /// Gets a value indicating whether this editor supports the painting of a representation of an object's value. + /// + public override bool GetPaintValueSupported(ITypeDescriptorContext context) + => imageEditor != null ? imageEditor.GetPaintValueSupported(context) : false; + + /// + /// Paints a representative value of the given object to the provided canvas. Painting should be done within the boundaries of the provided rectangle. + /// + public override void PaintValue(PaintValueEventArgs e) + { + if (ImageEditor != null) + { + Image image = null; + + if (e.Value is int) + { + image = GetImage(e.Context, (int)e.Value, null, true); + } + else if (e.Value is string) + { + image = GetImage(e.Context, -1, (string)e.Value, false); + } + + if (image != null) + { + ImageEditor.PaintValue(new PaintValueEventArgs(e.Context, image, e.Graphics, e.Bounds)); + } + } + } + + internal static PropertyDescriptor GetImageListProperty(PropertyDescriptor currentComponent, ref object instance) + { + //multiple selection is not supported by this class + if (instance is object[]) + { + return null; + } + + PropertyDescriptor imageListProp = null; + object parentInstance = instance; + + RelatedImageListAttribute relILAttr = currentComponent.Attributes[typeof(RelatedImageListAttribute)] as RelatedImageListAttribute; + if (relILAttr != null) + { + var pathInfo = relILAttr.RelatedImageList.Split('.'); + for (int i = 0; i < pathInfo.Length; i++) + { + if (parentInstance == null) + { + Debug.Fail("A property specified in the path is null or not yet instanciated at this time"); + break; // path is wrong + } + var prop = TypeDescriptor.GetProperties(parentInstance)[pathInfo[i]]; + if (prop == null) + { + Debug.Fail("The path specified to the property is wrong"); + break; // path is wrong + } + if (i == pathInfo.Length - 1) + { + // we're on the last one, look if that's our guy + if (typeof(ImageList).IsAssignableFrom(prop.PropertyType)) + { + instance = parentInstance; + imageListProp = prop; + break; + } + } + else + { + parentInstance = prop.GetValue(parentInstance); + } + } + } + + return imageListProp; + } + } +} diff --git a/src/System.Windows.Forms.Design.Editors/tests/UnitTests/EnsureEditorsTests.cs b/src/System.Windows.Forms.Design.Editors/tests/UnitTests/EnsureEditorsTests.cs index 5cab02ac2d9..303468fd33d 100644 --- a/src/System.Windows.Forms.Design.Editors/tests/UnitTests/EnsureEditorsTests.cs +++ b/src/System.Windows.Forms.Design.Editors/tests/UnitTests/EnsureEditorsTests.cs @@ -51,12 +51,12 @@ public void EnsureUITypeEditorForType(Type type, Type expectedEditorType) [Theory] //[InlineData(typeof(BindingSource), "DataMember", typeof(DataMemberListEditor))] - //[InlineData(typeof(ButtonBase), "ImageIndex", typeof(ImageIndexEditor))] - //[InlineData(typeof(ButtonBase), "ImageKey", typeof(ImageIndexEditor))] + [InlineData(typeof(ButtonBase), "ImageIndex", typeof(ImageIndexEditor))] + [InlineData(typeof(ButtonBase), "ImageKey", typeof(ImageIndexEditor))] [InlineData(typeof(ButtonBase), "Text", typeof(MultilineStringEditor))] //[InlineData(typeof(CheckedListBox), "Items", typeof(ListControlStringCollectionEditor))] - //[InlineData(typeof(ColumnHeader), "ImageIndex", typeof(ImageIndexEditor))] - //[InlineData(typeof(ColumnHeader), "ImageKey", typeof(ImageIndexEditor))] + [InlineData(typeof(ColumnHeader), "ImageIndex", typeof(ImageIndexEditor))] + [InlineData(typeof(ColumnHeader), "ImageKey", typeof(ImageIndexEditor))] //[InlineData(typeof(ComboBox), "AutoCompleteCustomSource", typeof(ListControlStringCollectionEditor))] //[InlineData(typeof(ComboBox), "Items", typeof(ListControlStringCollectionEditor))] //[InlineData(typeof(DataGrid), "DataMember", typeof(DataMemberListEditor))] @@ -74,8 +74,8 @@ public void EnsureUITypeEditorForType(Type type, Type expectedEditorType) //[InlineData(typeof(ErrorProvider), "DataMember", typeof(DataMemberListEditor))] //[InlineData(typeof(FolderBrowserDialog), "SelectedPath", typeof(SelectedPathEditor))] //[InlineData(typeof(HelpProvider), "HelpNamespace", typeof(HelpNamespaceEditor))] - //[InlineData(typeof(Label), "ImageIndex", typeof(ImageIndexEditor))] - //[InlineData(typeof(Label), "ImageKey", typeof(ImageIndexEditor))] + [InlineData(typeof(Label), "ImageIndex", typeof(ImageIndexEditor))] + [InlineData(typeof(Label), "ImageKey", typeof(ImageIndexEditor))] [InlineData(typeof(Label), "Text", typeof(MultilineStringEditor))] //[InlineData(typeof(LinkLabel), "LinkArea", typeof(LinkAreaEditor))] //[InlineData(typeof(ListBox), "Items", typeof(ListControlStringCollectionEditor))] @@ -85,22 +85,22 @@ public void EnsureUITypeEditorForType(Type type, Type expectedEditorType) //[InlineData(typeof(ListView), "Columns", typeof(ColumnHeaderCollectionEditor))] //[InlineData(typeof(ListView), "Groups", typeof(ListViewGroupCollectionEditor))] //[InlineData(typeof(ListView), "Items", typeof(ListViewItemCollectionEditor))] - //[InlineData(typeof(ListViewItem), "ImageIndex", typeof(ImageIndexEditor))] - //[InlineData(typeof(ListViewItem), "ImageKey", typeof(ImageIndexEditor))] - //[InlineData(typeof(ListViewItem), "StateImageIndex", typeof(ImageIndexEditor))] + [InlineData(typeof(ListViewItem), "ImageIndex", typeof(ImageIndexEditor))] + [InlineData(typeof(ListViewItem), "ImageKey", typeof(ImageIndexEditor))] + [InlineData(typeof(ListViewItem), "StateImageIndex", typeof(ImageIndexEditor))] //[InlineData(typeof(ListViewItem), "SubItems", typeof(ListViewSubItemCollectionEditor))] //[InlineData(typeof(MaskedTextBox), "Mask", typeof(MaskPropertyEditor))] //[InlineData(typeof(MaskedTextBox), "Text", typeof(MaskedTextBoxTextEditor))] [InlineData(typeof(NotifyIcon), "BalloonTipText", typeof(MultilineStringEditor))] [InlineData(typeof(NotifyIcon), "Text", typeof(MultilineStringEditor))] //[InlineData(typeof(TabControl), "TabPages", typeof(TabPageCollectionEditor))] - //[InlineData(typeof(TabPage), "ImageIndex", typeof(ImageIndexEditor))] - //[InlineData(typeof(TabPage), "ImageKey", typeof(ImageIndexEditor))] + [InlineData(typeof(TabPage), "ImageIndex", typeof(ImageIndexEditor))] + [InlineData(typeof(TabPage), "ImageKey", typeof(ImageIndexEditor))] //[InlineData(typeof(TextBox), "AutoCompleteCustomSource", typeof(ListControlStringCollectionEditor))] [InlineData(typeof(TextBoxBase), "Lines", typeof(StringArrayEditor))] [InlineData(typeof(TextBoxBase), "Text", typeof(MultilineStringEditor))] - //[InlineData(typeof(ToolBarButton), "ImageIndex", typeof(ImageIndexEditor))] - //[InlineData(typeof(ToolBarButton), "ImageKey", typeof(ImageIndexEditor))] + [InlineData(typeof(ToolBarButton), "ImageIndex", typeof(ImageIndexEditor))] + [InlineData(typeof(ToolBarButton), "ImageKey", typeof(ImageIndexEditor))] //[InlineData(typeof(ToolStripComboBox), "AutoCompleteCustomSource", typeof(ListControlStringCollectionEditor))] //[InlineData(typeof(ToolStripComboBox), "Items", typeof(ListControlStringCollectionEditor))] //[InlineData(typeof(ToolStripItem), "ImageIndex", typeof(ToolStripImageIndexEditor))] @@ -108,16 +108,16 @@ public void EnsureUITypeEditorForType(Type type, Type expectedEditorType) [InlineData(typeof(ToolStripItem), "ToolTipText", typeof(MultilineStringEditor))] //[InlineData(typeof(ToolStripTextBox), "AutoCompleteCustomSource", typeof(ListControlStringCollectionEditor))] [InlineData(typeof(ToolStripTextBox), "Lines", typeof(StringArrayEditor))] - //[InlineData(typeof(TreeNode), "ImageIndex", typeof(ImageIndexEditor))] - //[InlineData(typeof(TreeNode), "ImageKey", typeof(ImageIndexEditor))] - //[InlineData(typeof(TreeNode), "ImageIndexEditor", typeof(ImageIndexEditor))] - //[InlineData(typeof(TreeNode), "SelectedImageKey", typeof(ImageIndexEditor))] - //[InlineData(typeof(TreeNode), "StateImageKey", typeof(ImageIndexEditor))] - //[InlineData(typeof(TreeNode), "StateImageIndex", typeof(ImageIndexEditor))] - //[InlineData(typeof(TreeView), "ImageIndex", typeof(ImageIndexEditor))] - //[InlineData(typeof(TreeView), "ImageKey", typeof(ImageIndexEditor))] - //[InlineData(typeof(TreeView), "SelectedImageIndex", typeof(ImageIndexEditor))] - //[InlineData(typeof(TreeView), "SelectedImageKey", typeof(ImageIndexEditor))] + [InlineData(typeof(TreeNode), "ImageIndex", typeof(ImageIndexEditor))] + [InlineData(typeof(TreeNode), "ImageKey", typeof(ImageIndexEditor))] + [InlineData(typeof(TreeNode), "ImageIndexEditor", typeof(ImageIndexEditor))] + [InlineData(typeof(TreeNode), "SelectedImageKey", typeof(ImageIndexEditor))] + [InlineData(typeof(TreeNode), "StateImageKey", typeof(ImageIndexEditor))] + [InlineData(typeof(TreeNode), "StateImageIndex", typeof(ImageIndexEditor))] + [InlineData(typeof(TreeView), "ImageIndex", typeof(ImageIndexEditor))] + [InlineData(typeof(TreeView), "ImageKey", typeof(ImageIndexEditor))] + [InlineData(typeof(TreeView), "SelectedImageIndex", typeof(ImageIndexEditor))] + [InlineData(typeof(TreeView), "SelectedImageKey", typeof(ImageIndexEditor))] public void EnsureUITypeEditorForProperty(Type type, string propertyName, Type expectedEditorType) { var properties = TypeDescriptor.GetProperties(type);