看见最后三个图标的黑边没有, 操作系统本身显示这样的图标时绝不是这个效果, 第三方软件如Icon Workshop专门处理图标的, 对这个的支持自然也很完美.
我是在解决一个程序界面上一个看似bug的不完美之处时左冲右突最后阴差阳错在VS2008 中打开calc.exe时看到这个效果的, 正是因为这个效果, 让我开始怀疑这是.NET本身的bug所致.
果然, google结果都证实了这一点, 在.NET中 ImageList被广泛用来管理和显示ListView 等众多控件中的图像相关资源. 但是通过.NET form designer来给ImageList添加图像时, 其Alpha通道会被错误处理, 并不是简单地丢失或忽略, 而是看上去有部分的支持. 即对半透明的像素它给处理成完全不透明了. 对安全透明的像素还是可以正确处理的, 最终的视觉效果就是一些图标的边缘有锯齿效果, 很粗糙.
code project上两个文章分别解决了关于ImageList的两个不怎么相关的问题.
其一就是解决ImageList的内置Add 方法错误处理Alpha通道的. 地址在:
解决的很好, 充分考虑了各种向ImageList添加图像的来源, 图像文件, Assembly中的内嵌资源. 缺点有这么几个:
1. 跟Form Designer的机制脱节, 可以在代码里通过它提供的接口向ImageList 添加图像资源--完美保留ALpha通道, 但是在设计期跟你的ImageList的内容是不相关的, 它没办法让你在设计期就能看到ImageList中添置好的图像资源, 也因此让你的其它控制无法选取该资源, 比如一个Button选择ImageList中的某个图像作为其背景, 这没法办到, 除非你另外通过Form Designer向ImageList中添加资源--- 但这个资源无疑是不能正确包含Alpha通道的. 你得手工保证运行期 ImageList中通过该控制添加的内容与设计期开发者看到的效果是一致的, ImageIndex 尤其要保证, 对于.NET 2.0而言, 还得保证ImageKey.
2. 通过Assembly内嵌资源添加图像时, 接口如下:
public static void AddEmbeddedResource( string resourceName, ImageList destinationImagelist);
第一个是资源名, 第二个参数是ImageList(的引用).
该函数在内部确定嵌入了该资源的Assembly是:
Assembly.GetExecutingAssembly().GetManifestResourceStream( resourceName );
这就带来了一个问题, 如果希望这个可重用的组件独立编译在一个DLL中, 从而被其它众多项目共用的话, 那些图像资源往往是嵌入在那些使用它的项目的目标Assembly中, GetExecutingAssembly()则永远返回当前正在执行的那么指令所置身其中的那个Assembly, 也就是你独立编译出来的DLL中, 所以这样来得到Assembly是不行的, 我对它的一点改动是:
public static void AddEmbeddedResource(Assembly assembly, string resourceName, ImageList destinationImagelist);
多传进来一个Assembly参数. 这样调用者可以决定资源是在哪个Assembly中的.
再看第二个项目:
它解决的是在.NET的Form designer中, 默认的ImageList编辑器不好用的问题, 它集成到.NET 的Form designer中, 提供了一个确实更好用更方便的编缉器.
但是, 它丝毫没有触及第一个项目中解决的Alpha通道问题.
现在, 两位作者确实都解决了ImageList的一些方便, 是站在他们肩膀上的时候了.
把两个项目一结合, 略加修改, 就得到了一个比.NET Form Designer中内置的ImageList编辑器更好用的可视界面, 而且完美支持图像的Alpha通道. 不过严格说, 离完美还差一点, 下面的截图中会看到, 程序的最终运行效果是好的, 但在Form Designer中开发者在设计时看到的却差一些, 这总比事情反过来好, 开发者看到的效果再好, 如果程序运行时最终用户看到的更差, 那等于没做.
下面是VS.NET 2003中的截图:
一脸麻点的窗口自然是设计时的Form Designer窗口, 可以看到Alpha通道被处理的很差, 显示为全黑色, 而那个ImageListAssigner窗口显示的也是一样, 最上层的窗口显示的是实际运行时的效果, 透明效果完全出来了.
要出来这个效果还有一点是必需的, 就是上图中红线框起来的两行, 语句简单, 但里面的内在机制比较复杂, 这两行必需出现在任何UI代码运行之前. 而且, 无论是对VS2003还是VS2008都必需这样, 有另外一个work around的办法替代这红框里的现行代码是, 使用manifest文件. 这儿不多说这个.
下面的是VS2008中的效果:
可以看到在Form Designer设计时边缘效果比较差(但比VS2003好一些, 那个是完全的黑框), 但ImageListAssigner窗口和和运行时其效果都是完美的.
有一个极小的细节: 在一个特殊的时刻, VS2008 即使是Form Designer中也可以显示出完美的透明效果, 那就是 ImageListAssigner窗口的 "Apply"刚刚被点击之后, 此时VS.NET得到了一个ImageList, 其中的图像的Alpha通道是ImageListAssigner 刚刚做好新出炉的, 但是, 重新rebuild项目或其它一些操作, 会引发VS.NET从资源中的base64编码重新生成ImageList, 边缘锯齿就又回来了, 回来不要紧, 因为这只是从resx资源到VS.NET内部使用的ImageList的单向流动, 千万不要造成VS.NET再把这个ImageList写回resx中, 那时透明效果就会又被它搞砸锅.
通过ImageListAssigner 来编辑ImageList之后,
在属性窗口和ImageList的编辑器中, VS2008的显示也更好一些:
08中的透明效果出来了, 虽然图像小小的怎么都看不清楚.