简单的GUI应用程序



我通过编辑一个通过点击处理事件的模板应用程序来学习F#。当按下按钮时,我正试图更新TextBox。目前,点击只更新到控制台。

这个问题的主要目标是学习。我将尽我最大的努力解释我的思维过程,如果有什么错误,请随时纠正我。

主要:

open FSharpx.Control.Observable // Fsharpx
module Main =
type State = int list
module GuiInterface = 
type UserAction = 
| Zero
| One
| Two 
| Three
let observables = List.reduce Observable.merge [
Observable.map (fun _-> Zero) Gui.btnAdd0.Click;
Observable.map (fun _-> One) Gui.btnAdd1.Click;
Observable.map (fun _-> Two) Gui.btnAdd2.Click;
Observable.map (fun _-> Three) Gui.btnAdd3.Click;
]  
let rec loop observable (state : State) = async{

printfn "%A" state
let! somethingObservable = Async.AwaitObservable(observable) 
match somethingObservable with
| GuiInterface.Zero  -> return! loop observable (0::state)
| GuiInterface.One  -> return! loop observable (1::state)
| GuiInterface.Two  -> return! loop observable (2::state)
| GuiInterface.Three  -> return! loop observable (3::state)
| _  -> failwith "Not implemented yet!"
}
Async.StartImmediate(loop GuiInterface.observables []); System.Windows.Forms.Application.Run(Gui.form)  
Gui

:

module Gui = 
open System.Windows.Forms
let form = new Form(Text="FSharpx", TopMost=true)
let btnAdd0 = new Button(Text="Add 0")
let btnAdd1 = new Button(Text="Add 1", Top=20)
let btnAdd2 = new Button(Text="Add 2", Top=40)
let btnAdd3 = new Button(Text="Add 3", Top=60)
let display = new TextBox(Text="click the buttons", Width=200, Left=200)
form.Controls.AddRange [| btnAdd0 ; btnAdd1; btnAdd2; btnAdd3; display |]

let observables中,我认为它将点击转换为我们在用户操作类型中拥有的任何值。然后将它们合并在一起。

当用户单击时,observable等待输入,然后从GuiInterface类型转换为程序的任何逻辑。

如前所述,我不知道我将如何处理而不是打印列表到控制台做同样的事情,但TextBox。我知道列表中包含int类型,需要将其转换为string类型。

要将列表转换为字符串,可以使用sprintf。您可以将当前的printf语句替换为以下赋值:

Gui.display.Text <- sprintf "%A" state

这是有效的,但是它可能有点过于命令式,并且它将GUI与逻辑紧密地耦合在一起-对于学习来说很好,但可能不是一个很好的实践。另一种方法是定义一个新的事件(可观察对象),并通过

传递文本更新:
type State = int list
module GuiInterface = 
type UserAction = Zero | One | Two | Three
let observables = List.reduce Observable.merge [
Observable.map (fun _-> Zero) Gui.btnAdd0.Click;
Observable.map (fun _-> One) Gui.btnAdd1.Click;
Observable.map (fun _-> Two) Gui.btnAdd2.Click;
Observable.map (fun _-> Three) Gui.btnAdd3.Click;
]  
let stateChange = new Event<_>()
let rec loop observable (state : State) = async{
GuiInterface.stateChange.Trigger(sprintf "%A" state)
let! somethingObservable = Async.AwaitObservable(observable) 
match somethingObservable with
| GuiInterface.Zero  -> return! loop observable (0::state)
| GuiInterface.One  -> return! loop observable (1::state)
| GuiInterface.Two  -> return! loop observable (2::state)
| GuiInterface.Three  -> return! loop observable (3::state)
| _  -> failwith "Not implemented yet!"
}
GuiInterface.stateChange.Publish.Add(fun s ->
Gui.display.Text <- s)
Async.StartImmediate(loop GuiInterface.observables [])
System.Windows.Forms.Application.Run(Gui.form)  

然而,你也可以用Observable.scan函数替换你的循环,让你维护状态,并根据其他事件更新它:

type State = int list
module GuiInterface = 
type UserAction = Zero | One | Two | Three
let observables = List.reduce Observable.merge [
Observable.map (fun _-> Zero) Gui.btnAdd0.Click;
Observable.map (fun _-> One) Gui.btnAdd1.Click;
Observable.map (fun _-> Two) Gui.btnAdd2.Click;
Observable.map (fun _-> Three) Gui.btnAdd3.Click;
]  
let stateChanged = 
observables 
|> Observable.scan (fun state update ->
match update with
| Zero -> (0::state)
| One -> (1::state)
| Two -> (2::state)
| Three -> (3::state)
| _  -> failwith "Not implemented yet!") []
|> Observable.map (sprintf "%A")
GuiInterface.stateChanged.Add(fun s ->
Gui.display.Text <- s)
System.Windows.Forms.Application.Run(Gui.form)  

也就是说,如果你真的对f#中以函数方式开发UI感兴趣,那么最好看看Elmish,它是一组很好的函数抽象——比Windows Forms好用得多。

最新更新