none
为什么给ImageList.Image.Tag赋值后,该属性还是空 RRS feed

答案

  • 你好!

         这个问题相当复杂,我研究了大概有1天时间了:

    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的!


    周雪峰
    • 已标记为答案 jieon 2010年4月25日 7:28
    2010年4月23日 17:36
    版主

全部回复

  • 没遇到过。您试试看用

    image.Tag = new { Foo = "Bar" };

    看看会不会还是 null?如果是,这可能是一个 bug。


    Mark Zhou
    2010年4月5日 9:46
  • 没遇到过。您试试看用

    image.Tag = new { Foo = "Bar" };

    看看会不会还是 null?如果是,这可能是一个 bug。


    Mark Zhou

    还是null....不知道为何。麻烦又得弄个对照表
    2010年4月5日 10:09
  • 到 Connect 上去提交一个,我现在还没空验证这个。大伙如果有 Repro 的跟帖。
    Mark Zhou
    2010年4月5日 10:31
  • 到 Connect 上去提交一个,我现在还没空验证这个。大伙如果有 Repro 的跟帖。
    Mark Zhou
    Connect ? where....
    2010年4月5日 14:04
  • 你好!

         这个问题相当复杂,我研究了大概有1天时间了:

    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的!


    周雪峰
    • 已标记为答案 jieon 2010年4月25日 7:28
    2010年4月23日 17:36
    版主