Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / XAML

F# with WPF: Fun with async {} or Asynchronous Workflow - Bouncing Ellipse

4.00/5 (5 votes)
1 Nov 2010CPOL4 min read 21K  
Asynchronous Workflow supported by F# to manipulate UI objects in WPF

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:

XML
<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.

F#
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.

F#
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.

F#
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:

F#
[<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.

F#
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.

F#
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!

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)