• 关于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的!

  • 相关阅读:
    C++ new的使用
    乘风破浪,遇见最美Windows 11之新微软商店(Microsoft Store)生态 微软商店(Microsoft Store)与开发者社区共同成长
    温故知新,Windows命令知多少,学习Command Shell和PowerShell
    乘风破浪,遇见最美Windows 11之现代Windows桌面应用开发 Inno Setup如何设置退出代码
    乘风破浪,遇见未来元宇宙(Metaverse)之微软和Meta战略合作,选择Azure作为战略云供应商,帮助加速人工智能的研究和开发
    乘风破浪,遇见未来元宇宙(Metaverse)之中金元宇宙研究系列,元宇宙系列之Web3.0:新范式开启互联网新阶段
    乘风破浪,遇见最美Windows 11之现代Windows开发运维 新认识微软开发工具箱(Microsoft Dev Box)并注册预览计划
    乘风破浪,遇见最美Windows 11之现代Windows桌面应用开发 微软跨平台UI框架.NET MAUI 6正式发布,一套代码面向多平台
    乘风破浪,遇见最美Windows 11之新微软商店(Microsoft Store)生态 宣布微软商店应用奖获奖者(Microsoft Store App Awards Winners)!
    乘风破浪,遇见最美Windows 11之现代Windows桌面应用开发 利用Windows创建大规模的下一代体验
  • 原文地址:https://www.cnblogs.com/xuefeng1982/p/1719048.html
Copyright © 2020-2023  润新知