* BindingContext.cs: Changed argument names to fix corcompare errors.
[mono.git] / mcs / class / Managed.Windows.Forms / System.Windows.Forms / ImageList.cs
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
10 //
11
12 //
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:
20 //
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
23 //
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.
31 //
32
33 // COMPLETE
34
35 //
36 // Differences between MS.NET ImageList and this implementation:
37 //
38 // This is a fully managed image list implementation.
39 //
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.
43 //
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.
47 //
48 // Handle should be an HIMAGELIST returned by ImageList_Create. This
49 // implementation uses (IntPtr)(-1) that is a non-zero but invalid handle.
50 //
51 // MS.NET destroys handles using the garbage collector this implementation
52 // does the same with Image objects stored in an ArrayList.
53 //
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.
58 //
59 // MS.NET 2.0 does not clear keys when handle is destroyed that is treated as
60 // a bug.
61 //
62
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;
72
73 namespace System.Windows.Forms
74 {
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
83         {
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;
88
89 #if NET_2_0
90                 private object tag;
91 #endif
92                 private readonly ImageCollection images;
93                 #endregion // Private Fields
94
95                 #region Sub-classes
96                 [Editor("System.Windows.Forms.Design.ImageCollectionEditor, " + Consts.AssemblySystem_Design, typeof(UITypeEditor))]
97                 public sealed class ImageCollection : IList, ICollection, IEnumerable
98                 {
99                         private const int AlphaMask = unchecked((int)0xFF000000);
100
101                         private
102 #if NET_2_0
103                         static
104 #else
105                         sealed
106 #endif
107                         class IndexedColorDepths
108                         {
109 #if !NET_2_0
110                                 private IndexedColorDepths()
111                                 {
112                                 }
113 #endif
114                                 internal static readonly ColorPalette Palette4Bit;
115                                 internal static readonly ColorPalette Palette8Bit;
116                                 private static readonly int[] squares;
117
118                                 static IndexedColorDepths()
119                                 {
120                                         Bitmap bitmap;
121                                         int index;
122
123                                         bitmap = new Bitmap(1, 1, PixelFormat.Format4bppIndexed);
124                                         Palette4Bit = bitmap.Palette;
125                                         bitmap.Dispose();
126
127                                         bitmap = new Bitmap(1, 1, PixelFormat.Format8bppIndexed);
128                                         Palette8Bit = bitmap.Palette;
129                                         bitmap.Dispose();
130
131                                         squares = new int[511];
132                                         for (index = 0; index < 256; index++)
133                                                 squares[255 + index] = squares[255 - index] = index * index;
134                                 }
135
136                                 internal static int GetNearestColor(Color[] palette, int color)
137                                 {
138                                         int index;
139                                         int count;
140                                         int red;
141                                         int green;
142                                         int blue;
143                                         int nearestColor;
144                                         int minDistance;
145                                         int distance;
146
147                                         count = palette.Length;
148                                         for (index = 0; index < count; index++)
149                                                 if (palette[index].ToArgb() == color)
150                                                         return color;
151
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;
157
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;
162                                                 }
163
164                                         return nearestColor;
165                                 }
166                         }
167
168                         [Flags()]
169                         private enum ItemFlags
170                         {
171                                 None = 0,
172                                 UseTransparentColor = 1,
173                                 ImageStrip = 2
174                         }
175
176                         private sealed class ImageListItem
177                         {
178                                 internal readonly object Image;
179                                 internal readonly ItemFlags Flags;
180                                 internal readonly Color TransparentColor;
181                                 internal readonly int ImageCount = 1;
182
183                                 internal ImageListItem(Icon value)
184                                 {
185                                         if (value == null)
186                                                 throw new ArgumentNullException("value");
187
188                                         // Icons are cloned.
189                                         this.Image = (Icon)value.Clone();
190                                 }
191
192                                 internal ImageListItem(Image value)
193                                 {
194                                         if (value == null)
195                                                 throw new ArgumentNullException("value");
196
197                                         if (!(value is Bitmap))
198                                                 throw new ArgumentException("Image must be a Bitmap.");
199
200                                         // Images are not cloned.
201                                         this.Image = value;
202                                 }
203
204                                 internal ImageListItem(Image value, Color transparentColor) : this(value)
205                                 {
206                                         this.Flags = ItemFlags.UseTransparentColor;
207                                         this.TransparentColor = transparentColor;
208                                 }
209
210                                 internal ImageListItem(Image value, int imageCount) : this(value)
211                                 {
212                                         this.Flags = ItemFlags.ImageStrip;
213                                         this.ImageCount = imageCount;
214                                 }
215                         }
216
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
232
233                         #region ImageCollection Internal Constructors
234                         // For use in ImageList
235                         internal ImageCollection(ImageList owner)
236                         {
237                                 this.owner = owner;
238                         }
239                         #endregion // ImageCollection Internal Constructor
240
241                         #region ImageCollection Internal Instance Properties
242                         // For use in ImageList
243                         internal ColorDepth ColorDepth {
244                                 get {
245                                         return this.colorDepth;
246                                 }
247
248                                 set {
249                                         if (!Enum.IsDefined(typeof(ColorDepth), value))
250                                                 throw new InvalidEnumArgumentException("value", (int)value, typeof(ColorDepth));
251
252                                         if (this.colorDepth != value) {
253                                                 this.colorDepth = value;
254                                                 RecreateHandle();
255                                         }
256                                 }
257                         }
258
259                         // For use in ImageList
260                         internal IntPtr Handle {
261                                 get {
262                                         CreateHandle();
263                                         return (IntPtr)(-1);
264                                 }
265                         }
266
267                         // For use in ImageList
268                         internal bool HandleCreated {
269                                 get {
270                                         return this.handleCreated;
271                                 }
272                         }
273
274                         // For use in ImageList
275                         internal Size ImageSize {
276                                 get {
277                                         return this.imageSize;
278                                 }
279
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");
283
284                                         if (this.imageSize != value) {
285                                                 this.imageSize = value;
286                                                 RecreateHandle();
287                                         }
288                                 }
289                         }
290
291                         // For use in ImageList
292                         internal ImageListStreamer ImageStream {
293                                 get {
294                                         return this.Empty ? null : new ImageListStreamer(this);
295                                 }
296
297                                 set {
298                                         int index;
299                                         Image[] streamImages;
300
301                                         if (value == null) {
302 #if NET_2_0
303                                                 if (this.handleCreated)
304                                                         DestroyHandle();
305                                                 else
306                                                         this.Clear();
307 #endif
308                                         }
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
317
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
323                                                 }
324
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;
329
330 #if NET_2_0
331                                                 // Event is raised even when handle was not created yet.
332                                                 owner.OnRecreateHandle();
333 #endif
334                                         }
335                                 }
336                         }
337
338                         // For use in ImageList
339                         internal Color TransparentColor {
340                                 get {
341                                         return this.transparentColor;
342                                 }
343
344                                 set {
345                                         this.transparentColor = value;
346                                 }
347                         }
348                         #endregion // ImageCollection Internal Instance Properties
349
350                         #region ImageCollection Public Instance Properties
351                         [Browsable(false)]
352                         public int Count {
353                                 get {
354                                         return this.handleCreated ? list.Count : this.count;
355                                 }
356                         }
357
358                         public bool Empty {
359                                 get {
360                                         return this.Count == 0;
361                                 }
362                         }
363
364                         public bool IsReadOnly {
365                                 get {
366                                         return false;
367                                 }
368                         }
369
370                         [Browsable(false)]
371                         [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
372                         public Image this[int index] {
373                                 get {
374                                         return (Image)GetImage(index).Clone();
375                                 }
376
377                                 set {
378                                         Image image;
379
380                                         if (index < 0 || index >= this.Count)
381                                                 throw new ArgumentOutOfRangeException("index");
382
383                                         if (value == null)
384                                                 throw new ArgumentNullException("value");
385
386                                         if (!(value is Bitmap))
387                                                 throw new ArgumentException("Image must be a Bitmap.");
388
389                                         image = CreateImage(value, this.transparentColor);
390                                         CreateHandle();
391                                         list[index] = image;
392                                 }
393                         }
394
395 #if NET_2_0
396                         public Image this[string key] {
397                                 get {
398                                         int index;
399
400                                         return (index = IndexOfKey(key)) == -1 ? null : this[index];
401                                 }
402                         }
403
404                         public StringCollection Keys {
405                                 get {
406                                         int index;
407                                         string key;
408                                         StringCollection keyCollection;
409
410                                         // Returns all keys even when there are more keys than
411                                         // images. Null keys are returned as empty strings.
412
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);
416
417                                         return keyCollection;
418                                 }
419                         }
420 #endif
421                         #endregion // ImageCollection Public Instance Properties
422
423                         #region ImageCollection Private Static Methods
424 #if NET_2_0
425                         private static bool CompareKeys(string key1, string key2)
426                         {
427                                 // Keys are case-insensitive and keys with different length
428                                 // are not equal even when string.Compare treats them equal.
429
430                                 if (key1 == null || key2 == null || key1.Length != key2.Length)
431                                         return false;
432
433                                 return string.Compare(key1, key2, true, CultureInfo.InvariantCulture) == 0;
434                         }
435 #endif
436                         #endregion // ImageCollection Private Static Methods
437
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
444                         {
445                                 int itemIndex;
446 #if NET_2_0
447                                 int index;
448 #endif
449
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.
455
456                                         itemIndex = list.Add(item);
457                                         this.count += item.ImageCount;
458                                 }
459
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
467
468                                 return itemIndex;
469                         }
470
471                         internal event EventHandler Changed;
472
473                         private int AddItemInternal(ImageListItem item)
474                         {
475                                 if (Changed != null)
476                                         Changed (this, EventArgs.Empty);
477
478                                 if (item.Image is Icon) {
479                                         int imageWidth;
480                                         int imageHeight;
481                                         Bitmap bitmap;
482                                         Graphics graphics;
483
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();
488
489                                         ReduceColorDepth(bitmap);
490                                         return list.Add(bitmap);
491                                 }
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;
505
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.
517
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");
520
521                                         if (image.Height != (imageHeight = this.imageSize.Height))
522                                                 throw new ArgumentException("Height of image strip must be equal to ImageSize.Height.", "value");
523
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);
530                                         }
531
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();
538
539                                                 ReduceColorDepth(bitmap);
540                                                 list.Add(bitmap);
541                                         }
542
543                                         if (imageAttributes != null)
544                                                 imageAttributes.Dispose();
545
546                                         return index;
547                                 }
548                         }
549
550                         private void CreateHandle()
551                         {
552                                 int index;
553                                 ArrayList items;
554
555                                 if (!this.handleCreated) {
556                                         items = this.list;
557                                         this.list = new ArrayList(this.count);
558                                         this.count = 0;
559                                         this.handleCreated = true;
560
561                                         for (index = 0; index < items.Count; index++)
562                                                 AddItemInternal((ImageListItem)items[index]);
563                                 }
564                         }
565
566                         private Image CreateImage(Image value, Color transparentColor)
567                         {
568                                 int imageWidth;
569                                 int imageHeight;
570                                 Bitmap bitmap;
571                                 Graphics graphics;
572                                 ImageAttributes imageAttributes;
573
574                                 if (transparentColor.A == 0)
575                                         imageAttributes = null;
576                                 else {
577                                         imageAttributes = new ImageAttributes();
578                                         imageAttributes.SetColorKey(transparentColor, transparentColor);
579                                 }
580
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();
585
586                                 if (imageAttributes != null)
587                                         imageAttributes.Dispose();
588
589                                 ReduceColorDepth(bitmap);
590                                 return bitmap;
591                         }
592
593                         private void RecreateHandle()
594                         {
595                                 if (this.handleCreated) {
596                                         DestroyHandle();
597                                         this.handleCreated = true;
598                                         owner.OnRecreateHandle();
599                                 }
600                         }
601
602                         private unsafe void ReduceColorDepth(Bitmap bitmap)
603                         {
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;
614
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;
622
623                                                 if (this.colorDepth < ColorDepth.Depth16Bit) {
624                                                         palette = (this.colorDepth < ColorDepth.Depth8Bit ? IndexedColorDepths.Palette4Bit : IndexedColorDepths.Palette8Bit).Entries;
625
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;
631                                                         }
632                                                 }
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;
639                                                         }
640                                                 }
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;
647                                                         }
648                                                 }
649                                         }
650                                         finally {
651                                                 bitmap.UnlockBits(bitmapData);
652                                         }
653                                 }
654                         }
655                         #endregion // ImageCollection Private Instance Methods
656
657                         #region ImageCollection Internal Instance Methods
658                         // For use in ImageList
659                         internal void DestroyHandle()
660                         {
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
668                                 }
669                         }
670
671                         // For use in ImageList
672                         internal Image GetImage(int index)
673                         {
674                                 if (index < 0 || index >= this.Count)
675                                         throw new ArgumentOutOfRangeException("index");
676
677                                 CreateHandle();
678                                 return (Image)list[index];
679                         }
680
681                         // For use in ImageListStreamer
682                         internal Image[] ToArray()
683                         {
684                                 Image[] images;
685
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;
691                         }
692                         #endregion // ImageCollection Internal Instance Methods
693
694                         #region ImageCollection Public Instance Methods
695                         public void Add(Icon value)
696                         {
697 #if NET_2_0
698                                 Add(null, value);
699 #else
700                                 AddItem(new ImageListItem(value));
701 #endif
702                         }
703
704                         public void Add(Image value)
705                         {
706 #if NET_2_0
707                                 Add(null, value);
708 #else
709                                 AddItem(new ImageListItem(value));
710 #endif
711                         }
712
713                         public int Add(Image value, Color transparentColor)
714                         {
715 #if NET_2_0
716                                 return AddItem(null, new ImageListItem(value, transparentColor));
717 #else
718                                 return AddItem(new ImageListItem(value, transparentColor));
719 #endif
720                         }
721
722 #if NET_2_0
723                         public void Add(string key, Icon icon)
724                         {
725                                 // Argument has name icon but exceptions use name value.
726                                 AddItem(key, new ImageListItem(icon));
727                         }
728
729                         public void Add(string key, Image image)
730                         {
731                                 // Argument has name image but exceptions use name value.
732                                 AddItem(key, new ImageListItem(image));
733                         }
734
735                         public void AddRange(Image[] images)
736                         {
737                                 int index;
738
739                                 if (images == null)
740                                         throw new ArgumentNullException("images");
741
742                                 for (index = 0; index < images.Length; index++)
743                                         Add(images[index]);
744                         }
745 #endif
746
747                         public int AddStrip(Image value)
748                         {
749                                 int width;
750                                 int imageWidth;
751
752                                 if (value == null)
753                                         throw new ArgumentNullException("value");
754
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");
757
758                                 if (value.Height != this.imageSize.Height)
759                                         throw new ArgumentException("Height of image strip must be equal to ImageSize.Height.", "value");
760
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
766                         }
767
768                         public void Clear()
769                         {
770                                 list.Clear();
771                                 if (this.handleCreated)
772                                         this.count = 0;
773 #if NET_2_0
774                                 keys.Clear();
775 #endif
776                         }
777
778 #if NET_2_0
779                         [EditorBrowsable(EditorBrowsableState.Never)]
780 #endif
781                         public bool Contains(Image image)
782                         {
783                                 throw new NotSupportedException();
784                         }
785
786 #if NET_2_0
787                         public bool ContainsKey(string key)
788                         {
789                                 return IndexOfKey(key) != -1;
790                         }
791 #endif
792
793                         public IEnumerator GetEnumerator()
794                         {
795                                 Image[] images = new Image[this.Count];
796                                 int index;
797
798                                 if (images.Length != 0) {
799                                         // Handle is created only when there are images.
800                                         CreateHandle();
801
802                                         for (index = 0; index < images.Length; index++)
803                                                 images[index] = (Image)((Image)list[index]).Clone();
804                                 }
805
806                                 return images.GetEnumerator();
807                         }
808
809 #if NET_2_0
810                         [EditorBrowsable(EditorBrowsableState.Never)]
811 #endif
812                         public int IndexOf(Image image)
813                         {
814                                 throw new NotSupportedException();
815                         }
816
817 #if NET_2_0
818                         public int IndexOfKey(string key)
819                         {
820                                 int index;
821
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.
827
828                                         if (this.lastKeyIndex >= 0 && this.lastKeyIndex < this.Count && CompareKeys((string)keys[this.lastKeyIndex], key))
829                                                 return this.lastKeyIndex;
830
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;
835                                 }
836
837                                 return this.lastKeyIndex = -1;
838                         }
839 #endif
840
841 #if NET_2_0
842                         [EditorBrowsable(EditorBrowsableState.Never)]
843 #endif
844                         public void Remove(Image image)
845                         {
846                                 throw new NotSupportedException();
847                         }
848
849                         public void RemoveAt(int index)
850                         {
851                                 if (index < 0 || index >= this.Count)
852                                         throw new ArgumentOutOfRangeException("index");
853
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);
861                         }
862
863 #if NET_2_0
864                         public void RemoveByKey(string key)
865                         {
866                                 int index;
867
868                                 if ((index = IndexOfKey(key)) != -1)
869                                         RemoveAt(index);
870                         }
871
872                         public void SetKeyName(int index, string name)
873                         {
874                                 // Only SetKeyName throws IndexOutOfRangeException.
875                                 if (index < 0 || index >= this.Count)
876                                         throw new IndexOutOfRangeException();
877
878                                 keys[index] = name;
879                         }
880 #endif
881                         #endregion // ImageCollection Public Instance Methods
882
883                         #region ImageCollection Interface Properties
884                         object IList.this[int index] {
885                                 get {
886                                         return this[index];
887                                 }
888
889                                 set {
890                                         if (!(value is Image))
891                                                 throw new ArgumentException("value");
892
893                                         this[index] = (Image)value;
894                                 }
895                         }
896
897                         bool IList.IsFixedSize {
898                                 get {
899                                         return false;
900                                 }
901                         }
902
903                         bool ICollection.IsSynchronized {
904                                 get {
905                                         return false;
906                                 }
907                         }
908
909                         object ICollection.SyncRoot {
910                                 get {
911                                         return this;
912                                 }
913                         }
914                         #endregion // ImageCollection Interface Properties
915
916                         #region ImageCollection Interface Methods
917                         int IList.Add(object value)
918                         {
919                                 int index;
920
921                                 if (!(value is Image))
922                                         throw new ArgumentException("value");
923
924                                 index = this.Count;
925                                 this.Add((Image)value);
926                                 return index;
927                         }
928
929                         bool IList.Contains(object image)
930                         {
931                                 return image is Image ? this.Contains ((Image) image) : false;
932                         }
933
934                         int IList.IndexOf (object image)
935                         {
936                                 return image is Image ? this.IndexOf ((Image) image) : -1;
937                         }
938
939                         void IList.Insert(int index, object value)
940                         {
941                                 throw new NotSupportedException();
942                         }
943
944                         void IList.Remove (object image)
945                         {
946                                 if (image is Image)
947                                         this.Remove ((Image) image);
948                         }
949
950                         void ICollection.CopyTo(Array dest, int index)
951                         {
952                                 for (int imageIndex = 0; imageIndex < this.Count; imageIndex++)
953                                         dest.SetValue (this [index], index++);
954                         }
955                         #endregion // ImageCollection Interface Methods
956                 }
957                 #endregion // Sub-classes
958
959                 #region Public Constructors
960                 public ImageList()
961                 {
962                         images = new ImageCollection(this);
963                 }
964
965                 public ImageList(System.ComponentModel.IContainer container) : this()
966                 {
967                         container.Add(this);
968                 }
969                 #endregion // Public Constructors
970
971                 #region Private Instance Methods
972                 private void OnRecreateHandle()
973                 {
974                         EventHandler eh = (EventHandler)(Events [RecreateHandleEvent]);
975                         if (eh != null)
976                                 eh (this, EventArgs.Empty);
977                 }
978
979                 // MS's TypeDescriptor stuff apparently uses
980                 // non-public ShouldSerialize* methods, because it
981                 // picks up this behavior even though the methods
982                 // aren't public.  we can't make them private, though,
983                 // without adding compiler warnings.  so, make then
984                 // internal instead.
985
986                 internal bool ShouldSerializeTransparentColor ()
987                 {
988                         return this.TransparentColor != Color.LightGray;
989                 }
990
991                 internal bool ShouldSerializeColorDepth()
992                 {
993                         // ColorDepth is serialized in ImageStream when non-empty.
994                         // It is serialized even if it has its default value when empty.
995                         return images.Empty;
996                 }               
997
998                 internal bool ShouldSerializeImageSize()
999                 {
1000                         // ImageSize is serialized in ImageStream when non-empty.
1001                         // It is serialized even if it has its default value when empty.
1002 #if NET_2_0
1003                         return images.Empty;
1004 #else
1005                         return this.ImageSize != DefaultImageSize;
1006 #endif
1007                 }
1008
1009
1010                 internal void ResetColorDepth ()
1011                 {
1012                         this.ColorDepth = DefaultColorDepth;
1013                 }
1014
1015 #if NET_2_0
1016                 internal void ResetImageSize ()
1017                 {
1018                         this.ImageSize = DefaultImageSize;
1019                 }
1020
1021                 internal void ResetTransparentColor ()
1022                 {
1023                         this.TransparentColor = Color.LightGray;
1024                 }
1025 #endif
1026                 #endregion // Private Instance Methods
1027
1028                 #region Public Instance Properties
1029 #if !NET_2_0
1030                 [DefaultValue(DefaultColorDepth)]
1031 #endif
1032                 public ColorDepth ColorDepth {
1033                         get {
1034                                 return images.ColorDepth;
1035                         }
1036
1037                         set {
1038                                 images.ColorDepth = value;
1039                         }
1040                 }
1041
1042                 [Browsable(false)]
1043                 [EditorBrowsable(EditorBrowsableState.Advanced)]
1044                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
1045                 public IntPtr Handle {
1046                         get {
1047                                 return images.Handle;
1048                         }
1049                 }
1050
1051                 [Browsable(false)]
1052                 [EditorBrowsable(EditorBrowsableState.Advanced)]
1053                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
1054                 public bool HandleCreated {
1055                         get {
1056                                 return images.HandleCreated;
1057                         }
1058                 }
1059
1060                 [DefaultValue(null)]
1061                 [MergableProperty(false)]
1062                 [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
1063                 public ImageCollection Images {
1064                         get {
1065                                 return this.images;
1066                         }
1067                 }
1068
1069                 [Localizable(true)]
1070                 public Size ImageSize {
1071                         get {
1072                                 return images.ImageSize;
1073                         }
1074
1075                         set {
1076                                 images.ImageSize = value;
1077                         }
1078                 }
1079
1080                 [Browsable(false)]
1081                 [DefaultValue(null)]
1082                 [EditorBrowsable(EditorBrowsableState.Advanced)]
1083                 public ImageListStreamer ImageStream {
1084                         get {
1085                                 return images.ImageStream;
1086                         }
1087
1088                         set {
1089                                 images.ImageStream = value;
1090                         }
1091                 }
1092
1093 #if NET_2_0
1094                 [Bindable(true)]
1095                 [DefaultValue(null)]
1096                 [Localizable(false)]
1097                 [TypeConverter(typeof(StringConverter))]
1098                 public object Tag {
1099                         get {
1100                                 return this.tag;
1101                         }
1102
1103                         set {
1104                                 this.tag = value;
1105                         }
1106                 }
1107 #endif
1108
1109                 public Color TransparentColor {
1110                         get {
1111                                 return images.TransparentColor;
1112                         }
1113
1114                         set {
1115                                 images.TransparentColor = value;
1116                         }
1117                 }
1118                 #endregion // Public Instance Properties
1119
1120                 #region Public Instance Methods
1121                 public void Draw(Graphics g, Point pt, int index)
1122                 {
1123                         this.Draw(g, pt.X, pt.Y, index);
1124                 }
1125
1126                 public void Draw(Graphics g, int x, int y, int index)
1127                 {
1128                         g.DrawImage(images.GetImage(index), x, y);
1129                 }
1130
1131                 public void Draw(Graphics g, int x, int y, int width, int height, int index)
1132                 {
1133                         g.DrawImage(images.GetImage(index), x, y, width, height);
1134                 }
1135
1136                 public override string ToString()
1137                 {
1138                         return base.ToString() + " Images.Count: " + images.Count.ToString() + ", ImageSize: " + this.ImageSize.ToString();
1139                 }
1140                 #endregion // Public Instance Methods
1141
1142                 #region Protected Instance Methods
1143                 protected override void Dispose(bool disposing)
1144                 {
1145                         if (disposing)
1146                                 images.DestroyHandle();
1147
1148                         base.Dispose(disposing);
1149                 }
1150                 #endregion // Protected Instance Methods
1151
1152                 #region Events
1153                 static object RecreateHandleEvent = new object ();
1154
1155                 [Browsable(false)]
1156                 [EditorBrowsable(EditorBrowsableState.Advanced)]
1157                 public event EventHandler RecreateHandle {
1158                         add { Events.AddHandler (RecreateHandleEvent, value); }
1159                         remove { Events.RemoveHandler (RecreateHandleEvent, value); }
1160                 }
1161                 #endregion // Events
1162         }
1163 }