2 // System.Windows.Forms.ImageList.cs
5 // Peter Bartok <pbartok@novell.com>
6 // Kornél Pál <http://www.kornelpal.hu/>
8 // Copyright (C) 2004-2005 Novell, Inc.
9 // Copyright (C) 2005 Kornél Pál
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
36 // Differences between MS.NET ImageList and this implementation:
38 // This is a fully managed image list implementation.
40 // Images are stored as Format32bppArgb internally but ColorDepth is applied
41 // to the colors of images. Images[index] returns a Format32bppArgb copy of
42 // the image so this difference is only internal.
44 // MS.NET has no alpha channel support (except for icons in 32-bit mode with
45 // comctl32.dll version 6.0) but this implementation has full alpha channel
46 // support in 32-bit mode.
48 // Handle should be an HIMAGELIST returned by ImageList_Create. This
49 // implementation uses (IntPtr)(-1) that is a non-zero but invalid handle.
51 // MS.NET destroys handles using the garbage collector this implementation
52 // does the same with Image objects stored in an ArrayList.
54 // MS.NET 1.x shares the same HIMAGELIST between ImageLists that were
55 // initialized from the same ImageListStreamer and doesn't update ImageSize
56 // and ColorDepth that are treated as bugs and MS.NET 2.0 behavior is
59 // MS.NET 2.0 does not clear keys when handle is destroyed that is treated as
63 using System.Collections;
64 using System.Collections.Specialized;
65 using System.ComponentModel;
66 using System.ComponentModel.Design.Serialization;
68 using System.Drawing.Design;
69 using System.Drawing.Imaging;
70 using System.Globalization;
71 using System.Runtime.InteropServices;
73 namespace System.Windows.Forms
75 [DefaultProperty("Images")]
76 [Designer("System.Windows.Forms.Design.ImageListDesigner, " + Consts.AssemblySystem_Design)]
77 [DesignerSerializer("System.Windows.Forms.Design.ImageListCodeDomSerializer, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + Consts.AssemblySystem_Design)]
78 [ToolboxItemFilter("System.Windows.Forms")]
79 [TypeConverter(typeof(ImageListConverter))]
80 public sealed class ImageList : System.ComponentModel.Component
82 #region Private Fields
83 private const ColorDepth DefaultColorDepth = ColorDepth.Depth8Bit;
84 private static readonly Size DefaultImageSize = new Size(16, 16);
85 private static readonly Color DefaultTransparentColor = Color.Transparent;
87 private readonly ImageCollection images;
88 #endregion // Private Fields
91 [Editor("System.Windows.Forms.Design.ImageCollectionEditor, " + Consts.AssemblySystem_Design, typeof(UITypeEditor))]
92 public sealed class ImageCollection : IList, ICollection, IEnumerable
94 private const int AlphaMask = unchecked((int)0xFF000000);
96 private static class IndexedColorDepths
98 internal static readonly ColorPalette Palette4Bit;
99 internal static readonly ColorPalette Palette8Bit;
100 private static readonly int[] squares;
102 static IndexedColorDepths()
107 bitmap = new Bitmap(1, 1, PixelFormat.Format4bppIndexed);
108 Palette4Bit = bitmap.Palette;
111 bitmap = new Bitmap(1, 1, PixelFormat.Format8bppIndexed);
112 Palette8Bit = bitmap.Palette;
115 squares = new int[511];
116 for (index = 0; index < 256; index++)
117 squares[255 + index] = squares[255 - index] = index * index;
120 internal static int GetNearestColor(Color[] palette, int color)
131 count = palette.Length;
132 for (index = 0; index < count; index++)
133 if (palette[index].ToArgb() == color)
136 red = unchecked((int)(unchecked((uint)color) >> 16) & 0xFF);
137 green = unchecked((int)(unchecked((uint)color) >> 8) & 0xFF);
139 nearestColor = AlphaMask;
140 minDistance = int.MaxValue;
142 for (index = 0; index < count; index++)
143 if ((distance = squares[255 + palette[index].R - red] + squares[255 + palette[index].G - green] + squares[255 + palette[index].B - blue]) < minDistance) {
144 nearestColor = palette[index].ToArgb();
145 minDistance = distance;
153 private enum ItemFlags
156 UseTransparentColor = 1,
160 private sealed class ImageListItem
162 internal readonly object Image;
163 internal readonly ItemFlags Flags;
164 internal readonly Color TransparentColor;
165 internal readonly int ImageCount = 1;
167 internal ImageListItem(Icon value)
170 throw new ArgumentNullException("value");
173 this.Image = (Icon)value.Clone();
176 internal ImageListItem(Image value)
179 throw new ArgumentNullException("value");
181 if (!(value is Bitmap))
182 throw new ArgumentException("Image must be a Bitmap.");
184 // Images are not cloned.
188 internal ImageListItem(Image value, Color transparentColor) : this(value)
190 this.Flags = ItemFlags.UseTransparentColor;
191 this.TransparentColor = transparentColor;
194 internal ImageListItem(Image value, int imageCount) : this(value)
196 this.Flags = ItemFlags.ImageStrip;
197 this.ImageCount = imageCount;
201 #region ImageCollection Private Fields
202 private ColorDepth colorDepth = DefaultColorDepth;
203 private Size imageSize = DefaultImageSize;
204 private Color transparentColor = DefaultTransparentColor;
205 private ArrayList list = new ArrayList();
206 private ArrayList keys = new ArrayList();
208 private bool handleCreated;
209 private int lastKeyIndex = -1;
210 private readonly ImageList owner;
211 #endregion // ImageCollection Private Fields
213 #region ImageCollection Internal Constructors
214 // For use in ImageList
215 internal ImageCollection(ImageList owner)
219 #endregion // ImageCollection Internal Constructor
221 #region ImageCollection Internal Instance Properties
222 // For use in ImageList
223 internal ColorDepth ColorDepth {
225 return this.colorDepth;
229 if (!Enum.IsDefined(typeof(ColorDepth), value))
230 throw new InvalidEnumArgumentException("value", (int)value, typeof(ColorDepth));
232 if (this.colorDepth != value) {
233 this.colorDepth = value;
239 // For use in ImageList
240 internal IntPtr Handle {
247 // For use in ImageList
248 internal bool HandleCreated {
250 return this.handleCreated;
254 // For use in ImageList
255 internal Size ImageSize {
257 return this.imageSize;
261 if (value.Width < 1 || value.Width > 256 || value.Height < 1 || value.Height > 256)
262 throw new ArgumentException("ImageSize.Width and Height must be between 1 and 256", "value");
264 if (this.imageSize != value) {
265 this.imageSize = value;
271 // For use in ImageList
272 internal ImageListStreamer ImageStream {
274 return this.Empty ? null : new ImageListStreamer(this);
279 Image[] streamImages;
282 if (this.handleCreated)
287 // Only deserialized ImageListStreamers are used.
288 else if ((streamImages = value.Images) != null) {
289 this.list = new ArrayList(streamImages.Length);
291 this.handleCreated = true;
292 this.keys = new ArrayList(streamImages.Length);
294 for (index = 0; index < streamImages.Length; index++) {
295 list.Add((Image)streamImages[index].Clone());
299 // Invalid ColorDepth values are ignored.
300 if (Enum.IsDefined(typeof(ColorDepth), value.ColorDepth))
301 this.colorDepth = (ColorDepth)value.ColorDepth;
302 this.imageSize = value.ImageSize;
304 // Event is raised even when handle was not created yet.
305 owner.OnRecreateHandle();
310 // For use in ImageList
311 internal Color TransparentColor {
313 return this.transparentColor;
317 this.transparentColor = value;
320 #endregion // ImageCollection Internal Instance Properties
322 #region ImageCollection Public Instance Properties
326 return this.handleCreated ? list.Count : this.count;
332 return this.Count == 0;
336 public bool IsReadOnly {
343 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
344 public Image this[int index] {
346 return (Image)GetImage(index).Clone();
352 if (index < 0 || index >= this.Count)
353 throw new ArgumentOutOfRangeException("index");
356 throw new ArgumentNullException("value");
358 if (!(value is Bitmap))
359 throw new ArgumentException("Image must be a Bitmap.");
361 image = CreateImage(value, this.transparentColor);
367 public Image this[string key] {
371 return (index = IndexOfKey(key)) == -1 ? null : this[index];
375 public StringCollection Keys {
379 StringCollection keyCollection;
381 // Returns all keys even when there are more keys than
382 // images. Null keys are returned as empty strings.
384 keyCollection = new StringCollection();
385 for (index = 0; index < keys.Count; index++)
386 keyCollection.Add(((key = (string)keys[index]) == null || key.Length == 0) ? string.Empty : key);
388 return keyCollection;
391 #endregion // ImageCollection Public Instance Properties
393 #region ImageCollection Private Static Methods
394 private static bool CompareKeys(string key1, string key2)
396 // Keys are case-insensitive and keys with different length
397 // are not equal even when string.Compare treats them equal.
399 if (key1 == null || key2 == null || key1.Length != key2.Length)
402 return string.Compare(key1, key2, true, CultureInfo.InvariantCulture) == 0;
404 #endregion // ImageCollection Private Static Methods
406 #region ImageCollection Private Instance Methods
407 private int AddItem(string key, ImageListItem item)
412 if (this.handleCreated)
413 itemIndex = AddItemInternal(item);
415 // Image strips are counted as a single item in the return
416 // value of Add and AddStrip until handle is created.
418 itemIndex = list.Add(item);
419 this.count += item.ImageCount;
422 if ((item.Flags & ItemFlags.ImageStrip) == 0)
425 for (index = 0; index < item.ImageCount; index++)
431 internal event EventHandler Changed;
433 private int AddItemInternal(ImageListItem item)
436 Changed (this, EventArgs.Empty);
438 if (item.Image is Icon) {
444 bitmap = new Bitmap(imageWidth = this.imageSize.Width, imageHeight = this.imageSize.Height, PixelFormat.Format32bppArgb);
445 graphics = Graphics.FromImage(bitmap);
446 graphics.DrawIcon((Icon)item.Image, new Rectangle(0, 0, imageWidth, imageHeight));
449 ReduceColorDepth(bitmap);
450 return list.Add(bitmap);
452 else if ((item.Flags & ItemFlags.ImageStrip) == 0)
453 return list.Add(CreateImage((Image)item.Image, (item.Flags & ItemFlags.UseTransparentColor) == 0 ? this.transparentColor : item.TransparentColor));
464 ImageAttributes imageAttributes;
466 // When ImageSize was changed after adding image strips
467 // Count will return invalid values based on old ImageSize
468 // but when creating handle either ArgumentException will
469 // be thrown or image strip will be added according to the
470 // new ImageSize. This can result in image count
471 // difference that can result in exceptions in methods
472 // that use Count before creating handle. In addition this
473 // can result in the loss of sync with keys. When doing
474 // the same after handle was created there are no problems
475 // as handle will be recreated after changing ImageSize
476 // that results in the loss of images added previously.
478 if ((width = (image = (Image)item.Image).Width) == 0 || (width % (imageWidth = this.imageSize.Width)) != 0)
479 throw new ArgumentException("Width of image strip must be a positive multiple of ImageSize.Width.", "value");
481 if (image.Height != (imageHeight = this.imageSize.Height))
482 throw new ArgumentException("Height of image strip must be equal to ImageSize.Height.", "value");
484 imageRect = new Rectangle(0, 0, imageWidth, imageHeight);
485 if (this.transparentColor.A == 0)
486 imageAttributes = null;
488 imageAttributes = new ImageAttributes();
489 imageAttributes.SetColorKey(this.transparentColor, this.transparentColor);
493 for (imageX = 0; imageX < width; imageX += imageWidth) {
494 bitmap = new Bitmap(imageWidth, imageHeight, PixelFormat.Format32bppArgb);
495 graphics = Graphics.FromImage(bitmap);
496 graphics.DrawImage(image, imageRect, imageX, 0, imageWidth, imageHeight, GraphicsUnit.Pixel, imageAttributes);
499 ReduceColorDepth(bitmap);
503 if (imageAttributes != null)
504 imageAttributes.Dispose();
510 private void CreateHandle()
515 if (!this.handleCreated) {
517 this.list = new ArrayList(this.count);
519 this.handleCreated = true;
521 for (index = 0; index < items.Count; index++)
522 AddItemInternal((ImageListItem)items[index]);
526 private Image CreateImage(Image value, Color transparentColor)
530 ImageAttributes imageAttributes;
532 if (transparentColor.A == 0)
533 imageAttributes = null;
535 imageAttributes = new ImageAttributes();
536 imageAttributes.SetColorKey (transparentColor, transparentColor);
539 var bitmap = new Bitmap (imageWidth = this.imageSize.Width, imageHeight = this.imageSize.Height, PixelFormat.Format32bppArgb);
540 using (var graphics = Graphics.FromImage (bitmap))
541 graphics.DrawImage (value, new Rectangle(0, 0, imageWidth, imageHeight), 0, 0, value.Width, value.Height, GraphicsUnit.Pixel, imageAttributes);
543 if (imageAttributes != null)
544 imageAttributes.Dispose ();
546 ReduceColorDepth (bitmap);
550 private void RecreateHandle()
552 if (this.handleCreated) {
554 this.handleCreated = true;
555 owner.OnRecreateHandle();
559 private unsafe void ReduceColorDepth(Bitmap bitmap)
569 BitmapData bitmapData;
572 if (this.colorDepth < ColorDepth.Depth32Bit) {
573 bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
575 linePtr = (byte*)bitmapData.Scan0;
576 height = bitmapData.Height;
577 widthBytes = bitmapData.Width << 2;
578 stride = bitmapData.Stride;
580 if (this.colorDepth < ColorDepth.Depth16Bit) {
581 palette = (this.colorDepth < ColorDepth.Depth8Bit ? IndexedColorDepths.Palette4Bit : IndexedColorDepths.Palette8Bit).Entries;
583 for (line = 0; line < height; line++) {
584 lineEndPtr = linePtr + widthBytes;
585 for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
586 *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : IndexedColorDepths.GetNearestColor(palette, pixel | AlphaMask);
590 else if (this.colorDepth < ColorDepth.Depth24Bit) {
591 for (line = 0; line < height; line++) {
592 lineEndPtr = linePtr + widthBytes;
593 for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
594 *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : (pixel & 0x00F8F8F8) | AlphaMask;
599 for (line = 0; line < height; line++) {
600 lineEndPtr = linePtr + widthBytes;
601 for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
602 *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : pixel | AlphaMask;
608 bitmap.UnlockBits(bitmapData);
612 #endregion // ImageCollection Private Instance Methods
614 #region ImageCollection Internal Instance Methods
615 // For use in ImageList
616 internal void DestroyHandle()
618 if (this.handleCreated) {
619 this.list = new ArrayList();
621 this.handleCreated = false;
622 keys = new ArrayList();
626 // For use in ImageList
627 internal Image GetImage(int index)
629 if (index < 0 || index >= this.Count)
630 throw new ArgumentOutOfRangeException("index");
633 return (Image)list[index];
636 // For use in ImageListStreamer
637 internal Image[] ToArray()
641 // Handle is created even when the list is empty.
643 images = new Image[list.Count];
647 #endregion // ImageCollection Internal Instance Methods
649 #region ImageCollection Public Instance Methods
650 public void Add(Icon value)
655 public void Add(Image value)
660 public int Add(Image value, Color transparentColor)
662 return AddItem(null, new ImageListItem(value, transparentColor));
665 public void Add(string key, Icon icon)
667 // Argument has name icon but exceptions use name value.
668 AddItem(key, new ImageListItem(icon));
671 public void Add(string key, Image image)
673 // Argument has name image but exceptions use name value.
674 AddItem(key, new ImageListItem(image));
677 public void AddRange(Image[] images)
682 throw new ArgumentNullException("images");
684 for (index = 0; index < images.Length; index++)
688 public int AddStrip(Image value)
694 throw new ArgumentNullException("value");
696 if ((width = value.Width) == 0 || (width % (imageWidth = this.imageSize.Width)) != 0)
697 throw new ArgumentException("Width of image strip must be a positive multiple of ImageSize.Width.", "value");
699 if (value.Height != this.imageSize.Height)
700 throw new ArgumentException("Height of image strip must be equal to ImageSize.Height.", "value");
702 return AddItem(null, new ImageListItem(value, width / imageWidth));
708 if (this.handleCreated)
713 [EditorBrowsable(EditorBrowsableState.Never)]
714 public bool Contains(Image image)
716 throw new NotSupportedException();
719 public bool ContainsKey(string key)
721 return IndexOfKey(key) != -1;
724 public IEnumerator GetEnumerator()
726 Image[] images = new Image[this.Count];
729 if (images.Length != 0) {
730 // Handle is created only when there are images.
733 for (index = 0; index < images.Length; index++)
734 images[index] = (Image)((Image)list[index]).Clone();
737 return images.GetEnumerator();
740 [EditorBrowsable(EditorBrowsableState.Never)]
741 public int IndexOf(Image image)
743 throw new NotSupportedException();
746 public int IndexOfKey(string key)
750 if (key != null && key.Length != 0) {
751 // When last IndexOfKey was successful and the same key was
752 // assigned to an image with a lower index than the last
753 // result and the key of the last result equals to key
754 // argument the last result is returned.
756 if (this.lastKeyIndex >= 0 && this.lastKeyIndex < this.Count && CompareKeys((string)keys[this.lastKeyIndex], key))
757 return this.lastKeyIndex;
759 // Duplicate keys are allowed and first match is returned.
760 for (index = 0; index < this.Count; index++)
761 if (CompareKeys((string)keys[index], key))
762 return this.lastKeyIndex = index;
765 return this.lastKeyIndex = -1;
768 [EditorBrowsable(EditorBrowsableState.Never)]
769 public void Remove(Image image)
771 throw new NotSupportedException();
774 public void RemoveAt(int index)
776 if (index < 0 || index >= this.Count)
777 throw new ArgumentOutOfRangeException("index");
780 list.RemoveAt(index);
781 keys.RemoveAt(index);
783 Changed (this, EventArgs.Empty);
786 public void RemoveByKey(string key)
790 if ((index = IndexOfKey(key)) != -1)
794 public void SetKeyName(int index, string name)
796 // Only SetKeyName throws IndexOutOfRangeException.
797 if (index < 0 || index >= this.Count)
798 throw new IndexOutOfRangeException();
802 #endregion // ImageCollection Public Instance Methods
804 #region ImageCollection Interface Properties
805 object IList.this[int index] {
811 if (!(value is Image))
812 throw new ArgumentException("value");
814 this[index] = (Image)value;
818 bool IList.IsFixedSize {
824 bool ICollection.IsSynchronized {
830 object ICollection.SyncRoot {
835 #endregion // ImageCollection Interface Properties
837 #region ImageCollection Interface Methods
838 int IList.Add(object value)
842 if (!(value is Image))
843 throw new ArgumentException("value");
846 this.Add((Image)value);
850 bool IList.Contains(object image)
852 return image is Image ? this.Contains ((Image) image) : false;
855 int IList.IndexOf (object image)
857 return image is Image ? this.IndexOf ((Image) image) : -1;
860 void IList.Insert(int index, object value)
862 throw new NotSupportedException();
865 void IList.Remove (object image)
868 this.Remove ((Image) image);
871 void ICollection.CopyTo(Array dest, int index)
873 for (int imageIndex = 0; imageIndex < this.Count; imageIndex++)
874 dest.SetValue (this[imageIndex], index++);
876 #endregion // ImageCollection Interface Methods
878 #endregion // Sub-classes
880 #region Public Constructors
883 images = new ImageCollection(this);
886 public ImageList(System.ComponentModel.IContainer container) : this()
890 #endregion // Public Constructors
892 #region Private Instance Methods
893 private void OnRecreateHandle()
895 EventHandler eh = (EventHandler)(Events [RecreateHandleEvent]);
897 eh (this, EventArgs.Empty);
900 // MS's TypeDescriptor stuff apparently uses
901 // non-public ShouldSerialize* methods, because it
902 // picks up this behavior even though the methods
903 // aren't public. we can't make them private, though,
904 // without adding compiler warnings. so, make then
907 internal bool ShouldSerializeTransparentColor ()
909 return this.TransparentColor != Color.LightGray;
912 internal bool ShouldSerializeColorDepth()
914 // ColorDepth is serialized in ImageStream when non-empty.
915 // It is serialized even if it has its default value when empty.
919 internal bool ShouldSerializeImageSize()
921 // ImageSize is serialized in ImageStream when non-empty.
922 // It is serialized even if it has its default value when empty.
926 internal void ResetColorDepth ()
928 this.ColorDepth = DefaultColorDepth;
931 internal void ResetImageSize ()
933 this.ImageSize = DefaultImageSize;
936 internal void ResetTransparentColor ()
938 this.TransparentColor = Color.LightGray;
940 #endregion // Private Instance Methods
942 #region Public Instance Properties
943 public ColorDepth ColorDepth {
945 return images.ColorDepth;
949 images.ColorDepth = value;
954 [EditorBrowsable(EditorBrowsableState.Advanced)]
955 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
956 public IntPtr Handle {
958 return images.Handle;
963 [EditorBrowsable(EditorBrowsableState.Advanced)]
964 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
965 public bool HandleCreated {
967 return images.HandleCreated;
972 [MergableProperty(false)]
973 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
974 public ImageCollection Images {
981 public Size ImageSize {
983 return images.ImageSize;
987 images.ImageSize = value;
993 [EditorBrowsable(EditorBrowsableState.Advanced)]
994 public ImageListStreamer ImageStream {
996 return images.ImageStream;
1000 images.ImageStream = value;
1005 [DefaultValue(null)]
1006 [Localizable(false)]
1007 [TypeConverter(typeof(StringConverter))]
1018 public Color TransparentColor {
1020 return images.TransparentColor;
1024 images.TransparentColor = value;
1027 #endregion // Public Instance Properties
1029 #region Public Instance Methods
1030 public void Draw(Graphics g, Point pt, int index)
1032 this.Draw(g, pt.X, pt.Y, index);
1035 public void Draw(Graphics g, int x, int y, int index)
1037 g.DrawImage(images.GetImage(index), x, y);
1040 public void Draw(Graphics g, int x, int y, int width, int height, int index)
1042 g.DrawImage(images.GetImage(index), x, y, width, height);
1045 public override string ToString()
1047 return base.ToString() + " Images.Count: " + images.Count.ToString() + ", ImageSize: " + this.ImageSize.ToString();
1049 #endregion // Public Instance Methods
1051 #region Protected Instance Methods
1052 protected override void Dispose(bool disposing)
1055 images.DestroyHandle();
1057 base.Dispose(disposing);
1059 #endregion // Protected Instance Methods
1062 static object RecreateHandleEvent = new object ();
1065 [EditorBrowsable(EditorBrowsableState.Advanced)]
1066 public event EventHandler RecreateHandle {
1067 add { Events.AddHandler (RecreateHandleEvent, value); }
1068 remove { Events.RemoveHandler (RecreateHandleEvent, value); }
1070 #endregion // Events