PageRenderTime 41ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/class/Managed.Windows.Forms/System.Windows.Forms/ImageList.cs

https://bitbucket.org/danipen/mono
C# | 1072 lines | 787 code | 175 blank | 110 comment | 137 complexity | e49a5889817b2fa55ebbc091cf80a698 MD5 | raw file
Possible License(s): Unlicense, Apache-2.0, LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  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. // Permission is hereby granted, free of charge, to any person obtaining
  13. // a copy of this software and associated documentation files (the
  14. // "Software"), to deal in the Software without restriction, including
  15. // without limitation the rights to use, copy, modify, merge, publish,
  16. // distribute, sublicense, and/or sell copies of the Software, and to
  17. // permit persons to whom the Software is furnished to do so, subject to
  18. // the following conditions:
  19. //
  20. // The above copyright notice and this permission notice shall be
  21. // included in all copies or substantial portions of the Software.
  22. //
  23. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  24. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  25. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  26. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  27. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  28. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  29. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  30. //
  31. // COMPLETE
  32. //
  33. // Differences between MS.NET ImageList and this implementation:
  34. //
  35. // This is a fully managed image list implementation.
  36. //
  37. // Images are stored as Format32bppArgb internally but ColorDepth is applied
  38. // to the colors of images. Images[index] returns a Format32bppArgb copy of
  39. // the image so this difference is only internal.
  40. //
  41. // MS.NET has no alpha channel support (except for icons in 32-bit mode with
  42. // comctl32.dll version 6.0) but this implementation has full alpha channel
  43. // support in 32-bit mode.
  44. //
  45. // Handle should be an HIMAGELIST returned by ImageList_Create. This
  46. // implementation uses (IntPtr)(-1) that is a non-zero but invalid handle.
  47. //
  48. // MS.NET destroys handles using the garbage collector this implementation
  49. // does the same with Image objects stored in an ArrayList.
  50. //
  51. // MS.NET 1.x shares the same HIMAGELIST between ImageLists that were
  52. // initialized from the same ImageListStreamer and doesn't update ImageSize
  53. // and ColorDepth that are treated as bugs and MS.NET 2.0 behavior is
  54. // implemented.
  55. //
  56. // MS.NET 2.0 does not clear keys when handle is destroyed that is treated as
  57. // a bug.
  58. //
  59. using System.Collections;
  60. using System.Collections.Specialized;
  61. using System.ComponentModel;
  62. using System.ComponentModel.Design.Serialization;
  63. using System.Drawing;
  64. using System.Drawing.Design;
  65. using System.Drawing.Imaging;
  66. using System.Globalization;
  67. using System.Runtime.InteropServices;
  68. namespace System.Windows.Forms
  69. {
  70. [DefaultProperty("Images")]
  71. [Designer("System.Windows.Forms.Design.ImageListDesigner, " + Consts.AssemblySystem_Design)]
  72. [DesignerSerializer("System.Windows.Forms.Design.ImageListCodeDomSerializer, " + Consts.AssemblySystem_Design, "System.ComponentModel.Design.Serialization.CodeDomSerializer, " + Consts.AssemblySystem_Design)]
  73. [ToolboxItemFilter("System.Windows.Forms")]
  74. [TypeConverter(typeof(ImageListConverter))]
  75. public sealed class ImageList : System.ComponentModel.Component
  76. {
  77. #region Private Fields
  78. private const ColorDepth DefaultColorDepth = ColorDepth.Depth8Bit;
  79. private static readonly Size DefaultImageSize = new Size(16, 16);
  80. private static readonly Color DefaultTransparentColor = Color.Transparent;
  81. private object tag;
  82. private readonly ImageCollection images;
  83. #endregion // Private Fields
  84. #region Sub-classes
  85. [Editor("System.Windows.Forms.Design.ImageCollectionEditor, " + Consts.AssemblySystem_Design, typeof(UITypeEditor))]
  86. public sealed class ImageCollection : IList, ICollection, IEnumerable
  87. {
  88. private const int AlphaMask = unchecked((int)0xFF000000);
  89. private static class IndexedColorDepths
  90. {
  91. internal static readonly ColorPalette Palette4Bit;
  92. internal static readonly ColorPalette Palette8Bit;
  93. private static readonly int[] squares;
  94. static IndexedColorDepths()
  95. {
  96. Bitmap bitmap;
  97. int index;
  98. bitmap = new Bitmap(1, 1, PixelFormat.Format4bppIndexed);
  99. Palette4Bit = bitmap.Palette;
  100. bitmap.Dispose();
  101. bitmap = new Bitmap(1, 1, PixelFormat.Format8bppIndexed);
  102. Palette8Bit = bitmap.Palette;
  103. bitmap.Dispose();
  104. squares = new int[511];
  105. for (index = 0; index < 256; index++)
  106. squares[255 + index] = squares[255 - index] = index * index;
  107. }
  108. internal static int GetNearestColor(Color[] palette, int color)
  109. {
  110. int index;
  111. int count;
  112. int red;
  113. int green;
  114. int blue;
  115. int nearestColor;
  116. int minDistance;
  117. int distance;
  118. count = palette.Length;
  119. for (index = 0; index < count; index++)
  120. if (palette[index].ToArgb() == color)
  121. return color;
  122. red = unchecked((int)(unchecked((uint)color) >> 16) & 0xFF);
  123. green = unchecked((int)(unchecked((uint)color) >> 8) & 0xFF);
  124. blue = color & 0xFF;
  125. nearestColor = AlphaMask;
  126. minDistance = int.MaxValue;
  127. for (index = 0; index < count; index++)
  128. if ((distance = squares[255 + palette[index].R - red] + squares[255 + palette[index].G - green] + squares[255 + palette[index].B - blue]) < minDistance) {
  129. nearestColor = palette[index].ToArgb();
  130. minDistance = distance;
  131. }
  132. return nearestColor;
  133. }
  134. }
  135. [Flags()]
  136. private enum ItemFlags
  137. {
  138. None = 0,
  139. UseTransparentColor = 1,
  140. ImageStrip = 2
  141. }
  142. private sealed class ImageListItem
  143. {
  144. internal readonly object Image;
  145. internal readonly ItemFlags Flags;
  146. internal readonly Color TransparentColor;
  147. internal readonly int ImageCount = 1;
  148. internal ImageListItem(Icon value)
  149. {
  150. if (value == null)
  151. throw new ArgumentNullException("value");
  152. // Icons are cloned.
  153. this.Image = (Icon)value.Clone();
  154. }
  155. internal ImageListItem(Image value)
  156. {
  157. if (value == null)
  158. throw new ArgumentNullException("value");
  159. if (!(value is Bitmap))
  160. throw new ArgumentException("Image must be a Bitmap.");
  161. // Images are not cloned.
  162. this.Image = value;
  163. }
  164. internal ImageListItem(Image value, Color transparentColor) : this(value)
  165. {
  166. this.Flags = ItemFlags.UseTransparentColor;
  167. this.TransparentColor = transparentColor;
  168. }
  169. internal ImageListItem(Image value, int imageCount) : this(value)
  170. {
  171. this.Flags = ItemFlags.ImageStrip;
  172. this.ImageCount = imageCount;
  173. }
  174. }
  175. #region ImageCollection Private Fields
  176. private ColorDepth colorDepth = DefaultColorDepth;
  177. private Size imageSize = DefaultImageSize;
  178. private Color transparentColor = DefaultTransparentColor;
  179. private ArrayList list = new ArrayList();
  180. private ArrayList keys = new ArrayList();
  181. private int count;
  182. private bool handleCreated;
  183. private int lastKeyIndex = -1;
  184. private readonly ImageList owner;
  185. #endregion // ImageCollection Private Fields
  186. #region ImageCollection Internal Constructors
  187. // For use in ImageList
  188. internal ImageCollection(ImageList owner)
  189. {
  190. this.owner = owner;
  191. }
  192. #endregion // ImageCollection Internal Constructor
  193. #region ImageCollection Internal Instance Properties
  194. // For use in ImageList
  195. internal ColorDepth ColorDepth {
  196. get {
  197. return this.colorDepth;
  198. }
  199. set {
  200. if (!Enum.IsDefined(typeof(ColorDepth), value))
  201. throw new InvalidEnumArgumentException("value", (int)value, typeof(ColorDepth));
  202. if (this.colorDepth != value) {
  203. this.colorDepth = value;
  204. RecreateHandle();
  205. }
  206. }
  207. }
  208. // For use in ImageList
  209. internal IntPtr Handle {
  210. get {
  211. CreateHandle();
  212. return (IntPtr)(-1);
  213. }
  214. }
  215. // For use in ImageList
  216. internal bool HandleCreated {
  217. get {
  218. return this.handleCreated;
  219. }
  220. }
  221. // For use in ImageList
  222. internal Size ImageSize {
  223. get {
  224. return this.imageSize;
  225. }
  226. set {
  227. if (value.Width < 1 || value.Width > 256 || value.Height < 1 || value.Height > 256)
  228. throw new ArgumentException("ImageSize.Width and Height must be between 1 and 256", "value");
  229. if (this.imageSize != value) {
  230. this.imageSize = value;
  231. RecreateHandle();
  232. }
  233. }
  234. }
  235. // For use in ImageList
  236. internal ImageListStreamer ImageStream {
  237. get {
  238. return this.Empty ? null : new ImageListStreamer(this);
  239. }
  240. set {
  241. int index;
  242. Image[] streamImages;
  243. if (value == null) {
  244. if (this.handleCreated)
  245. DestroyHandle();
  246. else
  247. this.Clear();
  248. }
  249. // Only deserialized ImageListStreamers are used.
  250. else if ((streamImages = value.Images) != null) {
  251. this.list = new ArrayList(streamImages.Length);
  252. this.count = 0;
  253. this.handleCreated = true;
  254. this.keys = new ArrayList(streamImages.Length);
  255. for (index = 0; index < streamImages.Length; index++) {
  256. list.Add((Image)streamImages[index].Clone());
  257. keys.Add(null);
  258. }
  259. // Invalid ColorDepth values are ignored.
  260. if (Enum.IsDefined(typeof(ColorDepth), value.ColorDepth))
  261. this.colorDepth = (ColorDepth)value.ColorDepth;
  262. this.imageSize = value.ImageSize;
  263. // Event is raised even when handle was not created yet.
  264. owner.OnRecreateHandle();
  265. }
  266. }
  267. }
  268. // For use in ImageList
  269. internal Color TransparentColor {
  270. get {
  271. return this.transparentColor;
  272. }
  273. set {
  274. this.transparentColor = value;
  275. }
  276. }
  277. #endregion // ImageCollection Internal Instance Properties
  278. #region ImageCollection Public Instance Properties
  279. [Browsable(false)]
  280. public int Count {
  281. get {
  282. return this.handleCreated ? list.Count : this.count;
  283. }
  284. }
  285. public bool Empty {
  286. get {
  287. return this.Count == 0;
  288. }
  289. }
  290. public bool IsReadOnly {
  291. get {
  292. return false;
  293. }
  294. }
  295. [Browsable(false)]
  296. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  297. public Image this[int index] {
  298. get {
  299. return (Image)GetImage(index).Clone();
  300. }
  301. set {
  302. Image image;
  303. if (index < 0 || index >= this.Count)
  304. throw new ArgumentOutOfRangeException("index");
  305. if (value == null)
  306. throw new ArgumentNullException("value");
  307. if (!(value is Bitmap))
  308. throw new ArgumentException("Image must be a Bitmap.");
  309. image = CreateImage(value, this.transparentColor);
  310. CreateHandle();
  311. list[index] = image;
  312. }
  313. }
  314. public Image this[string key] {
  315. get {
  316. int index;
  317. return (index = IndexOfKey(key)) == -1 ? null : this[index];
  318. }
  319. }
  320. public StringCollection Keys {
  321. get {
  322. int index;
  323. string key;
  324. StringCollection keyCollection;
  325. // Returns all keys even when there are more keys than
  326. // images. Null keys are returned as empty strings.
  327. keyCollection = new StringCollection();
  328. for (index = 0; index < keys.Count; index++)
  329. keyCollection.Add(((key = (string)keys[index]) == null || key.Length == 0) ? string.Empty : key);
  330. return keyCollection;
  331. }
  332. }
  333. #endregion // ImageCollection Public Instance Properties
  334. #region ImageCollection Private Static Methods
  335. private static bool CompareKeys(string key1, string key2)
  336. {
  337. // Keys are case-insensitive and keys with different length
  338. // are not equal even when string.Compare treats them equal.
  339. if (key1 == null || key2 == null || key1.Length != key2.Length)
  340. return false;
  341. return string.Compare(key1, key2, true, CultureInfo.InvariantCulture) == 0;
  342. }
  343. #endregion // ImageCollection Private Static Methods
  344. #region ImageCollection Private Instance Methods
  345. private int AddItem(string key, ImageListItem item)
  346. {
  347. int itemIndex;
  348. int index;
  349. if (this.handleCreated)
  350. itemIndex = AddItemInternal(item);
  351. else {
  352. // Image strips are counted as a single item in the return
  353. // value of Add and AddStrip until handle is created.
  354. itemIndex = list.Add(item);
  355. this.count += item.ImageCount;
  356. }
  357. if ((item.Flags & ItemFlags.ImageStrip) == 0)
  358. keys.Add(key);
  359. else
  360. for (index = 0; index < item.ImageCount; index++)
  361. keys.Add(null);
  362. return itemIndex;
  363. }
  364. internal event EventHandler Changed;
  365. private int AddItemInternal(ImageListItem item)
  366. {
  367. if (Changed != null)
  368. Changed (this, EventArgs.Empty);
  369. if (item.Image is Icon) {
  370. int imageWidth;
  371. int imageHeight;
  372. Bitmap bitmap;
  373. Graphics graphics;
  374. bitmap = new Bitmap(imageWidth = this.imageSize.Width, imageHeight = this.imageSize.Height, PixelFormat.Format32bppArgb);
  375. graphics = Graphics.FromImage(bitmap);
  376. graphics.DrawIcon((Icon)item.Image, new Rectangle(0, 0, imageWidth, imageHeight));
  377. graphics.Dispose();
  378. ReduceColorDepth(bitmap);
  379. return list.Add(bitmap);
  380. }
  381. else if ((item.Flags & ItemFlags.ImageStrip) == 0)
  382. return list.Add(CreateImage((Image)item.Image, (item.Flags & ItemFlags.UseTransparentColor) == 0 ? this.transparentColor : item.TransparentColor));
  383. else {
  384. int imageX;
  385. int width;
  386. int imageWidth;
  387. int imageHeight;
  388. int index;
  389. Image image;
  390. Bitmap bitmap;
  391. Graphics graphics;
  392. Rectangle imageRect;
  393. ImageAttributes imageAttributes;
  394. // When ImageSize was changed after adding image strips
  395. // Count will return invalid values based on old ImageSize
  396. // but when creating handle either ArgumentException will
  397. // be thrown or image strip will be added according to the
  398. // new ImageSize. This can result in image count
  399. // difference that can result in exceptions in methods
  400. // that use Count before creating handle. In addition this
  401. // can result in the loss of sync with keys. When doing
  402. // the same after handle was created there are no problems
  403. // as handle will be recreated after changing ImageSize
  404. // that results in the loss of images added previously.
  405. if ((width = (image = (Image)item.Image).Width) == 0 || (width % (imageWidth = this.imageSize.Width)) != 0)
  406. throw new ArgumentException("Width of image strip must be a positive multiple of ImageSize.Width.", "value");
  407. if (image.Height != (imageHeight = this.imageSize.Height))
  408. throw new ArgumentException("Height of image strip must be equal to ImageSize.Height.", "value");
  409. imageRect = new Rectangle(0, 0, imageWidth, imageHeight);
  410. if (this.transparentColor.A == 0)
  411. imageAttributes = null;
  412. else {
  413. imageAttributes = new ImageAttributes();
  414. imageAttributes.SetColorKey(this.transparentColor, this.transparentColor);
  415. }
  416. index = list.Count;
  417. for (imageX = 0; imageX < width; imageX += imageWidth) {
  418. bitmap = new Bitmap(imageWidth, imageHeight, PixelFormat.Format32bppArgb);
  419. graphics = Graphics.FromImage(bitmap);
  420. graphics.DrawImage(image, imageRect, imageX, 0, imageWidth, imageHeight, GraphicsUnit.Pixel, imageAttributes);
  421. graphics.Dispose();
  422. ReduceColorDepth(bitmap);
  423. list.Add(bitmap);
  424. }
  425. if (imageAttributes != null)
  426. imageAttributes.Dispose();
  427. return index;
  428. }
  429. }
  430. private void CreateHandle()
  431. {
  432. int index;
  433. ArrayList items;
  434. if (!this.handleCreated) {
  435. items = this.list;
  436. this.list = new ArrayList(this.count);
  437. this.count = 0;
  438. this.handleCreated = true;
  439. for (index = 0; index < items.Count; index++)
  440. AddItemInternal((ImageListItem)items[index]);
  441. }
  442. }
  443. private Image CreateImage(Image value, Color transparentColor)
  444. {
  445. int imageWidth;
  446. int imageHeight;
  447. ImageAttributes imageAttributes;
  448. if (transparentColor.A == 0)
  449. imageAttributes = null;
  450. else {
  451. imageAttributes = new ImageAttributes();
  452. imageAttributes.SetColorKey (transparentColor, transparentColor);
  453. }
  454. var bitmap = new Bitmap (imageWidth = this.imageSize.Width, imageHeight = this.imageSize.Height, PixelFormat.Format32bppArgb);
  455. using (var graphics = Graphics.FromImage (bitmap))
  456. graphics.DrawImage (value, new Rectangle(0, 0, imageWidth, imageHeight), 0, 0, value.Width, value.Height, GraphicsUnit.Pixel, imageAttributes);
  457. if (imageAttributes != null)
  458. imageAttributes.Dispose ();
  459. ReduceColorDepth (bitmap);
  460. return bitmap;
  461. }
  462. private void RecreateHandle()
  463. {
  464. if (this.handleCreated) {
  465. DestroyHandle();
  466. this.handleCreated = true;
  467. owner.OnRecreateHandle();
  468. }
  469. }
  470. private unsafe void ReduceColorDepth(Bitmap bitmap)
  471. {
  472. byte* pixelPtr;
  473. byte* lineEndPtr;
  474. byte* linePtr;
  475. int line;
  476. int pixel;
  477. int height;
  478. int widthBytes;
  479. int stride;
  480. BitmapData bitmapData;
  481. Color[] palette;
  482. if (this.colorDepth < ColorDepth.Depth32Bit) {
  483. bitmapData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
  484. try {
  485. linePtr = (byte*)bitmapData.Scan0;
  486. height = bitmapData.Height;
  487. widthBytes = bitmapData.Width << 2;
  488. stride = bitmapData.Stride;
  489. if (this.colorDepth < ColorDepth.Depth16Bit) {
  490. palette = (this.colorDepth < ColorDepth.Depth8Bit ? IndexedColorDepths.Palette4Bit : IndexedColorDepths.Palette8Bit).Entries;
  491. for (line = 0; line < height; line++) {
  492. lineEndPtr = linePtr + widthBytes;
  493. for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
  494. *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : IndexedColorDepths.GetNearestColor(palette, pixel | AlphaMask);
  495. linePtr += stride;
  496. }
  497. }
  498. else if (this.colorDepth < ColorDepth.Depth24Bit) {
  499. for (line = 0; line < height; line++) {
  500. lineEndPtr = linePtr + widthBytes;
  501. for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
  502. *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : (pixel & 0x00F8F8F8) | AlphaMask;
  503. linePtr += stride;
  504. }
  505. }
  506. else {
  507. for (line = 0; line < height; line++) {
  508. lineEndPtr = linePtr + widthBytes;
  509. for (pixelPtr = linePtr; pixelPtr < lineEndPtr; pixelPtr += 4)
  510. *(int*)pixelPtr = ((pixel = *(int*)pixelPtr) & AlphaMask) == 0 ? 0x00000000 : pixel | AlphaMask;
  511. linePtr += stride;
  512. }
  513. }
  514. }
  515. finally {
  516. bitmap.UnlockBits(bitmapData);
  517. }
  518. }
  519. }
  520. #endregion // ImageCollection Private Instance Methods
  521. #region ImageCollection Internal Instance Methods
  522. // For use in ImageList
  523. internal void DestroyHandle()
  524. {
  525. if (this.handleCreated) {
  526. this.list = new ArrayList();
  527. this.count = 0;
  528. this.handleCreated = false;
  529. keys = new ArrayList();
  530. }
  531. }
  532. // For use in ImageList
  533. internal Image GetImage(int index)
  534. {
  535. if (index < 0 || index >= this.Count)
  536. throw new ArgumentOutOfRangeException("index");
  537. CreateHandle();
  538. return (Image)list[index];
  539. }
  540. // For use in ImageListStreamer
  541. internal Image[] ToArray()
  542. {
  543. Image[] images;
  544. // Handle is created even when the list is empty.
  545. CreateHandle();
  546. images = new Image[list.Count];
  547. list.CopyTo(images);
  548. return images;
  549. }
  550. #endregion // ImageCollection Internal Instance Methods
  551. #region ImageCollection Public Instance Methods
  552. public void Add(Icon value)
  553. {
  554. Add(null, value);
  555. }
  556. public void Add(Image value)
  557. {
  558. Add(null, value);
  559. }
  560. public int Add(Image value, Color transparentColor)
  561. {
  562. return AddItem(null, new ImageListItem(value, transparentColor));
  563. }
  564. public void Add(string key, Icon icon)
  565. {
  566. // Argument has name icon but exceptions use name value.
  567. AddItem(key, new ImageListItem(icon));
  568. }
  569. public void Add(string key, Image image)
  570. {
  571. // Argument has name image but exceptions use name value.
  572. AddItem(key, new ImageListItem(image));
  573. }
  574. public void AddRange(Image[] images)
  575. {
  576. int index;
  577. if (images == null)
  578. throw new ArgumentNullException("images");
  579. for (index = 0; index < images.Length; index++)
  580. Add(images[index]);
  581. }
  582. public int AddStrip(Image value)
  583. {
  584. int width;
  585. int imageWidth;
  586. if (value == null)
  587. throw new ArgumentNullException("value");
  588. if ((width = value.Width) == 0 || (width % (imageWidth = this.imageSize.Width)) != 0)
  589. throw new ArgumentException("Width of image strip must be a positive multiple of ImageSize.Width.", "value");
  590. if (value.Height != this.imageSize.Height)
  591. throw new ArgumentException("Height of image strip must be equal to ImageSize.Height.", "value");
  592. return AddItem(null, new ImageListItem(value, width / imageWidth));
  593. }
  594. public void Clear()
  595. {
  596. list.Clear();
  597. if (!this.handleCreated)
  598. this.count = 0;
  599. keys.Clear();
  600. }
  601. [EditorBrowsable(EditorBrowsableState.Never)]
  602. public bool Contains(Image image)
  603. {
  604. throw new NotSupportedException();
  605. }
  606. public bool ContainsKey(string key)
  607. {
  608. return IndexOfKey(key) != -1;
  609. }
  610. public IEnumerator GetEnumerator()
  611. {
  612. Image[] images = new Image[this.Count];
  613. int index;
  614. if (images.Length != 0) {
  615. // Handle is created only when there are images.
  616. CreateHandle();
  617. for (index = 0; index < images.Length; index++)
  618. images[index] = (Image)((Image)list[index]).Clone();
  619. }
  620. return images.GetEnumerator();
  621. }
  622. [EditorBrowsable(EditorBrowsableState.Never)]
  623. public int IndexOf(Image image)
  624. {
  625. throw new NotSupportedException();
  626. }
  627. public int IndexOfKey(string key)
  628. {
  629. int index;
  630. if (key != null && key.Length != 0) {
  631. // When last IndexOfKey was successful and the same key was
  632. // assigned to an image with a lower index than the last
  633. // result and the key of the last result equals to key
  634. // argument the last result is returned.
  635. if (this.lastKeyIndex >= 0 && this.lastKeyIndex < this.Count && CompareKeys((string)keys[this.lastKeyIndex], key))
  636. return this.lastKeyIndex;
  637. // Duplicate keys are allowed and first match is returned.
  638. for (index = 0; index < this.Count; index++)
  639. if (CompareKeys((string)keys[index], key))
  640. return this.lastKeyIndex = index;
  641. }
  642. return this.lastKeyIndex = -1;
  643. }
  644. [EditorBrowsable(EditorBrowsableState.Never)]
  645. public void Remove(Image image)
  646. {
  647. throw new NotSupportedException();
  648. }
  649. public void RemoveAt(int index)
  650. {
  651. if (index < 0 || index >= this.Count)
  652. throw new ArgumentOutOfRangeException("index");
  653. CreateHandle();
  654. list.RemoveAt(index);
  655. keys.RemoveAt(index);
  656. if (Changed != null)
  657. Changed (this, EventArgs.Empty);
  658. }
  659. public void RemoveByKey(string key)
  660. {
  661. int index;
  662. if ((index = IndexOfKey(key)) != -1)
  663. RemoveAt(index);
  664. }
  665. public void SetKeyName(int index, string name)
  666. {
  667. // Only SetKeyName throws IndexOutOfRangeException.
  668. if (index < 0 || index >= this.Count)
  669. throw new IndexOutOfRangeException();
  670. keys[index] = name;
  671. }
  672. #endregion // ImageCollection Public Instance Methods
  673. #region ImageCollection Interface Properties
  674. object IList.this[int index] {
  675. get {
  676. return this[index];
  677. }
  678. set {
  679. if (!(value is Image))
  680. throw new ArgumentException("value");
  681. this[index] = (Image)value;
  682. }
  683. }
  684. bool IList.IsFixedSize {
  685. get {
  686. return false;
  687. }
  688. }
  689. bool ICollection.IsSynchronized {
  690. get {
  691. return false;
  692. }
  693. }
  694. object ICollection.SyncRoot {
  695. get {
  696. return this;
  697. }
  698. }
  699. #endregion // ImageCollection Interface Properties
  700. #region ImageCollection Interface Methods
  701. int IList.Add(object value)
  702. {
  703. int index;
  704. if (!(value is Image))
  705. throw new ArgumentException("value");
  706. index = this.Count;
  707. this.Add((Image)value);
  708. return index;
  709. }
  710. bool IList.Contains(object image)
  711. {
  712. return image is Image ? this.Contains ((Image) image) : false;
  713. }
  714. int IList.IndexOf (object image)
  715. {
  716. return image is Image ? this.IndexOf ((Image) image) : -1;
  717. }
  718. void IList.Insert(int index, object value)
  719. {
  720. throw new NotSupportedException();
  721. }
  722. void IList.Remove (object image)
  723. {
  724. if (image is Image)
  725. this.Remove ((Image) image);
  726. }
  727. void ICollection.CopyTo(Array dest, int index)
  728. {
  729. for (int imageIndex = 0; imageIndex < this.Count; imageIndex++)
  730. dest.SetValue (this[imageIndex], index++);
  731. }
  732. #endregion // ImageCollection Interface Methods
  733. }
  734. #endregion // Sub-classes
  735. #region Public Constructors
  736. public ImageList()
  737. {
  738. images = new ImageCollection(this);
  739. }
  740. public ImageList(System.ComponentModel.IContainer container) : this()
  741. {
  742. container.Add(this);
  743. }
  744. #endregion // Public Constructors
  745. #region Private Instance Methods
  746. private void OnRecreateHandle()
  747. {
  748. EventHandler eh = (EventHandler)(Events [RecreateHandleEvent]);
  749. if (eh != null)
  750. eh (this, EventArgs.Empty);
  751. }
  752. // MS's TypeDescriptor stuff apparently uses
  753. // non-public ShouldSerialize* methods, because it
  754. // picks up this behavior even though the methods
  755. // aren't public. we can't make them private, though,
  756. // without adding compiler warnings. so, make then
  757. // internal instead.
  758. internal bool ShouldSerializeTransparentColor ()
  759. {
  760. return this.TransparentColor != Color.LightGray;
  761. }
  762. internal bool ShouldSerializeColorDepth()
  763. {
  764. // ColorDepth is serialized in ImageStream when non-empty.
  765. // It is serialized even if it has its default value when empty.
  766. return images.Empty;
  767. }
  768. internal bool ShouldSerializeImageSize()
  769. {
  770. // ImageSize is serialized in ImageStream when non-empty.
  771. // It is serialized even if it has its default value when empty.
  772. return images.Empty;
  773. }
  774. internal void ResetColorDepth ()
  775. {
  776. this.ColorDepth = DefaultColorDepth;
  777. }
  778. internal void ResetImageSize ()
  779. {
  780. this.ImageSize = DefaultImageSize;
  781. }
  782. internal void ResetTransparentColor ()
  783. {
  784. this.TransparentColor = Color.LightGray;
  785. }
  786. #endregion // Private Instance Methods
  787. #region Public Instance Properties
  788. public ColorDepth ColorDepth {
  789. get {
  790. return images.ColorDepth;
  791. }
  792. set {
  793. images.ColorDepth = value;
  794. }
  795. }
  796. [Browsable(false)]
  797. [EditorBrowsable(EditorBrowsableState.Advanced)]
  798. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  799. public IntPtr Handle {
  800. get {
  801. return images.Handle;
  802. }
  803. }
  804. [Browsable(false)]
  805. [EditorBrowsable(EditorBrowsableState.Advanced)]
  806. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  807. public bool HandleCreated {
  808. get {
  809. return images.HandleCreated;
  810. }
  811. }
  812. [DefaultValue(null)]
  813. [MergableProperty(false)]
  814. [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
  815. public ImageCollection Images {
  816. get {
  817. return this.images;
  818. }
  819. }
  820. [Localizable(true)]
  821. public Size ImageSize {
  822. get {
  823. return images.ImageSize;
  824. }
  825. set {
  826. images.ImageSize = value;
  827. }
  828. }
  829. [Browsable(false)]
  830. [DefaultValue(null)]
  831. [EditorBrowsable(EditorBrowsableState.Advanced)]
  832. public ImageListStreamer ImageStream {
  833. get {
  834. return images.ImageStream;
  835. }
  836. set {
  837. images.ImageStream = value;
  838. }
  839. }
  840. [Bindable(true)]
  841. [DefaultValue(null)]
  842. [Localizable(false)]
  843. [TypeConverter(typeof(StringConverter))]
  844. public object Tag {
  845. get {
  846. return this.tag;
  847. }
  848. set {
  849. this.tag = value;
  850. }
  851. }
  852. public Color TransparentColor {
  853. get {
  854. return images.TransparentColor;
  855. }
  856. set {
  857. images.TransparentColor = value;
  858. }
  859. }
  860. #endregion // Public Instance Properties
  861. #region Public Instance Methods
  862. public void Draw(Graphics g, Point pt, int index)
  863. {
  864. this.Draw(g, pt.X, pt.Y, index);
  865. }
  866. public void Draw(Graphics g, int x, int y, int index)
  867. {
  868. g.DrawImage(images.GetImage(index), x, y);
  869. }
  870. public void Draw(Graphics g, int x, int y, int width, int height, int index)
  871. {
  872. g.DrawImage(images.GetImage(index), x, y, width, height);
  873. }
  874. public override string ToString()
  875. {
  876. return base.ToString() + " Images.Count: " + images.Count.ToString() + ", ImageSize: " + this.ImageSize.ToString();
  877. }
  878. #endregion // Public Instance Methods
  879. #region Protected Instance Methods
  880. protected override void Dispose(bool disposing)
  881. {
  882. if (disposing)
  883. images.DestroyHandle();
  884. base.Dispose(disposing);
  885. }
  886. #endregion // Protected Instance Methods
  887. #region Events
  888. static object RecreateHandleEvent = new object ();
  889. [Browsable(false)]
  890. [EditorBrowsable(EditorBrowsableState.Advanced)]
  891. public event EventHandler RecreateHandle {
  892. add { Events.AddHandler (RecreateHandleEvent, value); }
  893. remove { Events.RemoveHandler (RecreateHandleEvent, value); }
  894. }
  895. #endregion // Events
  896. }
  897. }