11.4.2 在照片浏览器中缓存值

728 篇文章 1 订阅
30 篇文章 0 订阅
11.4.2 在照片浏览器中缓存值



    在下一个示例中,我们要写一个应用程序,找出指定文件夹中的所有照片,并显示为一个列表。当用户选择一张照片,应用程序调整它的大小,并显示在一个窗口中。(为了简便起见,我们不会允许用户调整窗口大小)。当我们画了这张照片时,需要调整其大小以适合屏幕,然后,显示调整大小后的图像。

    很明显,我们不想在应用程序启动时调整所有照片的大小:对于大型的照片集,需要花大量的时间。我们也不想在每次我们画出来时,调整照片的大小,因为,这会一次又一次调整相同照片的大小。从说明来看,相当明显,延迟值可以帮助我们。我们将演示如何在 F# 中写这个应用程序,你可以在本书的网站上找到对应的 C# 版本。



使用延迟值缓存



    这个应用程序最有趣的部分是,应用程序启动时,代码已执行。它在指定的目录中找到所有文件,并用每个文件的信息创建一个数组。信息包含文件名,和为了调整 preview 大小计算的延迟值。清单 11.21 显示了如何创建此数据结构。



Listing 11.21 Creating a collection of photo information (F#)



open System.IO
open System.Drawing

type ImageInfo = { Name : string; Preview : Lazy<Bitmap> }

let dir = @"C:\My Photos"
let createLazyResized(file) =
  lazy( use bmp = Bitmap.FromFile(file)  
          let resized = new Bitmap(400, 300)
          use gr = Graphics.FromImage(resized)

          let dst = Rectangle(0, 0, 400, 300)
          let src = Rectangle(0, 0, bmp.Width, bmp.Height)
          gr.InterpolationMode <- Drawing2D.InterpolationMode.High
          gr.DrawImage(bmp, dst, src, GraphicsUnit.Pixel)
          resized)

let files =
  Directory.GetFiles(dir, "*.jpg") |> Array.map (fun file –>;
    { Name = Path.GetFileName(file)
       Preview = createLazyResized(file) })



    我们首先声明一个记录类型,表示有关照片的信息。可以看到,preview 的类型是 Lazy<Bitmap>,这是一个延迟计算,在我们需要它时,会给我们一个位图(Bitmap)。接下来,我们实现一个函数,返回表示已调整大小的位图的延迟值。要绘制 preview ,我们写了常规代码,调整位图对象,并把整个函数体使用 lazy 关键字打包起来。

    接下来,我们创建包含照片有关信息的数据结构。使用通常的 .NET 法调用获得一个文件数组,并使用 Array.map 函数,为每张照片创建一个  ImageInfo 值。在这个 lambda 函数的内部,创建一个记录值,包含名字,和由 createLazyResized 函数返回的延迟 preview。

    这个程序的一个有趣属性是,我们可以删除所有使用 lazy 的关键字,把所有类型从Lazy<'T> 改为 'T,删除所有使用的 Value 属性,代码会仍正常工作,只是所有都会提前计算。



实现用户界面



现在,我们已经有了有关这些照片所需要的所有数据,下面,就使用 Windows 窗体可以添加一个简单的 GUI。在清单 11.22 中,我们将创建几个控件以显示数据和代码,显示选定的照片。



Listing 11.22 Adding a user interface for the photo browser (F#)



open System
open System.Windows.Forms

let main = new Form(Text="Photos", ClientSize=Size(600,300))
let pict = new PictureBox(Dock=DockStyle.Fill)
let list = new ListBox(Dock=DockStyle.Left,  
                               DataSource=files,
                               DisplayMember = "Name")
list.SelectedIndexChanged.Add(fun _ –>;;
  let info = files.[list.SelectedIndex]
  pict.Image <- info.Preview.Value)
main.Controls.Add(pict)
main.Controls.Add(list)

[<STAThread>]
do Application.Run(main)



    为显示 ListBox(列表框)控件中的图片列表,我们使用了数据绑定,这是一个可用于很多 .NET 控件的功能。我们只是指定控件的数据源(DataSource)是文件的数组。若要指定如何显示内容,可以设置 DataMember 属性,为想要显示的记录成员的名字(Name)。

    接下来,我们注册一个 lambda 函数,作为列表框的 SelectedIndexChanged 事件的处理程序。当它被触发后,我们选择 ImageInfo 值,并使用 Value 属性来获取已调整大小的位图。如果该位图是第一次显示,将在这里重新调整大小;如果之前已经浏览过,可以立即使用缓存中的结果。清单 11.22 显示的代码,是一个独立的应用程序,这意味着,我们用 Application.Run() 方法来运行。在 F# Interactive 中,可以使用 main.Show () 来显示窗体。可以看到,应用程序的外观如图 11.2。



图 11.2 照片可以从左侧的列表中选择。由于使用了延迟值,调整大小后的版本被自动缓存。



    如果运行这个应用程序,使用的文件夹中包含大型图片的话,由于使用延迟所带来的差别是明显的。选择"新"照片可能需要一些时间,但是如果你重新浏览已经看过的照片,它会立即呈现。



注意



    你可能想知道,我们是否可以使用多线程来改进这个应用程序。有两个地方使用多线程是有帮助的。首先,当用户选择文件时,可以开始计算,而不要阻止用户界面。目前,应用程序是被冻结的,直到图像调整大小之后。为此,我们可以使用异步编程技术,如 F# 异步工作流,在第 13 章中会讨论。

    另一个可能性是,应用程序可以在背景中预先调整位图的大小。不是什么也不做,提前调整某些图版的大小,使用户当单击照片时不必等待。在第 14 章,我们将看到,这是相当简单的,Task<T> 类型,就像 Lazy<T> 一样,除了它不延迟以外,另外,它在后台线程计算值。



    我们确信对于在 C# 中如何使用 Lazy<T> 类,实现相同的应用程序,你已经有了一个很好的概念,所以,我们不会在本书中讨论。可以在这本书的网站找到应用程序的源代码。在 C# 版本中,一个很有趣的地方是,可以使用 C# 3.0 的匿名类型来表示照片的信息,如果写的的代码在一个单独的方法内。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值