简体   繁体   中英

Access lists defined in constructor on override onPaint method F#

I want to override the onPaint method to make it draw the objects in two lists defined in the constructor, problem being I can't access the lists from the overrided onPaint method, I get the error saying the list or constructor is not defined when trying to use listOfSquares or listOfCircles. So basically, how do I access these lists from that override?

type MainForm = class
  inherit Form

  val mutable g : Graphics // mutable means its not read-only
  val mutable position : Point // position of the rectangle

  new () as form = {g=null;position = new Point(0,0)} then
     // double buffering
     form.SetStyle (ControlStyles.UserPaint, true);
     form.SetStyle (ControlStyles.DoubleBuffer, true);
     form.SetStyle (ControlStyles.AllPaintingInWmPaint, true);
     form.Width <- 900
     form.Height <- 500
     form.BackColor <- Color.White
     form.Text <- "2D Graphics Editor";
     let listOfSquares = ResizeArray()
     let listOfCircles = ResizeArray()
     let menu = new MenuStrip()

     let file = new ToolStripDropDownButton("File") // Menu
     ignore(menu.Items.Add(file))


     let create = new ToolStripDropDownButton("Create")  // Menu
     ignore(menu.Items.Add(create))

     let square = create.DropDownItems.Add("Square")
     let circle = create.DropDownItems.Add("Circle")
     let newFile = file.DropDownItems.Add("New file")
     let saveFile = file.DropDownItems.Add("Save file")
     let openFile = file.DropDownItems.Add("Open file")
     square.Click.Add(fun _ -> listOfSquares.Add(new square(5.0, 5.0)) |> ignore)
     circle.Click.Add(fun _ -> listOfCircles.Add(new circle(10.0, 10.0)) |> ignore)
     newFile.Click.Add(fun _ -> MessageBox.Show("newFile") |> ignore)
     saveFile.Click.Add(fun _ -> MessageBox.Show("saveFile") |> ignore)
     openFile.Click.Add(fun _ -> MessageBox.Show("openFile") |> ignore)
     let dc c = (c :> Control)
     form.Controls.AddRange([|dc menu|]);


     // show the form
     form.Show()

     // override of paint event handler
     override form.OnPaint e = 
        let g = e.Graphics in
        // draw objects in listOfSquares and listOfCircles

     end  

You defined their scope as being the constructor rather than the object. Move their declarations up to where position and g are defined.

I think this satisfies your requirements:

type test =
    val mutable private temp:int
    new() as this = {temp=5} then
        this.temp <- 6

The important bits are the private access modifier, the assignment of the private field in the secondary constructor using the {..} syntax and the use of this to access private members.

Here is your code rewritten to properly initialize your lists:

type MainForm =
  inherit Form

  val mutable g : Graphics // mutable means its not read-only
  val mutable position : Point // position of the rectangle
  val listOfSquares : ResizeArray
  val listOfCircles : ResizeArray

  new () as form = {g=null;position = new Point(0,0)} then
     // double buffering
     form.SetStyle (ControlStyles.UserPaint, true);
     form.SetStyle (ControlStyles.DoubleBuffer, true);
     form.SetStyle (ControlStyles.AllPaintingInWmPaint, true);
     form.Width <- 900
     form.Height <- 500
     form.BackColor <- Color.White
     form.Text <- "2D Graphics Editor";
     listOfSquares <- ResizeArray()
     listOfCircles <- ResizeArray()
     let menu = new MenuStrip()

     let file = new ToolStripDropDownButton("File") // Menu
     ignore(menu.Items.Add(file))


     let create = new ToolStripDropDownButton("Create")  // Menu
     ignore(menu.Items.Add(create))

     let square = create.DropDownItems.Add("Square")
     let circle = create.DropDownItems.Add("Circle")
     let newFile = file.DropDownItems.Add("New file")
     let saveFile = file.DropDownItems.Add("Save file")
     let openFile = file.DropDownItems.Add("Open file")
     square.Click.Add(fun _ -> listOfSquares.Add(new square(5.0, 5.0)) |> ignore)
     circle.Click.Add(fun _ -> listOfCircles.Add(new circle(10.0, 10.0)) |> ignore)
     newFile.Click.Add(fun _ -> MessageBox.Show("newFile") |> ignore)
     saveFile.Click.Add(fun _ -> MessageBox.Show("saveFile") |> ignore)
     openFile.Click.Add(fun _ -> MessageBox.Show("openFile") |> ignore)
     let dc c = (c :> Control)
     form.Controls.AddRange([|dc menu|]);


     // show the form
     form.Show()

     // override of paint event handler
     override form.OnPaint e = 
        let g = e.Graphics in
        // draw objects in listOfSquares and listOfCircles

     end  

As @leafgarland demonstrated, if you don't need to use a secondary constructor, then use the primary constructor for much cleaner syntax.

type test() =
    let mutable temp = 6

    ...

    override form.OnPaint e = 
        let g = e.Graphics
        printfn "%i" temp

If you did want to use a primary constructor then you could do it like this, using let bindings for all your private fields and do bindings for the constructor's code. The let bindings are accessible to all non-static members.

See the F# documentation on classes to read about this syntax.

type MainForm() as form =
    inherit Form()

    let mutable g : Graphics = null
    let mutable position : Point = Point(0,0)

    let listOfSquares = ResizeArray()
    let listOfCircles = ResizeArray()

    do
        form.SetStyle (ControlStyles.UserPaint, true);

        // ... your other initialization code

        // show the form
        form.Show()

    override form.OnPaint e = 
        let g = e.Graphics
        // draw objects in listOfSquares and listOfCircles

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM