先解释一下何谓“划算”:假定一个Winform程序包含若干个窗体,每个窗体左上角都要显示图标(即要设置Form.Icon属性),该程序本身也要有个图标(用于在OS资源管理器中显示),所有这些图标都是一个样子——这是一种很常见的情形。如图:

即同一个图标要用在程序本身和程序中的各个窗体之上。那么所谓“划算”就是指,在程序文件(exe)中只存储1份图标数据,所有要用到该图标的地方都从这里取。而不是存储多份,各取各的,因为这样显然会增大程序体积,很不“划算”。

之所以有这个话题,是因为如果不注意操作技巧,就会造成同一个图标存储多份的情况,VS和编译器并没有智能到会帮我们自动清除冗余资源的地步。那么如何才能做到划算而不浪费,关键就是要弄清楚每种操作会造成什么样的结果。

一、先看程序图标的设置方法

这个地方有几种选取方式:

1、直接浏览到ico文件进行选取。VS会自动把ico文件拷贝到项目根目录

2、把ico文件存放到项目根目录或任意子目录(该目录必须“包括在项目中”),然后就可以在这里下拉选取。如上图的Resources\test.ico和test.ico就是这种情况

无论用何种方式选取,项目编译成PE文件后,这个图标都是存放在PE文件的资源节中,可以用eXeScope之类的工具看出。有关PE的信息请参看:

http://msdn.microsoft.com/en-us/windows/hardware/gg463119.aspx

http://blog.csdn.net/evileagle/article/details/11693499

换句话说,程序图标的选取没什么可注意的,因为结果都一样。

二、窗体图标的设置方法

1、在VS的属性面板中直接浏览ico文件。如图:

这可能是最直观简单的方法了。但是不幸,这恰恰是最容易造成浪费的方法,因为这样选取的图标,会嵌在相应窗体的资源里(Form.resx),有几个窗体这样设置图标,图标数据就会存几份。

2、把图标添加进项目资源(Resources.resx)中。然后在所有窗体代码中都这样设置:

this.Icon = xx.Properties.Resources.test;//xx是项目默认命名空间;test是资源名

这种方式的结果是,图标会以程序集资源的形式存储1份在程序集中,所有窗体共用这个资源。相比第1种方式,这种方式不会造成图标存储多份。但也只是解决了多个窗体共用一个图标的问题,还有程序本身的图标是个问题。

上面说过,程序图标只有一个地方可以设置,设置的结果是把图标存放到PE资源中,这里存在【程序集资源】和【PE资源】两个概念,就是虽然在程序集资源只有1份图标数据,但只要一设置程序图标,项目编译时就会把图标再存一份到PE资源中,所以在整个PE文件中还是存在2份图标数据。那么要想让程序和窗体共用一个资源,就有两种思路,一是让程序图标使用程序集资源,二是让窗体使用PE资源。

对于前一种,我怎么可能去找虐呢,即使自宫也未必成功的事,pass~

3、获取程序图标,给窗体使用。一开始想到的自然是Icon.ExtractAssociatedIcon(),但是这个方法只能取到32x32的图标,而窗体图标是16x16的,会造成缩放,对于我这种纠结视觉细节的人来说,是不可接受的。理想的情况是,取到完整的图标组(包含多种尺寸和色深的图标集合)赋值给窗体Icon,这样才能在窗体左上角和NT6任务栏拥有完美的表现。如图:

那怎样才能取到图标组呢。为此我啃了若干对于由.net起步的码农来说臣妾做不到的知识,包括SHGetFileInfo、LoadIcon、LoadImage、ExtractIcon、FindResource等API,甚至啃了下PE结构,OMG~越啃越觉得想死的心都有了。天幸在codeproject.com找到了高人的现成方案:

http://www.codeproject.com/Articles/32617/Extracting-Icons-from-EXE-DLL-and-Icon-Manipulatio

先感谢一下这位仁兄,好人一生平安。他这方案挺全,可以从各种源获取图标,本来想精简一下,只要获取PE图标组的方法,但发现整个方案中,大部分代码就是干这个的,精简意义不大,索性整个用上。回正题,在所有窗体中写上:

this.Icon = IconHelper.ExtractIcon(Application.ExecutablePath, 0);

即可。当然我对他的IconHelper稍微改造了一下,增加AppIcon属性:

static Icon appIcon;
/// <summary>
/// 主程序图标
/// </summary>
public static Icon AppIcon
{
get
{
if (appIcon == null)
{
try { appIcon = ExtractIcon(Application.ExecutablePath, ); }
catch { return null; }
}
return appIcon;
}
}

这样,到窗体中,写成:
this.Icon = IconHelper.AppIcon;

即可。

至此,实现了程序和窗体共用一个图标,程序PE文件也只存储1份图标数据的目的。再次感谢高人!只是项目加入该方案后,最终生成的程序大概会增加接近20K的体积。所以是不是划算用上该方案,需从实际权衡。比如图标文件不大的程序,俩图标加起来还没有1图标+20K,那存两份就存两份,反正最终目的是程序体积,又不是追求彻底共用。

----------------------------------为什么非得给分隔线起个名字----------------------------------

另外,顺便说一下托盘区图标,如果给NotifyIcon.Icon设一个图标组,它是不会自动取16x16那一个的,取的是32x32,像这样:

非得直接丢给它一个16x16的才行,所以如果你已经拥有图标组,得这样设置托盘图标:

notifyIcon1.Icon = new System.Drawing.Icon(IconHelper.AppIcon,16,16);//意思就是从图标组中取出指定尺寸的单一图标。

这样就能得到完美的托盘图标了:

----------------------------------另一条分隔线----------------------------------

最后,附一张关于项目中各种资源的简易说明图:

最最后~我啥时候变这么啰嗦了,文中说的“划算”仅仅是在文件系统层面而言的,就是尽可能唯一存储PE文件中的图标数据,减小程序体积。然而在内存层面,上述方法是不是会造成复制多份图标数据,即文件是小了,但运行起来的内存占用可能并不少,这个我没求证,等蛋疼再追求一下内存层面的“划算”。

文毕。

【C#】注意用“划算”的方式使用图标的更多相关文章

  1. Ubuntu 如何为 XMind 添加快速启动方式和图标

    目录 Ubuntu 如何为 XMind 添加快速启动方式和图标 Ubuntu 如何为 XMind 添加快速启动方式和图标 按照教程Ubuntu16.04LTS安装XMind8并创建运行图标进行Xmin ...

  2. Element-UI 使用 class 方式和 css 方式引入图标

    今天在使用 vxe-table 时,需要引入 Element UI的图标,顺便就找了下这些组件库中图标的引用方式. 我们知道 Element .Ant Design.Font Awesome 等很多组 ...

  3. Windows中与系统关联自己开发的程序(默认打开方式、图标、右击菜单等)

    1. 默认打开方式 1.1. 代码支持 在Windows下,某个特定后缀名类型的文件,如果要双击时默认用某个程序(比如自己开发的WinForm程序)打开,代码中首先肯定要支持直接根据这个文件进行下一步 ...

  4. JAVA学习Swing章节标签JLabel中图标的使用

    package com.swing; import java.awt.Color; import java.awt.Component; import java.awt.Container; impo ...

  5. 强行替换exe图标的方法

    说句实话,要想用普通的方法来替换图标,不是完全不可行,当然也不是完全可行.这个看似简单的问题并不是想象中那么容易解决,为什么有人修改exe的图标总是失败,其实他忽视了exe和图标的复杂性,用简单的方法 ...

  6. ionic中修改图标的问题

    有两种修改图标的方法,一种是手动配置,另外一种是使用命令 1.手动配置 把图标icon.png复制到resources\android\icon目录下 修改根目录的config.xml文件 <p ...

  7. ionic2中使用自定义图标

    在ionic2中使用自定义图标,如iconfont(阿里巴巴矢量图标). 先在http://www.iconfont.cn/ 中找到自己需要的图标,然后将图标加入购物车,然后下载该图标. 下载完成后解 ...

  8. svn -- svn图标解析

    1.代表服务器端与客户端文件相同,没有任何更改 2.当前文件如果有修改,那么将显示如下图标 3.当前要提交的文件,与服务器上其他人提交的文件有冲突,那么将显示如下图标 4.当前文件,在服务器上已被删除 ...

  9. 在线使用iconfont字体图标

    登录https://www.iconfont.cn 把需要的图标加入购物车,然后加入项目 打开我的项目,生成代码 有3中方式使用图标 unicode和font class本质都是使用字体,好处在于兼容 ...

随机推荐

  1. JavaScript思维导图—正则表达式

    JavaScript思维导图-来自@王子墨http://julying.com/blog/the-features-of-javascript-language-summary-maps/

  2. Windows 安装 MongoDB 服务

    第一步 以管理员权限打开命令提示符 按Windows+R键(Ctrl和Alt中间的那个,有微软Logo的键),输入cmd打开命令提示符 第二步 创建数据库目录. 使用mkdir命令,创建数据库的目录和 ...

  3. Android Studio2.x版本无法自动关联源码的解决方法

    Android Studio2.x版本无法自动关联源码的解决方法 在学习android开发过程中,对于一个不熟悉的类,阅读源码是一个很好的学习方式,使用andorid studio开发工具的SDK M ...

  4. Android学习笔记50:使用WebView控件浏览网页

    在Android中,可以使用Webview控件来浏览网页.通过使用该控件,我们可以自制一个简单的浏览器,运行效果如图1所示. 图1 运行效果 1.WebView 在使用WebView控件时,首先需要在 ...

  5. MySQL记录

    1.unixtime和可读时间的转换 unixtime是距"1970-01-01 08:00:00"的时间秒数 unixtime -> readable select fro ...

  6. iOS 打包iPa

    http://blog.fir.im/how-to-build-adhoc-ipa/ 之前都是打包好ipa然后发送给客户,特麻烦,fir.im网站不错 迅速获取自己手机的udid:  http://f ...

  7. 谈谈java的运行机制

    1.高级语言的运行机制 我们编程都是用的高级语言(写汇编和机器语言的大牛们除外),计算机不能直接理解高级语言,只能理解和运行机器语言,所以必须要把高级语言翻译成机器语言,计算机才能运行高级语言所编写的 ...

  8. Lookup 组件异常

    Lookup组件有两个数据源,一个是上流组件的输出,一个是组件lookup的数据源,这个数据源是在Connection选项卡中进行配置.在开发package的过程中,我发现一个异常,当Lookup数据 ...

  9. 在SQL Server中添加供应用程序使用的帐号

        在之前客户咨询案例中,很多客户应用程序连接SQL Server直接用的就是SA帐号.如果对数据库管理稍微严格一点的话,就不应该给应用程序这种权限,通常应用程序只需要进行增删改查,而很少有DDL ...

  10. 找到SQL Server数据库历史增长信息

        很多时候,在我们规划SQL Server数据库的空间,或向存储方面要空间时,都需要估算所需申请数据库空间的大小,估计未来最简单的办法就是看过去的趋势,这通常也是最合理的方式.     通常来讲 ...