写.Net自定义控件有一段时间了,一日突然发现工具箱里的那个蓝色齿轮图标越看越不爽。怎么办?换!
于是查了一下MSDN按照它的做法试了N次都行不通。
百度、Google 也查了,但是不知道是网上的那些个解决办法没有被写清楚,还是我的理解能力的问题。
这个问题还是没解决。因为有新的任务下来,这个问题一直被悬着没解决,最近因为项目的原因需要自己开发特定功能的控件。
该是解决这个问题的时候了。研究了一个早上总于搞定了。现在把我得到的结论写在这里,希望对大家有所帮助。
不啰嗦了,进入正题。
首先,选择一张大小为16*16的bmp位图,尺寸超过16*16会被缩放。
而且也不仅仅只是支持bmp的图片,至于究竟支持那些格式的图片,有兴趣的朋友可以去研究下。
注意:位图的左下角的颜色将被设置为透明色。
准备:解决方案:Widgets
项目名称:Widgets.Windows
控件名称:Expander
两个Visual Studio 2008实例:一个用于打开解决方案Widgets,另一个打开解决方案Testing
补充:因为Widgets.Windows是Windows窗体应用程序的控件库,所以解决方案Testing中建立的项目也必须是Windows窗体应用程序。
ToolboxBitmap有三个重载,依次展开就很容易把问题解释清楚了。
重载一:
![](http://writeblog.csdn.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
/// <summary>
/// 使用来自指定文件的图像初始化新的 System.Drawing.ToolboxBitmapAttribute 对象。
/// </summary>
/// <param name="imageFile">包含一个 16 × 16 位图的文件的名称。</param>
public ToolboxBitmapAttribute(string imageFile);
个人不推荐用这种方式,因为imageFile要使用图片的绝对路径(试了几次相对路径,但没见到效果)。
而且控件的图标会随着你路径所指定的那张图片的改变而改变。
做一下实验就知道了。首先,我们准备两张图片,如下图:
Expander的元数据设置如下:
![](http://writeblog.csdn.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
/// <summary>
/// 表示一个可显示标题的控件,该标题具有一个用于显示内容的可折叠窗口。
/// </summary>
[ToolboxItem(true)]
[Designer(typeof(Designer))]
[DefaultProperty("Text"), DefaultEvent("Click")]
[ToolboxBitmap(@"Z:/Expander.bmp")]
[Description("表示一个可显示标题的控件,该标题具有一个用于显示内容的可折叠窗口。")]
public partial class Expander : Control
{
...
}
编译控件库Widgets.Windows,然后把编译后的Widgets.Windows.dll文件拖到 Testing 的工具箱里。就会看到以下效果。
不要感到奇怪为什么有两个Expander,而且图标还不一样,这是因为我第一次把它拖到工具箱后,就把路径 "Z:/Expander.bmp” 指向的图片换掉了,
然后再把Widgets.Windows.dll拖到工具箱里,无需再次编译 Expander 的图标随之改变。
重载二:
![](http://writeblog.csdn.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
/// <summary>
/// 基于作为资源嵌入到指定程序集的一个 16 × 16 位图初始化新的 System.Drawing.ToolboxBitmapAttribute 对象。
/// </summary>
/// <param name="t">一个 System.Type,将在它的定义程序集中搜索位图资源。</param>
public ToolboxBitmapAttribute(Type t);
准备一张图片,并命名为Expander.bmp,把它添加到Widgets.Windows项目中,然后把它的属性设置为“嵌入的资源”。
文件结构如下图:
Expander的元数据设置如下:
![](http://writeblog.csdn.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
/// <summary>
/// 表示一个可显示标题的控件,该标题具有一个用于显示内容的可折叠窗口。
/// </summary>
[ToolboxItem(true)]
[Designer(typeof(Designer))]
[DefaultProperty("Text"), DefaultEvent("Click")]
[ToolboxBitmap(typeof(Expander))]
[Description("表示一个可显示标题的控件,该标题具有一个用于显示内容的可折叠窗口。")]
public partial class Expander : Control
{
...
}
编译Widgets.Windows项目,先不急着把它拖到Testing的工具箱里,用Reflector看一下它生成后的文件结构:
可以看到图片被加上了"Widgets.Windows"前缀,首先要说明的是"Widgets.Windows"是这个项目的默认命名空间。
目前我们能得到的结论就是:嵌入的资源编译后会被自动加上该项目的默认命名空间名称。
之前看过一篇文章,说为了避免这个问题可以手动打开项目文件把默认命名空间删掉就可以了。
个人不推荐这么做,而且Visual Studio 2008也不允许你这么做,因为默认命名空间的重要性,是不需要我在这里说明的。
好了,回到正题上来,这里给大家看这张图片是为了与下面一种方法对比的。
现在把编译后的Widgets.Windows.dll文件拖到 Testing 的工具箱里,就可以看到你设置的图标了。这个就不截图了,因为我是比较懒滴。
重载三:
![](http://writeblog.csdn.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
/// <summary>
/// 基于作为资源嵌入到指定程序集的16×16位图,初始化新的 System.Drawing.ToolboxBitmapAttribute 对象。
/// </summary>
/// <param name="t">一个 System.Type,将在它的定义程序集中搜索位图资源。</param>
/// <param name="name">嵌入的位图资源的名称。</param>
public ToolboxBitmapAttribute(Type t, string name);
还是一样,准备一张图片,并命名为Expander.bmp,把它添加到Widgets.Windows项目中,然后把它的属性设置为“嵌入的资源”。
但是这次不一样的是:图片不是在项目的根目录中。
文件结构图如下:
Expander的元数据设置如下:
![](http://writeblog.csdn.net/Images/OutliningIndicators/ExpandedBlockStart.gif)
/// <summary>
/// 表示一个可显示标题的控件,该标题具有一个用于显示内容的可折叠窗口。
/// </summary>
[ToolboxItem(true)]
[Designer(typeof(Designer))]
[DefaultProperty("Text"), DefaultEvent("Click")]
[ToolboxBitmap(typeof(Expander), "Resources.Icon.Expander.bmp")]
[Description("表示一个可显示标题的控件,该标题具有一个用于显示内容的可折叠窗口。")]
public partial class Expander : Control
{
...
}
编译Widgets.Windows项目,再次用Reflector看一下它生成后的文件结构:
这次我们可以看到,Expander.bmp编译后的的不但被加上了默认命名空间名称,还加上了该图片的文件夹的名字。
但是我的元数据中并没有写上它被编译出来的全称"Widgets.Windows.Resources.Icon.Expander.bmp",而是"Resources.Icon.Expander.bmp"
也就是说我把默认命名空间去掉了,不要怀疑,把编译后的Widgets.Windows.dll文件拖到 Testing 的工具箱里,是可以看到预期的图标的。
这里也不截图了,原因我不说你也知道,呵呵!
之前看过一篇文章,说什么要把元数据设置为“解决方案名称+项目名称+图片文件夹名称+文件名”。
那时候还在摸索阶段,所以按他的方法试了又试却始终达不到想要的效果。后来认真一想,解决方案只是拿来管理项目的,关项目里的类什么事?
所以把解决方案名称去掉了,但还是没有用。然后想起第二种重载:当图片在项目的根目录下时,是不需要设置图片名称的,
难道项目名称也是不需要的?所以索性就把项目名称也去掉了。终于达到了想要的效果了!
再说了“解决方案名称+项目名称+图片文件夹名称+文件名”这种说法是不够正确的,项目名称只是项目名称,正真编译出来图片加上的前缀
既不是程序集的名称也不是项目名称,而是是默认命名空间名称。除非你的项目名称和你这个项目的默认命名空间名称相同,这种说法才成立!
我能达到效果的原因只不过是我的习惯是项目名称、程序集名称、默认命名空间名称三者通常设为一样的罢了。
总结:
个人是比较喜欢重载三的,原因有以下三点:
一、比较灵活,图片的名称不一定要与控件类的名称相同,虽然说相同比较好一些;
二、稳定。不像重载一那样需要去硬盘上找图片,我是把我的移动硬盘指定为Z:盘,难道当我把这个控件发布出去时,
也一定要让使用者在Ta的Z:盘放一张图片吗?
三、资源统一管理。第二种重载虽然方便、简洁,但它有着太多的限制,图片的文件名必须与控件类名一致、图片必须位于项目的根目录下,
这样一来当同一个项目中控件达到一定的数量时,势必会为文件的管理带来不便。