65.9K
CodeProject 正在变化。 阅读更多。
Home

F# 与 WPF:异步 {} 或异步工作流的乐趣 - 弹跳的椭圆

starIconstarIconstarIconstarIconemptyStarIcon

4.00/5 (5投票s)

2010 年 11 月 1 日

CPOL

4分钟阅读

viewsIcon

21575

F# 支持的异步工作流,用于操作 WPF 中的 UI 对象。

引言

用 F# 玩得开心。我正在探索 F# 带来的强大和简洁。我真的对它印象深刻,并确信任何研究它的人都会如此。 :)

背景

我认为需要了解 F# 的基础知识。

我们想要实现的目标。

虽然不是什么了不起的东西。让我们看看如何利用 F# 提供的许多强大功能之一。我们将尝试使用 F# 支持的异步工作流来异步执行计算或工作。在本文中,我们将使用它在 UI 上弹跳多个椭圆对象,而不会阻塞 UI 本身。最终结果将是响应迅速的 UI。

假设

  • 您已经在您的机器上安装了最新的 2010 版本。
  • 您了解 F# 的基础知识。
  • 您了解 WPF 的基础知识。

步骤

让我们开始吧...

创建窗口 XAML

让我们将 XAML 文件命名为 MainWindow.xaml。以下是简单的 XAML

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Title="Working with F# is really a fun.... : )" Height="450" Width="500" 
    WindowState="Maximized"> 
<Canvas Name="canvas" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" 
    Background="LightBlue"/> 
</Window>

正如您在上面的 XAML 中注意到的,我们有一个主窗口和一个 Canvas。您可能会问为什么使用 Canvas。答案很明显,我们可以在 Canvas 区域内的任何位置精确地定位子控件。

创建 .fs 文件

现在让我们在我们的项目中添加 .fs 文件,用于编写一些很酷的 F# 代码。让我们将其命名为 SomeFunThisTime.fs

在此文件的顶部,让我们打开(VB.NET 中的 Imports)一些命名空间。

open System
open System.Windows
open System.Windows.Controls
open System.Windows.Shapes

之后,让我们定义一个运算符,它将帮助我们通过名称在您在 XAML 中定义的 Window 子集合中找到一个控件。这位于根级别。

let (?) (fe:FrameworkElement) name : 'T =
  fe.FindName(name) :?> 'T

我们还将定义一些将在我们的代码中使用的常量值。所以让我们在模块下创建它们。

module CONST =
 
  let ELLIPSE_WIDTH = 20.0
  
  let ELLIPSE_HEIGHT = 20.0

我想我们已经完成了我们在根级别想要的辅助代码。所以现在让我们
在应用程序中创建一个入口点。我们将通过以下代码来完成此操作

[<STAThread>]
[<EntryPoint>]
let main(_) = (new Application()).Run(mainWindow)

此时不要在意 "mainWindow" 作为一个参数在上面的代码中。 假设它就在代码的某个地方。 我们接下来将定义它...

接下来,让我们定义 mainWindow 函数。 确保您注意代码中的缩进。 在 F# 中的 #light 开关下,缩进对编译器有意义。 因此请务必注意缩进。

let mainWindow = 

  let startupWin =Application.LoadComponent(

    new System.Uri("/App;component/mainwindow.xaml", UriKind.Relative)) :?> Window

  SomeFunThisTime.MyEllipse(startupWin) |> ignore

  startupWin

上面的代码加载 XAML mainwindow.xaml 文件,将其转换为 Window 对象并返回它。 如果您将鼠标悬停在 mainWindow 上,IDE 将显示返回类型为 "Window"。 我认为到目前为止一切都很简单。 在上面的代码中,有一行代码类似于 -> SomeFunThisTime.MyEllipse(startupWin) |> ignore. 让我们现在来实现它。

我们将创建一个名为 SomeFunThisTime 的模块。 在它下面,我们将创建一个名为 MyEllipse 的类,它的构造函数中有一个 Window 类型的参数。

MyEllipse 类中,我们将有两个函数和几行用于初始化的代码。

以下是完整的代码。 我不会详细介绍每一行,但会突出显示一些重要的代码行。

module SomeFunThisTime =

  type MyEllipse(win : Window) = class
    let winObj = win

     let elp width heigh = 
     let obj : Ellipse = new Ellipse()
     obj.Width <- width
     obj.Height <- heigh
     obj.Fill <- Media.Brushes.Red
     obj.Stroke <- Media.Brushes.Black
     obj.StrokeThickness <- 5.0
     obj

    let startFalling (elp : Ellipse) =
      let rnd = new Random()
      async {
             while true do 
              for x = 0 to 400 do
               do! Async.Sleep(rnd.Next(0, 3))
               Canvas.SetTop(elp, float x)

              for x = 400 downto 0 do
               do! Async.Sleep(rnd.Next(0, 3))
               Canvas.SetTop(elp, float x)
            } |> Async.StartImmediate

    do
      winObj.Loaded.Add(fun _ -> 
          let canvas : Canvas = winObj?canvas
          let rnd = new Random()
          [0..45]
          |> Seq.map(fun x -> elp CONST.ELLIPSE_WIDTH CONST.ELLIPSE_HEIGHT) 
          |> Seq.iteri(fun i e -> 
              async {
                     do! Async.Sleep(i * rnd.Next(0, 500))
                     canvas.Children.Add(e) |> ignore
                     System.Windows.Controls.Canvas.SetTop(e, 0.0)
                     System.Windows.Controls.Canvas.SetLeft(e, float (i * (
                         int CONST.ELLIPSE_WIDTH)))
                     e.Loaded.Add(fun _ -> startFalling(e))
                    } |> Async.StartImmediate

             )
        )

    member this.Window = winObj

  end

如果您注意到,在上面的代码中,在 do 绑定中,已注册了 Window Loaded 事件。 在该事件中,我们正在异步模式下从 0..45 生成一定数量的 Ellipse 对象。 我通过这段代码引入了延迟 -> do! Async.Sleep(i * rnd.Next(0, 500))。 这一行将把线程返回到线程池中,持续时间为 Sleep 方法中提供的毫秒数。 这将产生随机创建椭圆的效果,而不是按顺序创建。 以下代码还将确保将椭圆彼此相邻放置,而不是重叠。

另一行有趣的代码在 startFalling 下。 它异步地无限循环,使 Ellipse 从上到下、从下到上弹跳。

我没有使其万无一失或类似的东西,您可以随意使用它,您可以使其万无一失,您可以根据您的愿望进行编辑,但我只是想与大家分享如何利用 F# 支持的异步工作流来实现目标。

谢谢!

© . All rights reserved.