Being new to F#, I'm trying to understand how to make graphic updates in a Form triggered by timer events.
My expectation was that the below simple routine should continue drawing new "random" lines every second.
Calling line()
outside the timer event works seemingly without any problems, but I cannot get my head around why nothing gets displayed on the screen when the very same function is invoked via the timer event.
open System
open System.Drawing
open System.Windows.Forms
let form = new Form(Text="Simple Animation", Size=Size(400,500))
let pen = new Pen(Color.Red, 4.0f)
let random = new Random()
let line x =
let flexRight = random.Next(29,300)
form.Paint.Add (fun e -> e.Graphics.DrawLine(pen, 30, 30, 350, flexRight))
let timer=new Timer(Interval=1000, Enabled=true)
timer.Tick.Add(fun time -> line())
form.Show()
Application.Run(form)
Any help very much appreciated, Thanks.
The major problem with you code is that on each timer tick just another brand new Paint
event handler is added to your form instead of invoking a single registered OnPaint
callback that would perform the drawing.
You may get rid of your line
function definition and register instead a single Paint
callback as
form.Paint.Add(fun e -> e.Graphics.DrawLine(pen, 30, 30, 350, random.Next(29,300)))
Then on each timer tick Paint
event may be fired, for example, by invalidating the form. This may be achieved by changing timer's callback code to
timer.Tick.Add(fun _ -> form.Invalidate())
The entire behaving as expected snippet is listed below:
#r "System.Windows.Forms"
open System
open System.Drawing
open System.Windows.Forms
let form = new Form(Text="Simple Animation", Size=Size(400,500))
let pen = new Pen(Color.Red, 4.0f)
let random = new Random()
form.Paint.Add(fun e -> e.Graphics.DrawLine(pen, 30, 30, 350, random.Next(29,300)))
let timer=new System.Windows.Forms.Timer(Interval=1000, Enabled=true)
timer.Tick.Add(fun _ -> form.Invalidate())
form.Show()
UPDATE: as it came out the original intent was showing on the form the superposition of all subsequent drawn lines, I provide one of possible ways to accommodate such behavior with the help of GraphicsPath. Utilizing it would require the following changes to the snippet above:
before the line adding the form Paint
event handler add the line creating the instance of GraphicsPath
let gp = new System.Drawing.Drawing2D.GraphicsPath()
change the Paint
event handler to
form.Paint.Add(fun e -> gp.AddLine(30,30,350,random.Next(29,300))
e.Graphics.DrawPath(pen, gp))