Elmish.wpf: Access to WPF Application instance

Created on 6 Jul 2017  路  4Comments  路  Source: elmish/Elmish.WPF

I wanted to use Application.DispatcherUnhandledException. In Program.fs of Elmish.WPF we can see the application object being created, but not exposed in any way. However, it is called with "window" - that would be the main window in this case - as argument. I added a handler to the main window's Loaded event, and in that handler Application.Current is available (no longer null, to be precise) and its properties can be set, among them DispatcherUnhandledException. In that handler, the argument property "Handled" can be set to true to prevent further processing of the exception, and so hopefully keep the application running. Many developers choose to implement a dialog with Ignore/Abort or something in this handler, and also logging of unhandled exceptions. I also have a habit of setting app.ShutdownMode to OnMainWindowClose, since I believe that's what most users would expect.

Does this make sense? Is there another more elegant way to access the application object? If not, should Elmish.WPF somehow expose the application object?

Most helpful comment

Regardless of what @2sComplement may suggest, Elmish approach in general is in line with "railway-oriented" programming - the functionality that does things (and consequently may throw) should be invoked via Cmd module functions (ofFunc, etc). They handle the exceptions by converting them to messages, which you handle like any other message in your update. The idea is to eliminate unhandled exceptions and handle failure scenarios explicitly.

Ninja edit: Ultimately, there should be no unhandled exceptions when using elmish, because the dispatch loop that calls _everything_ would handle the exception. You can hookup into that mechanism, by setting up your own onError (we're missing a helper method to set it atm, use |> fun p -> { p with onError=your_error_handler }). If you see something escaping the Cmd and Program error handling - then it's something we need to fix.

All 4 comments

Regardless of what @2sComplement may suggest, Elmish approach in general is in line with "railway-oriented" programming - the functionality that does things (and consequently may throw) should be invoked via Cmd module functions (ofFunc, etc). They handle the exceptions by converting them to messages, which you handle like any other message in your update. The idea is to eliminate unhandled exceptions and handle failure scenarios explicitly.

Ninja edit: Ultimately, there should be no unhandled exceptions when using elmish, because the dispatch loop that calls _everything_ would handle the exception. You can hookup into that mechanism, by setting up your own onError (we're missing a helper method to set it atm, use |> fun p -> { p with onError=your_error_handler }). If you see something escaping the Cmd and Program error handling - then it's something we need to fix.

I appreciate what you explain, @et1975, and of course I do not regard unhandled exceptions as part of the normal program flow. They are by definition programming errors, and ideally should not occur. Nevertheless we can expect such errors to occur regardless of which tool, library, framework or language is in use - including Elmish.WPF - and so we must somehow also be able to handle them. The "unhandled exception handler" is the last safety net, and a valuable tool during development. From a pragmatic viewpoint I believe this is complimentary and not contradictory to the modern approaches you are referring to. Thanks for the info - useful right now since I'm new to all of this Elmish stuff, and I have to learn as I go.

Thanks for the Ninja edit @et1975. That is what I was looking for, and I needed your explanation to understand how to use it. It works just as expected.

After some experimentation I see the following two behaviors.

I need onError to log any exception originating from the update function, or else Elmish.WPF will silence them. In any case the application continues to run happily if the error didn't corrupt it.

I also need Application.DispatcherUnhandledException (with Handled=true) to handle exceptions originating from the "fun" functions that I have put in the view function. If not using this, then the exception escapes entirely, and is finally caught by a try-with that I have put around the startup code. In short, the application blows up, but at least I get to log the exception. If using the DispatcherUnhandledException handler however, the application can continue to run happily if the error didn't corrupt it.

To conclude, I am very happy with the way it works now, but there is perhaps a chance for improvement by somehow exposing the application object.

How about an alternative to runWindow? You could have runApplication app window, the point being that you could create the application object yourself, just as you do with the window. This will perhaps make it easier to use FsXaml in combination with Elmish.WPF, if somebody wants to do that. You could then use the FsXaml type provider to create the application object as well as the window, and hand them both to Elmish.WPF.

edit: Or it could be Application.withApplication app, and if not used then it works as before.

I think this would be in line with the idea that one should have libraries rather than frameworks. (See "Why frameworks are evil" by Tomas Petricek, http://tomasp.net/blog/2015/library-frameworks) In fact I think Elmish.WPF in this particular case behaves more like a framework, and I was just lucky enough to find a way to break out.

As per @et1975, unhandled exceptions will go through Program.onError, which is now exposed through Program.withExceptionHandler. Also included in a sample.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

dsyme picture dsyme  路  11Comments

dsyme picture dsyme  路  10Comments

TysonMN picture TysonMN  路  9Comments

cmeeren picture cmeeren  路  6Comments

TysonMN picture TysonMN  路  7Comments