这个问题是我在MSDN中文技术论坛中遇到的,现象十分诡异,仔细的研究了一下源代码,现在分享给大家,大家先看一段Demo:
- FileStream fs = new FileStream(@"D:\aa.jpg", FileMode.Open, FileAccess.Read);
- Image image = Image.FromStream(fs);
- image.Tag = "txtbmp";
- ImageList bigHeadPicList = new ImageList();
- bigHeadPicList.Images.Add("1",image);
- Console.WriteLine(bigHeadPicList.Images["1"].Tag); //这里Tag是null,相当意外!
- 直观上感觉image和 bigHeadPicList.Images["1"]并不是同一对象,让我们通过源代码来验证这一点:
- 先看ImageCollection 类的索引器的源代码:
- public Image this[string key] {
- get {
- // We do not support null and empty string as valid keys.
- if ((key == null) || (key.Length == 0)){
- return null;
- }
- // Search for the key in our collection
- int index = IndexOfKey(key);
- if (IsValidIndex(index)) {
- return this[index];
- }
- else {
- return null;
- }
- }
- }
关键的实现都在另外一个索引器中:
- [Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
- public Image this[int index] {
- get {
- if (index < 0 || index >= Count)
- throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture)));
- return owner.GetBitmap(index); //这行代码比较关键
- }
- set {
- if (index < 0 || index >= Count)
- throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture)));
- if (value == null) {
- throw new ArgumentNullException("value");
- }
- if (!(value is Bitmap))
- throw new ArgumentException(SR.GetString(SR.ImageListBitmap));
- AssertInvariant();
- Bitmap bitmap = (Bitmap)value;
- bool ownsImage = false;
- if (owner.UseTransparentColor) {
- // Since there's no ImageList_ReplaceMasked, we need to generate
- // a transparent bitmap
- Bitmap source = bitmap;
- bitmap = (Bitmap) bitmap.Clone();
- bitmap.MakeTransparent(owner.transparentColor);
- ownsImage = true;
- }
- try {
- IntPtr hMask = ControlPaint.CreateHBitmapTransparencyMask(bitmap);
- IntPtr hBitmap = ControlPaint.CreateHBitmapColorMask(bitmap, hMask);
- bool ok = SafeNativeMethods.ImageList_Replace(new HandleRef(owner, owner.Handle), index, new HandleRef(null, hBitmap), new HandleRef(null, hMask));
- SafeNativeMethods.DeleteObject(new HandleRef(null, hBitmap));
- SafeNativeMethods.DeleteObject(new HandleRef(null, hMask));
- if (!ok)
- throw new InvalidOperationException(SR.GetString(SR.ImageListReplaceFailed));
- } finally {
- if(ownsImage) {
- bitmap.Dispose();
- }
- }
- }
- }
这行代码比较关键:
- return owner.GetBitmap(index);
下面我们看看GetBitmap方法的实现:
- private Bitmap GetBitmap(int index) {
- if (index < 0 || index >= Images.Count)
- throw new ArgumentOutOfRangeException("index", SR.GetString(SR.InvalidArgument, "index", index.ToString(CultureInfo.CurrentCulture)));
- Bitmap result=null;
- if(ColorDepth == ColorDepth.Depth32Bit) {
- NativeMethods.IMAGEINFO imageInfo = new NativeMethods.IMAGEINFO(); // review? do I need to delete the mask and image inside of imageinfo?
- if(SafeNativeMethods.ImageList_GetImageInfo(new HandleRef(this, this.Handle), index, imageInfo)) {
- Bitmap tmpBitmap = null;
- BitmapData bmpData = null;
- BitmapData targetData = null;
- IntSecurity.ObjectFromWin32Handle.Assert();
- try {
- tmpBitmap = Bitmap.FromHbitmap(imageInfo.hbmImage);
- //
- bmpData = tmpBitmap.LockBits(new Rectangle(imageInfo.rcImage_left,imageInfo.rcImage_top, imageInfo.rcImage_right-imageInfo.rcImage_left, imageInfo.rcImage_bottom-imageInfo.rcImage_top), ImageLockMode.ReadOnly, tmpBitmap.PixelFormat);
- int offset = bmpData.Stride * imageSize.Height * index;
- // we need do the following if the image has alpha because otherwise the image is fully transparent even though it has data
- if(BitmapHasAlpha(bmpData)) {
- result = new Bitmap(imageSize.Width, imageSize.Height, PixelFormat.Format32bppArgb); //注意这里是新创建的Bitmap实例
- targetData = result.LockBits(new Rectangle(0, 0, imageSize.Width, imageSize.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
- CopyBitmapData(bmpData, targetData);//仅仅拷贝图像数据
- }
- } finally {
- CodeAccessPermission.RevertAssert();
- if(tmpBitmap != null && bmpData != null) {
- tmpBitmap.UnlockBits(bmpData);
- }
- if(result != null && targetData != null) {
- result.UnlockBits(targetData);
- }
- }
- }
- }
- if(result == null) { // paint with the mask but no alpha...
- result = new Bitmap(imageSize.Width, imageSize.Height); //新创建Bitmap实例
- Graphics graphics = Graphics.FromImage(result);
- try {
- IntPtr dc = graphics.GetHdc();
- try {
- SafeNativeMethods.ImageList_DrawEx(new HandleRef(this, Handle), index, new HandleRef(graphics, dc), 0, 0,
- imageSize.Width, imageSize.Height, NativeMethods.CLR_NONE, NativeMethods.CLR_NONE, NativeMethods.ILD_TRANSPARENT);
- }
- finally {
- graphics.ReleaseHdcInternal(dc);
- }
- }
- finally {
- graphics.Dispose();
- }
- }
- // gpr: See Icon for description of fakeTransparencyColor
- result.MakeTransparent(fakeTransparencyColor);
- return result;
- }
代码比较长,关键的部分我使用注释标记出来了,大家看我标记的注释就可以了,大家可以发现,无论BitmapHasAlpha是否为真,都是新创建Bitmap对象然后返回的,所以在mage和 bigHeadPicList.Images["1"]并不是同一对象,所以Tag是null的!