首页 > 代码库 > 7.2.2 在窗体上显示绘图
7.2.2 在窗体上显示绘图
7.2.2 在窗体上显示绘图
绘图与第四章的示例类似。因为绘图需要一定的时间,我们将在内存中创建位图,绘制好文档,然后,在窗体上显示位图,而不是每次窗体失效时都绘制文档。我们先看一下非常有用的函数式编程模式,这一节就将使用。
“Hole in the Middle(中间有洞)”模式
[真心不知道,Hole in the Middle 是什么意思?]
写代码的一个常见情形,是首先进行初始化,然后,是函数的核心部分,最后,是一些清理工作;在程序中的多个位置,重复执行类似的操作,初始化和清理部分没有变化,而只有核心部分不同。在内存中绘制位图的示例,用 C# 写的代码可能像这样:
var bmp = new Bitmap(width, height)
using(var gr = Graphics.FromImage(bmp)) {
(...)
}
这里,代码的核心部分是第三行的占位符,我们将用值 gr 来绘图。问题是,只使用面向对象的编程概念,不能简单地把执行初始化和清理的代码,包装成一个子程序,实现在绘制不同图形的所有地方共享。C# 语言支持这种模式众所周知,通常使用各种初始化和清理的类型;using 构造就是这种情况。我们如何为自己的代码模式实现类似的功能呢?
用函数式编程,解决起来很简单;可以写一个高阶函数,把核心部分包装到 lambda 函数中,并把它作为高阶函数的参数值:
var bmp = DrawImage(width, height, gr => {
(...)
});
从函数编程的角度来看,这仅是一个使用高阶函数的无趣示例,但是,它描述了这样一种情况,先执行一些初始化,接着是核心部分,然后是清理,而且这种情况非常普遍,所以,需要有一个专门的名字。这个名字是由 Brian Hurt 在他的博客《“Hole in the Middle(中间有洞)”模式》[Hurt, 2007] 中首次使用,它很好地描述了这种情况,即,每次使用代码,只有中间的部分需要用不同的功能填充。
清单 7.7 是用 F# 实现的函数,类似于补充材料“Hole in the Middle(中间有洞)模式”中的 DrawImage,有一点不同,用两个参数指定创建位图的大小,还能够指定图像边框的边距。
清单 7.7 绘制图形的函数 (F# Interactive)
> let drawImage (width:int, height:int) space coreDrawingFunc =
let bmp = new Bitmap(width, height)
use gr = Graphics.FromImage(bmp) [1]
gr.Clear(Color.White)
gr.TranslateTransform(space, space) <-- 移动整个图像
coreDrawingFunc(gr) [2]
bmp
;;
val drawImage : int * int -> float32 -> (Graphics -> unit) –> Bitmap [3]
我们使用这个函数来绘制图像,绘图的核心部分是作为最后参数给定的函数,从类型签名[3]可以看出,这个函数的参数为 Graphics 值,不返回结果;在代码的中间[2],创建位图和 Graphics 对象之后,被调用。在初始化阶段,我们还调用 TranslateTransform,提供图像的边距。
我们创建的 Graphics 对象实现了 IDisposable,所以,需要在完成绘图后释放。在 C# 中,我们使用知名的 using 构造;在 F# 中,用 use 关键字[1],可以做类似的工作。在清单 7.7 中,在返回结果的位图之前,自动释放 Graphics 对象。use 关键字的与 using 有点不同,我们将在第九章讨论。
最后,我们需要看代码的实际效果。现在,我们将用交互方式创建并测试窗体。清单 7.8 演示了如何绘制清单 7.5 的屏幕元素,以及把文档显示在窗体上。
清单 7.8 使用 WinForms 绘制文档 (F# Interactive)
> let docImage = drawImage (450, 400) 20.0f (drawElements elements) [1]
val docImg : Bitmap
> open System.Windows.Forms
let main = new Form(Text = "Document", BackgroundImage = docImage, | 创建有文档
Width = docImage.Width, Height = docImage.Height) | 的窗体
main.Show();; |
绘制位图[1]的这一行可能要解释一下。我们调用 drawImage,它的最后一个参数是函数,指定了绘图的核心部分。因为我们曾经在 drawElements中实现了这个函数,你可能希望能够将它直接作为最后一个参数值来传递;然而,drawElements 有两个参数,而 drawImage 函数只有一个参数(需要绘制的 Graphics 对象)。我们使用散函数应用指定有 ScreenElement 值的列表;散应用的结果是函数,以 Graphics 对象,并绘制文档,这正是我们需要的 (图 7.2)。
我们以前使用的文档表示方式,虽然使创建文档的代码是有点笨拙,但是,能够轻松地实现绘制。在函数式编程中,我们经常会发现,不同的上下文需要不同数据结构:在某种程度上,期望的用法决定好的表示形式;对于函数式程序,同样的信息,在同一个程序中,有不同的表示形式,并不少见。现在,我们已经找到了适合绘制图像的表示形式,那么,还需要设计适合构造和处理的表示形式,然后,再写一个转换函数,实现两种表示形式的转换。
绘图与第四章的示例类似。因为绘图需要一定的时间,我们将在内存中创建位图,绘制好文档,然后,在窗体上显示位图,而不是每次窗体失效时都绘制文档。我们先看一下非常有用的函数式编程模式,这一节就将使用。
“Hole in the Middle(中间有洞)”模式
[真心不知道,Hole in the Middle 是什么意思?]
写代码的一个常见情形,是首先进行初始化,然后,是函数的核心部分,最后,是一些清理工作;在程序中的多个位置,重复执行类似的操作,初始化和清理部分没有变化,而只有核心部分不同。在内存中绘制位图的示例,用 C# 写的代码可能像这样:
var bmp = new Bitmap(width, height)
using(var gr = Graphics.FromImage(bmp)) {
(...)
}
这里,代码的核心部分是第三行的占位符,我们将用值 gr 来绘图。问题是,只使用面向对象的编程概念,不能简单地把执行初始化和清理的代码,包装成一个子程序,实现在绘制不同图形的所有地方共享。C# 语言支持这种模式众所周知,通常使用各种初始化和清理的类型;using 构造就是这种情况。我们如何为自己的代码模式实现类似的功能呢?
用函数式编程,解决起来很简单;可以写一个高阶函数,把核心部分包装到 lambda 函数中,并把它作为高阶函数的参数值:
var bmp = DrawImage(width, height, gr => {
(...)
});
从函数编程的角度来看,这仅是一个使用高阶函数的无趣示例,但是,它描述了这样一种情况,先执行一些初始化,接着是核心部分,然后是清理,而且这种情况非常普遍,所以,需要有一个专门的名字。这个名字是由 Brian Hurt 在他的博客《“Hole in the Middle(中间有洞)”模式》[Hurt, 2007] 中首次使用,它很好地描述了这种情况,即,每次使用代码,只有中间的部分需要用不同的功能填充。
清单 7.7 是用 F# 实现的函数,类似于补充材料“Hole in the Middle(中间有洞)模式”中的 DrawImage,有一点不同,用两个参数指定创建位图的大小,还能够指定图像边框的边距。
清单 7.7 绘制图形的函数 (F# Interactive)
> let drawImage (width:int, height:int) space coreDrawingFunc =
let bmp = new Bitmap(width, height)
use gr = Graphics.FromImage(bmp) [1]
gr.Clear(Color.White)
gr.TranslateTransform(space, space) <-- 移动整个图像
coreDrawingFunc(gr) [2]
bmp
;;
val drawImage : int * int -> float32 -> (Graphics -> unit) –> Bitmap [3]
我们使用这个函数来绘制图像,绘图的核心部分是作为最后参数给定的函数,从类型签名[3]可以看出,这个函数的参数为 Graphics 值,不返回结果;在代码的中间[2],创建位图和 Graphics 对象之后,被调用。在初始化阶段,我们还调用 TranslateTransform,提供图像的边距。
我们创建的 Graphics 对象实现了 IDisposable,所以,需要在完成绘图后释放。在 C# 中,我们使用知名的 using 构造;在 F# 中,用 use 关键字[1],可以做类似的工作。在清单 7.7 中,在返回结果的位图之前,自动释放 Graphics 对象。use 关键字的与 using 有点不同,我们将在第九章讨论。
最后,我们需要看代码的实际效果。现在,我们将用交互方式创建并测试窗体。清单 7.8 演示了如何绘制清单 7.5 的屏幕元素,以及把文档显示在窗体上。
清单 7.8 使用 WinForms 绘制文档 (F# Interactive)
> let docImage = drawImage (450, 400) 20.0f (drawElements elements) [1]
val docImg : Bitmap
> open System.Windows.Forms
let main = new Form(Text = "Document", BackgroundImage = docImage, | 创建有文档
Width = docImage.Width, Height = docImage.Height) | 的窗体
main.Show();; |
绘制位图[1]的这一行可能要解释一下。我们调用 drawImage,它的最后一个参数是函数,指定了绘图的核心部分。因为我们曾经在 drawElements中实现了这个函数,你可能希望能够将它直接作为最后一个参数值来传递;然而,drawElements 有两个参数,而 drawImage 函数只有一个参数(需要绘制的 Graphics 对象)。我们使用散函数应用指定有 ScreenElement 值的列表;散应用的结果是函数,以 Graphics 对象,并绘制文档,这正是我们需要的 (图 7.2)。
图 7.2 保存在屏幕元素列表中的示例文档,使用 drawElements 函数在窗体上绘制。
我们以前使用的文档表示方式,虽然使创建文档的代码是有点笨拙,但是,能够轻松地实现绘制。在函数式编程中,我们经常会发现,不同的上下文需要不同数据结构:在某种程度上,期望的用法决定好的表示形式;对于函数式程序,同样的信息,在同一个程序中,有不同的表示形式,并不少见。现在,我们已经找到了适合绘制图像的表示形式,那么,还需要设计适合构造和处理的表示形式,然后,再写一个转换函数,实现两种表示形式的转换。
7.2.2 在窗体上显示绘图
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。