-
-
Notifications
You must be signed in to change notification settings - Fork 71
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for dialogs #24
Comments
I would recommend implementing your own "dialog", which is fairly easy and imho looks much better than windows dialogs, which are also notoriously difficult to deal with when using MVVM, let alone elmish. |
Closing unless this becomes an issue again. |
Sorry I missed this. Yes, this is great. Thanks for the example. |
@giuliohome (and @2sComplement):
|
Few thoughts :
Since the use of Cmd allows to send message back in the loop, we can await for the closing of a modeless window and inform the model, though I don’t think it’s a common need. I agree that a window call for a submodel binding. Since the window |
Of course, there should be no prohibition to use the very same I'll look at this this week-end. |
Just from the top of my head (because I'm working with such an app, though I ended up using material dialogs in the same window instead): You could have a data input app where the input form was a separate modeless window. This would allow you to browse the existing data while creating a new entry. Closing the modeless window via the "Submit" button would send an action containing the new data to be added. And the "Cancel" button would just close the window. Note that in this case, I can see some potential concerns right away. On the one hand, the actual closing of the window must be controlled outside of the Elmish loop (e.g. in the codebehind). On the other hand, the "Submit" button may first validate the form, and if it's valid, close the window and send the message containing the new data, or if it's invalid, display validation errors (and not close the window). The submit/validation message can be part of the Elmish loop/model (make This is what I was getting at with the FAQ answer: Controlling whether windows are open or closed cannot be part of the Elmish model. This is, however, trivial with a custom/material dialog as suggested (and demoed in the SubModelOpt sample). IMHO any official Elmish.WPF solution to opening new windows should be flexible enough to cater to many kinds of usecases such as this. If that's not possible or feasible, we might just as well add some documentation tipping users to use something akin to @giuliohome's example in a Command, which is a perfectly viable workaround if you want to open a new window.
You can easily do this using |
IIRC (based on the version of my app that used windows) there's no significant difference code-wise (apart from calling
This may be my inexperience talking, but I see two ways of closing windows that are relevant to the current discussion:
Let me know if I need to elaborate further. On another note: Could you elaborate on a couple of use-cases for modeless windows? Just to ensure we are on the same page. |
Ah, I think I see where each of us stand now. While I have made some apps in Xamarin.Forms, my experience with WPF is limited and mostly concerned with simple data entry. When I think of new windows, I think of a (temporary) window that in some way produces a result (such as a new business entity to be saved). The closing of the window is inextricably linked to some domain event (in this case, dispatching of an Elmish message containing the new business entity). You seem to be talking about windows that do not produce a result. If I understand you correctly, an example would be a "Preferences" window that automatically saves settings as you edit them. Domain events (e.g. message dispatches) happen while you use the window, but when you're done, you can just close it, e.g. using the X in the corner. (Though in this particular example, one could argue that a dialog works just as well as a new window.) These seem to be two different use-cases, and could be supported (or not) individually. I can envision a couple of A significant caveat though is that this requires the app's core Elmish project (where the I'd like your (and anyone else's) input on this. I may of course have overlooked a better solution. In any case, given my lack of WPF experience, I'd be very happy if you could explain a couple of use-cases you have for modeless windows, why a modeless window is a better solution than a Visibility-controlled dialog in these cases, and if relevant, how the windows would work in relation to the Elmish architecture. |
Thank you, it seems we understand each other then. You have shown a very simple way of opening a window from the update function, which I'm sure is trivial to use from a command instead of the update function. The fact that this is fairly simple, together with the fact that proper separation of concerns would include some kind of navigation service, makes me think that this does not really belong in Elmish.WPF. I think we could be fine with just a sample that shows how to open a new window and set the DataContext in a command. Do you agree? |
I have now added a new sample called |
according the new example in elmish/Elmish.WPF#24
|
Thanks for the clarification and concrete example, I understand what you mean now. Though I can't promise when I'll get to it, I'll try to come up with a sample that demonstrates how this can be done. |
With my tinkerings this week end, I found out that it's a good idea to call a dialog through a Binding.cmd. There, it automatically run in the application dispatcher in an asynchronous way. Shown there, they don't block the update loop. We can use the elmish dispatch function passed to the bindings/views function. Though there may be case where it's needed to call a window in the command part of the loop, that may help to keep the update function out of UI consideration. Also, a few week ago, I wanted to see if I can write an AutoCAD palette with Elmish. This lead me to write a Program.runUserControl that actually just bind a user control with an update loop (there's nothing to run in fact - just find a way to bind the control to the loop before/when the calling app load it). I was writing an exemple palatable to people who doesn't have an AutoCAD licence paid by their boss, but this issue make me consider a few things, notably that we can bind a window with their own elmish loop. That may feel anti-pattern, but I can see it used to implement something like the Outlook contact book (something that resembles @cmeeren submit modeless window). However for now, I just looked how to deal with modal dialogs. Please look here, see the OpenDialog example. |
@giuliohome : please, take a look at sample project ModelessWindow.SeparatedLoop (and at SharedLoop as well) |
Updated anew. No new functions, just refactoring, including samples where I try to define some good practices to deal with called windows. Feedback are welcome on this point, especially on what can be done with xaml. |
Fun fact : with |
Yes, that's true - if a bit hacky, and liable to break in the future since one is delving into implementation details. If there is a real need for such functionality, we should consider implementing a proper API for it. :) |
Reading the new v3 ReadMe.md, I'd like to point out it is possible to invoke a modal dialog without blocking the Elmish dispatch loop : Application.Current.Dispatcher.Invoke (fun () ->
let guiCtx = System.Threading.SynchronizationContext.Current
async {
let dlg = SomeModalWindow ()
dlg.Owner <- Application.Current.MainWindow
dlg.DataContext <- Application.Current.MainWindow.DataContext
let currentCtx = System.Threading.SynchronizationContext.Current
do! Async.SwitchToContext guiCtx
dlg.ShowDialog () |> ignore
do! Async.SwitchToContext currentCtx
} |> Async.StartImmediate) (also, unrelated, link to CmdMsg pattern from Fabulous documentation is broken) |
Thanks @JDiLenarda, that's great! I've experimented a bit now and it seems that the minimal code required is the below (a function similar to let showDialog (getWindow: unit -> #Window) =
let showWin () =
Application.Current.Dispatcher.Invoke(fun () ->
let guiCtx = System.Threading.SynchronizationContext.Current
async {
let win = getWindow ()
win.DataContext <- Application.Current.MainWindow.DataContext
do! Async.SwitchToContext guiCtx
win.ShowDialog () |> ignore
}
)
Cmd.OfAsync.attempt showWin () raise |
After testing, I suggest to keep the part where ownership of the dialog is given to the main window. This prevent the dialog to disappear behind the main window if it is clicked. let showDialog (getWindow: unit -> #Window) =
let showWin () =
Application.Current.Dispatcher.Invoke(fun () ->
let guiCtx = System.Threading.SynchronizationContext.Current
async {
let win = getWindow ()
win.Owner <- Application.Current.MainWindow
win.DataContext <- Application.Current.MainWindow.DataContext
do! Async.SwitchToContext guiCtx
win.ShowDialog () |> ignore
}
)
Cmd.OfAsync.attempt showWin () raise As such, this is all I currently need, so feel free to commit. But for further evolution, it would be interesting to consider the result of let showDialog (getWindow: unit -> #Window) (validate: 'model -> bool option) (onAccept: 'model -> 'msg) (onCancel: 'model -> 'msg) Again, this is a suggestion for the future, not an immediate need for me, neither the correct signature. |
I am thinking that the window owner should be controlled by the user (using the Regarding the dialog result stuff: Interesting! I'll have to think carefully on that functionality and signature; it requires careful consideration to ensure it meshes well with Elmish architecture. For example, one use-case I want to enable is clicking Ok and then have validation errors show (and the window not close). I'll certainly consider it. |
I didn't notice that. I modified my code and I'm ok with this. |
Great! I'll experiment a bit with result-producing windows as per your suggestion before I publish anything. |
Hmm, one problem with the proposed result stuff is that AFAIK there is no way to access the current model from a command (only the dispatch if using Cmd.ofSub). Any ideas? |
Also, I can't immediately think of any smart ways to have a long-lived dialog when using
Once I get the first result, that's it, Am I right? |
For retreiving the model : since the datacontext is the For the button behaviour : I’m confused. For the dialog closing right now on Ah, well, after getting another look at the documentation, I realize I was mistaken about the role of IsDefault. It’s not for the Ok button specifically, but for telling what button must be clicked when the user press enter. So the same button can be Look, I’ll try to dig further and come with a proof of concept, but that won’t be before a few weeks. Btw, I’ll make a post on what I have theorized on the subject in the next days. Anyway, thank you for considering the demand (and for that very good v3, too). |
Another way to do this which I kinda like, is to have the signature be The downside is that it doesn't feel "pure" to have handles in state. But then again, dialogs like this aren't idiomatic Elm anyway, and static views has its own idiosyncrasies no matter how you look at it, so I'm not too concerned about this. The upside is that it's very simple, requiring no special continuations or out-of-message-loop stuff, and apart from having a handle in the state, it meshes very well with Elmish architecture. (To be clear; with this method, we don't use |
Try the |
Okay, I'm making progress with a new approach to windows I like much better: Use In effect, this gets rid of most current drawbacks mentioned in the readme:
Will push a new branch when I'm ready to show something resembling what I have in mind. |
All right! I have pushed some new delightful stuff to a new How delightful, you ask? I'd say the readme update commit pretty much sums it up nicely - from a wall-of-text "yes you can use windows, buuut" to a single-paragraph "sure!". Check out the NewWindow sample, give the new API a spin, and let me know what you think. I'm pretty sold on this, given how well it works and all the usability problems it solves. IMHO it's in a shape where I'm comfortable releasing, but I'd like a second opinion first. |
Calling windows and dialogs through Binding rather than Cmd is a very good idea. Be sure I'll get a further look at it, I just don't know when. |
It's not clear how to implement a modal dialog in Elmish.WPF.
Seems like using
Window.ShowDialog()
should be the way to go, but it's not clear how to hook into the dialog's dispatch loop. Is there a need for explicit dialog support, or is it just a case of providing an example?I'd be happy to provide a sample if someone could give me some guidance.
The text was updated successfully, but these errors were encountered: