7.2.2 在窗体上显示绘图

本文介绍了一种称为“中间有洞”的编程模式,并通过一个具体的绘图示例展示了如何利用该模式简化代码结构。文章详细解释了如何使用F#实现此模式,并通过实例演示了如何在内存中创建位图并将其显示在窗体上。
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)。
 


图 7.2 保存在屏幕元素列表中的示例文档,使用 drawElements 函数在窗体上绘制。



我们以前使用的文档表示方式,虽然使创建文档的代码是有点笨拙,但是,能够轻松地实现绘制。在函数式编程中,我们经常会发现,不同的上下文需要不同数据结构:在某种程度上,期望的用法决定好的表示形式;对于函数式程序,同样的信息,在同一个程序中,有不同的表示形式,并不少见。现在,我们已经找到了适合绘制图像的表示形式,那么,还需要设计适合构造和处理的表示形式,然后,再写一个转换函数,实现两种表示形式的转换。
下载前可以先看下教程 https://pan.quark.cn/s/a426667488ae 标题“仿淘宝jquery图片左右切换带数字”揭示了这是一个关于运用jQuery技术完成的图片轮播机制,其特色在于具备淘宝在线平台普遍存在的图片切换表现,并且在整个切换环节中会展示当前图片的序列号。 此类功能一般应用于电子商务平台的产品呈现环节,使用户可以便捷地查看多张商品的照片。 说明中的“NULL”表示未提供进一步的信息,但我们可以借助标题来揣摩若干核心的技术要点。 在构建此类功能时,开发者通常会借助以下技术手段:1. **jQuery库**:jQuery是一个应用广泛的JavaScript框架,它简化了HTML文档的遍历、事件管理、动画效果以及Ajax通信。 在此项目中,jQuery将负责处理用户的点击动作(实现左右切换),并且制造流畅的过渡效果。 2. **图片轮播扩展工具**:开发者或许会采用现成的jQuery扩展,例如Slick、Bootstrap Carousel或个性化的轮播函数,以达成图片切换的功能。 这些扩展能够辅助迅速构建功能完善的轮播模块。 3. **即时数字呈现**:展示当前图片的序列号,这需要通过JavaScript或jQuery来追踪并调整。 每当图片切换时,相应的数字也会同步更新。 4. **CSS美化**:为了达成淘宝图片切换的视觉效果,可能需要设计特定的CSS样式,涵盖图片的排列方式、过渡效果、点状指示器等。 CSS3的动画和过渡特性(如`transition`和`animation`)在此过程中扮演关键角色。 5. **事件监测**:运用jQuery的`.on()`方法来监测用户的操作,比如点击左右控制按钮或自动按时间间隔切换。 根据用户的交互,触发相应的函数来执行...
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值