工作多年后积累的设计灵活,稳定,优秀WinForms应用程序的最佳实践 WinForms best practice

工作几年后,技术方面的积累越来越多,设计的程序也越来越灵活,稳定。如果开始入门的时候有人指点这些知识,每个人的成长都非常快,可惜IT行业的分享氛围不好,同事与同事之间,一不小心就变成从不交流技术的那种情况。我理解IT人,跑市场不会,与领导打交道不善到察言观色,唯一能让自己自信的,可能就是一堆很酷的代码,让自己把最重要的本钱拿出来,这种情况很少会出现。

  1. 经常在公共类库和应用程序中写日志Log,写跟踪Trace。程序一旦部署到客户那边,基本上就没有办法去Debug,让你一下子知道问题在哪里,也不可能在客户的机器上有机会装个Visual Studio来调试。所以,为了找到问题的根源,经常写Log和Trace。
  2. Exception Handing 对应用程序发生的错误,要记得catch,并且提供友好的界面。不能在发生错误时,程序就stop working,也不能让.NET Framework抛出异常对话框,这样会暴露细节,最合适的办法就是自定义对话框,显示异常。

image 

对于.NET程序,也就是下面几句话,把它加到你的程序中,就可以实现自定义异常对话框。

AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
System.Windows.Forms.Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);

这两句可以捕获应用程序的各种异常,然后对异常进行过滤处理,或是封装。这时,也可用用像下面的代码,记录异常

LoggerHelper.Log(e.Exception.ToString());

 

3.  总是对系统运行环境进行检测。
1)如果是.NET编写的程序,请检测你编译时的Target(.NET3.5,.NET 4)在目标机器中是否存在;
2)  如果要连接数据库,请先检测SQL Server数据库是否可连接,可用;如果不允许在x64的系统中运行,请先检测当前运行程序的操作系统;
3)  如果你的程序用到了Windows 7的任务栏进度条功能,请先检测当前操作系统是否是Windows7;
4)  如果要用到Access数据库,请检测操作系统如果是x64,则抛出不支持的异常,因为Jet.OLEDB不支持在x64中运行;
5) 如果你给程序加入Strong Name,请在程序中加上对strong name的检测。这一条,你可用关键字StrongNameIdentityPermissionAttribute,StrongNameIdentityPermission,StrongNameSignatureVerificationEx来找到你需要的答案。

6) 如果程序要访问文件,请先检测文件是否存在(File.Exist),即使文件存在,你还要保证它不被其他的程序占用,这两个问题在使用文件时,经常遇到。

 

4 如果你觉得你的程序不够完美,或是有可以改进的地方,可以学习Microsoft Visual Studio的Help—>Customer Feedback Options…,SQL Server也有这个功能,以获取客户的改善意见。我自己的小程序中,则实现如下

image

写一个窗体,让他表达他遇到的问题或是意见,你在服务器端收集这些信息,这都是宝贵的改善意见。用户通常是最好的程序设计师,这句话是有道理的。

 

5 如果情况允许,应该对资源进入嵌入进程序集处理,这样可以获取稳定的程序运行环境。如下图所示

image

DatabaseCleanup.txt便是嵌入进程序集中的资源,你调用它可以保证它随时是可得到的,可用的(available),而放到本机磁盘或是目录中,则可能会该文件不存在,或是文件被改动过,不符合预期的要求。这种方法并会增加代码数量

Assembly assm = Assembly.GetAssembly(typeof(FlexDataTable));
string file = "DatabaseCleanup.txt";
Stream input = assm.GetManifestResourceStream(Shared.ResourcePrefix + "." + file);   

就这三行代码就可以了,它所达到的效果比本机目录要好很多。当然,如果文件很大,你可能会说,这样不好。

如果文件很大,你可以考虑在程序运行时,把资源先从程序集中提取出来,放到cache中,之后再对cache中的文件进行读写,程序退出时,再清空cache中的文件。

 

6 考虑给程序加启动参数。即使是WinForms程序,你也可以这样。如下面的代码,args可以命令参数。

[STAThread]
static void Main(String[] args)

有了这个基础,就可以实现不同的进程间通信。比如在我的Data Loader程序中,要实现PDF转化为DOC,这个功能实现起来不容易,于是我到一个程序,它可以被我的代码调用,同时也接受我传入的参数,代码看起来是这样的

Process job = new Process();
ProcessStartInfo startinfo = new ProcessStartInfo(appPath, parameters);
startinfo.UseShellExecute = true;
startinfo.CreateNoWindow = true;
job.StartInfo = startinfo;
这样达到的效果,可以实现进程间通信,又不会增加很多技术复杂度。如果你有很多命令行小工具,试着用这个方法,

让它们可以在同一个IDE中一起工作。

 

7.  让你的程序保持单一实例(Singleton)。应用单例模式,达到只启动一个应用程序的实例子。特别是基于数据库应用的程序,允许多个实例子并行运行,会导致不可预期的结果。

 

8.  给程序建立Error Reporting机制,以处理不可预料的异常。比如著名的Reflector,它的这个窗体

image 

由用户来选择,是否发送错误报告。Windows也内置了这种机制。至于要发送的内容,通常就是堆栈或是传递进入方示的参数,可以使用System.Diagnostics.StackTrace来获取这些内容。发送的方式可以是邮件,或是TCP消息。

 

9 在应用程序启动时,记得检测必须的组件,是否都在磁盘中。比如DataLoader.exe依赖于Interop.CDO.dll这个程序集,以实现下载网页为mht文件到本机磁盘中,在启动时,我可以这样做

public static void PreInitCoreSetup()
{
      Assembly common = Assembly.Load("Interop.CDO");
      EnsureAssemblyExistAndIsSigned(common);
}

这个方法,可以检测Interop.CDO是否存在,看它是否有合适的strong name。如果不符合条件,应该马上抛出FatalException,终止应用程序。通常是看是否在当前目录中,程序集也不一定是在当前目录中,或者是下面设置指定的文件夹中

 <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <publisherPolicy  apply="yes"   />
      <probing privatePath="Assembly"/>
    </assemblyBinding>
  </runtime>

这个技巧,可以把程序集放到指定的文件夹中而不影响正确运行。

 

10 如果可以,请制作Splash sceen,About Dialog,这都是标准的对话框,也是程序的一面镜子。这两个窗体做的精致,人家看你的程序就会觉得比较专业。这就好比医生在医院要穿白大褂,建筑工人要带安全帽,地铁工人不仅仅要带安全帽,而且还要面带微笑,脸上有汗水。一样的道理,外表看起来专业,看起来像那么一回事,人家才会觉得你很专业。

请参考下面的代码,给你的应用程序加上Splash screen,这也是微软推荐的做法。

class SplashScreenUsingFramework :Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
    {
        private void Initialize()
        {
            this.EnableVisualStyles = true;
            this.IsSingleInstance = true;
            this.SaveMySettingsOnExit = false;
            this.ShutdownStyle = Microsoft.VisualBasic.ApplicationServices.ShutdownMode.AfterMainFormCloses;
        }

        protected override void OnCreateSplashScreen()
        {
            base.OnCreateSplashScreen();            
            this.SplashScreen = new SplashScreen();
        }
}

SplashSreen就是你需要制作的启动窗体,它一般会有这三个属性

this.FormBorderStyle = FormBorderStyle.None;
this.BackgroundImage = Properties.Resources.Splash;
this.StartPosition = FormStartPosition.CenterScreen;

没有边框,背景是图片,启动位置通常是屏幕中央。
如果你喜欢,还可以给它加上许可授权的用户名称,像下面的代码这样,

lblLicense.Text = string.Format(lblLicense.Text, LicenseHelper.GetLicenseName());

 

请到epn.codeplex.com(http://epn.codeplex.com/releases/view/68647)中下载最新版的Data Loader。

posted @ 2011-11-14 09:30  信息化建设  阅读(4762)  评论(21编辑  收藏  举报