第八章 用户界面(一)

在这一章,我们将学习程序员最重要的任务之一:把像素放到屏幕上艺术。在 F# 中,所要做的就是调用库和 API,在这方面有很多选择,因为有了 .NET 平台,就更加多样。第一个选择是要确定是想创建桌面应用程序,它运行在本地,用一系列窗口和控件把信息显示给用户;还是想创建网站应用程序,应用程序的界面用 HTML 定义,然后,通过浏览器渲染。

用 .NET 创建桌面应用程序,可以有四种选择:WinForms、Windows Presentation Foundation (WPF,[ 原文这里有笔误 ])、GTK# 和 DirectX。在这一章,我们将学习 WinForms、WPF 和GTK#,但不包括 DirectX。WinForms、WPF 和GTK# 对窗口和控件有基本相同的隐喻(metaphors)[ 不懂说的是什么 ]。WinForms 最古老也最简单,我们已经遇到过几个WinForms 的示例了;WPF 是一个新的库,比WinForms
稍微复杂一些,但是,也更一致,提供了更多的功能,包括令人印象深刻的3D 图形;GTK# 提供了比其他两个库更好的平台。因为有了 Mono,现在WinForms 可以运行在所有平台上,Mono 团队建议 GTK# 运行在非 Windows 平台。DirectX 的目标是想得到更快 3D 图形的游戏制造商;WPF [ 同误 ] 提供了更简单的产生 3D 图形的方法,因此,本书不讨论DirectX。

要创建网站应用程序,可以使用 ASP.NET 框架,它提供了简单的方法创建基于服务器的、动态 HTML 的应用程序。ASP.NET 提供了灵活的方式产生 HTML,以响应来自浏览器的 HTML 请求。近年来,网站应用程序已经有了极大地进步,因此,ASP.NET 平台的进步也毫不为奇。ASP.NET 中增加了ASP.NET AJAX、ASP.NET MVC 和 Silverlight,作为竞争对手的平台Mono Rail,也有了很大的进步。然而,本书并不讨论这些事情,因为我们的目标只是为了展示用 F# 生成
HTML 的基础知识。

本章将要学习的每个主题,已经写在全书中了,因此,这里只关注一些基本知识。无论选择哪种技术创建用户界面,都需要在之前花点时间学习如何使用库。

介绍WinForm

WinForms 是包含在 System.Drawing.dll和 System.Forms.Windows.dll 的类,编译所有的 WinForms 都需要添加对它们的引用。这个库是基于 System.Windows.Forms.Form 类的,它代表显示给用户的窗口。当创建这个类的实例时,本质上就可创建了一个新的窗口。然后,必须创建一个事件循环(event loop),它可以保证用户响应与窗口的交互;通过调用 System.Windows.Application.Run 方法,并把创建的窗体对象传递进去即可完成。通过设置属性、调用方法,可以控制窗体的外观。看下面的例子:

open System.Drawing

openSystem.Windows.Forms

//create a new form

let form =
new Form(BackColor =
Color.Purple, Text = "IntroducingWinForms")

//show the form

Application.Run(form)

[

1、需要引用 System.Drawing,System.Windows.Forms;

2、交互运行用 form.Show()

以下同

]

这个例子不能运行在 F# 的交互模式 fsi 下,是因为不能从 fsi 启动事件循环。为了能在 fsi 下运行,有一个简单的方法,调用窗体的 Show 方法,或设置窗体的 Visible 为 true。看下面的代码:

open System.Drawing

open System.Windows.Forms

let form = newForm(BackColor=Color.Purple,Text="IntroducingWinForms",Visible=true);;

不论哪种方法,都可以动态地和窗体进行交互。例如:

> form.Text <- "Dynamic!!!";;

使用 WinForms,有两种可选的方案:自己画窗体,或者使用控件构建。下面我们首先看一下如何自己画窗体,然后再尝试用控件。

画 WinForm

自己画窗体,就要负责把像素表示到屏幕上。这种低级方法对许多 F# 用户仍有吸引力,因为他们相信许多 WinForms 库中控件不可能非常适合显示一些数据结构,或函数和算法的结果。然而,要注意这种方法会耗费大量时间,通常更多地花在寻找抽象表现逻辑的图形库上。

为了画 WinForm,需要将事件处理程序附加到窗体或控件的 Paint 事件上。就是说,每次 Windows 请求绘制窗体时,函数都会调用。传递到此函数的事件参数有一个属性 Graphics,它包含一个类的实例,也叫 Graphics,这个类的方法(如 DrawLine)可以在窗体上绘制像素。下面的例子显示一个简单的窗体,在上面画了一个饼:

let form =

// create a new form setting the minimum size

let temp =
new Form(MinimumSize =
new Size(96, 96))

// repaint the form when it is resize

temp.Resize.Add (fun _
-> temp.Invalidate())

// a brush to provide the shapes color

let brush =
new SolidBrush(Color.Red)

temp.Paint.Add (fun e
->

// calculate the width and height of the shape

let width, height = temp.Width - 64, temp.Height - 64

// draw the required shape

e.Graphics.FillPie (brush, 32, 16, width,height, 0, 290))

// return the form to the top level

temp

Application.Run(form)

// form.Show()

可以在图 8-1 中看到这个窗体,即代码的运行结果。

图 8-1. 包含饼图的 WinForm

这个图像的大小和窗体相关,因此,无论什么时候改变窗体大小时,必须告诉窗体必须重画自己,这是通过把事件处理函数附加到 Resize 事件实现的。在这个函数中,调用窗体的 Invalidate 方法,告诉窗体需要重画自己。

现在,我们再看一个更完整的例子。假设我们想创建一个窗体,显示 Tree 类型,在下面的代码中定义,显示结果如图 8-2。

//The tree type

type ‘a
Tree =

|Node of ‘a
Tree * ‘a Tree

|Leaf of ‘a

//The definition of the tree

let tree =

Node(

Node(

Leaf "one",

Node(Leaf "two", Leaf"three")),

Node(

Node(Leaf "four", Leaf"five"),

Leaf "six"))

图 8-2. 显示树型结构的 WinForm

可以用清单8-1 的代码画出这个树。

清单 8-1 画树

open System

open System.Drawing

openSystem.Windows.Forms

//The tree type

type ‘a
Tree =

|Node of ‘a
Tree * ‘a Tree

|Leaf of ‘a

//The definition of the tee

let tree =

Node(

Node(

Leaf "one",

Node(Leaf "two", Leaf"three")),

Node(

Node(Leaf "four", Leaf"five"),

Leaf "six"))

// Afunction for finding the maximum depth of a tree

let getDepth t =

let rec getDepthInner t d =

match t
with

| Node (l, r) ->

max

(getDepthInner l d + 1.0F)

(getDepthInner r d + 1.0F)

| Leaf x -> d

getDepthInner t 0.0F

//Constants required for drawing the form

let brush =
new SolidBrush(Color.Black)

let pen =
new Pen(Color.Black)

let font =
new Font(FontFamily.GenericSerif, 8.0F)

// auseful function for calculating the maximum number

//of nodes at any given depth

let raise2ToPower (x :
float32) =

Convert.ToSingle(Math.Pow(2.0,Convert.ToDouble(x)))

let drawTree (g :
Graphics) t =

// constants that relate to the size and position

// of the tree

let center = g.ClipBounds.Width / 2.0F

let maxWidth = 32.0F * raise2ToPower (getDepth t)

// function for drawing a leaf node

let drawLeaf (x :
float32) (y : float32) v =

let value = sprintf
"%A" v

let l = g.MeasureString(value, font)

g.DrawString(value, font, brush, x -(l.Width / 2.0F), y)

// draw a connector between the nodes when necessary

let connectNodes (x :
float32) y p =

match p
with

| Some(px, py) -> g.DrawLine(pen, px,py, x, y)

| None -> ()

// the main function to walk the tree structure drawingthe

// nodes as we go

let rec drawTreeInner t d w p =

let x = center - (maxWidth * w)

let y = d * 32.0F

connectNodes x y p

match t
with

| Node (l, r) ->

g.FillPie(brush, x - 3.0F, y - 3.0F, 7.0F,7.0F, 0.0F, 360.0F)

let d = (d + 1.0F)

drawTreeInner l d (w + (1.0F / d))(Some(x, y))

drawTreeInner r d (w - (1.0F / d))(Some(x, y))

| Leaf v -> drawLeaf x y v

drawTreeInner t 0.0F 0.0F None

//create the form object

let form =

let temp =
new Form(WindowState =
FormWindowState.Maximized)

temp.Resize.Add(fun _
-> temp.Invalidate())

temp.Paint.Add

(fun e
->

e.Graphics.Clip <-

new
Region(new
Rectangle(0, 0, temp.Width,temp.Height))

drawTree e.Graphics tree)

temp

form.Show()

//Application.Run(form)

注意如何定义函数 drawTree,它有两个参数:Graphics 对象和需要画的树:

let drawTree (g : Graphics) t =

这是画 WinForm 的通常模式,创建一个函数,参数为 Graphics 对象和需要画的数据类型,这样,函数可以很容易被其他窗体和控件重用。

为了实现 drawTree,首先计算出这个函数的两个常量center 和 maxWidth。不能在函数 drawTree 的外边看到这些常量,这样在函数 drawTree 的内部使用,而不必须作为参数来传递:

// constants that relate to the size andposition

// of the tree

let center = g.ClipBounds.Width / 2.0F

let maxWidth = 32.0F * raise2ToPower(getDepth t)

实现函数的其余部分,是通过把任务分解成许多内部函数。定义 drawLeaf 去画叶结点:

// function for drawing a leaf node

let drawLeaf (x : float32) (y : float32) v=

letvalue = any_to_string v

letl = g.MeasureString(value, font)

g.DrawString(value,font, brush, x - (l.Width / 2.0F), y)

用 connectNodes 在适当的地方画结点之间的连接线:

// draw a connector between the nodes whennecessary

let connectNodes (x : float32) y p =

matchp with

|Some(px, py) -> g.DrawLine(pen, px, py, x, y)

|None -> ()

最后,定义一个递归函数 drawTreeInner,实际完成遍历这个 Tree 类型,并画出来:

// the main function to walk the treestructure drawing the

// nodes as we go

let rec drawTreeInner t d w p =

letx = center - (maxWidth * w)

lety = d * 32.0F

connectNodesx y p

matcht with

|Node (l, r) ->

g.FillPie(brush,x - 3.0F, y - 3.0F, 7.0F, 7.0F, 0.0F, 360.0F)

letd = (d + 1.0F)

drawTreeInnerl d (w + (1.0F / d)) (Some(x, y))

drawTreeInnerr d (w - (1.0F / d)) (Some(x, y))

|Leaf v -> drawLeaf x y v

该函数使用参数保存递归调用期间的值,可以知道,因为这是内部函数,不会被外边程序初始化成不正确的值而滥用,在外边根本看不到它。隐藏参数保存递归调用期间的临时值,是函数编程的另一个常用模式。

在某种程度上,这个画树的函数还是令人满意的,仅用了简短的 86 行 F# 代码,就提供了树不错的层次视图。然而,这种方法只能用于小规模的图形。当画更为复杂的图形时,代码数就会迅速膨胀,规则出所有的几何图形是非常耗时的。为了有效地控制复杂性,F# 可以使用控件,我们在下一章讨论。

为了更好地在 WinForm 上画图,应该了解 System.Drawing.dll 中的 System.Drawing 命名空间。还应该注意两个方面,第一,需要学习如何使用 Graphics 对象,特别是重载前缀为 Draw 或 Fill 的方法。表 8-1 中汇总了Graphics 对象的方法。

表 8-1 System.Drawing.Graphics 对象中的重要方法


方法名


描述


DrawArc


画椭圆的一部分


DrawBezier


绘制贝塞尔曲线,这条曲线由两个端点和两个控制曲线角度的自由点表示


DrawCurve


画曲线,由点数组定义


DrawClosedCurve


画封闭曲线,由点数组定义


DrawEllipse


画椭圆,由矩形或点的矩形集合表示


DrawPie


画椭圆的一部分,用矩形和两条射线表示,说明起止角度


DrawLine


画线,用两点


DrawLines


画一组线,用点数组


DrawPolygon


画多边形,是一组封闭直线集合,用点数组


DrawRectangle


画矩形,用座标和宽、高表示


DrawRectangles


画一组矩形,用矩形数组表示


FillClosedCurve


画实心的封闭曲线,用点数组表示


FillEllipse


画实心椭圆,由矩形、矩形点集合表示


FillPie


画实心椭圆的一部分,用矩形和两条射线表示,说明起止角度


FillPolygon


画实心多边形,由封闭线条构成,用点数组表示


FillRectangle


画实心矩形,用座标和宽、高表示


FillRectangles


画一组实心矩形,用矩形数组表示


DrawIcon


画图标,由 System.Drawing.Icon 类型指定


DrawImage


画图,由 System.Drawing.Image 类型指定


DrawImageUnscaled


画不能缩放的图,由 System.Drawing.Image 类型指定


DrawString


画字符串


MeasureString


测量字符串,因此可以计算出放在图形中的位置


DrawPath


画 System.Drawing.Drawing2D.GraphicsPath 表示的轮廓。 这个类可以向其中添加前边描述的几何结构,比如:圆弧、矩形、椭圆和多边形,以节省每次重新计算的时间(这对于想画一些复杂但完全固定的图形非常有用)


FillPath


同上面的 DrawPath 功能,但是实心图形

第二个需要关注的部分是与 System.Drawing.Graphics 对象密切相关的 System.Drawing的命名空间,需要学习如何使用Graphics 对象的方法创建 Icon、Image、Pen 和 Brush 对象。表 8-2 展示了如何通过各自的构造函数创建这些对象。

表 8-2 使用System.Drawing.Graphics 对象创建对象


代码段


描述


Color.FromArgb(33, 44, 55)


用 红、绿、蓝组件创建颜色


Color.FromKnownColor(KnownColor.Crimson)


用枚举 KnownColor 的成员颜色


Color.FromName("HotPink")


用名字,以字符串形式创建颜色


new Font(FontFamily.GenericSerif, 8.0f)


创建一个新的 generic serif 字体、8  点高


Image.FromFile("myimage.jpg")


用文件创建图片


Image.FromStream(File.OpenRead ("myimage.gif"))


用流创建图片


new Icon("myicon.ico")


用文件创建图标


new Icon(File.OpenRead("myicon.ico"))


用流创建图标


new Pen(Color.FromArgb(33, 44, 55))


创建有颜色的笔,可以画线


new Pen(SystemColors.Control, 2.0f)


用颜色、2 个像素宽度创建笔,可以画线


new SolidBrush(Color.FromName("Black"))


创建实心笔刷,能够画实心图形


new TexturedBrush(Image.FromFile ("myimage.jpg"))


用图片创建纹理笔刷,用图片充填图形

如果你喜欢使用标准对象,可以使用 System.Drawing 命名空间中的几个类,它预定义了一些对象,包括:Brushes、Pens、SystemBrushes、SystemColors、SystemFonts、SystemIcons、SystemPens。下面简单例子就说明了如何使用这些预定义的对象:

open System.Drawing

let myPen =
Pens.Aquamarine

let myFont =
SystemFonts.DefaultFont

第八章 用户界面(一)

时间: 2024-12-31 03:37:14

第八章 用户界面(一)的相关文章

第八章 用户界面(三)

在 F# 中使用 Visual Studio 窗体设计器 到现在,F# 还没有自己的窗体设计器:然而,由于 .NET 具有很强的互操作性,因此,很容易在 F# 中使用由 Visual Studio 设计器创建的窗体.有两种选择:第一,创建一个 F# 库,然后,在自己的 Windows 窗体中调用这个库中的函数:第二:创建一个窗体库,然后,在 F# 应用程序中使用.下面分别讨论这两种方法,并对比它们的优势与不足:示例都是计算斐波那契数列(Fibonacci),如图 8-4. 警告 本书是有关 F#

第八章 用户界面(二)

在 WinForm 上使用控件 控件就是类,派生自 System.Windows.Forms.Control,由此类派生出的任何类都能显示在窗体上,只要将它添加到窗体对象的 Controls 集合中. 现在我们看一下用控件画树形的方法.WinForms 库定义了 TreeView 类,这是专门用于显示树形结构的:自然,我们就用这个控件来显示树.使用 TreeView,需要创建它的实例,并设置属性.调用方法,对它进行配置.最重要的,是将需要显示的结点添加到 Nodes 集合中.这个控件准备好以后,

第八章 用户界面(四)

处理 WinForms 事件和事件模块 在第七章我们讨论过事件(Event)模块,它能够用于处理 WinForms 中的事件.当处理 WinForms 中的事件时,通常会遇到没有完全符合想要事件的情况.例如,当鼠标的左.右键单击时会触发MouseButton 事件,但是,我们可能只希望它响应鼠标左键的单击.这时,用 Event.filter 函数可能会很有用,用它创建一个新的事件,只响应鼠标左键的单击.看下面例子的演示: openSystem.Windows.Forms let form = /

软件工程—第八章

第八章—面向对象设计 设计活动划分成系统设计和详细设计两个主要状态.设计原则有模块化.耦合度和内聚性.复用性,降低系统的复杂性的有效方法是将系统模块化,耦合度是指两个系统之间的关联程度,耦合越低越好,内聚性是指子系统内部的相关程度,内聚越高越好. 几种典型的软件体系结构:仓库体系结构.分层体系结构.MVC体系结构.客户机/服务器体系结构.管道和过滤器体系结构.MiniLibrary系统体系结构. 在系统设计阶段,先要识别设计元素(三个基本原则),然后选用数据存储策略(数据文件.关系数据库.面向对

软件工程——理论、方法与实践 第八章

第八章 主要讲 1.设计的概念  设计活动分为系统设计和详细设计,设计活动实现从需求分析到软件实现之间的跨越,设计活动结束后需形成设计规格说明书.设计原则模块化.强内聚.弱耦合.可复用.抽象.信息隐藏. 2.软件体系结构包括仓库体系结构.分层体系结构.MVC体系结构.客户机/服务器体系结构和管道和过滤器体系结构几种,各有优劣. 3.系统设计分为识别设计元素.数据存储策略.部署子系统.系统设计评审几方面,系统设计评审需要检查正确性.完整性.一致性和可行性. 4.详细设计有方法建模.属性建模.状态建

软件工程概论总结第八章

第八章 面向对象设计 设计的概念 设计活动 面向对象设计过程的主要活动 设计原则 1.模块化 对于复杂系统而言,降低复杂性的有效方法是将系统模块化,也就是将一个复杂的大系统分解成若干个相对简单的较小部分,称为子系统.如果一个子系统依然是复杂的,那么继续分解直到易于开发和管理为止.子系统的层数不要超过5±2,同一层次内的子系统数目不要超过7±2个. 系统分解的另一种形式是将系统分解为对等的子系统,每一个子系统负责一个不同类型的服务,子系统之间相互独立. 2.耦合度和内聚性 耦合度是表示两个子系统之

《Linux内核设计与实现》读书笔记 第十八章 调试

第十八章调试 18.1 准备开始          需要准备的东西: l  一个bug:大部分bug通常都不是行为可靠而且定义明确的 l  一个藏匿bug的内核版本:找出bug首先出现的版本 l  相关内核代码的知识和运气 最好能让bug重现,有一些bug存在而且有人没办法让他重现,因为内核与用户程序和硬件间的交互很微妙. 18.2内核中的bug 可以有无数种原因产生,表象也变化多端.代码中的错误往往引发一系列连锁反应,目击者才看到bug. 18.3通过打印来调试 内核提供了打印函数printk

15个最佳的用户体验和用户界面工具和资源

无论你是新手还是专业的网页设计师或者开发人员,你总是从创建线框开始来创建 Web 项目,而不是直接开发. 这不仅有助于让你保持在正确的轨道上,以免项目建成后出现了各种各样的问题. 目前有很多的工具和资源都能网上找到,使你的工作更容易.这里给大家推荐15个最佳的用户体验和用户界面工具和资源. 您可能感兴趣的相关文章 Web 前端开发人员和设计师必读精华文章推荐 精心挑选的优秀jQuery Ajax分页插件和教程 12个让人惊叹的的创意的 404 错误页面设计 让网站动起来!12款优秀的 jQuer

第八章、Linux 磁盘与文件系统管理

第八章.Linux 磁盘与文件系统管理 1. 认识 EXT2 文件系统 1.1 硬盘组成与分割的复习 1.2 文件系统特性: 索引式文件系统 1.3 Linux 的 EXT2 文件系统(inode): data block, inode table, superblock, dumpe2fs 1.4 与目录树的关系 1.5 EXT2/EXT3 文件的存取与日志式文件系统的功能 1.6 Linux 文件系统的运行 1.7 挂载点的意义 (mount point) 1.8 其他 Linux 支持的文