代码改变世界

F#探险之旅(八):使用F#开发Windows应用程序

2008-12-17 22:11 Anders Cui 阅读(...) 评论(...) 编辑 收藏

我们身在何处?

前面写过的随笔主要关注的是F#与FP的基本概念,对UI涉及很少。我们知道,没有UI就没法与用户进行交互,所以写两篇随笔来了解一下这方面的内容。本文主要关注的是如何使用F#开发WinForm应用程序,后面还会介绍如何使用F#开发ASP.NET应用程序。

不过说真的,我对WinForm开发了解不多,所以如果有不妥之处,欢迎您的指正。

WinForm中的基础类型是System.Windows.Forms.Form类,通过它我们可以创建一个窗体,在窗体上我们可以根据需要进行绘制。绘制的时候我们可以选择使用.NET提供的API手工“画”出一些图形,比如像素着色、直线、圆等等,也可以使用.NET里面的一些标准控件,比如文本框、按钮等等。接下来基于Windows应用的事件驱动的特性,通过事件(Event)和事件处理函数(Event Handler)使用户可与程序进行交互。另外,如果标准控件或窗体不能满足需要,我们还可以对其进行扩展,创建用户控件和自定义控件。

下面就来看看如何实现这些过程。

分析

在使用C#开发的WinForm应用中可以看到,我们创建的新窗体类继承了System.Windows.Forms.Form类,在程序的入口函数Main中,有一行关键的代码:

C# Code
Application.Run(new MainForm());


也就是说创建Form类的一个实例,传给Application.Run方法,程序由此开始执行。使用C#开发的一大优势在于VS IDE提供的窗体设计器,这样就可以在设计时设置窗体和控件的属性,VS会为我们生成代码,从而大大提高了开发效率。

遗憾的是,VS还没有为F#提供这样的设计时支持,也就是说我们必须手工编写所有的代码,那如果界面很复杂,不得累死啊?也不尽然。首先考虑下面的简化了的三层架构示意图:

这里并不是说程序一定要分为三层,或者说三个Project,而是说从逻辑上大体可以这样来看。大部分情况下,与设计器相关的只有UI层(不要忘了其它地方VS也可以生成代码),如果UI相当简单,完全手工编写这些代码也没什么大问题,如果UI相当复杂,就得考虑别的办法了。

F#基于.NET平台,在这一点上,它与C#、VB.NET是平等的,这一次我们又可以获益于它的互操作性(Interoperability)了!既然C#类库可被F#调用,如果我们把窗体类放在一个类库内不就可以了吗?这样一方面我们可以利用VS的设计时支持,另一方面又可利用F#带来的好处,岂不妙哉?退一步讲,我们也没必要非得使用F#开发UI部分,完全可以使用C#开发UI,其余部分使用F#开发,这也是不错的方法。

总结上面的分析,可以得到这样的结论:在开发WinForm应用时使用F#,如果UI非常简单(比如画出一条数学曲线或者只有两三个控件的小工具等),我们完全可以手工编写所有代码;如果UI比较复杂,我们使用C#开发UI类库供F#调用,也可以只采用F#编写UI之外部分的代码

下面我们来讨论下这三种方法的具体实现。

使用F#手工编写所有UI代码

前面写过的一篇使用F#绘制Mandelbrot集合就是这么做的,因为窗体上根本就没有任何控件,此时需要处理Paint事件完成窗体的绘制。这里再提供一个简单的例子,使用WebBrowser控件创建简单的浏览器:

F# Code - Simple Web Browser


像这样的小工具,也没必要兴师动众,劳驾C#了。试试浏览一下博客园:


注意:看看上面例子的事件处理函数是怎么写的,比如go按钮的Click事件。我们知道C#中控件的事件处理函数一般有两个参数sender(:object)和e(:EventArgs),这里却只有一个,这是因为对于标准的.NET事件,F#会忽略第一个参数

在F#中使用C#开发的UI类库

前段时间看到一个例子,是使用C#中获取本机的进程信息,感觉它难易适中,就用它做例子吧。在原文中,只有两个主要的文件,一是工具类ProcessValidation.cs,一是窗体类Form1.cs,现在我们就是要把Form1分为两部分,UI设计器相关的部分放在C#类库中,后台的代码则采用F#编写,顺便把ProcessValidation类也改为F#代码。

不过这里我按照自己的习惯给它们重新命名了,窗体是ProcessChecker,工具类是processHelper,然后在processFinder使用其中的窗体和控件,最后在Program类中定义了入口函数(FsSamples.UI中的其它文件可以略过)。

这个过程比较简单,不过有个小问题。在C#中定义的窗体类中,各个控件在默认情况下是private的,如何在processFinder中使用呢?要想把它们暴露出来有几种方法,一是把private改为public,这样虽然简单,不过不是一个好的practice;二是把控件封装为public的属性,这样做如果控件少还比较好办,多了也很麻烦;我这里采用的是第三种方法(来自Allen Lee的建议),即把控件的private修饰符改为protected,然后继承该窗体,这样仍有比较好的封装性,操作起来也比较简单,只要选中要修改的控件,把它们的Modifiers属性设置为Protected就可以了。(下载示例代码

值得一提的是,在Application.Run方法之前,要添加一行代码:

 

F# Code
Application.EnableVisualStyles()


这样就可以使用XP/Vista的可视化样式了。程序的运行效果为:


仅使用F#开发UI之外的代码

得益于F#与其它.NET语言良好的互操作性,我们可以在C#中开发UI部分,然后调用F#类库,这个过程跟调用C#开发的类库相比没有很大的区别,在此就不赘述了。

其它

关于WinForm开发的主题还有创建用户控件、自定义控件等,这方面已经有大量的资料了,相信使用本文的方法,开发起来也不会很难:)

小结

本文主要讨论了如何使用F#开发Windows应用程序。通过分析,确定了三种主要的方法,重点讨论了如何在F#中利用VS中的窗体设计器,这会使WinForm程序的开发简单不少。其中的一个例子是processFinder,它可以获取当前机器上的进程和应用程序信息。

下载示例代码
(要了解本人所写的其它F#随笔请查看 F#系列随笔索引

参考
《Foundations of F#》 by Robert Pickering
《Expert F#》 by Don Syme , Adam Granicz , Antonio Cisternino
Finding and Listing Processes in C#