Introduction
Have some fun with F#. I am in the process of exploring the power and simplicity that F# brings with it. I am really impressed with it and bet anyone who looks into it would surely be. :)
Background
I think understanding of the basics of F# will be needed.
What We Are Trying to Achieve.
Nothing great, though. Let us see how we can leverage one of the many great functionalities provided by F#. We will try to use Asynchronous Workflow supported by F# for performing computations or work asynchronously. In this article, we are going to use it to bounce multiple Ellipse objects on the UI without blocking the UI itself. The end result will be responsive UI.
Assumptions
- You have the latest version of 2010 on your machine.
- You understand the basics of F#.
- You understand the basics of WPF.
Steps
Let us start on it...
Creating Window XAML
Let us name the XAML file as MainWindow.xaml. Below is the simple 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>
As you can notice in the XAML above, we got a main Window and a Canvas in it. You may ask why Canvas. The answer is obvious, we can position a child control precisely anywhere we want in the canvas area.
Creating .fs File
Lets now add .fs file to our project for writing some cool F# code. Let us name it SomeFunThisTime.fs.
On the top of this file, let us open (Imports in VB.NET) some of the namespace.
open System
open System.Windows
open System.Windows.Controls
open System.Windows.Shapes
After this, let us define an operator which will help us to find a control by name in the Window childs collection you define in the XAML. This is at the root level.
let (?) (fe:FrameworkElement) name : 'T =
fe.FindName(name) :?> 'T
We will also define some constant values we will be using in our code. So let us create them under a module.
module CONST =
let ELLIPSE_WIDTH = 20.0
let ELLIPSE_HEIGHT = 20.0
I think we are done with what helper code we want at the root level. So now let us
create an entry point in the application. We will do this through the below code:
[<STAThread>]
[<EntryPoint>]
let main(_) = (new Application()).Run(mainWindow)
Do not bother about "mainWindow
" as a parameter in the above code at this point in time. Just assume it is there somewhere in the code. We are going to define it next...
Next, let's define the mainWindow
function. Be sure you take care of the indentation in the code. Indentation make sense to the compiler under #light
switch in F#. So be very mindful of the indentation.
let mainWindow =
let startupWin =Application.LoadComponent(
new System.Uri("/App;component/mainwindow.xaml", UriKind.Relative)) :?> Window
SomeFunThisTime.MyEllipse(startupWin) |> ignore
startupWin
The above code loads the XAML mainwindow.xaml file cast it to Window object and returns it. If you hover your mouse over the mainWindow
, the IDE will show the return type as "Window
". I think till now everything is straightforward. In the above code, there is a line of code which says something like this -> SomeFunThisTime.MyEllipse(startupWin) |> ignore. Let us now have the implementation of it.
We will create a module called SomeFunThisTime
. And under it, we will create a class named MyEllipse
with one parameter of type Window
in its constructor.
In MyEllipse
class, we will have two functions and a couple of lines of code for initializing.
Below is the full code. I am not going go in detail of each line but will highlight some important lines of code.
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
If you have noticed, in the above code in the do
binding a Window Loaded events has been registered. In that event, we are creating numbers of Ellipse
object from 0..45 in numbers in asynchronous mode. I have introduced a delay through this code -> do! Async.Sleep(i * rnd.Next(0, 500))
. This line will return the thread to the thread pool for that many number of ms as supplied to its Sleep
method. This will create an effect of random Ellipses being created rather than in sequence. The below code will also make sure to place the Ellipses adjacent to each other rather than overlaying others.
Another interesting line of code is under startFalling
. It loops indefinitely in asynchronously to bounce the Ellipse
from top to bottom, bottom to up again in.
I have not made it fool proof or sort, you may use it, you may make it fool proof, you may edit it according to your wish, but I just wanted to share with you guys what can be achieved out of the Asynchronous Workflow supported by F#.
Thank you!