[转载]APP设计当中不容忽视的20个小细节,必收藏 - A5站长网

mikel阅读(897)

来源: [转载]APP设计当中不容忽视的20个小细节,必收藏 – A5站长网

【文章摘要】产品设计,你能做的最重要的事情就是保证用户界面运作的一致性。对于列表框来说,如果双击其中的项,使得某些事件发生,那么双击任何其它列表框中的项,都应该有同样的事件发生。所有窗口按钮的位置要一致,标签和讯息的措辞要一致,颜色方案要一致。

 

APP设计当中不容忽视的20个小细节,必收藏
 

在此非常感谢来自腾讯的庄晓文设计师的精彩语录。

 1、一致,一致,还是一致

你能做的最重要的事情就是保证用户界面运作的一致性。对于列表框来说,如果双击其中的项,使得某些事件发生,那么双击任何其它列表框中的项,都 应该有同样的事件发生。所有窗口按钮的位置要一致,标签和讯息的措辞要一致,颜色方案要一致。用户界面的—致性使得在用户对于界面运作建立起精确的心理模 型,从而降低培训和支持成本。

提炼:一致性体现在 色彩搭配和运用、字体的运用、相同的交互效果等。

一般我们使用的色彩不超过3种。交互效果也不超过3种。

 2、建立标准并遵循之

在应用软件中保持一致的唯一途径就是建立设计标准并加以遵循。最好的办法是采取一套行业标准,对自身特殊的需要加以补充。已有的行业标准,如 IBM标准(1993)与Microsoft标准(1995),通常可满足95%到99%的需要。采用行业标准,只需利用已有的成果,也使你的应用软件看 起来或感觉上更象用户已购买或建立的其它应用软件。应当在定义基础构造阶段就建立用户界面设计标准。

严格按照移动APP规范来设计,比如字体的大小、间距、图标的大小等等。

 3、阐明设计规则

用户要知道怎么使用你为他们开发的软件。软件运作的一致性表明,规则你只需解释一遍。这比一步步详细讲解如何使用应用软件每个特性要容易得多。

可以制定属于你的一些工作方式和设计规范。

  4、同时支持生手和熟手

图书馆目录符号对图书馆系统的一般用户来说,也许就够用了,但对熟手用户,如图书管理员,很可能就没有那么有效了。图书管理员是受过专门训练,能够使用复杂的查询系统找到信息,因此,应当考虑建立一套查询界面以满足他们的独特需要。

 

APP设计当中不容忽视的20个小细节,必收藏
 

 5、UI界面间切换很重要,也必须重视

如果从一个屏幕转换到另一屏幕很困难,用户会很快灰心并放弃。当屏幕流程与用户想完成的工作流程相符,此软件对用户才有意义。由于不同用户工作方式不同,应用软件需要有足够的灵活以支持他们不同的方式。在建模阶段,界面流程图可用来模拟屏幕之间的流程。

 6、界面上的布局很重要

在西方,人们是自左而右,从上而下阅读,基于人们的习惯,屏幕的组织也应当是自左而右,从上而下。屏幕小部件的布局也应以用户熟悉的方式进行。

 7、讯息和标签措辞要适当

屏幕上显示的文本是用户主要的信息源。如果文本措辞很糟,用户的理解就会很糟。要使用完整的措辞和句子,而不要用缩写和代码,使文本易于理解。讯息措辞要积极,显示用户处于控制之中,并提示如何正确使用软件。

如,下面哪一条讯息更吸引人: “你输入了错误信息”还是”帐号应为8位数”?此外,讯息措辞要一致,在屏幕上显示的位置要一致。尽管这样的讯息”须输入名字”和”应输入帐号”分别来说措辞上没问题,放在一起就不一致了。

根据第一条讯息的措辞,第二条讯息更好的措辞应当是”须输入帐号”,这就使得两条讯息措辞一致了。

 

APP设计当中不容忽视的20个小细节,必收藏
 

 8、了解小部件工具

为恰当的任务使用恰当的小部件,首先可以帮助增强应用软件的一致性,可能使得应用软件很容易构造。学会如何正确使用小部件的唯一途径是阅读和理解你们所采用的用户界面标准及准则。

 9、对其它软件不盲从

除非你知道一个应用软件是遵循了你们的用户界面标准和准则,否则你绝不能认定它做的都是对的。尽管看看人家怎么做,从中获得些主意是不错的想 法,但在懂得怎样区分用户界面设计的好坏之前,你得留神。太多的开发者错误地模仿其它应用软件的用户界面,而那些界面却设计得很糟,最好将界面设计的美工 外包项目请专业的设计外包公司来完成。

 10、颜色使用要适当 使用颜色要谨慎。

如果使用了,也要使用指示符。问题就在于有些用户可能是色盲一一如果在屏幕上使用了颜色来突出显示某些东西,假若想让色盲的用户注意到,那么需 要做些另外的工作来突出它,如在其旁边显示一个符号。颜色的使用也得一致,以使整个应用软件有同样的观感。此外,在不同平台上,色彩的表现不尽人意一一在 一个系统上看上去很好,在另一个系统上常常看上去很糟。展示会上我们经常听到展示者这样说: “在我家中的机器上看上去可是很好的呀。”

  11、遵循对比原则

打算在应用软件中使用颜色,要确保屏幕的可读性。最好的方法是遵循对比原则:在浅色背景上使用深色文字,在深色背景上使用浅色文字。蓝色文字以白色为背景很容易读,但以红色为背景很难辨认。问题出在蓝色与红色之间没有足够反差,而蓝色与白色之间则反差很大。

 12、字体使用要适当

老式英语字体可能在莎士比亚的剧本封面看上去很合适,但在屏幕上却很难认。要用那些可读性好的字体,如serif或Times Roman。此外,字体的使用要一致。节俭、有效地使用两、三种字体的屏幕看上去远胜于使用五、六种字体的屏幕。要记住每次改变了字体的大小、风格(粗 体、斜体、下划线,……)、样式或颜色,都是在使用不同的字体。

 13、灰掉而不是移走

在某些时刻,用户经常只能访问应用软件的某些功能。在删除一个对象之前,要先选中它,由此加深用户的心理模型,软件应当用删除按钮及(或)菜单 项去做一些事。按钮应当移去还是灰掉?灰掉它,决不能移走!当用户不该使用时就灰掉它,可使用户对如何使用应用软件建立精确的心理模型。如果仅仅移走一个 小部件或菜单项,而不是灰掉它,用户很难建立精确的心理模型,因为用户只知道当前可用的,而不知道什么是不可用的。

 14、使用非破坏性的缺省按钮

通常每个屏幕定义一个缺省按钮,如果用户按了回车键调用此按钮。问题是有时用户会意外敲击回车键,结果激活了缺省按钮。缺省按钮决不能有潜在的破坏性,如删除或保存(也许用户根本不想保存)。

 

APP设计当中不容忽视的20个小细节,必收藏
 

15、区域排列

当屏幕有多个编辑区域,要以视觉效果和效率来组织这些区域。如图1所示,编辑区域左对齐是最好的方法。换句话说,要使编辑区域左边界在一条直线上且上下排列。与之相应的标签则应右对齐,置于编辑区域旁。这是屏幕上组织区域的一个整洁有效的方式。

16、数据对齐要适当

对一列列的数据,通常的作法是整浮点数右对齐,字符串左对齐。

 17、屏幕不能拥挤

拥挤的屏幕让人难以理解,因而难以使用。实验结果显示屏幕总体盖度不应超过40%,而分组中屏幕盖度不应超过62%。

 

APP设计当中不容忽视的20个小细节,必收藏
 

 18、有效组合

逻辑上关联的项目在屏幕上应当加以组合,以显示其关联性。反之,任何相互之间毫不相关的项目应当分隔开。在项目集合间用间隔对其进行分组/或用方框也同样可做到这一点。

19、在操作焦点处打开窗口

当用户双击一个对象显示其编辑/详情屏幕,用户的注意力亦集中于此。因而在此处而不是其它地方打开窗口才有意义。

20、弹出菜单不应是唯一的功能来源

如果主要功能被隐藏起来,用户就不能学会怎样使用软件。开发人员最让人灰心的作法是滥用弹出菜单,也称作上下文相关菜单。一种使用鼠标的典型方法,是用来显示一个隐藏的弹出菜单,提供针对当前工作的屏幕区域特定功能的访问。

这20个在APP设计当中不容忽视的小细节,仅供大家参考。毕竟每个人都有各自的一些看法和理解。

最后老谭同学再跟大家分享几个APP设计知识点:

1、AI线框,PS视觉是比较常用的,在这里推荐下FW,线框视觉都是可以的,也比较方便。就是用得人不多,但是这个软件上手快,可以推荐使用。

2、Icon当然要按合适尺寸做,但是你要打出来的话,分辨率要在300,而UI的分辨率一般是72,分辨率还是要改一下,否则打出来没法看, 不管怎么样,保存好源文件。IOS系统界面里,工具栏及图标的大小,建议题主找一下设计规范,没有特别固定的值,但是不能差太远。

3、ICON:桌面图标114*114,标签栏图标<=60,导航条图标40*40,APPstore图标1024*1024

控件:状态栏40 标题栏 88 标签栏 98 导航栏按钮 60 列表高度〉=88

字体:大 40 中 34 小 24(如果太小就看不清)

[转载].Net缓存管理框架CacheManager - JustRun - 博客园

mikel阅读(962)

来源: [转载].Net缓存管理框架CacheManager – JustRun – 博客园

Cache缓存在计算机领域是一个被普遍使用的概念。硬件中CPU有 一级缓存,二级缓存, 浏览器中有缓存,软件开发中也有分布式缓存memcache, redis。缓存无处不在的原因是它能够极大地提高硬件和软件的运行速度。在项目开发中,性能慢的地方常常是IO操作频繁的地方,读取数据库是我们常见的 消耗性能的地方。这个时候,如果将使用频繁的数据缓存到能够高速读取的介质中,下次访问时,不用再去请求数据库,直接从缓存中获取所需的数据,就能够大大 提高性能。这篇文章主要讨论的是在.Net开发中,如何使用CacheManager框架方便的管理项目中的缓存。

一,CacheManager介绍以及优点

CacheManager是开源的.Net缓存管理框架。它不是具体的缓存实现,而是在缓存之上,方便开发人员配置和管理各种不同的缓存,为上层应用程序提供统一的缓存接口的中间层。

下面是CacheManager的一些优点:

  • 让开发人员的生活更容易处理和配资缓存,即使是非常复杂的缓存方案。
  • CacheManager能够管理多种缓存,包含 内存, appfabric, redis, couchbase, windows azure cache, memorycache等。
  • 提供了额外的功能,如缓存同步、并发更新、事件、性能计数器等…

二,CacheManager开始之旅

CacheManager上手还是非常简单的。下面使用内存缓存结合CacheManager的一个实例,能够帮助我们快速的熟悉CacheManager如何使用。

首先在Visual Studio中创建一个Console Application.

使用Nuget为项目添加CacheManager包引用。CacheManager包含了很多的Package. 其中CacheManager.Core是必须的,其它的针对不同缓存平台上有不同的对应Package.

这个Demo中,我们使用内存作为缓存,所以只是需要CacheManager.Core和CacheManager.SystemRuntimeCaching

接着在Main函数中配置好我们的缓存:

复制代码
 1 using System;
 2 using CacheManager.Core;
 3 namespace ConsoleApplication
 4 {
 5     class Program
 6     {
 7         static void Main(string[] args)
 8         {
 9             var cache = CacheFactory.Build("getStartedCache", settings =>
10             {
11                 settings.WithSystemRuntimeCacheHandle("handleName");
12             });
13         }
14     }
15 }
复制代码

上面代码中使用CacheFactory创建了一个名称为 getStartedCache的缓存实例,这个缓存实例使用的是SystemRunTime Cache, 内存缓存。一个缓存实例是可以配置多个Handle的,我们可以使用内存来作为存储介质,也可以使用Redis分布式缓存作为存储介质,并且可以同时在一 个缓存实例中使用,后面我们再介绍多级缓存的配置和使用。

接下来,我们添加一些测试缓存的代码

复制代码
 1 static void Main(string[] args)
 2 {
 3 
 4     var cache = CacheFactory.Build("getStartedCache", settings =>
 5     {
 6         settings.WithSystemRuntimeCacheHandle("handleName");
 7     });
 8 
 9     cache.Add("keyA", "valueA");
10     cache.Put("keyB", 23);
11     cache.Update("keyB", v => 42);
12     Console.WriteLine("KeyA is " + cache.Get("keyA"));      // should be valueA
13     Console.WriteLine("KeyB is " + cache.Get("keyB"));      // should be 42
14     cache.Remove("keyA");
15     Console.WriteLine("KeyA removed? " + (cache.Get("keyA") == null).ToString());
16     Console.WriteLine("We are done...");
17     Console.ReadKey();
18 }
复制代码

三,CacheManager多级缓存配置

实际开发中,我们常常会需要使用多级缓存。

一种常见的情况是,你有一个分布式式缓存服务器,例如redis,独立的缓存服务器能够让我们的多个系统应用程序都能够共享这些缓存的数据,因为这些缓存项的创建是昂贵的。

和访问数据库相比,分布式缓存速度较快,但是和内存相比,还是不够快。因为分布式缓存使用还需要序列化和网络传输的时间消耗。

这个时候里,做个分级缓存是个好的解决方案,将内存缓存结合分布式缓存使用,使用频率高的数据直接从内存中读取,这将大大提高应用程序的整体性能。

使用内存缓存的读取速度能够达到分布式缓存的100倍,甚至更高。

使用CacheManager, 配置多级缓存是一件非常容易的事情

复制代码
 1 var cache = CacheFactory.Build<int>("myCache", settings =>
 2 {
 3     settings
 4         .WithSystemRuntimeCacheHandle("inProcessCache")//内存缓存Handle
 5         .And
 6         .WithRedisConfiguration("redis", config =>//Redis缓存配置
 7         {
 8             config.WithAllowAdmin()
 9                 .WithDatabase(0)
10                 .WithEndpoint("localhost", 6379);
11         })
12         .WithMaxRetries(1000)//尝试次数
13         .WithRetryTimeout(100)//尝试超时时间
14         .WithRedisBackPlate("redis")//redis使用Back Plate
15         .WithRedisCacheHandle("redis", true);//redis缓存handle
16 });
复制代码

上面代码中,内存缓存和Redis缓存配置部分很容易看明白。但是BackPlate是什么作用? 接下来,我们看看CacheManager中的BackPlate挡板机制。

四, BackPlate解决分布式缓存中的同步问题

对于大型的软件系统,常常都是分为很多独立的子项目,各个子项目为了节约成本或者是方便数据共享,常常会共用同一个分布缓存服务器。这样在使用多级缓存的时候,就有可能出现数据不一致的情况。

假设在系统A中的更新了缓存中的一个数据项,这个时候CacheManager会在A设置的所有的缓存handle中更新改数据,这里也包括了分布 式缓存上的数据。但是在系统B中的内存缓存中,还是会存在着旧的未更新的数据。当系统B从缓存中取这条记录的时候,就会出现内存缓存和分布式缓存中的数据 不一致的情况。

为了防止这一点,缓存管理器有一个功能叫做cachebackplate将尝试同步多个系统中的缓存。

上面设置的多级缓存中,我们就将redis作为BackPlate的源. 也就是说所有的数据都需要以redis中缓存的数据为蓝本。

在设置redis作为BackPlate之后,同样发生上面的数据不一致的情况的时候,只要redis中的数据被修改了,就会触发CacheManager更新所有系统中的内存缓存中的数据,和redis中的数据保持一致。

同步的工作是如何完成的?

每次一条缓存记录被删除或更新的时候,Cache Manager会发送一个消息,让BackPlate存储这次的数据变化信息。所有其它的系统将异步接收这些消息,并将相应地作出更新和删除操作,保证数据的一致性。

五,ExpirationMode和CacheUpdateMode

涉及到缓存,就必然有缓存过期的问题。CacheManager中提供了一些简单的缓存过期方式设置。

复制代码
1 public enum ExpirationMode
2 {
3     None = 0,
4     Sliding = 1,
5     Absolute = 2,
6 }
复制代码

同时CacheManager还为多级缓存之间设置不同的数据更新策略

复制代码
1 public enum CacheUpdateMode
2 {
3     None = 0,
4     Full = 1,
5     Up = 2,
6 }
复制代码

使用Sliding和Up, 我们我可以为多级缓存设置不同的缓存过期时间,这样使用频率高的数据就能够保存在访问速度更快的内存中,访问频率次高的放到分布式缓存中。当 CacheManager在内存中找不到缓存数据的时候,就会尝试在分布式缓存中找。找到后,根据Up设置,会再将该缓存数据保存到内存缓存中。

具体的配置方式如下:

复制代码
 1 var cache = CacheFactory.Build<int>("myCache", settings =>
 2 {
 3     settings.WithUpdateMode(CacheUpdateMode.Up)
 4         .WithSystemRuntimeCacheHandle("inProcessCache")//内存缓存Handle
 5         .WithExpiration(ExpirationMode.Sliding, TimeSpan.FromSeconds(60)))
 6         .And
 7         .WithRedisConfiguration("redis", config =>//Redis缓存配置
 8         {
 9             config.WithAllowAdmin()
10                 .WithDatabase(0)
11                 .WithEndpoint("localhost", 6379);
12         }).
13         .WithExpiration(ExpirationMode.Sliding, TimeSpan. FromHours  (24)))
14         .WithMaxRetries(1000)//尝试次数
15         .WithRetryTimeout(100)//尝试超时时间
16         .WithRedisBackPlate("redis")//redis使用Back Plate
17         .WithRedisCacheHandle("redis", true);//redis缓存handle
18 
19 });
复制代码

六,缓存使用分析

在缓存使用中,对于缓存hit和miss数据态比较关系,这些数据能够帮助我们分析和调整缓存的设置,帮助缓存使用地更加合理。

1 var cache = CacheFactory.Build("cacheName", settings => settings
2     .WithSystemRuntimeCacheHandle("handleName")
3         .EnableStatistics()
4         .EnablePerformanceCounters());

在配置好缓存的Statistic功能后,我们就能够跟踪到缓存的使用情况了, 下面就是分别打印各个缓存handle中的分析数据。

复制代码
 1 foreach (var handle in cache.CacheHandles)
 2 {
 3     var stats = handle.Stats;
 4     Console.WriteLine(string.Format(
 5             "Items: {0}, Hits: {1}, Miss: {2}, Remove: {3}, ClearRegion: {4}, Clear: {5}, Adds: {6}, Puts: {7}, Gets: {8}",
 6                 stats.GetStatistic(CacheStatsCounterType.Items),
 7                 stats.GetStatistic(CacheStatsCounterType.Hits),
 8                 stats.GetStatistic(CacheStatsCounterType.Misses),
 9                 stats.GetStatistic(CacheStatsCounterType.RemoveCalls),
10                 stats.GetStatistic(CacheStatsCounterType.ClearRegionCalls),
11                 stats.GetStatistic(CacheStatsCounterType.ClearCalls),
12                 stats.GetStatistic(CacheStatsCounterType.AddCalls),
13                 stats.GetStatistic(CacheStatsCounterType.PutCalls),
14                 stats.GetStatistic(CacheStatsCounterType.GetCalls)
15             ));
16 }
复制代码

七,结尾

缓存是个好东西,用好了能够极大的提高性能。缓存的使用本身是个很大的话题,这边文章只是从缓存管理这个角度介绍了CachManager的使用。

下面是CacheManager相关的资料和链接:

官方主页

http://cachemanager.net/

源代码

https://github.com/MichaCo/CacheManager

官方MVC项目的Sample

https://github.com/MichaCo/CacheManager/tree/master/samples/CacheManager.Samples.Mvc

最近在思考不同情况下缓存使用的区别问题。对于互联网项目来说,数据的一致性要求常常不太高,缓存管理中,关注点可能在缓存的命中率上。对于应用系统,访问请求不大,但是对于数据的一致性要求较高,缓存中的数据更新策略可能更加重要。

怎样才是好的适合应用系统的缓存设计呢? 如果大家有兴趣,欢迎探讨指教。

Web开发常见的几个漏洞解决方法

mikel阅读(1175)

9)用Webscan、Fuzzer进行SQL注入和XSS漏洞初步分析;

10)手工检测SQL注入和XSS漏洞;

11)采用类似OScanner的工具对数据库进行分析;

12)基于通用设备、数据库、操作系统和应用的攻击;采用各种公开及私有的缓冲区溢出程序代码,也采用诸如MetasploitFramework 之类的利用程序集合。

13)基于应用的攻击。基于Web、数据库或特定的B/S或C/S结构的网络应用程序存在的弱点进行攻击。

14)口令猜解技术。进行口令猜解可以采用 X-Scan、Brutus、Hydra、溯雪等工具。

第三步就是尝试由普通权限提升为管理员权限,获得对系统的完全控制权。在时间许可的情况下,必要时从第一阶段重新进行。采用方法

1)口令嗅探与键盘记录。嗅探、键盘记录、木马等软件,功能简单,但要求不被防病毒软件发觉,因此通常需要自行开发或修改。

2)口令破解。有许多著名的口令破解软件,如 L0phtCrack、John the Ripper、Cain 等。

以上一些是他们测试的步骤,不过我们不一定要关注这些过程性的东西,我们可能对他们反馈的结果更关注,因为可能会爆发很多安全漏洞等着我们去修复的。

  2、SQL注入漏洞的出现和修复

1)SQL注入定义:

SQL注入攻击是黑客对数据库进行攻击的常用手段之一。随着B/S模式应用开发的发展,使用这种模式编写应用程序的程序员也越来越多。但是由于 程序员的水平及经验也参差不齐,相当大一部分程序员在编写代码的时候,没有对用户输入数据的合法性进行判断,使应用程序存在安全隐患。用户可以提交一段数 据库查询代码,根据程序返回的结果,获得某些他想得知的数据,这就是所谓的SQL Injection,即SQL注入。

SQL注入有时候,在地址参数输入,或者控件输入都有可能进行。如在链接后加入’号,页面报错,并暴露出网站的物理路径在很多时候,很常见,当然如果关闭了Web.Config的CustomErrors的时候,可能就不会看到。

另外,Sql注入是很常见的一个攻击,因此,如果对页面参数的转换或者没有经过处理,直接把数据丢给Sql语句去执行,那么可能就会暴露敏感的信息给对方了。如下面两个页面可能就会被添加注入攻击:

①HTTP://xxx.xxx.xxx/abc.asp?p=YY and (select top 1 name from TestD … type=’U’ and status>0)>0 得到第一个用户建立表的名称,并与整数进行比较,显然abc.asp工作异常,但在异常中却可以发现表的名称。假设发现的表名是xyz,则

②HTTP://xxx.xxx.xxx/abc.asp?p=YY and (select top 1 name from TestDB.dbo.sysobjects& … tatus>0 and name not in(‘xyz’))>0 可以得到第二个用户建立的表的名称,同理就可得到所有用建立的表的名称。

为了屏蔽危险Sql语句的执行,可能需要对进行严格的转换,例如如果是整形的,就严格把它转换为整数,然后在操作,这样可以避免一些潜在的危 险,另外对构造的sql语句必须进行Sql注入语句的过滤,如我的框架(Winform开发框架、Web开发框架等)里面就内置了对这些有害的语句和符号 进行清除工作,由于是在基类进行了过滤,因此基本上子类都不用关心也可以避免了这些常规的攻击了。

        /// <summary>
        /// 验证是否存在注入代码(条件语句)
        /// </summary>
        /// <param name="inputData"></param>
        public bool HasInjectionData(string inputData)
        {
            if (string.IsNullOrEmpty(inputData))
                return false;
            //里面定义恶意字符集合
            //验证inputData是否包含恶意集合
            if (Regex.IsMatch(inputData.ToLower(), GetRegexString()))
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// 获取正则表达式
        /// </summary>
        /// <returns></returns>
        private static string GetRegexString()
        {
            //构造SQL的注入关键字符
            string[] strBadChar =
            {
                //"select\\s",
                //"from\\s",
                "insert\\s",
                "delete\\s",
                "update\\s",
                "drop\\s",
                "truncate\\s",
                "exec\\s",
                "count\\(",
                "declare\\s",
                "asc\\(",
                "mid\\(",
                "char\\(",
                "net user",
                "xp_cmdshell",
                "/add\\s",
                "exec master.dbo.xp_cmdshell",
                "net localgroup administrators"
            };
            //构造正则表达式
            string str_Regex = ".*(";
            for (int i = 0; i < strBadChar.Length - 1; i++)
            {
                str_Regex += strBadChar[i] + "|";
            }
            str_Regex += strBadChar[strBadChar.Length - 1] + ").*";
            return str_Regex;
        }

上面的语句用于判别常规的Sql攻击字符,我在数据库操作的基类里面,只需要判别即可,如下面的一个根据条件语句查找数据库记录的函数。

        /// <summary>
        /// 根据条件查询数据库,并返回对象集合
        /// </summary>
        /// <param name="condition">查询的条件</param>
        /// <param name="orderBy">自定义排序语句,如Order By Name Desc;如不指定,则使用默认排序</param>
        /// <param name="paramList">参数列表</param>
        /// <returns>指定对象的集合</returns>
        public virtual List<T> Find(string condition, string orderBy, IDbDataParameter[] paramList)
        {
            if (HasInjectionData(condition))
            {
                LogTextHelper.Error(string.Format("检测出SQL注入的恶意数据, {0}", condition));
                throw new Exception("检测出SQL注入的恶意数据");
            }
            ...........................
        }

以上只是防止Sql攻击的一个方面,还有就是坚持使用参数化的方式进行赋值,这样很大程度上减少可能受到SQL注入攻击。

            Database db = CreateDatabase();
            DbCommand command = db.GetSqlStringCommand(sql);
            command.Parameters.AddRange(param);

  3、跨站脚本攻击漏洞出现和修复

跨站脚本攻击,又称XSS代码攻击,也是一种常见的脚本注入攻击。例如在下面的界面上,很多输入框是可以随意输入内容的,特别是一些文本编辑框里面,可以输入例如<script>alert(‘这是一个页面弹出警告’);</script> 这样的内容,如果在一些首页出现很多这样内容,而又不经过处理,那么页面就不断的弹框,更有甚者,在里面执行一个无限循环的脚本函数,直到页面耗尽资源为 止,类似这样的攻击都是很常见的,所以我们如果是在外网或者很有危险的网络上发布程序,一般都需要对这些问题进行修复。

XSS代码攻击还可能会窃取或操纵客户会话和 Cookie,它们可能用于模仿合法用户,从而使黑客能够以该用户身份查看或变更用户记录以及执行事务。[建议措施]清理用户输入,并过滤出 JavaScript 代码。我们建议您过滤下列字符:

[1] <>(尖括号)

[2] “(引号)

[3] ‘(单引号)

[4] %(百分比符号)

[5] ;(分号)

[6] ()(括号)

[7] &(& 符号)

[8] +(加号)

为了避免上述的XSS代码攻击,解决办法是可以使用HttpUitility的HtmlEncode或者最好使用微软发布的AntiXSSLibrary进行处理,这个更安全。

微软反跨站脚本库(AntiXSSLibrary)是一种编码库,旨在帮助保护开发人员保护他们的基于Web的应用不被XSS攻击。

编码方法 使用场景 示例
HtmlEncode(String) 不受信任的HTML代码。 <a href=”http://www.cnblogs.com”>Click Here [不受信任的输入]</a>
HtmlAttributeEncode(String) 不受信任的HTML属性 <hr noshade size=[不受信任的输入]>
JavaScriptEncode(String) 不受信任的输入在JavaScript中使用 <script type=”text/javascript”>

[Untrusted input]

</script>

UrlEncode(String) 不受信任的URL <a href=”http://cnblogs.com/results.aspx?q=[Untrusted input]”>Cnblogs.com</a>
VisualBasicScriptEncode(String) 不受信任的输入在VBScript中使用 <script type=”text/vbscript” language=”vbscript”>

[Untrusted input]

</script>

XmlEncode(String) 不受信任的输入用于XML输出 <xml_tag>[Untrusted input]</xml_tag>
XmlAttributeEncode(String) 不 受信任的输入用作XML属性 <xml_tag attribute=[Untrusted input]>Some Text</xml_tag>
        protected void Page_Load(object sender, EventArgs e)
        {
            this.lblName.Text = Encoder.HtmlEncode("<script>alert('OK');</SCRIPT>");
        }

例如上面的内容,赋值给一个Lable控件,不会出现弹框的操作。

但是,我们虽然显示的时候设置了转义,输入如果要限制它们怎么办呢,也是使用AntiXSSLibrary里面的HtmlSanitizationLibrary类库Sanitizer.GetSafeHtmlFragment即可。

        protected void btnPost_Click(object sender, EventArgs e)
        {
            this.lblName.Text = Sanitizer.GetSafeHtmlFragment(txtName.Text);
        }

这样对于特殊脚本的内容,会自动剔除过滤,而不会记录了,从而达到我们想要的目的。

  4、IIS短文件/文件夹漏洞出现和修复

通过猜解,可能会得出一些重要的网页文件地址,如可能在/Pages/Security/下存在UserList.aspx和MenuList.aspx文件。

[建议措施]

1)禁止url中使用“~”或它的Unicode编码。

2)关闭windows的8.3格式功能。

修复可以参考下面的做法,或者找相关运维部门进行处理即可。

http://sebug.net/vuldb/ssvid-60252

http://webscan.360.cn/vul/view/vulid/1020

http://www.bitscn.com/network/security/200607/36285.html

  5、系统敏感信息泄露出现和修复

如果页面继承一般的page,而没有进行Session判断,那么可能会被攻击者获取到页面地址,进而获取到例如用户名等重要数据的。

一般避免这种方式是对于一些需要登录才能访问到的页面,一定要进行Session判断,可能很容易给漏掉了。如我在Web框架里面,就是继承一个BasePage,BasePage 统一对页面进行一个登录判断。

    public partial class UserList : BasePage
    {
        protected void Page_Load(object sender, EventArgs e)
        {
          ...............
    /// <summary>
    /// BasePage 集成自权限基础抽象类FPage,其他页面则集成自BasePage
    /// </summary>
    public class BasePage : FPage
    {
        /// <summary>
        /// 默认构造函数
        /// </summary>
        public BasePage()
        {
            this.IsFunctionControl = true;//默认页面启动权限认证
        }
        /// <summary>
        /// 检查用户是否登录
        /// </summary>
        private void CheckLogin()
        {
            if (string.IsNullOrEmpty(Permission.Identity))
            {
                string url = string.Format("{0}/Pages/CommonPage/Login.aspx?userRequest={1}",
                    Request.ApplicationPath.TrimEnd('/'), HttpUtility.UrlEncode(Request.Url.ToString()));
                Response.Redirect(url);
            }
        }
        /// <summary>
        /// 覆盖HasFunction方法以使权限类判断是否具有某功能点的权限
        /// </summary>
        /// <param name="functionId"></param>
        /// <returns></returns>
        protected override bool HasFunction(string functionId)
        {
            CheckLogin();
            bool breturn = false;
            try
            {
                breturn = Permission.HasFunction(functionId);
            }
            catch (Exception)
            {
                Helper.Alerts(this, "BasePage调用权限系统的HasFunction函数出错");
            }
            return breturn;
        }
        protected override void OnInit(EventArgs e)
        {
            Response.Cache.SetNoStore(); //清除缓存
            base.OnInit(e);
            CheckLogin();
        }

否则可能会受到攻击,并通过抓包软件发现页面数据,获得一些重要的用户名或者相关信息。

还有一个值得注意的地方,就是一般这种不是很安全的网络,最好要求输入比较复杂一点的密码(强制要求),例如不能全部是数字密码或者不能是纯字 符,对位数也要求多一点,因为很多人输入12345678,123456,123这样的密码,很容易被猜出来并登录系统,造成不必要的损失。

  6、总结性建议

针对上面发现的问题,提出下面几条建议。

1)在服务器与网络的接口处配置防火墙,用于阻断外界用户对服务器的扫描和探测。

2)限制网站后台访问权限,如:禁止公网IP访问后台;禁止服务员使用弱口令。

3)对用户输入的数据进行全面安全检查或过滤,尤其注意检查是否包含SQL 或XSS特殊字符。这些检查或过滤必须在服务器端完成。

4)关闭windows的8.3格式功能。

5)限制敏感页面或目录的访问权限。

[转载]致我们终将组件化的 Web - Web前端 - ITeye资讯

mikel阅读(739)

来源: [转载]致我们终将组件化的 Web – Web前端 – ITeye资讯

本文转自:AlloyTeam
这篇文章将从两年前的一次技术争论开始。争论的聚焦就是下图的两个目录分层结构。我说按模块划分好,他说你傻逼啊,当然是按资源划分。

”按模块划分“目录结构,把当前模块下的所有逻辑和资源都放一起了,这对于多人独自开发和维护个人模块不是很好吗?当然了,那争论的结果是我乖乖 地改回主流的”按资源划分“的目录结构。因为,没有做到JS模块化和资源模块化,仅仅物理位置上的模块划分是没有意义的,只会增加构建的成本而已。
虽然他说得好有道理我无言以对,但是我心不甘,等待他日前端组件化成熟了,再来一战!
而今天就是我重申正义的日子!只是当年那个跟你撕逼的人不在。

模块化的不足
模块一般指能够独立拆分且通用的代码单元。由于JavaScript语言本身没有内置的模块机制(ES6有了!!),我们一般会使用CMD或 ADM建立起模块机制。现在大部分稍微大型一点的项目,都会使用requirejs或者seajs来实现JS的模块化。多人分工合作开发,其各自定义依赖 和暴露接口,维护功能模块间独立性,对于项目的开发效率和项目后期扩展和维护,都是是有很大的帮助作用。
但,麻烦大家稍微略读一下下面的代码

Java代码
  1. require([
  2.     ‘Tmpl!../tmpl/list.html’,‘lib/qqapi’,‘module/position’,‘module/refresh’,‘module/page’,‘module/net’
  3. ], function(listTmpl, QQapi, Position, Refresh, Page, NET){
  4.     var foo = ,
  5.         bar = [];
  6.     QQapi.report();
  7.     Position.getLocaiton(function(data){
  8.         //…
  9.     });
  10.     var init = function(){
  11.         bind();
  12.         NET.get(‘/cgi-bin/xxx/xxx’,function(data){
  13.             renderA(data.banner);
  14.             renderB(data.list);
  15.         });
  16.     };
  17.     var processData = function(){
  18.     };
  19.     var bind = function(){
  20.     };
  21.     var renderA = function(){
  22.     };
  23.     var renderB = function(data){
  24.         listTmpl.render(‘#listContent’,processData(data));
  25.     };
  26.     var refresh = function(){
  27.         Page.refresh();
  28.     };
  29.     // app start
  30.     init();
  31. });

上面是具体某个页面的主js,已经封装了像Position,NET,Refresh等功能模块,但页面的主逻辑依旧是”面向过程“的代码结构。 所谓面向过程,是指根据页面的渲染过程来编写代码结构。像:init -> getData -> processData -> bindevent -> report -> xxx 。 方法之间线性跳转,你大概也能感受这样代码弊端。随着页面逻辑越来越复杂,这条”过程线“也会越来越长,并且越来越绕。加之缺少规范约束,其他项目成员根 据各自需要,在”过程线“加插各自逻辑,最终这个页面的逻辑变得难以维护。

开发需要小心翼翼,生怕影响“过程线”后面正常逻辑。并且每一次加插或修改都是bug泛滥,无不令产品相关人员个个提心吊胆。

页面结构模块化
基于上面的面向过程的问题,行业内也有不少解决方案,而我们团队也总结出一套成熟的解决方案:Abstractjs,页面结构模块化。我们可以把 我们的页面想象为一个乐高机器人,需要不同零件组装,如下图,假设页面划分为tabContainer,listContainer和 imgsContainer三个模块。最终把这些模块add到最终的pageModel里面,最终使用rock方法让页面启动起来。

 

下面是伪代码的实现

Java代码
  1. require([
  2.     ‘Tmpl!../tmpl/list.html’,‘Tmpl!../tmpl/imgs.html’,‘lib/qqapi’,‘module/refresh’,‘module/page’
  3. ], function(listTmpl, imgsTmpl, QQapi, Refresh, Page ){
  4.     var tabContainer = new RenderModel({
  5.         renderContainer: ‘#tabWrap’,
  6.         data: {},
  7.         renderTmpl: “<li soda-repeat=’item in data.tabs’>{{item}}</li>”,
  8.         event: function(){
  9.             // tab’s event
  10.         }
  11.     });
  12.     var listContainer = new ScrollModel({
  13.         scrollEl: $.os.ios ? $(‘#Page’) : window,
  14.         renderContainer: ‘#listWrap’,
  15.         renderTmpl: listTmpl,
  16.         cgiName: ‘/cgi-bin/index-list?num=1’,
  17.         processData: function(data) {
  18.             //…
  19.         },
  20.         event: function(){
  21.             // listElement’s event
  22.         },
  23.         error: function(data) {
  24.             Page.show(‘数据返回异常[‘ + data.retcode + ‘]’);
  25.         }
  26.     });
  27.     var imgsContainer = new renderModel({
  28.         renderContainer: ‘#imgsWrap’,
  29.         renderTmpl: listTmpl,
  30.         cgiName: ‘/cgi-bin/getPics’,
  31.         processData: function(data) {
  32.             //…
  33.         },
  34.         event: function(){
  35.             // imgsElement’s event
  36.         },
  37.         complete: function(data) {
  38.            QQapi.report();
  39.         }
  40.     });
  41.     var page = new PageModel();
  42.     page.add([tabContainer,listContainer,imgsContainer]);
  43.     page.rock();
  44. });

我们把这些常用的请求CGI,处理数据,事件绑定,上报,容错处理等一系列逻辑方法,以页面块为单位封装成一个Model模块。
这样的一个抽象层Model,我们可以清晰地看到该页面块,请求的CGI是什么,绑定了什么事件,做了什么上报,出错怎么处理。新增的代码就应该 放置在相应的模块上相应的状态方法(preload,process,event,complete…),杜绝了以往的无规则乱增代码的行文。并且,根据 不同业务逻辑封装不同类型的Model,如列表滚动的ScrollModel,滑块功能的SliderModel等等,可以进行高度封装,集中优化。

现在基于Model的页面结构开发,已经带有一点”组件化“的味道。每个Model都带有各自的数据,模板,逻辑。已经算是一个完整的功能单元。但距离真正的WebComponent还是有一段距离,至少满足不了我的”理想目录结构“。

WebComponents 标准
我们回顾一下使用一个datapicker的JQuery的插件,所需要的步奏:
1.引入插件js
2. 引入插件所需的css(如果有)
3. copy 组件的所需的html片段
4. 添加代码触发组件启动
现阶段的“组件”基本上只能达到是某个功能单元上的集合。他的资源都是松散地分散在三种资源文件中,而且组件作用域暴露在全局作用域下,缺乏内聚性很容易就会跟其他组件产生冲突,如最简单的css命名冲突。对于这种“组件”,还不如上面的页面结构模块化。
于是W3C按耐不住了,制定一个WebComponents标准,为组件化的未来指引了明路。
下面以较为简洁的方式介绍这份标准,力求大家能够快速了解实现组件化的内容。(对这部分了解的同学,可以跳过这一小节)

1. <template>模板能力
模板这东西大家最熟悉不过了,前些年见的较多的模板性能大战 artTemplate,juicer,tmpl,underscoretemplate等等。而现在又有mustachejs无逻辑模板引擎等新入选 手。可是大家有没有想过,这么基础的能力,原生HTML5是不支持的(T_T)。
而今天WebComponent将要提供原生的模板能力

Html代码
  1. <template id=“datapcikerTmpl”>
  2. <div>我是原生的模板</div>
  3. </template>

template标签内定义了myTmpl的模板,需要使用的时候就要innerHTML= document.querySelector(‘#myTmpl’).content;可以看出这个原生的模板够原始,模板占位符等功能都没有,对于动 态数据渲染模板能力只能自力更新。

2. ShadowDom 封装组件独立的内部结构
ShadowDom可以理解为一份有独立作用域的html片段。这些html片段的CSS环境和主文档隔离的,各自保持内部的独立性。也正是ShadowDom的独立特性,使得组件化成为了可能。

Html代码
  1. var wrap = document.querySelector(‘#wrap’);
  2. var shadow = wrap.createShadowRoot();
  3. shadow.innerHTML = ‘<p>you can not see me </p>’

在具体dom节点上使用createShadowRoot方法即可生成其ShadowDom。就像在整份Html的屋子里面,新建了一个shadow的房间。房间外的人都不知道房间内有什么,保持shadowDom的独立性。

3. 自定义原生标签
初次接触Angularjs的directive指令功能,设定好组件的逻辑后,一个<Datepicker />就能引入整个组件。如此狂炫酷炸碉堡天的功能,实在令人拍手称快,跃地三尺。

Html代码
  1. var tmpl = document.querySelector(‘#datapickerTmpl’);
  2. var datapickerProto = Object.create(HTMLElement.prototype);
  3. // 设置把我们模板内容我们的shadowDom
  4. datapickerProto.createdCallback = function() {
  5.     var root = this.createShadowRoot();
  6.     root.appendChild(document.importNode(tmpl.content, true));
  7. };
  8. var datapicker = docuemnt.registerElement(‘datapicker’,{
  9.     prototype: datapickerProto
  10. });

Object.create方式继承HTMLElement.prototype,得到一个新的prototype。当解析器发现我们在文档中标 记它将检查是否一个名为createdCallback的方法。如果找到这个方法它将立即运行它,所以我们把克隆模板的内容来创建的ShadowDom。
最后,registerElement的方法传递我们的prototype来注册自定义标签。

上面的代码开始略显复杂了,把前面两个能力“模板”“shadowDom”结合,形成组件的内部逻辑。最后通过registerElement的方式注册组件。之后可以愉快地<datapicker></datapicker>的使用。

4. imports解决组件间的依赖

Html代码
  1. <link rel=“import” href=“datapciker.html”>

这个类php最常用的html导入功能,HTML原生也能支持了。
WebComponents标准内容大概到这里,是的,我这里没有什么Demo,也没有实践经验分享。由于webComponents新特性,基 本上除了高版本的Chrome支持外,其他浏览器的支持度甚少。虽然有polymer帮忙推动webcompoents的库存在,但是polymer自身 的要求版本也是非常高(IE10+)。所以今天的主角并不是他。
我们简单来回顾一下WebCompoents的四部分功能:
1 .<template>定义组件的HTML模板能力
2. Shadow Dom封装组件的内部结构,并且保持其独立性
3. Custom Element 对外提供组件的标签,实现自定义标签
4. import解决组件结合和依赖加载

组件化实践方案

官方的标准看完了,我们思考一下。一份真正成熟可靠的组件化方案,需要具备的能力。
“资源高内聚”—— 组件资源内部高内聚,组件资源由自身加载控制
“作用域独立”—— 内部结构密封,不与全局或其他组件产生影响
“自定义标签”—— 定义组件的使用方式
“可相互组合”—— 组件正在强大的地方,组件间组装整合
“接口规范化”—— 组件接口有统一规范,或者是生命周期的管理
个人认为,模板能力是基础能力,跟是否组件化没有强联系,所以没有提出一个大点。
既然是实践,现阶段WebComponent的支持度还不成熟,不能作为方案的手段。而另外一套以高性能虚拟Dom为切入点的组件框架 React,在facebook的造势下,社区得到了大力发展。另外一名主角Webpack,负责解决组件资源内聚,同时跟React极度切合形成互补。
所以【Webpack】+【React】将会是这套方案的核心技术。
不知道你现在是“又是react+webpack”感到失望,还是“太好了是react+webpack”不用再学一次新框架的高兴dd57e48b489e172cb8bd1a7eb08311c1。无论如何下面的内容不会让你失望的。
### 一,组件生命周期

React天生就是强制性组件化的,所以可以从根本性上解决面向过程代码所带来的麻烦。React组件自身有生命周期方法,能够满足“接口规范 化”能力点。并且跟“页面结构模块化”的所封装抽离的几个方法能一一对应。另外react的jsx自带模板功能,把html页面片直接写在render方 法内,组件内聚性更加紧密。

由于React编写的JSX是会先生成虚拟Dom的,需要时机才真正插入到Dom树。使用React必须要清楚组件的生命周期,其生命周期三个状态:
Mount: 插入Dom
Update: 更新Dom
Unmount: 拔出Dom
mount这单词翻译增加,嵌入等。我倒是建议“插入”更好理解。插入!拔出!插入!拔出!默念三次,懂了没?别少看黄段子的力量。

组件状态就是: 插入-> 更新 ->拔出。

然后每个组件状态会有两种处理函数,一前一后,will函数和did函数。
componentWillMount()  准备插入前
componentDidlMount()  插入后
componentWillUpdate() 准备更新前
componentDidUpdate()  更新后
componentWillUnmount() 准备拔出前
因为拔出后基本都是贤者形态(我说的是组件),所以没有DidUnmount这个方法。
另外React另外一个核心:数据模型props和state,对应着也有自个状态方法
getInitialState()     获取初始化state。
getDefaultProps() 获取默认props。对于那些没有父组件传递的props,通过该方法设置默认的props
componentWillReceiveProps()  已插入的组件收到新的props时调用
还有一个特殊状态的处理函数,用于优化处理
shouldComponentUpdate():判断组件是否需要update调用

加上最重要的render方法,React自身带的方法刚刚好10个。对于初学者来说是比较难以消化。但其实getInitialState,componentDidMount,render三个状态方法都能完成大部分组件,不必望而却步。

回到组件化的主题。
一个页面结构模块化的组件,能独立封装整个组件的过程线

我们换算成React生命周期方法:

组件的状态方法流中,有两点需要特殊说明:
1,二次渲染:
由于React的虚拟Dom特性,组件的render函数不需自己触发,根据props和state的改变自个通过差异算法,得出最优的渲染。
请求CGI一般都是异步,所以必定带来二次渲染。只是空数据渲染的时候,有可能会被React优化掉。当数据回来,通过setState,触发二次render

2,componentWiillMount与componentDidMount的差别
和大多数React的教程文章不一样,ajax请求我建议在WillMount的方法内执行,而不是组件初始化成功之后的DidMount。这样能在“空数据渲染”阶段之前请求数据,尽早地减少二次渲染的时间。
willMount只会执行一次,非常适合做init的事情。
didMount也只会执行一次,并且这时候真实的Dom已经形成,非常适合事件绑定和complete类的逻辑

### 二,JSX很丑,但是组件内聚的关键!
WebComponents的标准之一,需要模板能力。本是以为是我们熟悉的模板能力,但React中的JSX这样的怪胎还是令人议论纷纷。 React还没有火起来的时候,大家就已经在微博上狠狠地吐槽了“JSX写的代码这TM的丑”。这其实只是Demo阶段JSX,等到实战的大型项目中的 JSX,包含多状态多数据多事件的时候,你会发现………….JSX写的代码还是很丑。

 

(即使用sublime-babel等插件高亮,逻辑和渲染耦合一起,阅读性还是略差)

为什么我们会觉得丑?因为我们早已经对“视图-样式-逻辑”分离的做法潜移默化。
基于维护性和可读性,甚至性能,我们都不建议直接在Dom上面绑定事件或者直接写style属性。我们会在JS写事件代理,在CSS上写上 classname,html上的就是清晰的Dom结构。我们很好地维护着MVC的设计模式,一切安好。直到JSX把他们都糅合在一起,所守护的技术栈受 到侵略,难免有所抵制。

但是从组件化的目的来看,这种高内聚的做法未尝不可。
下面的代码,之前的“逻辑视图分离”模式,我们需要去找相应的js文件,相应的event函数体内,找到td-info的class所绑定的事件。
对比起JSX的高度内聚,所有事件逻辑就是在本身jsx文件内,绑定的就是自身的showInfo方法。组件化的特性能立马体现出来。

Java代码
  1. <p className=“td-info” onClick={this.showInfo}>{obj.info}</p>

(注意:虽然写法上我们好像是HTML的内联事件处理器,但是在React底层并没有实际赋值类似onClick属性,内层还是使用类似事件代理的方式,高效地维护着事件处理器)
再来看一段style的jsx。其实jsx没有对样式有硬性规定,我们完全可遵循之前的定义class的逻辑。任何一段样式都应该用class来 定义。在jsx你也完全可以这样做。但是出于组件的独立性,我建议一些只有“一次性”的样式直接使用style赋值更好。减少冗余的class。

Java代码
  1. <div className=“list” style={{background: “#ddd”}}>
  2.    {list_html}
  3. </div>

或许JSX内部有负责繁琐的逻辑样式,可JSX的自定义标签能力,组件的黑盒性立马能体验出来,是不是瞬间美好了很多。

Java代码
  1. render: function(){
  2.     return (
  3.       <div>
  4.          <Menus bannerNums={this.state.list.length}></Menus>
  5.          <TableList data={this.state.list}></TableList>
  6.       </div>
  7.    );
  8. }

虽然JSX本质上是为了虚拟Dom而准备的,但这种逻辑和视图高度合一对于组件化未尝不是一件好事。

学习完React这个组件化框架后,看看组件化能力点的完成情况
“资源高内聚”—— (33%)  html与js内聚
“作用域独立”—— (50%)  js的作用域独立
“自定义标签”—— (100%)jsx
“可相互组合”—— (50%)  可组合,但缺乏有效的加载方式
“接口规范化”—— (100%)组件生命周期方法
### Webpack 资源组件化
对于组件化的资源独立性,一般的模块加载工具和构建流程视乎变得吃力。组件化的构建工程化,不再是之前我们常见的,css合二,js合三,而是体 验在组件间的依赖于加载关系。webpack正好符合需求点,一方面填补组件化能力点,另一方帮助我们完善组件化的整体构建环境。

首先要申明一点是,webpack是一个模块加载打包工具,用于管理你的模块资源依赖打包问题。这跟我们熟悉的requirejs模块加载工具,和grunt/gulp构建工具的概念,多多少少有些出入又有些雷同。

首先webpak对于CommonJS与AMD同时支持,满足我们模块/组件的加载方式。

Java代码
  1. require(“module”);
  2. require(“../file.js”);
  3. exports.doStuff = function() {};
  4. module.exports = someValue;

 

Java代码
  1. define(“mymodule”, [“dep1”“dep2”], function(d1, d2) {
  2.     return someExportedValue;
  3. });

当然最强大的,最突出的,当然是模块打包功能。这正是这一功能,补充了组件化资源依赖,以及整体工程化的能力
根据webpack的设计理念,所有资源都是“模块”,webpack内部实现了一套资源加载机制,可以把想css,图片等资源等有依赖关系的“模块”加载。这跟我们使用requirejs这种仅仅处理js大大不同。而这套加载机制,通过一个个loader来实现。

Java代码
  1. // webpack.config.js
  2. module.exports = {
  3.     entry: {
  4.         entry: ‘./index.jsx’,
  5.     },
  6.     output: {
  7.         path: __dirname,
  8.         filename: ‘[name].min.js’
  9.     },
  10.     module: {
  11.         loaders: [
  12.             {test: /\.css$/, loader: ‘style!css’ },
  13.             {test: /\.(jsx|js)?$/, loader: ‘jsx?harmony’, exclude: /node_modules/},
  14.             {test: /\.(png|jpg|jpeg)$/, loader: ‘url-loader?limit=10240’}
  15.         ]
  16.     }
  17. };

上面一份简单的webpack配置文件,留意loaders的配置,数组内一个object配置为一种模块资源的加载机制。test的正则为匹配文件规则,loader的为匹配到文件将由什么加载器处理,多个处理器之间用!分隔,处理顺序从右到左。

如style!css,css文件通过css-loader(处理css),再到style-loader(inline到html)的加工处理流。
jsx文件通过jsx-loader编译,‘?’开启加载参数,harmony支持ES6的语法。
图片资源通过url-loader加载器,配置参数limit,控制少于10KB的图片将会base64化。

#### 资源文件如何被require?

Java代码
  1. // 加载组件自身css
  2. require(‘./slider.css’);
  3. // 加载组件依赖的模块
  4. var Clip = require(‘./clipitem.js’);
  5. // 加载图片资源
  6. var spinnerImg = require(‘./loading.png’);

在webpack的js文件中我们除了require我们正常的js文件,css和png等静态文件也可以被require进来。我们通过webpack命令,编译之后,看看输出结果如何:

Java代码
  1. webpackJsonp([0], {
  2. /* 0 */
  3. /***/ function(module, exports, __webpack_require__) {
  4.           // 加载组件自身css
  5.           __webpack_require__(1);
  6.           // 加载组件依赖的模块
  7.           var Clip = __webpack_require__(5);
  8.           // 加载图片资源
  9.           var spinnerImg = __webpack_require__(6);
  10. /***/ },
  11. /* 1 */
  12. /***/ function(module, exports, __webpack_require__) {
  13. /***/ },
  14. /* 2 */
  15. /***/ function(module, exports, __webpack_require__) {
  16.           exports = module.exports = __webpack_require__(3)();
  17.           exports.push([module.id, “.slider-wrap{\r\n position: relative;\r\n width: 100%;\r\n margin: 50px;\r\n background: #fff;\r\n}\r\n\r\n.slider-wrap li{\r\n text-align: center;\r\n line-height: 20px;\r\n}”“”]);
  18. /***/ },
  19. /* 3 */
  20. /***/ function(module, exports) {
  21. /***/ },
  22. /* 4 */
  23. /***/ function(module, exports, __webpack_require__) {
  24. /***/ },
  25. /* 5 */
  26. /***/ function(module, exports) {
  27.           console.log(‘hello, here is clipitem.js’) ;
  28. /***/ },
  29. /* 6 */
  30. /***/ function(module, exports) {
  31.           module.exports = “data:image/png;base64,iVBORw0KGg……”
  32. /***/ }
  33. ]);

webpack编译之后,输出文件视乎乱糟糟的,但其实每一个资源都被封装在一个函数体内,并且以编号的形式标记(注释)。这些模块,由 webpack的__webpack_require__内部方法加载。入口文件为编号0的函数index.js,可以看到 __webpack_require__加载其他编号的模块。
css文件在编号1,由于使用css-loader和style-loader,编号1-4都是处理css。其中编号2我们可以看我们的css的string体。最终会以内联的方式插入到html中。
图片文件在编号6,可以看出exports出base64化的图片。

#### 组件一体输出

Java代码
  1. // 加载组件自身css
  2. require(‘./slider.css’);
  3. // 加载组件依赖的模块
  4. var React = require(‘react’);
  5. var Clip = require(‘../ui/clipitem.jsx’);
  6. // 加载图片资源
  7. var spinnerImg = require(‘./loading.png’);
  8. var Slider = React.createClass({
  9.     getInitialState: function() {
  10.         // …
  11.     },
  12.     componentDidMount: function(){
  13.         // …
  14.     },
  15.     render: function() {
  16.         return (
  17.             <div>
  18.                <Clip data={this.props.imgs} />
  19.                <img className=“loading” src={spinnerImg} />
  20.             </div>
  21.         );
  22.     }
  23. });
  24. module.exports = Slider;

如果说,react使到html和js合为一体。
那么加上webpack,两者结合一起的话。js,css,png(base64),html 所有web资源都能合成一个JS文件。这正是这套方案的核心所在:组件独立一体化。如果要引用一个组件,仅仅require(‘./slider.js’) 即可完成。

加入webpack的模块加载器之后,我们组件的加载问题,内聚问题也都成功地解决掉
“资源高内聚”—— (100%) 所有资源可以一js输出
“可相互组合”—— (100%)  可组合可依赖加载

### CSS模块化实践
很高兴,你能阅读到这里。目前我们的组件完成度非常的高,资源内聚,易于组合,作用域独立互不污染。。。。等等eee,视乎CSS模块的完成度有欠缺。
那么目前组件完成度来看,CSS作用域其实是全局性的,并非组件内部独立。下一步,我们要做得就是如何让我们组件内部的CSS作用域独立。
这时可能有人立马跳出,大喊一句“德玛西亚!”,哦不,应该是“用sass啊傻逼!”。可是项目组件化之后,组件的内部封装已经很好了,其内部 dom结构和css趋向简单,独立,甚至是破碎的。LESS和SASS的一体式样式框架的设计,他的嵌套,变量,include,函数等丰富的功能对于整 体大型项目的样式管理非常有效。但对于一个功能单一组件内部样式,视乎就变的有点格格不入。“不能为了框架而框架,合适才是最好的”。视乎原生的css能 力已经满足组件的样式需求,唯独就是上面的css作用域问题。

这里我给出思考的方案: classname随便写,保持原生的方式。编译阶段,根据组件在项目路径的唯一性,由【组件classname+组件唯一路径】打成md5,生成全局唯 一性classname。正当我要写一个loader实现我的想法的时候,发现歪果仁已经早在先走一步了。。。。
这里具体方案参考我之前博客的译文:http://www.alloyteam.com/2015/10/8536/

之前我们讨论过JS的模块。现在通过Webpack被加载的CSS资源叫做“CSS模块”?我觉得还是有问题的。现在style-loader插 件的实现本质上只是创建link[rel=stylesheet]元素插入到document中。这种行为和通常引入JS模块非常不同。引入另一个JS模 块是调用它所提供的接口,但引入一个CSS却并不“调用”CSS。所以引入CSS本身对于JS程序来说并不存在“模块化”意义,纯粹只是表达了一种资源依 赖——即该组件所要完成的功能还需要某些asset。
因此,那位歪果仁还扩展了“CSS模块化”的概念,除了上面的我们需要局部作用域外,还有很多功能,这里不详述。具体参考原文 http://glenmaddern.com/articles/css-modules

非常赞的一点,就是cssmodules已经被css-loader收纳。所以我们不需要依赖额外的loader,基本的css-loader开启参数modules即可。

Java代码
  1. //webpack.config.js
  2.     module: {
  3.         loaders: [
  4.             {test: /\.css$/, loader: ‘style!css?modules&localIdentName=[local]__[name]_[hash:base64:5]’ },
  5.         ]
  6.     }
  7. ….

modules参数代表开启css-modules功能,loaclIdentName为设置我们编译后的css名字,为了方便Debug,我们 把classname(local)和组件名字(name)输出。当然可以在最后输出的版本为了节省提交,仅仅使用hash值即可。另外在react中的 用法大概如下。

Java代码
  1. var styles = require(‘./banner.css’);
  2. var Banner = new React.createClass({
  3.     …
  4.     render: function(){
  5.         return (
  6.             <div>
  7.                 <div className={styles.classA}></div>
  8.             </div>
  9.         )
  10.     }
  11. });

最后这里关于出于对CSS一些思考,
关于css-modules的其它功能,我并不打算使用。在内部分享【我们竭尽所能地让CSS变得复杂】中提及:

引用
我们项目中大部分的CSS都不会像boostrap那样需要变量来设置,身为一线开发者的我们大概能够感受到:设计师们改版UI,绝对不是简单的换个色或改个间距,而是面目全非的全新UI,这绝对不是一个变量所能解决的”维护性“。
反而项目实战过程中,真正要解决的是:在版本迭代过程中那些淘汰掉的过期CSS,大量地堆积在项目当中。我们像极了家中的欧巴酱不舍得丢掉没用的东西,因为这可是我们使用sass或less编写出具有高度的可维护性的,肯定有复用的一天。
这些堆积的过期CSS(or sass)之间又有部分依赖,一部分过期没用了,一部分又被新的样式复用了,导致没人敢动那些历史样式。结果现网项目迭代还带着大量两年前没用的样式文件。

组件化之后,css的格局同样被革新了。可能postcss才是你现在手上最适合的工具,而不在是sass。

到这里,我们终于把组件化最后一个问题也解决了。
“作用域独立”—— (100%) 如同shadowDom作用域独立

到这里,我们可以开一瓶82年的雪碧,好好庆祝一下。不是吗?

### 组件化之路还在继续
webpack和react还有很多新非常重要的特性和功能,介于本文仅仅围绕着组件化的为核心,没有一一阐述。另外,配搭gulp/grunt 补充webpack构建能力,webpack的codeSplitting,react的组件通信问题,开发与生产环境配置等等,都是整套大型项目方案的 所必须的,限于篇幅问题。可以等等我更新下篇,或大家可以自行查阅。
但是,不得不再安利一下react-hotloader神器。热加载的开发模式绝对是下一代前端开发必备。严格说,如果没有了热加载,我会很果断 地放弃这套方案,即使这套方案再怎么优秀,我都讨厌react需要5~6s的编译时间。但是hotloader可以在我不刷新页面的情况下,动态修改代 码,而且不单单是样式,连逻辑也是即时生效。

如上在form表单内。使用热加载,表单不需要重新填写,修改submit的逻辑立刻生效。这样的开发效率真不是提高仅仅一个档次。必须安利一下。

或许你发现,使用组件化方案之后,整个技术栈都被更新了一番。学习成本也不少,并且可以预知到,基于组件化的前端还会很多不足的问题,例如性能优 化方案需要重新思考,甚至最基本的组件可复用性不一定高。后面很长一段时间,需要我们不断磨练与优化,探求最优的前端组件化之道。
至少我们可以想象,不再担心自己写的代码跟某个谁谁冲突,不再为找某段逻辑在多个文件和方法间穿梭,不再copy一片片逻辑然后改改。我们每次编写都是可重用,可组合,独立且内聚的组件。而每个页面将会由一个个嵌套组合的组件,相互独立却相互作用。

对于这样的前端未来,有所期待,不是很好吗
至此,感谢你的阅读。

[转载]WEB免费打印控件推荐 - 邛水河畔的娃 - 博客园

mikel阅读(948)

来源: [转载]WEB免费打印控件推荐 – 邛水河畔的娃 – 博客园

在WEB系统中,打印的确是个烦人的问题。

要么自己开发打印控件,如果项目时间紧,肯定来不及。

要么购买成熟的打印控件,如果是大项目可以考虑,但如果项目只有几K到1、2W之间,这就麻烦了。

前段时间有机会接触了一下WEB打印。在博客园、CSDN上找了老半天,终于,功夫不付有心人,终于找到了。

现在就推荐给大家分享。

1、  DLPrinter

DLPrinter打印控件完全免费,界面大方、使用简单、但无签名,支持打印预览、直接打印,可设置页眉、页脚、页边距、打印份数、纸张大小等信息。遗憾的是作者不知道是什么原因,从2007年至今没的更新。

作者博客:http://www.cnblogs.com/Yahong111/

下载地址:/Files/panshenglu/DLPrinter.rar

打印预览图:

 

 

 

 

2、墙外打印控件

墙外打印控件(QWPrint)是一款小巧的打印辅助软件,能够帮助众多制作B/S类程序的程序员更加灵活的控制客户端打印。

功能特点:

  • 小巧轻便,客户端在第一次使用时只要下载一个ActiveX控件即可使用。
  • 控制多种打印设置。程序员可以通过控件进行多项设置,包括设置打纸的页边距,页眉页脚,纸张大小等参数。
  • 精确控制打印。可以方便实现web下的套打操作。

作者博客:http://www.xwangye.com/

下载地址:/Files/panshenglu/墙外打印控件.rar

打印预览图:

 

 

 

 

 

 

3、Lodop(强烈推荐)

对于这个打印控件,用一个词来形容:强大!不仅调用方便,而且功能比你想像中要强大得多。

更多介绍大家到作者博客详细了解。

作者博客:http://blog.sina.com.cn/caoyanqingwebsite/

下载地址:/Files/panshenglu/lodop4.0.zip

打印预览图:

 

 

4、楚琳Web打印控件

楚琳Web打印控件是以C/S架构的报表工具进行B/S架构的报表设计
【软件特点】
1、报表功能强大,报表设计采用FastReport,可以和水晶报表媲美,支持自定义纸张大小,支持直接打印。
2、报表设计高效而简单,和C/S架构下的报表设计效率差不多。
3、方便的中国式报表的设计,可以快速而准确设计出报表中的表格线。
4、支持ASP, ASP.NET, PHP, JSP等多开发语言。
5、支持简体中文、英文、繁体中文等多国语言。
6、免费使用,免费版无功能和时间限制。

作者博客:http://www.cnblogs.com/dong0738/

下载地址:http://www.onlinedown.net/soft/112053.htm
http://www.duote.com/soft/30400.html
http://www.xinyuerj.com/ASP/楚琳Web打印控件.rar

打印预览图:

——————————————————————————-

[转载]Android四大组件之Activity详解 - caobotao - 博客园

mikel阅读(852)

来源: [转载]Android四大组件之Activity详解 – caobotao – 博客园

.Activity的本质

Activity是Android提供的四大组件之一,是进行Android开发 必不可少的组件.Activity是一个界面的载体,可以把它与html页面进行类比,html页面由各种各样的标签组成,而Activity则可以由各 种控件组成.然而Activity也并不是那么简单.查看Activity类的源码我们就可以看到,这个类大概有六千多行代码,说明Android对 Activity的处理是相当复杂的.不过我们平时进行开发的时候不需要了解到那么深入的地步,因为我们可以根据Activity中提供的方法快速的开 发,Activity生命周期中涉及到七个回调方法.

.Activity的生命周期

Android官方文档给出的Activity生命周期图

当打开一个Activity的时候,会调用这个Activity的 onCreate()方法,接着调用onStart()方法,然后调用onResume()方法.当onStart()方法执行之后,我们就可以看到这个 Activity界面了.下面通过代码对单个Activity生命周期做进一步的解释.

MainActivity.java

复制代码
package com.example.caobotao.activitylifecircle;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i("info","MainActivity onCreate()");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.i("info","MainActivity onStart()");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.i("info","MainActivity onResume()");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.i("info", "MainActivity onPause()");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i("info", "MainActivity onStop()");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i("info","MainActivity onDestroy()");
    }


    @Override
    protected void onRestart() {
        super.onRestart();
        Log.i("info","MainActivity onRestart()");
    }

}
复制代码

 

运行项目后,Logcat输出:

11-24 20:00:49.160 20104-20104/com.example.caobotao.activitylifecircle I/info: MainActivity onCreate()
11-24 20:00:49.170 20104-20104/com.example.caobotao.activitylifecircle I/info: MainActivity onStart()
11-24 20:00:49.170 20104-20104/com.example.caobotao.activitylifecircle I/info: MainActivity onResume()

 

点击返回键,Logcat输出:

11-24 20:05:15.370 20104-20104/com.example.caobotao.activitylifecircle I/info: MainActivity onStop()
11-24 20:05:15.370 20104-20104/com.example.caobotao.activitylifecircle I/info: MainActivity onDestroy()

 

下面简单讲一下Activity生命周期中每个函数的作用:

onCreate() :

当Activity第一次被创建的时候调用此方法.一般在此方法中进行控件的声明,添加事件等初始化工作.

onStart():

当Activity被显示到屏幕上的时候调用此方法.

onResume():

当此Activity能够被操作之前,也就是能够获得用户的焦点之前调用此方法.

onRestart():

当Activity被停止后又被再次启动之前调用此方法.接着将调用onStart()方法.

onPause():

当第一个Activity通过Intent启动第二个Activity的时候,将 调用第一个Activity的onPause()方法.然后调用第二个Activity的 onCreate(),onStart(),onResume()方法,接着调用第一个Activity的onStop()方法.如果Activity重 新获得焦点,则将调用onResume()方法;如果此Activity进入用户不可见状态,那么将调用onStop()方法.

onStop():

当第一个Activity被第二个Activity完全覆盖,或者被销毁的时候回调用此方法.如果此Activity还会与用户进行交互,将调用onRestart方法();如果此Activity将被销毁,那么将调用onDestroy()方法.

onDestroy():

Activity被销毁之前调用此方法.或者是调用finish()方法结束Activity的时候调用此方法.可以在此方法中进行收尾工作,比如释放资源等.

(注意:重写某个Activity的这些回调方法的时候需要首先在第一行调用基类Activity的相应的回调方法.比如super.onCreate(),super.onStart()等等.)

 

接下来了解一下多个Activity交互中的生命周期.在第一个Activity中添加一个按钮,用于启动第二个Activity.

AActivity.java

复制代码
package com.example.caobotao.activitylifecircle;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class AActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i("info","AActivity onCreate()");
        findViewById(R.id.btnStartBAty).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(AActivity.this,BActivity.class));
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.i("info","AActivity onStart()");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.i("info","AActivity onResume()");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.i("info", "AActivity onPause()");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i("info", "AActivity onStop()");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i("info","AActivity onDestroy()");
    }


    @Override
    protected void onRestart() {
        super.onRestart();
        Log.i("info","AActivity onRestart()");
    }

}
复制代码

 

BActivity.java

复制代码
package com.example.caobotao.activitylifecircle;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;

public class BActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_baty);
        Log.i("info","BActivity oncreate()");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.i("info","BActivity onStart()");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.i("info","BActivity onResume()");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i("info","BActivity onStop()");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i("info", "BActivity onDestroy()");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.i("info","BActivity onRestart()");
    }

}
复制代码

 

运行项目后,Logcat输出:

11-26 21:14:38.960 6208-6208/com.example.caobotao.activitylifecircle I/info: AActivity onCreate()
11-26 21:14:38.970 6208-6208/com.example.caobotao.activitylifecircle I/info: AActivity onStart()
11-26 21:14:38.970 6208-6208/com.example.caobotao.activitylifecircle I/info: AActivity onResume()

 

点击按钮打开BActivity后,Logcat输出:

11-26 21:14:42.050 6208-6208/com.example.caobotao.activitylifecircle I/info: AActivity onPause()
11-26 21:14:42.080 6208-6208/com.example.caobotao.activitylifecircle I/info: BActivity oncreate()
11-26 21:14:42.090 6208-6208/com.example.caobotao.activitylifecircle I/info: BActivity onstart()
11-26 21:14:42.090 6208-6208/com.example.caobotao.activitylifecircle I/info: BActivity onresume()
11-26 21:14:45.750 6208-6208/com.example.caobotao.activitylifecircle I/info: AActivity onstop()

 

然后点击BACK键,Logcat输出:

11-26 21:20:38.650 6208-6208/com.example.caobotao.activitylifecircle I/info: BActivity onPause()  
11-26 21:20:38.970 6208-6208/com.example.caobotao.activitylifecircle I/info: AActivity onRestart()
11-26 21:20:38.970 6208-6208/com.example.caobotao.activitylifecircle I/info: AActivity onStart()
11-26 21:20:38.970 6208-6208/com.example.caobotao.activitylifecircle I/info: AActivity onResume()
11-26 21:20:41.710 6208-6208/com.example.caobotao.activitylifecircle I/info: BActivity onStop()
11-26 21:20:41.710 6208-6208/com.example.caobotao.activitylifecircle I/info: BActivity onDestroy()

可以看出,当启动第二个Activity时,总是先调用第一个Activity的 onPause()方法,然后如果第二个Activity是第一次创建的话,则再调用第二个Activity的onCreate()方法,否则调用第二个 Activity的onRestart()方法,接着调用onStart(),onResume()方法.

.Activity生命周期交互设计思想

到这里,大家可能会有些疑问,谷歌为什么要这样设计呢?如:

1.当从第一个Activity启动第二个Activity的时候,为什么先调用第一个Activity的onPause()方法,然后再调用第二个Activity的onCreate()等方法呢?

解释:假如有这样一个情况,你正在使用APP听音乐,突然来了一个电话,那么当然 需要先暂停音乐,然后进行电话的处理。所以这就是onPause()方法的作用:可以用来保存当前的各种信息.可以在这个APP的onPause()方法 中实现暂停音乐的逻辑,然后再处理电话的业务处理.

2.当从第一个Activity启动第二个Activity的时候,为什么第一个Activity的onStop()方法是调用完第二个Activity的系列方法后才调用呢,为什么不在第一个Activity的onPause()方法之后就调用呢?

解释:这是谷歌对安全方面的一个考虑.假如先调用第一个Activity的 onStop()方法,那么此时第一个Activity将不可见,如果接下来调用第二个Activity的一系列创建方法失败了,那么就会导致这两个 Activity都没显示在屏幕上,就会出现黑屏等不友好界面.如果是调用完第二个Activity一系列创建方法后,再调用第一个Activity的 onStop()方法,就会避免这种情况的发生.

 

.Activity生命周期的应用场景

讲了这么多,大家可能会问,Activity生命周期到底实际中怎么使用.下面通 过实例来做简单的运用.在AActivity中进行音乐的播放,当点击按钮打开BActivity后,音乐需要暂停,然后点击BACK键返回到 AActivity后音乐继续播放.这就需要在AActivity中的onPause()方法中进行音乐的暂停操作,以及暂停时音乐播放位置的记录.在 AActivity中的onResume()方法中实现重新返回前台时继续音乐的播放.

AActivity.java

复制代码
package com.example.caobotao.activitylifecircle;

import android.app.Activity;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class AActivity extends Activity {
    private MediaPlayer mediaPlayer;
    private int position;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mediaPlayer = MediaPlayer.create(this,R.raw.song);
        mediaPlayer.start();
        Log.i("info","AActivity onCreate()");
        findViewById(R.id.btnStartBAty).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(AActivity.this,BActivity.class));
            }
        });
    }


    @Override
    protected void onStart() {
        super.onStart();
        Log.i("info","AActivity onStart()");
    }

    @Override
    protected void onResume() {
        super.onResume();
        //如果播放的位置不是0
        if (position != 0){
            mediaPlayer.seekTo(position);//获取播放的位置
            mediaPlayer.start();//开始播放
        }
        Log.i("info","AActivity onResume()");
    }

    @Override
    protected void onPause() {
        super.onPause();
        //如果播放器正在播放
        if (mediaPlayer.isPlaying()){
            mediaPlayer.pause();//暂停播放
            position = mediaPlayer.getCurrentPosition();//获得暂停时播放的位置
        }
        Log.i("info", "AActivity onPause()");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i("info", "AActivity onStop()");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i("info","AActivity onDestroy()");
    }


    @Override
    protected void onRestart() {
        super.onRestart();
        Log.i("info","AActivity onRestart()");
    }

}
复制代码

BActivity.java

复制代码
package com.example.caobotao.activitylifecircle;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;

public class BActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_baty);
        Log.i("info","BActivity onCreate()");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.i("info","BActivity onStart()");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.i("info","BActivity onResume()");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.i("info","BActivity onPause()");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.i("info","BActivity onStop()");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i("info", "BActivity onDestroy()");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.i("info","BActivity onRestart()");
    }

}
复制代码

 

.启动系统中常见的Activity

系统给我们提供了很多常用的Activity,可以用来打开浏览器,打开发短信界面,打开相册界面,打开拨号界面等等.

打开浏览器网页:

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com")); 
startActivity(intent);

打开相册:

Intent intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivity(intent);

打开发送短信界面:

Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.setType("text/plain");
intent.putExtra(Intent.EXTRA_TEXT,"Hello World !");
startActivity(intent);

打开拨号界面:

Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("tel:110"));
startActivity(intent);

.Activity之间的数据交互

我们可以使用Intent对象进行数据的传递.Intent重载了很多putExtra()方法,囊括了JAVA八大基本类型及其数组类型等.

Activity1.java

View Code

 

Activity2.java

View Code

 

Intent对象还有一个putExtras(Bundle bundle)方法,bundle的意思为一捆,就是把需要传递的数据打成一捆进行传递.下面利用Bundle实现上面的示例,效果一样.

Activity1.java

View Code

 

Activity2.java

View Code

 

在实际开发中,可能需要传递的数据比较多,不仅有姓名,年龄,可能还有性别,学校,爱好等等.根据面向对象的思想,我们应该想到把这些信息封装成一个java实体类,以达到更好的复用性,耦合性等目的,然后使用Bundle对象的putSerializable()方法将此需要传递的对象传递出去,在另一个Activity中用Intent对象的getSerializableExtra()方法进行接收.

Person.java

View Code

 

Activity1.java

View Code

 

Activity2.java

View Code

.Task与Back Stack(引自官方文档)

task 是一系列被操作的 activity 的集合,用户进行操作时将与这些 activity 进行交互。 这些 activity 按照启动顺序排队存入一个栈(即“back stack”)。我们知道栈是一个后进先出的数据结构,在一个task中,每打开一个新的activity,就会将其压入栈顶的位置;每次点击Back键,栈顶的activity就会被移除.

大部分 task 都启动自 Home 屏幕。当用户触摸 application launcher 中的图标(或 Home 屏幕上的快捷图标)时,应用程序的 task 就进入前台。 如果该应用不存在 task(最近没有使用过此应用),则会新建一个 task,该应用的“main”activity 作为栈的根 activity 被打开。

当用户返回到 home屏幕执行另一个 task 时,一个 task 被移动到后台执行,此时它的返回栈(back stack)也被保存在后台, 同时 android 为新 task 创建一个新的返回栈(back stack),当它被再次运行从而返回前台时,它的返回栈(back stack)被移到前台,并恢复其之前执行的activity,如下图所示。 如果后台有太多运行 task ,系统将会杀死一些 task 释放内存。

如果当前 activity 启动了另一个 activity,则新的 activity 被压入栈顶并获得焦点。 前一个 activity 仍保存在栈中,但是被停止。activity 停止时,系统会保存用户界面的当前状态。 当用户按下返回键,则当前 activity 将从栈顶弹出(被销毁),前一个 activity 将被恢复(之前的用户界面状态被恢复)。 activity 在栈中的顺序永远不会改变,只会压入和弹出——被当前 activity 启动时压入栈顶,用户用返回键离开时弹出。 这样,back stack 以“后进先出”的方式运行。下图以时间线的方式展示了多个 activity 切换时对应当前时间点的 back stack 状态。

图: task 中的每个新 activity 都会相应在 back stack 中增加一项。当用户按下返回键时,

当前 activity 被销毁,前一个 activity 被恢复。

如果用户不停地按下返回键,则栈中每个 activity 都会依次弹出,并显示前一个 activity,直至用户回到 Home 屏幕(或者任一启动该 task 的 activity)。 当所有 activity 都从栈中弹出后, task 就此消失。

.Activity启动模式(引自官方文档)

在 manifest 文件中声明 activity 时,你可以利用activity元素的launchMode属性来设定 activity 与 task 的关系。

launchMode 属性指明了 activity 启动 task 的方式。 launchMode 属性可设为四种启动模式:

 

standard(默认模式):
默认值。系统在启动 activity 的 task 中创建一个新的 activity 实例,并把 intent 传送路径指向它。该 activity 可以被实例化多次,各个实例可以属于不同的 task,一个 task 中也可以存在多个实例。

 singleTop:

如果 activity 已经存在一个实例并位于当前 task 的栈顶,则系统会调用已有实例的onNewIntent()方法把 intent 传递给已有实例,而不是创建一个新的 activity 实例。activity 可以被实例化多次,各个实例可以属于不同的 task,一个 task 中可以存在多个实例(但仅当 back stack 顶的 activity 实例不是该 activity 的)。
比如,假定 task 的 back stack 中包含了根 activity A 和 activities B、C、D(顺序是 A-B-C-D;D 在栈顶)。
这时过来一个启动 D 的 intent。如果 D 的启动模式是默认的”standard”,则会启动一个新的实例,栈内容变为 A-B-C-D-D。
但是,如果 D 的启动模式是”singleTop”,则已有的 D 实例会通过onNewIntent():接收这个 intent,因为该实例位于栈顶——栈中内容仍然维持 A-B-C-D 不变。当然,如果 intent 是要启动 B 的,则 B 的一个新实例还是会加入栈中,即使 B 的启动模式是”singleTop”也是如此。 

注意: 一个 activity 的新实例创建完毕后,用户可以按回退键返回前一个 activity。 但是当 activity 已有实例正在处理刚到达的 intent 时,用户无法用回退键回到 onNewIntent() 中 intent 到来之前的 activity 状态。

singleTask:
系统将创建一个新的 task,并把 activity 实例作为根放入其中。但是,如果 activity 已经在其它 task 中存在实例,则系统会通过调用其实例的onNewIntent() 方法把 intent 传给已有实例,而不是再创建一个新实例。 此 activity 同一时刻只能存在一个实例。

注意: 虽然 activity 启动了一个新的 task,但用户仍然可以用回退键返回前一个 activity。

singleInstance:

除了系统不会把其它 activity 放入当前实例所在的 task 之外,其它均与”singleTask”相同。activity 总是它所在 task 的唯一成员;它所启动的任何 activity 都会放入其它 task 中。

举个例子,Android 的浏览器应用就把 web 浏览器 activity 声明为总是在它自己独立的 task 中打开——把 activity设为singleTask模式。 这意味着,如果你的应用提交 intent 来打开 Android 的浏览器,则其 activity 不会被放入你的应用所在的 task 中。 取而代之的是,或是为浏览器启动一个新的 task;或是浏览器已经在后台运行,只要把 task 调入前台来处理新 intent 即可。

无论 activity 是在一个新的 task 中启动,还是位于其它已有的 task 中,用户总是可以用回退键返回到前一个 activity 中。 但是,如果你启动了一个启动模式设为singleTask的 activity,且有一个后台 task 中已存在实例的话,则这个后台 task 就会整个转到前台。 这时,当前的 back stack 就包含了这个转入前台的 task 中所有的 activity,位置是在栈顶。下图就展示了这种场景。

 

图 . 启动模式为“singleTask”的 activity 如何加入 back stack 的示意图。 如果 activity 已经是在后台 task 中并带有

自己的 back stack,则整个后台 back stack 都会转入前台,并放入当前 task 的栈顶。

 

[转载]移动端页面(css)调试之“weinre大法” - 韩子迟 - 博客园

mikel阅读(949)

来源: [转载]移动端页面(css)调试之“weinre大法” – 韩子迟 – 博客园

移动端页面(css)调试之“weinre大法”

移动端页面调试一般分两步。第一步我们需要把本地(pc端)写的页面效果展现在移动端,一个很方便的办法是用 fiddler 作为代理,具体可以参考 如何用 fiddler 代理调试本地手机页面, 这样我们就能在自己的手机上看到真机效果,有时还可以用 alert 调试一些 js;当然,有时仅仅展现样式还不够,我们希望能像 chrome 面板或者 firebug 一样查看元素的 css 样式,甚至可以在控制台看到 js 的 console 输出,UC浏览器开发者版可以方便地做到这点,具体可以参考 如何使用 UC浏览器开发者版 进行移动端调试

接下来我们来隆重介绍下兼容所有浏览器的 “weinre大法” (html & css 调试利器,无法调试 js

1、weinre 是什么鬼


Weinre 是 Web Inspector Remote 的缩写(远程 web 检查器),它的作用就是相当于 chrome 的审查元素面板(Elements)一样,界面和用法也基本一样,无非不同的是:weinre 适合在移动端页面调试,比如手机访问页面的时候,我们可以使用 chrome 浏览器查看页面的 html 元素和 css 代码,我们可以对此进行更改,然后在手机端不需要刷新,立即可以看到效果。看到这里您或许已经明白了,没错,weinre 是调试 html 和 css 的利器,对于 js 的调试却爱莫能助了。

2、Node.js & weinre 安装


当然首先得安装 weinre。以前安装要依靠 java 环境(jdk),很麻烦,现在有了 Node.js 一下子方便许多。我们先安装 Node.js(参考 Node.js入门),安装完后进入 Node.js 的安装目录,进行 weinre 的安装。

weinre 安装完后,需要在本地开启一个监听服务器。我们先获取自己的 ip(ipconfig),比如我的 ip 为 192.168.1.101。我们在命令行敲下如下命令即可开启服务:

weinre -boundHost 192.168.1.101

3、访问 weinre


服务已经开启,我们赶紧在浏览器上访问服务地址吧!

在打开的页面中我们找到 Target Script 模块,将下图中这段 js 引入到需要调试的 html 文件中。

比如我在本地写了如下代码,将 js 引入后:

<style>
  div {
    width: 100px;
    height: 100px;
    background-color: blue;
  }
</style>
<div></div>
<script src="http://192.168.1.101:8080/target/target-script-min.js#anonymous"></script>

我们在移动端先任意找个浏览器打开该页面(fidder 代理),然后在 pc 端点击刚才打开页面最上面的链接(下图所示)

页面便会跳转至调试页面,这时就可以愉快地调试 css 了!


read more:各种 真机远程调试 方法 汇总

[转载]ASP.NET WebAPI 13 Filter - Barlow Du - 博客园

mikel阅读(983)

来源: [转载]ASP.NET WebAPI 13 Filter – Barlow Du – 博客园

Filter(筛选器)是基于AOP(面向方面编程)的设计,它的作用是Actionr的执行注入额外的逻辑,以达到横切注入的目的。

 

IFilter

在WebAPI中所以的Filter都实现了IFilter接口 ,IFilter接口只有一个只读属性AllowMultiple,它表示同类的Filter是否可以应用到同一目标对象上。

 

 

1
2
3
4
5
6
7
public interface IFilter
{
bool AllowMultiple { get; }
 }

 

 

 

 

FilterInfo

在HttpActionDescriptor与HttpControllerDescriptor中也存储了Action使用到的Filter。但 它不是直接以IFilter的形式进行存储,也是以FilterInfo(System.Web.Http.Filters)。

 

1
2
3
4
5
6
7
8
9
10
11
public sealed class FilterInfo
 {
public FilterInfo(IFilter instance, FilterScope scope);
public IFilter Instance { get; }
public FilterScope Scope { get; }
 }

 

 

 

在FilterInfo中有两个属性:Instance,Scope。其中Instance是IFilter对象。在实际对Filter运用中我们应该注意并发的情况,因为对于同一个Filter调用的都是同一个Filter对象。

 

 

 

FilterScope

 

对于FilterInfo类的Scope属性,它表示Filter的域。在WebAPI中Filter有3个域:Global,Controller,Action。

 

1
2
3
4
5
6
7
8
9
10
11
public enum FilterScope
 {
 Global = 0,
 Controller = 10,
 Action = 20,
 }

 

 

 

对于Controller,Action的Filter添加都是采用特性(Attribute)的方式。对于全局Filter是添加在HttpConfiguration的Filter。

 

 

 

FilterProvider

WebAPI提供了IFilterProvider(System.Web.Http.Filters)用于提供不同域下的Filter。

 

1
2
3
4
5
6
7
public interface IFilterProvider
{
IEnumerable<FilterInfo> GetFilters(HttpConfiguration configuration, HttpActionDescriptor actionDescriptor);
 }

 

 

 

在WebAPI中FilterProvider也是”标准化组件”,但是它是以multi的形式注入的.WebAPI中IFilter有两个默认实 现: ConfigurationFilterProvider, ActionDescriptorFilterProvider,从命名上我们就可以知道前者是提供全局Filter,后者是提供 Controller,Action的Filter。

 

 

 

 

5类Filter

WebAPI为我们定义了5类Filter。分别如下:

AuthenticationFilter:用于请求认证(IAuthenticationFilter)。

AuthorizationFilter:用于请求授权(IAuthorizationFilter)。

ActionFilter:ActionFilter:注册的操作会在Action执行的前后被调用(IActionFilter)。

ExceptionFilter:当目标Action抛出异常是调用(IExceptionFilter)。

OverrideFilter:用于屏蔽当前域之前的Filter(IOverrideFilter)。

 

本文重点讲后三类Filter,对于前两类,后续再做跟进。对于后三类Filter默认实现是ActionFilterAttribute,ExceptionFilterAttribute,OverrideActionFiltersAttribute。如下:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public abstract class ActionFilterAttribute : FilterAttribute, IActionFilter, IFilter
 {
protected ActionFilterAttribute();
public virtual void OnActionExecuted(HttpActionExecutedContext actionExecutedContext);
public virtual Task OnActionExecutedAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken);
public virtual void OnActionExecuting(HttpActionContext actionContext);
public virtual Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken);
 }

 

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public abstract class ExceptionFilterAttribute : FilterAttribute, IExceptionFilter, IFilter
 {
protected ExceptionFilterAttribute();
public virtual void OnException(HttpActionExecutedContext actionExecutedContext);
public virtual Task OnExceptionAsync(HttpActionExecutedContext actionExecutedContext, CancellationToken cancellationToken);
 }

 

 

1
2
3
4
5
6
7
8
9
10
11
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)]
public sealed class OverrideActionFiltersAttribute : Attribute, IOverrideFilter, IFilter
 {
public OverrideActionFiltersAttribute();
public Type FiltersToOverride { get; }
 }

 

 

 

 

 

 

唯一性

因为Filter有三个域,那么在采用特性标示的方式注入Filter的,必定有可能会对同一个Action上注入多个相同的Filter。显然在 使用中一般不会允许这种情况发生。WebAPI中提供了相应的策略用来保证Filter的唯一性。即对FilterAttribute添加 AttributeUsage特性,并将AllowMultiple设置为false.

当然这个设置过后你会发现在三个域上我还是可以添加相同的Filter,那么这个时候WebAPI会为我们筛选出一个Filter去调用。筛选规则如下:

Action>Controller>Global

即FilterScope的值越大,优先级超高。

 

 

ActionFilter

ActionFilterAttribute提供了4个虚方法: OnActionExecuted,OnActionExecutedAsync,OnActionExecuting,OnActionExecutingAsync 其实两个异步方法只是对两个同步的方法的封装,所以我们一般只重写两个同步方法。

 

 

在执行顺序上ActionFilter的规则是:

  1. 不同域:Global>ControllerAction
  2. 相同域:按照注入顺序执行

 

在执行OnActionExecuting的过程中如果给actionContext.Response赋值,那么Filter管道就会做返回处理,不再去做后续操作。

 

 

ExceptionFilter

在执行Action与整个ActionFilter管道中,如果抛出异常,

 

  1. ExpceptionFilter的执行顺序与ActionFilter正好相反
  2. ExceptionFilter管道处理的是整个ActionFilter管道抛出的异常,并不是单纯的Action抛出的异常
  3. 如果在ExceptionFilter管道中抛出异常,那么该ExceptionFilter都不会执行。

 

源码

 

Github: https://github.com/BarlowDu/WebAPI (API_13)

 

OverrideFilter

一个Action的所有Filter是Global,Controller,Action三个域下的Filter的总合,但有些时候我们并想要上一 级域的Filter这个时候就需要用到OverrideFilter。WebAPI的OverrideActionFiltersAttribute只会 屏蔽IActionFilter,并不会对其它的Filter进行屏蔽。

[转载]很多人很想知道怎么扫一扫二维码就能打开网站,就能添加联系人,就能链接wifi,今天做个demo(续集) - 毒逆天 - 博客园

mikel阅读(1165)

来源: [转载]很多人很想知道怎么扫一扫二维码就能打开网站,就能添加联系人,就能链接wifi,今天做个demo(续集) – 毒逆天 – 博客园

有些功能部分手机不能使用,网站,通讯录,wifi基本上每个手机都可以使用。(浏览器自带的扫描就够了,QQ扫码和微信扫码部分手机不能直接连接wifi)

在看之前你可以扫一扫下面几个二维码先看看效果:

上篇网站介绍了一下常用格式(http://www.cnblogs.com/dunitian/p/4998714.html),其实扫二维码的本质就是解析出一段字符串,为什么有一些神奇的功能呢?那是字符串的格式满足一些系统内置的协议或者格式,所以系统就帮你干了类似于发短信,打电话,添加联系人,连接wifi之类的事情。可以想像,你开了个店,店门口有个免费wifi的二维码,然后自己名片也是一个二维码~~~~是不是有点高大上的感觉?

其实代码并没有什么技术含量,既然有人要求,那就写一下吧,这次就不局限平台了,写了个通用的demo

核心类库:JQuery.qrcode.min.js

核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//中文字符处理
        function toUtf8(str) {
            var out, i, len, c;
            out = "";
            len = str.length;
            for (i = 0; i < len; i++) {
                c = str.charCodeAt(i);
                if ((c >= 0x0001) && (c <= 0x007F)) {
                    out += str.charAt(i);
                } else if (c > 0x07FF) {
                    out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
                    out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
                    out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
                } else {
                    out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
                    out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
                }
            }
            return out;
        }

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//生成二维码
        function outputQRCod(txt, width, height) {
            //先清空
            $("#code").empty();
            //中文格式转换
            var str = toUtf8(txt);
            //生成二维码
            $("#code").qrcode({
                render: "table",
                width: width,
                height: height,
                text: str
            });
        }

完整代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
<!DOCTYPE HTML>
<html>
<head>
    <meta charset="utf-8">
    <title>生成二维码</title>
    <script src="JavaScript/JQuery-1.8.3.min.js"></script>
    <script src="JavaScript/jQuery.qrcode.min.js"></script>
    <script type="text/javascript">
        $(function () {
            //没有中文就可以这么简单
            $('#code').qrcode("http://dnt.dkill.net");
            //普通转换
            $("#txt_btn").click(function () {
                outputQRCod($("#inputTxt").val(), 200, 200);
            });
            //URL演示
            $("#url_btn").click(function () {
                var urlTxt = $("#inputUrl").val();
                if (urlTxt.indexOf("http://") < 0) {
                    urlTxt = 'http://' + urlTxt;
                }
                outputQRCod(urlTxt, 400, 400);
            });
            //联系人添加演示
            $("#people_btn").click(function () {
                var txt = "BIZCARD:N:" + $('#inputName').val() + ";T:" + $('#inputPost').val() + ";C:" + $('#inputCompany').val() + ";A:" + $('#inputAddress').val() + ";B:" + $('#inputMobile').val() + ";E:" + $('#inputEmail').val() + ";;";
                outputQRCod(txt, 400, 400);
            });
            //WiFi连接演示
            $("#wifi_btn").click(function () {
                var txt = "WIFI:T:" + $('#WiFiType').val() + ";S:" + $('#inputWiFiName').val() + ";P:" + $('#inputWiFiPass').val() + ";;";
                console.log(txt);
                outputQRCod(txt, 400, 400);
            });
        });
        //中文字符处理
        function toUtf8(str) {
            var out, i, len, c;
            out = "";
            len = str.length;
            for (i = 0; i < len; i++) {
                c = str.charCodeAt(i);
                if ((c >= 0x0001) && (c <= 0x007F)) {
                    out += str.charAt(i);
                } else if (c > 0x07FF) {
                    out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
                    out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
                    out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
                } else {
                    out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
                    out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
                }
            }
            return out;
        }
        //生成二维码
        function outputQRCod(txt, width, height) {
            //先清空
            $("#code").empty();
            //中文格式转换
            var str = toUtf8(txt);
            //生成二维码
            $("#code").qrcode({
                render: "table",
                width: width,
                height: height,
                text: str
            });
        }
    </script>
</head>
<body>
    <div id="main">
        <div class="demo">
            <p>请输入内容然后点击按钮生成二维码:</p>
            <div id="code"></div>
            <h2>演示1:</h2>
            <p>
                普通文本:<input type="text" class="input" id="inputTxt" value="">
                <input type="button" id="txt_btn" value="生成二维码">
            </p>
            <h2>演示2:</h2>
            <p>
                URL 演示:<input type="text" class="input" id="inputUrl" value="">
                <input type="button" id="url_btn" value="生成二维码">
            </p>
            <h2>演示3:</h2>
            <p>加联系人:(选填)<input type="button" id="people_btn" value="生成二维码"></p>
            <p>
                姓名:<input type="text" class="input" id="inputName" value=""><br />
                <br />
                职位:<input type="text" class="input" id="inputPost" value=""><br />
                <br />
                公司:<input type="text" class="input" id="inputCompany" value=""><br />
                <br />
                地址:<input type="text" class="input" id="inputAddress" value=""><br />
                <br />
                手机:<input type="text" class="input" id="inputMobile" value=""><br />
                <br />
                邮箱:<input type="text" class="input" id="inputEmail" value=""><br />
                <br />
            </p>
            <h2>演示4:(现在的wifi一般都是WPA的,WEP的基本上10分钟内就能破解了)</h2>
            <p>
                WiFi名称:<input type="text" class="input" id="inputWiFiName" value=""><br />
                <br />
                WiFi密码:<input type="text" class="input" id="inputWiFiPass" value=""><br />
                <br />
                WiFi类型:<select id="WiFiType"><option value="WPA">WPA/WPA2</option>
                    <option value="WEP">WEP</option>
                    <option value="nopass">无加密</option>
                </select>
                <input type="button" id="wifi_btn" value="生成二维码">
            </p>
        </div>
    </div>
</body>
</html>

你们要什么效果就安装格式自己编辑一下就可以了,先闪了~~有机会再说一说二维码的美化

demo下载:http://pan.baidu.com/s/1pJGhV0f

[转载]如何在Visual Studio中开发自己的代码生成器插件 - JackWang-CUMT - 博客园

mikel阅读(953)

来源: [转载]如何在Visual Studio中开发自己的代码生成器插件 – JackWang-CUMT – 博客园

    Visual Studio是美国微软公司开发的一个基本完整的开发工具集,它包括了整个软件生命周期中所需要的大部分工具,如UML工具、代码管控工具、集成开发环境 (IDE)等等,且所写的目标代码适用于微软支持的所有平台.可以说.NET开发人员离不开它,它可以极大的提高编写软件的效率. Visual Studio作为一个世界级开发工具,当然支持通过插件方式对其功能进行扩展,开发人员可以定制自己的插件来进一步提升Visual Studio的功能.

1 什么是Add In?

所谓的add-in就是一些被Visual Studio加载到内存中运行的,能给用户提供特定功能的DLL动态链接库. 对于一般的开发情景来说,最常见的add-in用法就是可以通过.NET语言访问 DTE2 对象. DTE2是Visual Studio Automation Model的顶层对象,它具有一组接口和对象可以与 Visual Studio进行交互.DTE2可以做以下这些事情:

  • 访问和调用Visual Studio内置的函数和对象
  • 执行编译
  • 遍历解决方案中的项目
  • 在Visual Studio IDE中定制UI
  • 扩展Visual Studio功能…

2 创建VS Add In项目

用Visual Studio 2012创建名为MyVisualStudioAddin的项目(根据向导进行设置,这里不赘述),界面如下:

3 核心 Connect 类

插件入口就是Connect 类,先看一下Connect的类图:

  • Connect 实现外接程序对象的构造函数。请将您的初始化代码置于此方法内。
  • OnConnection 实现 IDTExtensibility2 接口的 OnConnection 方法。接收正在加载外接程序的通知。
  • OnDisconnection 实现 IDTExtensibility2 接口的 OnDisconnection 方法。接收正在卸载外接程序的通知。
  • OnAddInsUpdate 实现 IDTExtensibility2 接口的 OnAddInsUpdate 方法。当外接程序集合已发生更改时接收通知。
  • OnStartupComplete 实现 IDTExtensibility2 接口的 OnStartupComplete 方法。接收宿主应用程序已完成加载的通知。
  • OnBeginShutdown 实现 IDTExtensibility2 接口的 OnBeginShutdown 方法。接收正在卸载宿主应用程序的通知。
  • QueryStatus 实现 IDTCommandTarget 接口的 QueryStatus 方法。此方法在更新该命令的可用性时调用。
  • Exec 实现 IDTCommandTarget 接口的 Exec 方法。此方法在调用该命令时调用。
  • _applicationObject 是DTE2实例,是宿主应用程序的根对象。
  • _addInInstance是当前插件实例,表示此外接程序的对象。

首先定义一些内部的对象,主要是自定义的命令,如下所示:

复制代码
 1     /// <summary>用于实现外接程序的对象。</summary>
 2     /// <seealso class='IDTExtensibility2' />
 3     public class Connect : IDTExtensibility2, IDTCommandTarget
 4     {
 5         #region 命令定义  除了FindInSolutionExplorer外,此处的命令不是根据功能来命令的,而是根据命令所出现的位置来命令的
 6         private readonly string MY_COMMAND_FindInSolutionExplorer = "FindInSolutionExplorer";
 7         private readonly string MY_COMMAND_Project = "cmdInProject";//在项目上
 8         private readonly string MY_COMMAND_Solution = "cmdInSolution";//在解决方案上
 9         private readonly string MY_COMMAND_MenuBar = "cmdInMenuBar";//在菜单栏上
10         private readonly string MY_COMMAND_CodeWindow = "cmdInCodeWindow";//代码窗口
11         private readonly string MY_COMMAND_Files = "cmdInFiles";
12         #endregion
13 
14         private Command findCommand = null;
15         private CommandBarButton findCommandBarButtonButton = null;
16         private AddInLogger logger = null;
17 
18         private DTE2 _applicationObject;
19         private EnvDTE.AddIn _addInInstance;
20      ......
21 }
复制代码

初始化插件UI的代码:

复制代码
 1         public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
 2         {
 3           
 4             _applicationObject = (DTE2)application;
 5             _addInInstance = (AddIn)addInInst;
 6            
 7 
 8             if (connectMode == ext_ConnectMode.ext_cm_UISetup)
 9             {
10                 object[] contextGUIDS = new object[] { };
11                 Commands2 commands = (Commands2)_applicationObject.Commands;
12                 string toolsMenuName = "Tools";
13 
14                 //将此命令置于“工具”菜单上。
15                 //查找 MenuBar 命令栏,该命令栏是容纳所有主菜单项的顶级命令栏:
16                 Microsoft.VisualStudio.CommandBars.CommandBar menuBarCommandBar = ((Microsoft.VisualStudio.CommandBars.CommandBars)_applicationObject.CommandBars)["MenuBar"];
17 
18                 //在 MenuBar 命令栏上查找“工具”命令栏:
19                 CommandBarControl toolsControl = menuBarCommandBar.Controls[toolsMenuName];
20                 CommandBarPopup toolsPopup = (CommandBarPopup)toolsControl;
21 
22                 //如果希望添加多个由您的外接程序处理的命令,可以重复此 try/catch 块,
23                 //  只需确保更新 QueryStatus/Exec 方法,使其包含新的命令名。
24                 try
25                 {
26                     //将一个命令添加到 Commands 集合:
27                     Command command = commands.AddNamedCommand2(_addInInstance, "MyVisualStudioAddin", "MyVS外接程序", "Executes the command for MyVisualStudioAddin", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton);
28 
29                     //将对应于该命令的控件添加到“工具”菜单:
30                     if ((command != null) && (toolsPopup != null))
31                     {
32                         command.AddControl(toolsPopup.CommandBar, 1);
33                     }
34                 }
35                 catch (System.ArgumentException)
36                 {
37                     //如果出现此异常,原因很可能是由于具有该名称的命令
38                     //  已存在。如果确实如此,则无需重新创建此命令,并且
39                     //  可以放心忽略此异常。
40                 }
41 
42 
43 
44                 bool logtoOutputWindow = System.Diagnostics.Debugger.IsAttached;
45                 logger = new AddInLogger((DTE)_applicationObject, "MyVisualStudioAddin", logtoOutputWindow);
46                 logger.LogMessage(string.Format("OnConnection() called with connectMode: '{0}'", connectMode));
47                 try
48                 {
49                     switch (connectMode)
50                     {
51                         case ext_ConnectMode.ext_cm_UISetup:
52                             // We should never get here, this is temporary UI
53                             AddAddInUI();
54                             break;
55 
56                         case ext_ConnectMode.ext_cm_Startup:
57                             // The add-in was marked to load on startup
58                             AddAddInUI();
59                             break;
60 
61                         case ext_ConnectMode.ext_cm_AfterStartup:
62                             // The add-in was loaded by hand after startup using the Add-In Manager
63                             // Initialize it in the same way that when is loaded on startup
64                             AddAddInUI();
65                             break;
66                     }
67                 }
68                 catch (Exception ex)
69                 {
70                     logger.LogError(ex.ToString());
71                 }
72 
73 
74             }
75         }
复制代码

复制代码
  1         /// <summary>
  2         /// 设置插件UI
  3         /// </summary>
  4         private void AddAddInUI()
  5         {
  6             #region 获取CommandBars的名称
  7             //CommandBars commandBars = (CommandBars)applicationObject.CommandBars;
  8             //System.Text.StringBuilder sb = new System.Text.StringBuilder();
  9 
 10             //foreach (CommandBar cbar in commandBars)
 11             //{
 12             //    sb.AppendLine(cbar.Name);
 13             //}
 14 
 15             //System.Windows.Forms. Clipboard.SetText(sb.ToString());
 16 
 17             #region name
 18                         // MenuBar
 19                         //Standard
 20                         //Build
 21                         //Context Menus
 22                         //Data Design
 23                         //Formatting
 24                         //Style Application
 25                         //HTML Source Editing
 26                         //Class Designer Toolbar
 27                         //Text Editor
 28                         //Workflow
 29                         //Dialog Editor
 30                         //Image Editor
 31                         //Style Sheet
 32                         //Source Control
 33                         //Recorder
 34                         //Microsoft XML Editor
 35                         //Query Designer
 36                         //View Designer
 37                         //Database Diagram
 38                         //Table Designer
 39                         //Layout
 40                         //Help
 41                         //Debug Location
 42                         //Debug
 43                         //Report Formatting
 44                         //Report Borders
 45                         //Device
 46                         //Microsoft Office Excel 2007
 47                         //Microsoft Office Excel 2003
 48                         //Microsoft Office Word 2007
 49                         //Microsoft Office Word 2003
 50                         //Test Tools
 51                         //CrystalReportMain
 52                         //CrystalReportInsert
 53                         //ClearCase - Base
 54                         //ClearCase - UCM
 55                         //Error List
 56                         //Docked Window
 57                         //Menu Designer
 58                         //Properties Window
 59                         //Toolbox
 60                         //Task List
 61                         //Results List
 62                         //Stub Project
 63                         //No Commands Available
 64                         //Command Window
 65                         //AutoHidden Windows
 66                         //Expansion Manager
 67                         //Find Regular Expression Builder
 68                         //Replace Regular Expression Builder
 69                         //Wild Card Expression Builder
 70                         //Wild Card Expression Builder
 71                         //External Tools Arguments
 72                         //External Tools Directories
 73                         //Easy MDI Tool Window
 74                         //Easy MDI Document Window
 75                         //Easy MDI Dragging
 76                         //Open Drop Down
 77                         //Object Browser Objects Pane
 78                         //Object Browser Members Pane
 79                         //Object Browser Description Pane
 80                         //Find Symbol
 81                         //Drag and Drop
 82                         //Bookmark Window
 83                         //Error Correction
 84                         //EzMDI Files
 85                         //Ca&ll Browser
 86                         //Preview Changes
 87                         //Discover Service References
 88                         //Smart Tag
 89                         //Editor Context Menus
 90                         //Class View Context Menus
 91                         //Debugger Context Menus
 92                         //Project and Solution Context Menus
 93                         //Other Context Menus
 94                         //Sort By
 95                         //Show Columns
 96                         //Implement Interface
 97                         //Resolve
 98                         //Refactor
 99                         //Organize Usings
100                         //Create Private Accessor
101                         //Class View Multi-select Project references Items
102                         //Class View Multi-select Project references members
103                         //Class View Project
104                         //Class View Item
105                         //Class View Folder
106                         //Class View Grouping Folder
107                         //Class View Multi-select
108                         //Class View Multi-select members
109                         //Class View Member
110                         //Class View Grouping Members
111                         //Class View Project References Folder
112                         //Class View Project Reference
113                         //Class View Project Reference Item
114                         //Class View Project Reference Member
115                         //Project
116                         //Solution Folder
117                         //Cross Project Solution Project
118                         //Cross Project Solution Item
119                         //Cross Project Project Item
120                         //Cross Project Multi Project
121                         //Cross Project Multi Item
122                         //Cross Project Multi Solution Folder
123                         //Cross Project Multi Project/Folder
124                         //Item
125                         //Folder
126                         //Reference Root
127                         //Reference Item
128                         //Web Reference Folder
129                         //App Designer Folder
130                         //Web Project Folder
131                         //Web Folder
132                         //Web Item
133                         //Web SubWeb
134                         //References
135                         //Misc Files Project
136                         //Solution
137                         //Code Window
138                         //XAML Editor
139                         //Surface
140                         //DataSourceContext
141                         //DbTableContext
142                         //DataTableContext
143                         //RelationContext
144                         //FunctionContext
145                         //ColumnContext
146                         //QueryContext
147                         //DataAccessorContext
148                         //Context
149                         //Basic Context
150                         //Context
151                         //Context
152                         //Context
153                         //HTML Context
154                         //Script Context
155                         //ASPX Context
156                         //ASAX Context
157                         //ASPX Code Context
158                         //ASAX Code Context
159                         //ASPX VB Code Context
160                         //ASAX VB Code Context
161                         //ASMX Code Context
162                         //ASMX VB Code Context
163                         //Change &View
164                         //Static Node
165                         //Object Node
166                         //Multiple Static Nodes
167                         //Multiple Homogenous Object Nodes
168                         //Multiple Heterogenous Object Nodes
169                         //Multiple Heterogenous Nodes
170                         //Add &New
171                         //Selection
172                         //Container
173                         //TraySelection
174                         //Document Outline
175                         //Component Tray
176                         //Propertysheet
177                         //Configuration
178                         //Project
179                         //Multi-Select
180                         //System Propertysheet
181                         //Topic Menu
182                         //Topic Source Menu
183                         //Favorites Window Context Menu
184                         //Data Sources
185                         //Server Explorer
186                         //Managed Resources Editor Context Menu
187                         //Settings Designer
188                         //My Extensibility
189                         //Class Designer Context Menu
190                         //Class Diagram Context Menu
191                         //Class Details Context Menu
192                         //Selection
193                         //&Zoom
194                         //Page Layout
195                         //Designer Actions
196                         //&Navigation Tools
197                         //Resource View
198                         //Resource Editors
199                         //Resource Dialog Editors
200                         //Binary Editor
201                         //CSSDocOutline
202                         //CSSSource
203                         //Checkin Dialog Context Menu
204                         //Pending Checkin Window Context Menu
205                         //Standard TreeGrid context menu
206                         //GetVersion Dialog Context Menu
207                         //Check Out Dialog Context Menu
208                         //Macro
209                         //Module
210                         //Project
211                         //Root
212                         //TocContext
213                         //ResListContext
214                         //Query Diagram Pane
215                         //Query Diagram Table
216                         //Query Diagram Table Column
217                         //Query Diagram Join Line
218                         //Query Diagram Multi-select
219                         //Query Grid Pane
220                         //Query SQL Pane
221                         //Query Results Pane
222                         //Database Designer
223                         //Database Designer Table
224                         //Database Designer Relationship
225                         //Text Annotation
226                         //Database Project
227                         //DB Project Connection
228                         //DB Project Folder
229                         //Database References Folder
230                         //Folders
231                         //DB Project File
232                         //Query
233                         //Script
234                         //Database Reference Node
235                         //Files
236                         //Multi-select
237                         //PropertyBrowser
238                         //Editor
239                         //Script Outline
240                         //DefaultContext
241                         //ImageContext
242                         //SelectionContext
243                         //AnchorContext
244                         //Step Into Specific
245                         //Autos Window
246                         //Breakpoint
247                         //Load Symbols From
248                         //Breakpoints Window
249                         //Call Stack Window
250                         //Thread Tip Window
251                         //Data Tip Window
252                         //Disassembly Window
253                         //Locals Window
254                         //Memory Window
255                         //Modules Window
256                         //Output Window
257                         //Processes Window
258                         //Registers Window
259                         //Threads Window
260                         //Watch Window
261                         //Script Project
262                         //Thread IP Marker
263                         //Thread IP Markers
264                         //Control
265                         //Report
266                         //Row/Column
267                         //Cell
268                         //Field Chooser
269                         //Row/Column
270                         //Chart
271                         //Registry
272                         //File System
273                         //File System
274                         //File Types
275                         //User Interface
276                         //Launch Conditions
277                         //Custom Actions
278                         //New
279                         //Add
280                         //Add Special Folder
281                         //View
282                         //Project Node
283                         //A&dd
284                         //Cab Project Node
285                         //A&dd
286                         //File nodes
287                         //Dep. file nodes
288                         //Assembly nodes
289                         //Dep. assembly nodes
290                         //MSM nodes
291                         //Dep. MSM nodes
292                         //Output nodes
293                         //Simple file nodes
294                         //Simple output nodes
295                         //Dependency node
296                         //Multiple selections
297                         //Dep. Multiple selections
298                         //View
299                         //Editor
300                         //ORDesigner Context Menu
301                         //ORDesigner Context Menu
302                         //ORDesigner Context Menu
303                         //OTBObjCtxtMenu
304                         //SIDE Left Pane Context Menu
305                         //SIDE CertMgr Context Menu
306                         //Registry
307                         //File System
308                         //File System
309                         //New
310                         //Add
311                         //Add Special Folder
312                         //View
313                         //Project Node
314                         //A&dd
315                         //Cab Project Node
316                         //A&dd
317                         //File nodes
318                         //Dep. file nodes
319                         //Assembly nodes
320                         //Dep. assembly nodes
321                         //MSM nodes
322                         //Dep. MSM nodes
323                         //Output nodes
324                         //Dependency node
325                         //Multiple selections
326                         //Dep. Multiple selections
327                         //View
328                         //AppNet Designer Context
329                         //AppNet Project Node Context
330                         //Exe Project
331                         //Debug
332                         //Test Results Context Menu
333                         //Test List Editor Context Menu
334                         //Test List Context Menu
335                         //Test Run Context Menu
336                         //View Context Menu
337                         //Group
338                         //Database
339                         //Edit Text
340                         //Formula Parameter
341                         //Section
342                         //Default
343                         //Object Selection
344                         //Insert to Report
345                         //SchemaExplorer
346                         //AddNewItem
347                         //MicrosoftDataEntityDesign Context
348                         //MicrosoftDataEntityDesign Context
349                         //Find Checkouts
350                         //Pending Solution Checkins
351                         //Views Folder item context menu
352                         //UCM Project item context menu
353                         //View item context menu
354                         //Solution item context menu
355                         //Deliver
356                         //Rebase
357                         //ClearCase search Context Menus
358                         //System
359 
360             #endregion
361             #endregion
362           //------------------------------Code Window------------------------------------------------------
363             object[] contextUIGuids = new object[] { };
364             Commands2 commands = (Commands2)_applicationObject.Commands;
365             try
366             {
367                 findCommand = commands.Item(string.Format("{0}.{1}", _addInInstance.ProgID, MY_COMMAND_CodeWindow), -1);
368             }
369             catch
370             {
371                 // command doesn't exist
372             }
373 
374             if (findCommand == null)
375             {
376                 findCommand = commands.AddNamedCommand2(
377                     _addInInstance,
378                     MY_COMMAND_CodeWindow,
379                     MY_COMMAND_CodeWindow,
380                     MY_COMMAND_CodeWindow,
381                     false,
382                     MyVisualStudioAddin.Properties.Resources._default,
383                     ref contextUIGuids,
384                     (int)(vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled));
385             }
386 
387             CommandBars cmdBars = (CommandBars)_applicationObject.CommandBars;
388 
389             if (findCommand != null)
390             {
391                 // Add a button to the code window context window
392                 //代码
393                 CommandBar codeWindowCommandBar = cmdBars["Code Window"];
394                //Project
395                //Solution Folder
396                 if (codeWindowCommandBar != null)
397                 {
398                     findCommandBarButtonButton = (CommandBarButton)findCommand.AddControl(
399                         codeWindowCommandBar, codeWindowCommandBar.Controls.Count + 1);
400                     findCommandBarButtonButton.Caption = "Code Window";
401                 }
402             }
403 
404             //-------------------------------------project---------------------------------------------------------------
405             findCommand = null;
406             contextUIGuids = new object[] { };
407             try
408             {
409                 findCommand = commands.Item(string.Format("{0}.{1}", _addInInstance.ProgID, MY_COMMAND_Project), -1);
410             }
411             catch
412             {
413                 // command doesn't exist
414             }
415 
416             if (findCommand == null)
417             {
418                 findCommand = commands.AddNamedCommand2(
419                     _addInInstance,
420                     MY_COMMAND_Project,
421                     MY_COMMAND_Project,
422                     MY_COMMAND_Project,
423                     false,
424                     MyVisualStudioAddin.Properties.Resources.man,
425                     ref contextUIGuids,
426                     (int)(vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled));
427             }
428             if (findCommand != null)
429             {
430                 //项目
431                 CommandBar codeWindowCommandBar2 = cmdBars["Project"];
432                 //Solution Folder
433                 if (codeWindowCommandBar2 != null)
434                 {
435                     findCommandBarButtonButton = (CommandBarButton)findCommand.AddControl(
436                         codeWindowCommandBar2, codeWindowCommandBar2.Controls.Count + 1);
437                     findCommandBarButtonButton.Caption = "生成表结构类";
438                 }
439             }
440             //-----------------------------------------解决方案---------------------------------------------------------
441             findCommand = null;
442             contextUIGuids = new object[] { };
443             try
444             {
445                 findCommand = commands.Item(string.Format("{0}.{1}", _addInInstance.ProgID, MY_COMMAND_Solution), -1);
446             }
447             catch
448             {
449                 // command doesn't exist
450             }
451 
452             if (findCommand == null)
453             {
454                 findCommand = commands.AddNamedCommand2(
455                     _addInInstance,
456                     MY_COMMAND_Solution,
457                     MY_COMMAND_Solution,
458                     MY_COMMAND_Solution,
459                     false,
460                     MyVisualStudioAddin.Properties.Resources.FindHS,
461                     ref contextUIGuids,
462                     (int)(vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled));
463             }
464             if (findCommand != null)
465             {
466                 //解决方案
467                 CommandBar codeWindowCommandBar3 = cmdBars["Solution"];
468                 if (codeWindowCommandBar3 != null)
469                 {
470                     findCommandBarButtonButton = (CommandBarButton)findCommand.AddControl(
471                         codeWindowCommandBar3, codeWindowCommandBar3.Controls.Count + 1);
472                     findCommandBarButtonButton.Caption = "生成表结构类";
473                 }
474             }
475             //-------------------------------------------MenuBar-------------------------------------------------------
476             findCommand = null;
477             contextUIGuids = new object[] { };
478             try
479             {
480                 findCommand = commands.Item(string.Format("{0}.{1}", _addInInstance.ProgID, MY_COMMAND_MenuBar), -1);
481             }
482             catch
483             {
484                 // command doesn't exist
485             }
486 
487             if (findCommand == null)
488             {
489                 findCommand = commands.AddNamedCommand2(
490                     _addInInstance,
491                     MY_COMMAND_MenuBar,
492                     MY_COMMAND_MenuBar,
493                     MY_COMMAND_MenuBar,
494                     false,
495                     MyVisualStudioAddin.Properties.Resources.man,
496                     ref contextUIGuids,
497                     (int)(vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled));
498             }
499             if (findCommand != null)
500             {
501                 //menubar
502                 CommandBar codeWindowCommandBar4 = cmdBars["MenuBar"];
503                 if (codeWindowCommandBar4 != null)
504                 {
505                     findCommandBarButtonButton = (CommandBarButton)findCommand.AddControl(
506                         codeWindowCommandBar4, codeWindowCommandBar4.Controls.Count + 1);
507                     findCommandBarButtonButton.Caption = "JackWang";
508                 }
509 
510             }
511             //--------------------------Files------------------------------
512             findCommand = null;
513             contextUIGuids = new object[] { };
514             try
515             {
516                 findCommand = commands.Item(string.Format("{0}.{1}", _addInInstance.ProgID, MY_COMMAND_Files), -1);
517             }
518             catch
519             {
520                 // command doesn't exist
521             }
522 
523             if (findCommand == null)
524             {
525                 findCommand = commands.AddNamedCommand2(
526                     _addInInstance,
527                     MY_COMMAND_Files,
528                     MY_COMMAND_Files,
529                     MY_COMMAND_Files,
530                     false,
531                     MyVisualStudioAddin.Properties.Resources.man,
532                     ref contextUIGuids,
533                     (int)(vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled));
534             }
535             if (findCommand != null)
536             {
537                 //menubar
538                 CommandBar codeWindowCommandBar4 = cmdBars["Item"];
539                 if (codeWindowCommandBar4 != null)
540                 {
541                     findCommandBarButtonButton = (CommandBarButton)findCommand.AddControl(
542                         codeWindowCommandBar4, codeWindowCommandBar4.Controls.Count + 1);
543                     findCommandBarButtonButton.Caption = "生成表结构类";
544                 }
545 
546             }
547 
548 
549 
550         }
复制代码

复制代码
 1         public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText, ref vsCommandStatus status, ref object commandText)
 2         {
 3             try
 4             {
 5                 if (neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
 6                 {
 7                     if (commandName == "MyVisualStudioAddin.Connect.MyVisualStudioAddin")
 8                     {
 9                         status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
10                         return;
11                     }
12 
13                     if (commandName == string.Format("MyVisualStudioAddin.Connect.{0}", MY_COMMAND_FindInSolutionExplorer))
14                     {
15                         Solution solution = _applicationObject.Solution;
16                         status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
17                         return;
18                     }
19                     if (commandName == string.Format("MyVisualStudioAddin.Connect.{0}", MY_COMMAND_CodeWindow))
20                     {
21                         Solution solution = _applicationObject.Solution;
22                         status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
23                         return;
24                     }
25                     if (commandName == string.Format("MyVisualStudioAddin.Connect.{0}", MY_COMMAND_MenuBar))
26                     {
27                         Solution solution = _applicationObject.Solution;
28                         status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
29                         return;
30                     }
31                     if (commandName == string.Format("MyVisualStudioAddin.Connect.{0}", MY_COMMAND_Project))
32                     {
33                         Solution solution = _applicationObject.Solution;
34                         status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
35                         return;
36                     }
37                     if (commandName == string.Format("MyVisualStudioAddin.Connect.{0}", MY_COMMAND_Solution))
38                     {
39                         Solution solution = _applicationObject.Solution;
40                         status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
41                         return;
42                     }
43                     if (commandName == string.Format("MyVisualStudioAddin.Connect.{0}", MY_COMMAND_Files))
44                     {
45                         Solution solution = _applicationObject.Solution;
46                         status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
47                         return;
48                     }
49                 }
50             }
51             catch (Exception ex)
52             {
53                 logger.LogError(ex.ToString());
54             }
55         }
复制代码

复制代码
 1         public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
 2         {
 3             try
 4             {
 5                 handled = false;
 6                 if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
 7                 {
 8                     //命名空间.Connect.命名
 9                     if (commandName == string.Format("MyVisualStudioAddin.Connect.{0}", MY_COMMAND_FindInSolutionExplorer))
10                     {
11                         FindCurrentActiveDocumentInSolutionExplorer();
12                         handled = true;
13                         return;
14                     }
15                     if (commandName == string.Format("MyVisualStudioAddin.Connect.{0}", MY_COMMAND_CodeWindow))
16                     {
17                         string fullpath = this.GetActiveProjectFullPath();
18                         if (fullpath != "")
19                         {
20                             POCOGenerator.ConnectionForm frm = new POCOGenerator.ConnectionForm(fullpath);
21                             frm.Show();
22                         }
23                         handled = true;
24                         return;
25                     }
26                     if (commandName == string.Format("MyVisualStudioAddin.Connect.{0}", MY_COMMAND_MenuBar))
27                     {
28                         string fullpath = this.GetActiveProjectFullPath();
29                         if (fullpath != "")
30                         {
31                             POCOGenerator.ConnectionForm frm = new POCOGenerator.ConnectionForm(fullpath);
32                             frm.Show();
33                         }
34                         handled = true;
35                         return;
36                     }
37                     if (commandName == string.Format("MyVisualStudioAddin.Connect.{0}", MY_COMMAND_Project))
38                     {
39                         string fullpath = this.GetActiveProjectFullPath();
40                         if (fullpath != "")
41                         {
42                             POCOGenerator.ConnectionForm frm = new POCOGenerator.ConnectionForm(fullpath);
43                             frm.Show();
44                         }
45                       
46                         handled = true;
47                         return;
48                     }
49                     if (commandName == string.Format("MyVisualStudioAddin.Connect.{0}", MY_COMMAND_Solution))
50                     {
51                         string fullpath = this.GetActiveProjectFullPath();
52                         if (fullpath != "")
53                         {
54                             POCOGenerator.ConnectionForm frm = new POCOGenerator.ConnectionForm(fullpath);
55                             frm.Show();
56                         }
57                         handled = true;
58                         return;
59                     }
60                     if (commandName == string.Format("MyVisualStudioAddin.Connect.{0}", MY_COMMAND_Files))
61                     {
62                         string fullpath = this.GetActiveProjectFullPath();
63                         if (fullpath != "")
64                         {
65                             POCOGenerator.ConnectionForm frm = new POCOGenerator.ConnectionForm(fullpath);
66                             frm.Show();
67                         }
68                         handled = true;
69                         return;
70                     }
71                    
72                     if (commandName == "MyVisualStudioAddin.Connect.MyVisualStudioAddin")
73                     {
74                         string fullpath = this.GetActiveProjectFullPath();
75                         if (fullpath != "")
76                         {
77                             POCOGenerator.ConnectionForm frm = new POCOGenerator.ConnectionForm(fullpath);
78                             frm.Show();
79                         }
80                         handled = true;
81                         return;
82                     }
83 
84                 
85                 }
86             }
87             catch (Exception ex)
88             {
89                 logger.LogError(ex.ToString());
90             }
91         
92         }
复制代码

获取当前IDE激活项目的路径:

复制代码
 1        /// <summary>
 2         /// Gets the Active project FullPath
 3         /// </summary>
 4         /// <returns></returns>
 5         public string GetActiveProjectFullPath()
 6         {
 7            // Returns the name of the currently selected project in the solution.
 8             Project proj = getActiveProject();
 9             if (proj!=null)
10             {
11                 string fullPath = proj.Properties.Item("FullPath").Value.ToString();
12                 return fullPath;
13                // return proj.FullName;
14             }
15             return "";
16             
17         }
18         /// <summary>
19         /// Gets the Active project
20         /// </summary>
21         /// <returns></returns>
22         public Project getActiveProject()
23         {
24             Array projects = (Array)_applicationObject.ActiveSolutionProjects;
25             if (projects != null && projects.Length > 0)
26             {
27                 return projects.GetValue(0) as Project;
28             }
29             projects = (Array)_applicationObject.Solution.SolutionBuild.StartupProjects;
30             if (projects != null && projects.Length >= 1)
31             {
32                 return projects.GetValue(0) as Project;
33             }
34             projects = (Array)_applicationObject.Solution.Projects;
35             if (projects != null && projects.Length > 0)
36             {
37                 return projects.GetValue(0) as Project;
38             }
39             return null;
40         }
复制代码

关于如何根据数据库结构生成C# Code代码,可以参加此文章.

4 插件发布

创建了外接程序后,必须先向 Visual Studio 注册此外接程序,然后才能在“外接程序管理器”中激活它。 使用具有 .addin 文件扩展名的 XML 文件来完成此操作。.addin 文件描述了 Visual Studio 在“外接程序管理器”中显示外接程序所需的信息。 在 Visual Studio 启动时,它会查找 .addin 文件位置,获取任何可用的 .addin 文件。 如果找到相应文件,则会读取 XML 文件并向“外接程序管理器”提供在单击外接程序进行启动时所需的信息。使用外接程序向导创建外接程序时,会自动创建一个 .addin 文件。 你也可以使用本主题中的信息手动创建 .addin 文件。我是用Visual Studio2012 所以将.addin文件和对应的dll拷贝到C:\Users\wangming\Documents\Visual Studio 2012\Addins文件下:

如果发布没有错误,那么重新启动Visual Studio2012后,在项目文件上右击弹出菜单,可以看到下面的界面:

同时在菜单栏创建了一个JackWang的命令按钮和工具菜单下还添加了一个MyVS外接程序的命令按钮,如下图:

5 代码生成器

代码生成器(此处用的是yuvalsol的工程,我将其整合到插件中)可以根据用户选择的数据库,选择对应的表,然后生成表结构对应的C#类:

 6 插件卸载

如果自己定义的插件想卸载怎么办?可参见https://msdn.microsoft.com/en-us/library/ms228765.aspx.

  • 删除插件对应的.addin文件. 默认路径为..\Users\username\My Documents\Visual Studio 2012\Addins\(请根据实际情况查看具体路径)
  • 在 Visual Studio开发人员命令行中, 输入devenv /resetaddin MyVisualStudioAddin.Connect  进行卸载(MyVisualStudioAddin.Connect 是MyVisualStudioAddin.AddIn文件中的FullClassName;
  • 至此, add-in 不会出现在IDE中,卸载完成. 但是要完整去除必须手动删除插件对应的项目文件(如果你再次调试,可能会再次进行注册);

 7 总结

通过插件机制可以方便的定制VS IDE, 一般软件公司都有自己的一套框架,其代码也有一定的封装,且各不相同,可以通过扩展VS,通过定制的代码生成工具来快速生成符合本公司所需的代码,从而从 重复机械的劳动中解放出来(虽然完全自动生成的代码不可能直接能用,但是人工在此基础上进行调整,也提升了代码的编写效率,而且减少类似于拼写/标点等人 为的错误点等.

虽然我们不生产代码,是代码的搬运工,但是正确的打开方式是用代码去帮我们搬运代码!!!

水平有限,望各位园友不吝赐教!如果觉得不错,请点击推荐和关注!
出处:http://www.cnblogs.com/isaboy/
声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。