In Elmish.wpf / F#, I have the following code:
type Page =
| SettingsModel of Settings.Model
| MainModel of Main.Model
type Model = CurrentPage of Page
type Msg =
| SettingsMsg of Settings.Msg
| MainMsg of Main.Msg
| NavigateToMainModel
let mutable currentModel : Model option = None
let bindings () : Binding<Model, Msg> list =
match currentModel with
| Some (CurrentPage (SettingsModel settingsModel)) ->
Settings.bindings ()
|> List.map (Binding.mapMsg SettingsMsg >> Binding.mapModel (function
| CurrentPage (SettingsModel sm) -> sm
| _ -> failwith "Unexpected model in SettingsModel binding"))
| Some (CurrentPage (MainModel mainModel)) ->
Main.bindings ()
|> List.map (Binding.mapMsg MainMsg >> Binding.mapModel (function
| CurrentPage (MainModel mm) -> mm
| _ -> failwith "Unexpected model in MainModel binding"))
| None -> failwith "Model not initialized"
The problem is that when currentModel
is correctly changed to CurrentPage (MainModel mainModel))
, the first pattern still matches on Some(CurrentPage (SettingsModel settingsModel))
. Why? How to fix?
Edit#1: As requested, here is more of the code. Essentially, I would like to open a single (preferrably) modal window, "Settings". Upon closing this window, update correctly executes "NavigateToMainModel" which is responsible for placing the second window, "Main", as the Model. (I will need to add code to remove the attached events for loading and closing of the Settings window, but I don't believe that should have any effect on this question).
let subscribeToClosingEvent dispatch =
let p = EventPublisher.Instance
p.Publish.Add (fun _ -> dispatch NavigateToMainModel)
let init () : Model * Cmd<Msg> =
let settingsModel, settingCmd = Settings.init()
let initialModel = CurrentPage (SettingsModel settingsModel)
currentModel <- Some initialModel
initialModel, Cmd.batch [
Cmd.map SettingsMsg settingCmd
Cmd.ofEffect subscribeToClosingEvent
]
let update msg (CurrentPage page) =
match page, msg with
| SettingsModel settingsModel, SettingsMsg settingsMsg ->
let newSettingsModel, cmd = Settings.update settingsMsg settingsModel
match settingsMsg with
| Settings.Success ->
let mainModel, mainCmd = Main.init()
let updatedModel = CurrentPage (MainModel mainModel)
currentModel <- Some updatedModel
updatedModel, Cmd.batch [ Cmd.map MainMsg mainCmd;
Cmd.ofEffect (fun dispatch ->
let settingsWindow = Application.Current.MainWindow :?> Settings
settingsWindow.Closing.Add(fun args ->
args.Cancel <- false
dispatch NavigateToMainModel
settingsWindow.Close())
) ]
| _ ->
let updatedModel = CurrentPage (SettingsModel newSettingsModel)
currentModel <- Some updatedModel
updatedModel, Cmd.map SettingsMsg cmd
| MainModel mainModel, MainMsg mainMsg ->
let newMainModel, cmd = Main.update mainMsg mainModel
let updatedModel = CurrentPage (MainModel newMainModel)
currentModel <- Some updatedModel
updatedModel, Cmd.map MainMsg cmd
| _, NavigateToMainModel ->
let mainModel, mainCmd = Main.init()
let initialModel = CurrentPage (MainModel mainModel)
currentModel <- Some initialModel
initialModel, Cmd.map MainMsg mainCmd
| _ -> CurrentPage page, Cmd.none
module MainModule =
[<EntryPoint; STAThread>]
let main argv =
// Create an instance of the Application class.
// Note: If the OutputType of this project (e.g., MyApp.Business) is set to Exe, the app will
// open with a console (black) window. Changing the OutputType to WinExe will avoid the
// console window and open as a WPF application.
let app = new Application()
// Create the Settings window
let closingEventPublisher = CustomEvents.getInstance()
let settingsWindow = new Settings()
// Set the main window
app.MainWindow <- settingsWindow
// Attach event handler for the Loaded event to start the Elmish loop
settingsWindow.Loaded.Add(fun _ ->
// Start the Elmish loop after the window is loaded
WpfProgram.mkProgram AppModule.init AppModule.update AppModule.bindings
|> WpfProgram.startElmishLoop(settingsWindow)
)
settingsWindow.Closing.Add(fun args ->
args.Cancel <- true
closingEventPublisher.Trigger(settingsWindow, EventArgs.Empty)
)
// Show the window
settingsWindow.Show()
// settingsWindow.Closing.RemoveHandler(closingHandler)
// settingsWindow.Close()
// Run the application
app.Run() |> ignore
0