前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【C#】注意用“划算”的方式使用图标

【C#】注意用“划算”的方式使用图标

作者头像
AhDung
发布2018-09-13 11:35:45
8050
发布2018-09-13 11:35:45
举报
文章被收录于专栏:AhDungAhDung

先解释一下何谓“划算”:假定一个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属性:

代码语言:javascript
复制
static Icon appIcon;
/// <summary>
/// 主程序图标
/// </summary>
public static Icon AppIcon
{
    get
    {
        if (appIcon == null)
        {
            try { appIcon = ExtractIcon(Application.ExecutablePath, 0); }
            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文件中的图标数据,减小程序体积。然而在内存层面,上述方法是不是会造成复制多份图标数据,即文件是小了,但运行起来的内存占用可能并不少,这个我没求证,等蛋疼再追求一下内存层面的“划算”。

文毕。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2014-04-10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云开发 CloudBase
云开发(Tencent CloudBase,TCB)是腾讯云提供的云原生一体化开发环境和工具平台,为200万+企业和开发者提供高可用、自动弹性扩缩的后端云服务,可用于云端一体化开发多种端应用(小程序、公众号、Web 应用等),避免了应用开发过程中繁琐的服务器搭建及运维,开发者可以专注于业务逻辑的实现,开发门槛更低,效率更高。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档