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





4.00/5 (5投票s)
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# 支持的异步工作流来实现目标。
谢谢!