Caliburn ShowDialog and MessageBox
Asked Answered
U

3

6

I'm making a small demo application for MVVM with caliburn.

Now I want to show a MessageBox, but the MVVM way.

For dialogs I created an event, that is handled in the ShellView (the root view) and just calls WindowManager.ShowDialog with a Dialogs ViewModel type. Seems to stick to MVVM for me.

But what is the way to show a messagebox and get its result (Okay or cancel)?

I already saw this question, but it contains no answer either.

Mr Eisenberg hisself answers with

"Caliburn has services built-in for calling custom message boxes."

Can anyone tell what he means with that? I don't see it in the samples.

Uraemia answered 7/7, 2013 at 9:9 Comment(0)
S
5

In the article A Billy Hollis Hybrid Shell (written by the framework coordinator) the author showed a nice way to handle both dialog and message boxes, but he used dependency injection (you can go without DI of course but it makes things simpler). The main idea is that you can let your main window, the one used as the application shell implement an interface that looks something like this:

public interface IDialogManager
    {

        void ShowDialog(IScreen dialogModel);
        void ShowMessageBox(string message, string title = null, MessageBoxOptions options = MessageBoxOptions.Ok, Action<IMessageBox> callback = null);

    }

and then he registers this interface with the IoC container, I guess you can use your imagination from there on and if you don't have time then you can look at the source code that accompanies the article.

Strepitous answered 7/7, 2013 at 12:35 Comment(8)
Thank you very much! The only problem I have is to return what the user chose in the messagebox (or dialog). Have to take a closer look.Uraemia
Okay, it is Silverlight and MEF, so it is not very self-explanatory to me.Uraemia
@MareInfinitus you can figure out what what the user chose in the ShowMessageBox method via the Action<IMessageBox> callback. On the other hand, if you don't know of silverlight and MEF, i have downloaded the sample and converted it to WPF with Ninject. I can upload that to you somewhere if you like.Strepitous
@MareInfinitus you can download it HelloScreensWPF.Strepitous
You are wonderful! Thank you so much! This should be distributed along with Caliburn.micro!Uraemia
@IbrahimNajjar All the links mentioned in the answer are expired. Do you have updated links?Jinks
@IbrahimNajjar can you post your full code here? or solution in github or somewhere else, cant really get to 4shared.com aswell, as it is one of the blocked site..Whallon
Unfortunately this has been a very long time ago and I no longer have the source code nor the knowledge in that area :(Strepitous
D
7

As you mentioned, you just prepare the view model (e.g. ConfirmationBoxViewModel) and an appropriate view. You'll have to create two actions (after inheriting the view model from Screen, which is necessary to use TryClose. You can always implement IScreen instead, but that would be more work):

public void OK()
{
    TryClose(true);
}

public void Cancel()
{
    TryClose(false);
} 

and then in your other view model:

var box = new ConfirmationBoxViewModel()
var result = WindowManager.ShowDialog(box);
if(result == true)
{
// OK was clicked
}

Notice that after the dialog closes, you can access the view model properties if you need to pull additional data from the dialog (e.g. Selected item, display name etc).

Durand answered 7/7, 2013 at 9:40 Comment(2)
Thank you very much! Okay, this is making a dialog that looks like a messagebox. But is there an easy way to encapsulate a Messagebox? Is there a decoupled way to achieve that with events? MessageBox is just so versatile, I would love to use it somehow.Uraemia
I don't really think so, at least there's no real easy way I know of. Considering you can create an easily customizable and easy to use dialog box-like view model & view, I don't see any reason to wrap the default MessageBox...Extremity
S
5

In the article A Billy Hollis Hybrid Shell (written by the framework coordinator) the author showed a nice way to handle both dialog and message boxes, but he used dependency injection (you can go without DI of course but it makes things simpler). The main idea is that you can let your main window, the one used as the application shell implement an interface that looks something like this:

public interface IDialogManager
    {

        void ShowDialog(IScreen dialogModel);
        void ShowMessageBox(string message, string title = null, MessageBoxOptions options = MessageBoxOptions.Ok, Action<IMessageBox> callback = null);

    }

and then he registers this interface with the IoC container, I guess you can use your imagination from there on and if you don't have time then you can look at the source code that accompanies the article.

Strepitous answered 7/7, 2013 at 12:35 Comment(8)
Thank you very much! The only problem I have is to return what the user chose in the messagebox (or dialog). Have to take a closer look.Uraemia
Okay, it is Silverlight and MEF, so it is not very self-explanatory to me.Uraemia
@MareInfinitus you can figure out what what the user chose in the ShowMessageBox method via the Action<IMessageBox> callback. On the other hand, if you don't know of silverlight and MEF, i have downloaded the sample and converted it to WPF with Ninject. I can upload that to you somewhere if you like.Strepitous
@MareInfinitus you can download it HelloScreensWPF.Strepitous
You are wonderful! Thank you so much! This should be distributed along with Caliburn.micro!Uraemia
@IbrahimNajjar All the links mentioned in the answer are expired. Do you have updated links?Jinks
@IbrahimNajjar can you post your full code here? or solution in github or somewhere else, cant really get to 4shared.com aswell, as it is one of the blocked site..Whallon
Unfortunately this has been a very long time ago and I no longer have the source code nor the knowledge in that area :(Strepitous
A
0

When the root/main/shell view-model implements a kind of DialogService interface, every other view-model needing to show dialogs will end up with a dependency on the root view-model. Sometimes this might not be desiderable, e.g. if it could cause a dependency loop:
DialogService (aka RootViewModel) -> SomeViewModel -> RootViewModel.

A more involved approach to break this dependency chain (and actually invert it) is the following:

  • Implement a behavior that detects Window.OnSourceInitialized event and attach it to main view Window component. That is the event fired when the window handle is available. Upon event, behavior will notify some handler passed in via attached property:
<my:WindowSourceBehavior InitListener="{Binding WindowListener}" />
public class WindowSourceBehavior : Behavior<Window>
{
  // ...
  // boilerplate code for IWindowListener InitListener dependency property
  // ...

  attachedWindow.SourceInitialized += (sender, evt) =>
  {
     // ...
     InitListener.SourceInitialized(sender as Window);
  }
}
  • DialogService exposes a handler - or interface - as requested by behavior:
public class DialogService : IWindowListener
{
  // ...
  public void SourceInitialized(Window rootWindow) { /* ... */ }
}
  • In root view-model, (indirectly) get the DialogService injected as a dependency. During construction, sets view-model bound property, WindowListener, to the DialogService handler/interface:
public MainViewModel(IWindowListener dialogServiceInDisguise)
{
  WindowListener = dialogServiceInDisguise;
}

public IWindowListener WindowListener { get; private set; }

Doing so, the DialogService is able to get a hold of root Window, and whichever view-model needs to show a dialog does not create a(n indirect) dependency on main view-model.

Ansel answered 30/3, 2015 at 19:30 Comment(5)
I do not see where the accepted answer has a (indirect) dependency to the main / shell viewmodel. Having lots of things in disguise is also not the best idea, I like being explicit about dependencies and things done.Uraemia
If I got it right, in that case you have the main view-model implementing a IDialogManager interface, and that interface I guess would be injected in each other view-model needing to show dialogs. So a number of view-models would end up depending on root view-model. Incidentally, in that case it wouldn't be an explicit dependency on root view-model, as they don't need that. Again, that would be a dependency in disguise on IDialogManager interface, without knowing that it's actually the root view-model. Which I think is correct, by the way. What do you think?Ansel
No, the DialogManager is just injected, so there is no (direct or indirect) dependency on the rootviewmodel. There is just the dialogmanager, which takes ViewModels and combines them with the views the way caliburn does that. The DialogManager itself is not the rootviewmodel, but another class, which does dialog management only. So in my eyes this a very clear way of having dialogs in an MVVM way (as ViewModels only see other ViewModels, no dependency on views). Despite that the DialogManager is not the rootviewmodel, as said before. But your solution can be useful for sure.Uraemia
This is the part that led me to the conclusion: you can let your main window [...] implement an interface public interface IDialogManager. Isn't this saying that root view-model implements the DialogManager interface?Ansel
If you implement the interface, well, then it is implemented. But in the code in the accepted answer it is injected, i.e. it uses some dialogmanager, not it is a dialogmanager.Uraemia

© 2022 - 2024 — McMap. All rights reserved.