先解释一下何谓“划算”:假定一个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. java提高篇(十六)-----异常(一)

         Java的基本理念是“结构不佳的代码不能运行”!!!!! 大成若缺,其用不弊. 大盈若冲,其用不穷. 在这个世界不可能存在完美的东西,不管完美的思维有多么缜密,细心,我们都不可能考虑所有的因 ...

  2. [安卓] 12、开源一个基于SurfaceView的飞行射击类小游戏

    前言  这款安卓小游戏是基于SurfaceView的飞行射击类游戏,采用Java来写,没有采用游戏引擎,注释详细,条理比较清晰,适合初学者了解游戏状态转化自动机和一些继承与封装的技巧. 效果展示    ...

  3. http学习笔记(一)

    写在前面: 第一次想写系列文章,学习了一些web知识后,发现自己还有很大的不足,但又不知道该学习些什么来完善自己的知识体系,偶然在网上看到了一篇介绍http的文章,觉得对自己有一些帮助,于是想要开始学 ...

  4. jni和java对应关系

    Java 类型 本地类型 描述 boolean jboolean C/C++8位整型 byte jbyte C/C++带符号的8位整型 char jchar C/C++无符号的16位整型 short ...

  5. IOS 手势-轻点、触摸、手势、事件

    1.概念 手势是从你用一个或多个手指接触屏幕时开始,直到手指离开屏幕为止所发生的所有事件.无论手势持续多长时间,只要一个或多个手指仍在屏幕上,这个手势就存在. 触摸是指把手指放到IOS设备的屏幕上,从 ...

  6. tr

    参照http://www.cnblogs.com/djcsch2001/archive/2011/05/08/2040255.html tr -options set1 [set2] ---- set ...

  7. Java-面向对象基础练习

    1.编写一个Java应用程序,该应用程序包括2个类:Print类和主类E.Print 类里有一个方法output()功能是输出100 ~ 999之间的所有水仙花数(各位数字的 立方和等于这个三位数本身 ...

  8. js常用函数

    日期时间函数(需要用变量调用): var c=new Date; c.getDate(); document.write(c) //获取当前时间 var c=new Date(); c.getTime ...

  9. mybatis插入的同时获取主键id

    <insert id="insertAndReturnId" parameterType="com.qianlong.cms.entity.AppCmsRole&q ...

  10. Tomcat源码解读系列(一)——server.xml文件的配置

    Tomcat是J2EE开发人员最常用到的开发工具,在Java Web应用的调试开发和实际部署中,我们都可以看到Tomcat的影子.大多数时候,我们可以将Tomcat当做一个黑盒来看待,只需要将编写的J ...