Popup dialogs

Mar 21, 2010 at 12:32 PM

Hi,

This is more a general MVVM question I think. How do I create modal dialogs with Calcium?

I have create a generic Popup class that I fill with callbacks when a right-mouse click occurs:

        private PopupMenu listBoxGroupsItemPopupMenu = null;
        private void listBoxGroupsItem_PreviewMouseRightButtonDown(object sender, MouseEventArgs e)
        {
                ...
                listBoxGroupsItemPopupMenu = new PopupMenu(
                    new PopupMenu.Choice { Data = group, DisplayName = "Join group", CallBack = new JoinGroupChosen() },
                    ...
                    );
                listBoxGroupsItemPopupMenu.InitializeAndShow(this, item.PointToScreen(e.GetPosition(item)));
            }
        }

The idea is that right-clicking shows a little context-menu on the spot of the mouse cursor and selecting one of the choices triggers the callback. Note that I pass 'this' to the InitializeAndShow method (which is derived from Window), which gave the callback a way of accessing data bound up in the view but also allowed me to set the Owner property of the new window and thus make it modal.

Now that I switched to using Calcium, the View is no longer derived from Window and I can't use it to make the popup modal.

Is there some way I can retrieve a reference to the main window?

The whole approach is probably flawed from a MVVM standpoint, but I would like to switch gradually instead of whole at once. However, what would the best approach be to implement the described functionality properly?

Bye,

Steven

 

 

Coordinator
Mar 21, 2010 at 11:10 PM

Steven,

You can get the main window from the ServiceLocatorSingleton like so:

var mainWindow = (Window)ServiceLocatorSingleton.Instance.GetInstance<IMainWindow>();

There is a work item for adapting modal windows. Currently there is a region adapter for showing new windows though. This is done by placing your view in the RegionNames.Shell region. We are yet to provide modal capability. Please vote for the feature if you want it.

Cheers,

Daniel

Mar 23, 2010 at 12:32 PM

Hi,

Thanks for your reply, Daniel. I have looked around for the proper way to do popups and finally found something I really liked: http://www.thesilvermethod.com/Default.aspx?Id=ModalDialogManagerAsimpleapproachtodealingwithmodaldialogsinMVVM (the comment there is mine, in case you were wondering). I urge you to take a look at it, since I think it solves the problem quite nicely with very little code.

In essence, what it does is map the View of the popup to its ViewModel in a DataTemplate with specified DataType. The ViewModel that contains the code that wants to show the popup has a property for the ViewModel of the popup. A custom control databinds its datacontext to that property, and binds its own members (specifically 'IsOpen') to properties of the ViewModel. Bringing up the popup is done by setting the property 'IsOpen' on the viewmodel to true, which is databound to the 'IsOpen' property of the custom control that then displays a new window with the popup ViewModel (which has an associated DataTemplate so its View is shown).

My MainViewModel now looks like this:

public class MainViewModel : ViewModelBase
{
    private MessageWindowViewModel messageWindow;
    public MessageWindowViewModel MessageWindow 
    { 
        get { return messageWindow; } 
        set { Notifier.Assign("MessageWindow", ref messageWindow, value); } 
    }

    private void CreateGroup(object o)
    {
        MessageWindow = new MessageWindowViewModel { Title = "Example dialog", Text = "Hi there." };
        MessageWindow.IsOpen = true;
    }

    public MainViewModel()
    {
        CrescendoCommands.CreateGroupCommand.RegisterCommand(new UICommand(CreateGroup));
    }
}

And my MainView looks like this:

<Gui:ViewControl x:Class="SummaDigita.Crescendo.Client.MainView"
                 xmlns:Module="clr-namespace:SummaDigita.Crescendo.Client"
                 xmlns:SilverMethod="clr-namespace:theSilverMethod.MVVM.ModalDialogs">
  <Gui:ViewControl.Resources>
    <DataTemplate DataType="{x:Type Module:MessageWindowViewModel}">
      <Module:MessageWindow />
    </DataTemplate>
  </Gui:ViewControl.Resources>
  <DockPanel LastChildFill="False">
    <Button Name="buttonCreateGroup"
            Command="{x:Static Module:CrescendoCommands.CreateGroupCommand}">Create new group</Button>
    <SilverMethod:ModalDialogManager DataContext="{Binding MessageWindow}"
                                     IsOpen="{Binding IsOpen}"
                                     Title="{Binding Title}"
                                     DialogHeight="300"
                                     DialogWidth="300"
                                     DialogResizeMode="NoResize"
                                     CloseCommand="{Binding OKCommand}" />
  </DockPanel>
</Gui:ViewControl>

What do you think?

Bye,

Steven

Coordinator
Mar 24, 2010 at 8:04 AM

Hi Steven,

I had intended to provide the modal capability with a content interface on a viewmodel. When a view is placed into the ShellRegion, the RegionAdapter will determine if it is a modal dialog based on the interface, and display it accordingly. How does that sound?

Thanks for message and code example.

Mar 25, 2010 at 6:06 AM

I'm not sure I understand what you mean, but it sound you can do away with the addition of the custom control to the xaml of the invoking view. So that sounds better. In the meantime I have a solution that works and will probably require very little work to change to your solution when you get around to implementing it.

 

Bye,

Steven

 

 

 

Coordinator
Mar 27, 2010 at 4:41 PM

Steven,

Just checked in the modal support infrastructure.

Implement IProvider<IViewOptions> on a view or a viewmodel, and have the IViewOptions Modal property return true. Then show the view in the RegionNames.Shell region. There is also a method on the IViewOptions called PrepareContainer, which provides the opportunity to modify the window before it is shown.

Cheers,

Daniel

Mar 28, 2010 at 8:21 AM

Hi Daniel,

 

That's great! I'll give it a try and will let you know what I think.

 

Bye,

Steven

Mar 28, 2010 at 11:38 AM

Hi,

 

It seems to work well. One small thing: I needed to put

 

                            window.Content = null;

after the ShowDialog() to prevent an exception when showing the dialog a second time.

Thanks,

Steven

 

Coordinator
Mar 28, 2010 at 12:36 PM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.
Apr 6, 2010 at 1:15 PM

Hi Daniel,

Another thing: I had to implement IViewOptions on my View in order to save a reference to the Window that holds it. This allows me to call the Close() on the window that holds the view from code instead of having to close the window manually. I don't think this is what you intended, is it? The problem with calling viewService.CloseView() from code is that is throws an exception since the call to ShowDialog has not returned yet and Calcium is still handling the Add event on the views collection when a Remove on the same collection is initiated. I hope this makes sense.

Bye,

Steven

Coordinator
Apr 7, 2010 at 9:10 AM
This discussion has been copied to a work item. Click here to go to the work item and continue the discussion.