关于ImageList.Images集合的特殊行为!

这个问题是我在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的!

原文地址:https://www.cnblogs.com/xuefeng1982/p/1719048.html