Creating Popup Dialog using Jounce Region.

It is little trickier to create a Silverlight pop up dialog box using Jounce. Jounce creates view and associate the viewmodel to it for user controls while pop up dialog boxes are derived off of ChildWindow. So you can not just navigate/region to the control. Even if you would ‘new’ up a dialog, you will lose the View Model associated with it from Jounce Frame work, since you are newing the view. If you would new the view, then you need to revert back to old school view mode injection through code behind, if you would take that approach then you are not really using Jounce to create a good MVVM code. To make it short, it is trickier to use Jounce Framework to show your Child Window (in our case we use C1Window – Component One Child Window). But as you can imagine that since I am writing the block, it is possible and surprisingly easy.

One of my colleague found a hack by goggling. Once I get the link, I will put the link up here so that you get the actual implementation by the original author. We modified a little bit to handle our situation,  Here is the solution;

1. Create a normal User Control page and wrap the content in a Canvas, it is important you have canvas, if you use Grid then the user will not able to move the pop up window around. You can only maximize, minimize and close. If you would use Canvas, then you can do all the things you do with Grid but also move the pop up window around. By default make the canvas collapsed.

2. Create a C1Window, name the control.

3. Inside the C1Window, create a ‘ContentControl’ and give a region name. Here is where we are going to place our view.

4. When user want to activate the dialog, make the Canvas visible. Also navigate to the view, which will be exported to region defined in the Content Control.

5. When all the actions are done, close the dialog, navigate back to the calling page, make the Canvas visibility to Collapsed.

I wrote a simple solution to show how it works. Here is the main page XAML, which has a button, when clicked it is suppose to show the pop up dialog.

    <Grid x:Name="LayoutRoot" Background="White"
          d:DataContext="{d:DesignInstance sampleData:DesignMainViewModel, IsDesignTimeCreatable=True}">
        <Grid.Resources>
            <local:Converters x:Key="myConverter"/>
        </Grid.Resources>
        <StackPanel>
        <Button Content="Click me" Command="{Binding ClickMe}"/>
        <Canvas Name="hiddencanvas" Visibility="Collapsed">            
                <c1:C1Window Header="Title" Height="200" Width="200">
                    <i:Interaction.Triggers>
                        <i:EventTrigger EventName="Closing">
                            <i:InvokeCommandAction Command="{Binding ClosingCommand}" CommandParameter="{Binding Converter={StaticResource myConverter}}"/>
                        </i:EventTrigger>
                    </i:Interaction.Triggers>
                    <ContentControl  Region:ExportAsRegion.RegionName="ShowMeRegion"/>
                </c1:C1Window>
            </Canvas>
        </StackPanel>
    </Grid>

 

The button which initiate the pop up dialog bound to the command ClickMe in the ViewModel. For time being ignore the event trigger.

*** Edit on the above code **

Replaced the trigger with behavior as follows

<i:Interaction.Behaviors>
   <local:CloseBehavior/>
</i:Interaction.Behaviors>  

local class which implements close behavior as follow
public class CloseBehavior : Behavior<C1Window>
    {
        public CloseBehavior()
            : base()
        {
        }

        protected override void OnAttached()
        {
            base.OnAttached();
            AssociatedObject.Closing += new CancelEventHandler(AssociatedObject_Closing);
        }

        void AssociatedObject_Closing(object sender, CancelEventArgs e)
        {
            e.Cancel = true;
            Canvas c = AssociatedObject.Parent as Canvas;
            c.Visibility = Visibility.Collapsed;
        }

        protected override void OnDetaching()
        {
            base.OnDetaching();
            AssociatedObject.Closing -= new CancelEventHandler(AssociatedObject_Closing); 
        }

    }

*** End Edit ****

 
[ExportAsViewModel("MainViewModel")]
    public class MainViewModel : BaseViewModel
    {
        public IActionCommand<string> ClickMe { get; set; }
        public IActionCommand<CancelEventArgs> ClosingCommand { get; set; }

        public MainViewModel()
        {
            ClickMe = new ActionCommand<string>(
                view =>
                {
                    EventAggregator.Publish("ShowCanvas");
                    EventAggregator.Publish("ShowMe".AsViewNavigationArgs());
                }
                );

            ClosingCommand = new ActionCommand<CancelEventArgs>(
                view =>
                {
                    view.Cancel = true;
                }
                );
        }

    }

 

If you notice the ClickMe method implementation, it fires two events. First one is called ShowCanvas, which will be intercepted at the code behind to make the canvas visible. The second event is to stage the view you want to show in the view model. Lets look at the code behind and see how it makes the canvas visible.

[ExportAsView("MainPage", IsShell = true)]
    public partial class MainPage : UserControl, IPartImportsSatisfiedNotification, IEventSink<string>
    {
        [Import]
        public IEventAggregator EventAggregator { get; set; }

        public MainPage()
        {
            InitializeComponent();
        }

        [Export]
        public ViewModelRoute Binding
        {
            get { return ViewModelRoute.Create("MainViewModel", "MainPage"); }
        }

        #region IPartImportsSatisfiedNotification Members

        public void OnImportsSatisfied()
        {
            EventAggregator.Subscribe<string>(this);
        }

        #endregion

        #region IEventSink<string> Members

        public void HandleEvent(string publishedEvent)
        {
            if (publishedEvent.Equals("ShowCanvas"))
                hiddencanvas.Visibility = System.Windows.Visibility.Visible;
        }

        #endregion

    }

If you look at the HandleEvent method at the end of the code, you will see, we are checking the incoming message to see if it is ‘ShowCanvas’ then make the canvas visible.

Now lets go back to the second call made from the view model to load the ‘ShowMe’ page. it is important that, here you use User Control, so that Jounce does the wiring for you. The ShowMe.XAMl look like the following, nothing fancy

    <Grid x:Name="LayoutRoot" Background="White">
        <TextBlock Text="Hello from Popup Window" FontSize="16"/>
    </Grid>

In the code behind, as you would do to any user control which needed to be hosted in a region, export it to the region when called.

[ExportAsView("ShowMe")]
[ExportViewToRegion("ShowMe", "ShowMeRegion")]
public partial class ShowMe : UserControl
{
    public ShowMe()
    {
        InitializeComponent();
    }
}

 

That’s about it. I added a small twist to the code, by listening to Closing event of the pop up window using Event Trigger. Just an experiment that’s all. You can very well add the Closing event in the code behind and all will be good as well.