2007-05-03 Chris Toshok <toshok@ximian.com>
[mono-project.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ImageList.cs
blob1c47c3a6e395a8d6c54d136a0e58f174210bf5c3
1 //
2 // System.Windows.Forms.ImageList.cs
3 //
4 // Authors:
5 // Peter Bartok <pbartok@novell.com>
6 // Kornél Pál <http://www.kornelpal.hu/>
7 //
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.
33 // COMPLETE
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
57 // implemented.
59 // MS.NET 2.0 does not clear keys when handle is destroyed that is treated as
60 // a bug.
63 using System.Collections;
64 using System.Collections.Specialized;
65 using System.ComponentModel;
66 using System.ComponentModel.Design.Serialization;
67 using System.Drawing;
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 #if NET_2_0
78 [DesignerSerializer("System.Windows.Forms.Design.ImageListCodeDomSerializer, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + Consts.AssemblySystem_Design)]
79 #endif
80 [ToolboxItemFilter("System.Windows.Forms")]
81 [TypeConverter(typeof(ImageListConverter))]
82 public sealed class ImageList : System.ComponentModel.Component
84 #region Private Fields
85 private const ColorDepth DefaultColorDepth = ColorDepth.Depth8Bit;
86 private static readonly Size DefaultImageSize = new Size(16, 16);
87 private static readonly Color DefaultTransparentColor = Color.Transparent;
89 #if NET_2_0
90 private object tag;
91 #endif
92 private readonly ImageCollection images;
93 #endregion // Private Fields
95 #region Sub-classes
96 [Editor("System.Windows.Forms.Design.ImageCollectionEditor, " + Consts.AssemblySystem_Design, typeof(UITypeEditor))]
97 public sealed class ImageCollection : IList, ICollection, IEnumerable
99 private const int AlphaMask = unchecked((int)0xFF000000);
101 private
102 #if NET_2_0
103 static
104 #else
105 sealed
106 #endif
107 class IndexedColorDepths
109 #if !NET_2_0
110 private IndexedColorDepths()
113 #endif
114 internal static readonly ColorPalette Palette4Bit;
115 internal static readonly ColorPalette Palette8Bit;
116 private static readonly int[] squares;
118 static IndexedColorDepths()
120 Bitmap bitmap;
121 int index;
123 bitmap = new Bitmap(1, 1, PixelFormat.Format4bppIndexed);
124 Palette4Bit = bitmap.Palette;
125 bitmap.Dispose();
127 bitmap = new Bitmap(1, 1, PixelFormat.Format8bppIndexed);
128 Palette8Bit = bitmap.Palette;
129 bitmap.Dispose();
131 squares = new int[511];
132 for (index = 0; index < 256; index++)
133 squares[255 + index] = squares[255 - index] = index * index;
136 internal static int GetNearestColor(Color[] palette, int color)
138 int index;
139 int count;
140 int red;
141 int green;
142 int blue;
143 int nearestColor;
144 int minDistance;
145 int distance;
147 count = palette.Length;
148 for (index = 0; index < count; index++)
149 if (palette[index].ToArgb() == color)
150 return color;
152 red = unchecked((int)(unchecked((uint)color) >> 16) & 0xFF);
153 green = unchecked((int)(unchecked((uint)color) >> 8) & 0xFF);
154 blue = color & 0xFF;
155 nearestColor = AlphaMask;
156 minDistance = int.MaxValue;
158 for (index = 0; index < count; index++)
159 if ((distance = squares[255 + palette[index].R - red] + squares[255 + palette[index].G - green] + squares[255 + palette[index].B - blue]) < minDistance) {
160 nearestColor = palette[index].ToArgb();
161 minDistance = distance;
164 return nearestColor;
168 [Flags()]
169 private enum ItemFlags
171 None = 0,
172 UseTransparentColor = 1,
173 ImageStrip = 2
176 private sealed class ImageListItem
178 internal readonly object Image;
179 internal readonly ItemFlags Flags;
180 internal readonly Color TransparentColor;
181 internal readonly int ImageCount = 1;
183 internal ImageListItem(Icon value)
185 if (value == null)
186 throw new ArgumentNullException("value");
188 // Icons are cloned.
189 this.Image = (Icon)value.Clone();
192 internal ImageListItem(Image value)
194 if (value == null)
195 throw new ArgumentNullException("value");
197 if (!(value is Bitmap))
198 throw new ArgumentException("Image must be a Bitmap.");
200 // Images are not cloned.
201 this.Image = value;
204 internal ImageListItem(Image value, Color transparentColor) : this(value)
206 this.Flags = ItemFlags.UseTransparentColor;
207 this.TransparentColor = transparentColor;
210 internal ImageListItem(Image value, int imageCount) : this(value)
212 this.Flags = ItemFlags.ImageStrip;
213 this.ImageCount = imageCount;
217 #region ImageCollection Private Fields
218 private ColorDepth colorDepth = DefaultColorDepth;
219 private Size imageSize = DefaultImageSize;
220 private Color transparentColor = DefaultTransparentColor;
221 private ArrayList list = new ArrayList();
222 #if NET_2_0
223 private ArrayList keys = new ArrayList();
224 #endif
225 private int count;
226 private bool handleCreated;
227 #if NET_2_0
228 private int lastKeyIndex = -1;
229 #endif
230 private readonly ImageList owner;
231 #endregion // ImageCollection Private Fields
233 #region ImageCollection Internal Constructors
234 // For use in ImageList
235 internal ImageCollection(ImageList owner)
237 this.owner = owner;
239 #endregion // ImageCollection Internal Constructor
241 #region ImageCollection Internal Instance Properties
242 // For use in ImageList
243 internal ColorDepth ColorDepth {
244 get {
245 return this.colorDepth;
248 set {
249 if (!Enum.IsDefined(typeof(ColorDepth), value))
250 throw new InvalidEnumArgumentException("value", (int)value, typeof(ColorDepth));
252 if (this.colorDepth != value) {
253 this.colorDepth = value;
254 RecreateHandle();
259 // For use in ImageList
260 internal IntPtr Handle {
261 get {
262 CreateHandle();
263 return (IntPtr)(-1);
267 // For use in ImageList
268 internal bool HandleCreated {
269 get {
270 return this.handleCreated;
274 // For use in ImageList
275 internal Size ImageSize {
276 get {
277 return this.imageSize;
280 set {
281 if (value.Width < 1 || value.Width > 256 || value.Height < 1 || value.Height > 256)
282 throw new ArgumentException("ImageSize.Width and Height must be between 1 and 256", "value");
284 if (this.imageSize != value) {
285 this.imageSize = value;
286 RecreateHandle();
291 // For use in ImageList
292 internal ImageListStreamer ImageStream {
293 get {
294 return this.Empty ? null : new ImageListStreamer(this);
297 set {
298 int index;
299 Image[] streamImages;
301 if (value == null) {
302 #if NET_2_0
303 if (this.handleCreated)
304 DestroyHandle();
305 else
306 this.Clear();
307 #endif
309 // Only deserialized ImageListStreamers are used.
310 else if ((streamImages = value.Images) != null) {
311 this.list = new ArrayList(streamImages.Length);
312 this.count = 0;
313 this.handleCreated = true;
314 #if NET_2_0
315 this.keys = new ArrayList(streamImages.Length);
316 #endif
318 for (index = 0; index < streamImages.Length; index++) {
319 list.Add((Image)streamImages[index].Clone());
320 #if NET_2_0
321 keys.Add(null);
322 #endif
325 // Invalid ColorDepth values are ignored.
326 if (Enum.IsDefined(typeof(ColorDepth), value.ColorDepth))
327 this.colorDepth = (ColorDepth)value.ColorDepth;
328 this.imageSize = value.ImageSize;
330 #if NET_2_0
331 // Event is raised even when handle was not created yet.
332 owner.OnRecreateHandle();
333 #endif
338 // For use in ImageList
339 internal Color TransparentColor {
340 get {
341 return this.transparentColor;
344 set {
345 this.transparentColor = value;
348 #endregion // ImageCollection Internal Instance Properties
350 #region ImageCollection Public Instance Properties
351 [Browsable(false)]
352 public int Count {
353 get {
354 return this.handleCreated ? list.Count : this.count;
358 public bool Empty {
359 get {
360 return this.Count == 0;
364 public bool IsReadOnly {
365 get {
366 return false;
370 [Browsable(false)]
371 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
372 public Image this[int index] {
373 get {
374 return (Image)GetImage(index).Clone();
377 set {
378 Image image;
380 if (index < 0 || index >= this.Count)
381 throw new ArgumentOutOfRangeException("index");
383 if (value == null)
384 throw new ArgumentNullException("value");
386 if (!(value is Bitmap))
387 throw new ArgumentException("Image must be a Bitmap.");
389 image = CreateImage(value, this.transparentColor);
390 CreateHandle();
391 list[index] = image;
395 #if NET_2_0
396 public Image this[string key] {
397 get {
398 int index;
400 return (index = IndexOfKey(key)) == -1 ? null : this[index];
404 public StringCollection Keys {
405 get {
406 int index;
407 string key;
408 StringCollection keyCollection;
410 // Returns all keys even when there are more keys than
411 // images. Null keys are returned as empty strings.
413 keyCollection = new StringCollection();
414 for (index = 0; index < keys.Count; index++)
415 keyCollection.Add(((key = (string)keys[index]) == null || key.Length == 0) ? string.Empty : key);
417 return keyCollection;
420 #endif
421 #endregion // ImageCollection Public Instance Properties
423 #region ImageCollection Private Static Methods
424 #if NET_2_0
425 private static bool CompareKeys(string key1, string key2)
427 // Keys are case-insensitive and keys with different length
428 // are not equal even when string.Compare treats them equal.
430 if (key1 == null || key2 == null || key1.Length != key2.Length)
431 return false;
433 return string.Compare(key1, key2, true, CultureInfo.InvariantCulture) == 0;
435 #endif
436 #endregion // ImageCollection Private Static Methods
438 #region ImageCollection Private Instance Methods
439 #if NET_2_0
440 private int AddItem(string key, ImageListItem item)
441 #else
442 private int AddItem(ImageListItem item)
443 #endif
445 int itemIndex;
446 #if NET_2_0
447 int index;
448 #endif
450 if (this.handleCreated)
451 itemIndex = AddItemInternal(item);
452 else {
453 // Image strips are counted as a single item in the return
454 // value of Add and AddStrip until handle is created.
456 itemIndex = list.Add(item);
457 this.count += item.ImageCount;
460 #if NET_2_0
461 if ((item.Flags & ItemFlags.ImageStrip) == 0)
462 keys.Add(key);
463 else
464 for (index = 0; index < item.ImageCount; index++)
465 keys.Add(null);
466 #endif
468 return itemIndex;
471 internal event EventHandler Changed;
473 private int AddItemInternal(ImageListItem item)
475 if (Changed != null)
476 Changed (this, EventArgs.Empty);
478 if (item.Image is Icon) {
479 int imageWidth;
480 int imageHeight;
481 Bitmap bitmap;
482 Graphics graphics;
484 bitmap = new Bitmap(imageWidth = this.imageSize.Width, imageHeight = this.imageSize.Height, PixelFormat.Format32bppArgb);
485 graphics = Graphics.FromImage(bitmap);
486 graphics.DrawIcon((Icon)item.Image, new Rectangle(0, 0, imageWidth, imageHeight));
487 graphics.Dispose();
489 ReduceColorDepth(bitmap);
490 return list.Add(bitmap);
492 else if ((item.Flags & ItemFlags.ImageStrip) == 0)
493 return list.Add(CreateImage((Image)item.Image, (item.Flags & ItemFlags.UseTransparentColor) == 0 ? this.transparentColor : item.TransparentColor));
494 else {
495 int imageX;
496 int width;
497 int imageWidth;
498 int imageHeight;
499 int index;
500 Image image;
501 Bitmap bitmap;
502 Graphics graphics;
503 Rectangle imageRect;
504 ImageAttributes imageAttributes;
506 // When ImageSize was changed after adding image strips
507 // Count will return invalid values based on old ImageSize
508 // but when creating handle either ArgumentException will
509 // be thrown or image strip will be added according to the
510 // new ImageSize. This can result in image count
511 // difference that can result in exceptions in methods
512 // that use Count before creating handle. In addition this
513 // can result in the loss of sync with keys. When doing
514 // the same after handle was created there are no problems
515 // as handle will be recreated after changing ImageSize
516 // that results in the loss of images added previously.
518 if ((width = (image = (Image)item.Image).Width) == 0 || (width % (imageWidth = this.imageSize.Width)) != 0)
519 throw new ArgumentException("Width of image strip must be a positive multiple of ImageSize.Width.", "value");
521 if (image.Height != (imageHeight = this.imageSize.Height))
522 throw new ArgumentException("Height of image strip must be equal to ImageSize.Height.", "value");
524 imageRect = new Rectangle(0, 0, imageWidth, imageHeight);
525 if (this.transparentColor.A == 0)
526 imageAttributes = null;
527 else {
528 imageAttributes = new ImageAttributes();
529 imageAttributes.SetColorKey(this.transparentColor, this.transparentColor);
532 index = list.Count;
533 for (imageX = 0; imageX < width; imageX += imageWidth) {
534 bitmap = new Bitmap(imageWidth, imageHeight, PixelFormat.Format32bppArgb);
535 graphics = Graphics.FromImage(bitmap);
536 graphics.DrawImage(image, imageRect, imageX, 0, imageWidth, imageHeight, GraphicsUnit.Pixel, imageAttributes);
537 graphics.Dispose();
539 ReduceColorDepth(bitmap);
540 list.Add(bitmap);
543 if (imageAttributes != null)
544 imageAttributes.Dispose();
546 return index;
550 private void CreateHandle()
552 int index;
553 ArrayList items;
555 if (!this.handleCreated) {
556 items = this.list;
557 this.list = new ArrayList(this.count);
558 this.count = 0;
559 this.handleCreated = true;
561 for (index = 0; index < items.Count; index++)
562 AddItemInternal((ImageListItem)items[index]);
566 private Image CreateImage(Image value, Color transparentColor)
568 int imageWidth;
569 int imageHeight;
570 Bitmap bitmap;
571 Graphics graphics;
572 ImageAttributes imageAttributes;
574 if (transparentColor.A == 0)
575 imageAttributes = null;
576 else {
577 imageAttributes = new ImageAttributes();
578 imageAttributes.SetColorKey(transparentColor, transparentColor);
581 bitmap = new Bitmap(imageWidth = this.imageSize.Width, imageHeight = this.imageSize.Height, PixelFormat.Format32bppArgb);
582 graphics = Graphics.FromImage(bitmap);
583 graphics.DrawImage(value, new Rectangle(0, 0, imageWidth, imageHeight), 0, 0, value.Width, value.Height, GraphicsUnit.Pixel, imageAttributes);
584 graphics.Dispose();
586 if (imageAttributes != null)
587 imageAttributes.Dispose();
589 ReduceColorDepth(bitmap);
590 return bitmap;
593 private void RecreateHandle()
595 if (this.handleCreated) {
596 DestroyHandle();
597 this.handleCreated = true;
598 owner.OnRecreateHandle();
602 private unsafe void ReduceColorDepth(Bitmap bitmap)
604 byte* pixelPtr;
605 byte* lineEndPtr;
606 byte* linePtr;
607 int line;
608 int pixel;
609 int height;
610 int widthBytes;
611 int stride;
612 BitmapData bitmapData;
613 Color[] palette;
615 if (this.colorDepth < ColorDepth.Depth32Bit) {
616 bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
617 try {
618 linePtr = (byte*)bitmapData.Scan0;
619 height = bitmapData.Height;
620 widthBytes = bitmapData.Width << 2;
621 stride = bitmapData.Stride;
623 if (this.colorDepth < ColorDepth.Depth16Bit) {
624 palette = (this.colorDepth < ColorDepth.Depth8Bit ? IndexedColorDepths.Palette4Bit : IndexedColorDepths.Palette8Bit).Entries;
626 for (line = 0; line < height; line++) {
627 lineEndPtr = linePtr + widthBytes;
628 for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
629 *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : IndexedColorDepths.GetNearestColor(palette, pixel | AlphaMask);
630 linePtr += stride;
633 else if (this.colorDepth < ColorDepth.Depth24Bit) {
634 for (line = 0; line < height; line++) {
635 lineEndPtr = linePtr + widthBytes;
636 for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
637 *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : (pixel & 0x00F8F8F8) | AlphaMask;
638 linePtr += stride;
641 else {
642 for (line = 0; line < height; line++) {
643 lineEndPtr = linePtr + widthBytes;
644 for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
645 *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : pixel | AlphaMask;
646 linePtr += stride;
650 finally {
651 bitmap.UnlockBits(bitmapData);
655 #endregion // ImageCollection Private Instance Methods
657 #region ImageCollection Internal Instance Methods
658 // For use in ImageList
659 internal void DestroyHandle()
661 if (this.handleCreated) {
662 this.list = new ArrayList();
663 this.count = 0;
664 this.handleCreated = false;
665 #if NET_2_0
666 keys = new ArrayList();
667 #endif
671 // For use in ImageList
672 internal Image GetImage(int index)
674 if (index < 0 || index >= this.Count)
675 throw new ArgumentOutOfRangeException("index");
677 CreateHandle();
678 return (Image)list[index];
681 // For use in ImageListStreamer
682 internal Image[] ToArray()
684 Image[] images;
686 // Handle is created even when the list is empty.
687 CreateHandle();
688 images = new Image[list.Count];
689 list.CopyTo(images);
690 return images;
692 #endregion // ImageCollection Internal Instance Methods
694 #region ImageCollection Public Instance Methods
695 public void Add(Icon value)
697 #if NET_2_0
698 Add(null, value);
699 #else
700 AddItem(new ImageListItem(value));
701 #endif
704 public void Add(Image value)
706 #if NET_2_0
707 Add(null, value);
708 #else
709 AddItem(new ImageListItem(value));
710 #endif
713 public int Add(Image value, Color transparentColor)
715 #if NET_2_0
716 return AddItem(null, new ImageListItem(value, transparentColor));
717 #else
718 return AddItem(new ImageListItem(value, transparentColor));
719 #endif
722 #if NET_2_0
723 public void Add(string key, Icon icon)
725 // Argument has name icon but exceptions use name value.
726 AddItem(key, new ImageListItem(icon));
729 public void Add(string key, Image image)
731 // Argument has name image but exceptions use name value.
732 AddItem(key, new ImageListItem(image));
735 public void AddRange(Image[] images)
737 int index;
739 if (images == null)
740 throw new ArgumentNullException("images");
742 for (index = 0; index < images.Length; index++)
743 Add(images[index]);
745 #endif
747 public int AddStrip(Image value)
749 int width;
750 int imageWidth;
752 if (value == null)
753 throw new ArgumentNullException("value");
755 if ((width = value.Width) == 0 || (width % (imageWidth = this.imageSize.Width)) != 0)
756 throw new ArgumentException("Width of image strip must be a positive multiple of ImageSize.Width.", "value");
758 if (value.Height != this.imageSize.Height)
759 throw new ArgumentException("Height of image strip must be equal to ImageSize.Height.", "value");
761 #if NET_2_0
762 return AddItem(null, new ImageListItem(value, width / imageWidth));
763 #else
764 return AddItem(new ImageListItem(value, width / imageWidth));
765 #endif
768 public void Clear()
770 list.Clear();
771 if (this.handleCreated)
772 this.count = 0;
773 #if NET_2_0
774 keys.Clear();
775 #endif
778 #if NET_2_0
779 [EditorBrowsable(EditorBrowsableState.Never)]
780 #endif
781 public bool Contains(Image image)
783 throw new NotSupportedException();
786 #if NET_2_0
787 public bool ContainsKey(string key)
789 return IndexOfKey(key) != -1;
791 #endif
793 public IEnumerator GetEnumerator()
795 Image[] images = new Image[this.Count];
796 int index;
798 if (images.Length != 0) {
799 // Handle is created only when there are images.
800 CreateHandle();
802 for (index = 0; index < images.Length; index++)
803 images[index] = (Image)((Image)list[index]).Clone();
806 return images.GetEnumerator();
809 #if NET_2_0
810 [EditorBrowsable(EditorBrowsableState.Never)]
811 #endif
812 public int IndexOf(Image image)
814 throw new NotSupportedException();
817 #if NET_2_0
818 public int IndexOfKey(string key)
820 int index;
822 if (key != null && key.Length != 0) {
823 // When last IndexOfKey was successful and the same key was
824 // assigned to an image with a lower index than the last
825 // result and the key of the last result equals to key
826 // argument the last result is returned.
828 if (this.lastKeyIndex >= 0 && this.lastKeyIndex < this.Count && CompareKeys((string)keys[this.lastKeyIndex], key))
829 return this.lastKeyIndex;
831 // Duplicate keys are allowed and first match is returned.
832 for (index = 0; index < this.Count; index++)
833 if (CompareKeys((string)keys[index], key))
834 return this.lastKeyIndex = index;
837 return this.lastKeyIndex = -1;
839 #endif
841 #if NET_2_0
842 [EditorBrowsable(EditorBrowsableState.Never)]
843 #endif
844 public void Remove(Image image)
846 throw new NotSupportedException();
849 public void RemoveAt(int index)
851 if (index < 0 || index >= this.Count)
852 throw new ArgumentOutOfRangeException("index");
854 CreateHandle();
855 list.RemoveAt(index);
856 #if NET_2_0
857 keys.RemoveAt(index);
858 #endif
859 if (Changed != null)
860 Changed (this, EventArgs.Empty);
863 #if NET_2_0
864 public void RemoveByKey(string key)
866 int index;
868 if ((index = IndexOfKey(key)) != -1)
869 RemoveAt(index);
872 public void SetKeyName(int index, string name)
874 // Only SetKeyName throws IndexOutOfRangeException.
875 if (index < 0 || index >= this.Count)
876 throw new IndexOutOfRangeException();
878 keys[index] = name;
880 #endif
881 #endregion // ImageCollection Public Instance Methods
883 #region ImageCollection Interface Properties
884 object IList.this[int index] {
885 get {
886 return this[index];
889 set {
890 if (!(value is Image))
891 throw new ArgumentException("value");
893 this[index] = (Image)value;
897 bool IList.IsFixedSize {
898 get {
899 return false;
903 bool ICollection.IsSynchronized {
904 get {
905 return false;
909 object ICollection.SyncRoot {
910 get {
911 return this;
914 #endregion // ImageCollection Interface Properties
916 #region ImageCollection Interface Methods
917 int IList.Add(object value)
919 int index;
921 if (!(value is Image))
922 throw new ArgumentException("value");
924 index = this.Count;
925 this.Add((Image)value);
926 return index;
929 bool IList.Contains(object value)
931 return value is Image ? this.Contains((Image)value) : false;
934 int IList.IndexOf(object value)
936 return value is Image ? this.IndexOf((Image)value) : -1;
939 void IList.Insert(int index, object value)
941 throw new NotSupportedException();
944 void IList.Remove(object value)
946 if (value is Image)
947 this.Remove((Image)value);
950 void ICollection.CopyTo(Array array, int index)
952 int imageIndex;
954 for (imageIndex = 0; imageIndex < this.Count; imageIndex++)
955 array.SetValue(this[index], index++);
957 #endregion // ImageCollection Interface Methods
959 #endregion // Sub-classes
961 #region Public Constructors
962 public ImageList()
964 images = new ImageCollection(this);
967 public ImageList(System.ComponentModel.IContainer container) : this()
969 container.Add(this);
971 #endregion // Public Constructors
973 #region Private Instance Methods
974 private void OnRecreateHandle()
976 EventHandler eh = (EventHandler)(Events [RecreateHandleEvent]);
977 if (eh != null)
978 eh (this, EventArgs.Empty);
981 // MS's TypeDescriptor stuff apparently uses
982 // non-public ShouldSerialize* methods, because it
983 // picks up this behavior even though the methods
984 // aren't public. we can't make them private, though,
985 // without adding compiler warnings. so, make then
986 // internal instead.
988 internal bool ShouldSerializeTransparentColor ()
990 return this.TransparentColor != Color.LightGray;
993 internal bool ShouldSerializeColorDepth()
995 // ColorDepth is serialized in ImageStream when non-empty.
996 // It is serialized even if it has its default value when empty.
997 return images.Empty;
1000 internal bool ShouldSerializeImageSize()
1002 // ImageSize is serialized in ImageStream when non-empty.
1003 // It is serialized even if it has its default value when empty.
1004 #if NET_2_0
1005 return images.Empty;
1006 #else
1007 return this.ImageSize != DefaultImageSize;
1008 #endif
1012 internal void ResetColorDepth ()
1014 this.ColorDepth = DefaultColorDepth;
1017 #if NET_2_0
1018 internal void ResetImageSize ()
1020 this.ImageSize = DefaultImageSize;
1023 internal void ResetTransparentColor ()
1025 this.TransparentColor = Color.LightGray;
1027 #endif
1028 #endregion // Private Instance Methods
1030 #region Public Instance Properties
1031 #if !NET_2_0
1032 [DefaultValue(DefaultColorDepth)]
1033 #endif
1034 public ColorDepth ColorDepth {
1035 get {
1036 return images.ColorDepth;
1039 set {
1040 images.ColorDepth = value;
1044 [Browsable(false)]
1045 [EditorBrowsable(EditorBrowsableState.Advanced)]
1046 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
1047 public IntPtr Handle {
1048 get {
1049 return images.Handle;
1053 [Browsable(false)]
1054 [EditorBrowsable(EditorBrowsableState.Advanced)]
1055 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
1056 public bool HandleCreated {
1057 get {
1058 return images.HandleCreated;
1062 [DefaultValue(null)]
1063 [MergableProperty(false)]
1064 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
1065 public ImageCollection Images {
1066 get {
1067 return this.images;
1071 [Localizable(true)]
1072 public Size ImageSize {
1073 get {
1074 return images.ImageSize;
1077 set {
1078 images.ImageSize = value;
1082 [Browsable(false)]
1083 [DefaultValue(null)]
1084 [EditorBrowsable(EditorBrowsableState.Advanced)]
1085 public ImageListStreamer ImageStream {
1086 get {
1087 return images.ImageStream;
1090 set {
1091 images.ImageStream = value;
1095 #if NET_2_0
1096 [Bindable(true)]
1097 [DefaultValue(null)]
1098 [Localizable(false)]
1099 [TypeConverter(typeof(StringConverter))]
1100 public object Tag {
1101 get {
1102 return this.tag;
1105 set {
1106 this.tag = value;
1109 #endif
1111 public Color TransparentColor {
1112 get {
1113 return images.TransparentColor;
1116 set {
1117 images.TransparentColor = value;
1120 #endregion // Public Instance Properties
1122 #region Public Instance Methods
1123 public void Draw(Graphics g, Point pt, int index)
1125 this.Draw(g, pt.X, pt.Y, index);
1128 public void Draw(Graphics g, int x, int y, int index)
1130 g.DrawImage(images.GetImage(index), x, y);
1133 public void Draw(Graphics g, int x, int y, int width, int height, int index)
1135 g.DrawImage(images.GetImage(index), x, y, width, height);
1138 public override string ToString()
1140 return base.ToString() + " Images.Count: " + images.Count.ToString() + ", ImageSize: " + this.ImageSize.ToString();
1142 #endregion // Public Instance Methods
1144 #region Protected Instance Methods
1145 protected override void Dispose(bool disposing)
1147 if (disposing)
1148 images.DestroyHandle();
1150 base.Dispose(disposing);
1152 #endregion // Protected Instance Methods
1154 #region Events
1155 static object RecreateHandleEvent = new object ();
1157 [Browsable(false)]
1158 [EditorBrowsable(EditorBrowsableState.Advanced)]
1159 public event EventHandler RecreateHandle {
1160 add { Events.AddHandler (RecreateHandleEvent, value); }
1161 remove { Events.RemoveHandler (RecreateHandleEvent, value); }
1163 #endregion // Events