How can ContentDialog be used with Prism?

Dec 21, 2014 at 7:51 PM
I have just started digging into Prism and Store app development and one of the things I ran in to was wanting to use ContentDialog on the Phone which provides a light weight way to gather small amounts of input in a modal dialog fashion but the problem is Prism dictates that all views derive from VisualStateAwarePage.

Is there a way to make use of ContentDialog or am I stuck recreating it so that it's derived from VisualStatePage? If so, is there a way to see the base XAML used by ContentDialog?
Dec 22, 2014 at 3:42 AM
Edited Dec 22, 2014 at 3:52 AM
If you are just wanting to display a ContentDialog from the View Model, you can build out a service to handle that for you. I use the following service interface/implementation.

Interface
public interface IAlertService
{
   Task ShowAsync(string title, string content);
   Task ShowAsync(string title, string content, params CallbackDelegate[] callback);
}
Implementation
    public class AlertService : IAlertService
    {
        private bool isShowing = false;

        public async Task ShowAsync(string title, string content)
        {
            await this.ShowAsync(title, content, new CallbackDelegate[0]);
        }

        public async Task ShowAsync(string title, string content, params CallbackDelegate[] callbacks)
        {
            if (isShowing)
            {
                return;
            }

            var messageDialog = new MessageDialog(content, title);
            if (callbacks != null && callbacks.Any())
            {
                var commands = callbacks.Select(callback => new UICommand(callback.Name, cmd => callback.Callback()));
                
                foreach(var command in commands)
                {
                    messageDialog.Commands.Add(command);
                }
            }

            this.isShowing = true;
            await messageDialog.ShowAsync();
            this.isShowing = false;
        }
    }
Now inside of my View Model, I use it like this:
        private async Task ExecuteSignin()
        {
            // Attempt to sign in to the users account.
            this.IsSigningIn = true;
            OperationResult result = await this.accountService.Signin(this.CurrentUser.Username, this.Password);

            // If it failed, let the user know
            if (!result.IsSuccessful)
            {
                await this.alertService.ShowAsync(
                    "Signin Failure",
                    result.Result);
                this.ClearPasswords();
            }
            else
            {
                // If we succeeded, navigate to the main window.
                this.navigationService.Navigate("Main", null);

                // We clear the navigation history so the back button doesn't navigate back to the sign-in view model
                this.navigationService.ClearHistory();
            }

            this.IsSigningIn = false;
        }
You can optionally provide callbacks.
           await alertService.ShowAsync(
                "Unsaved changes",
                "You have unsaved changes, do you want to save?",
                new CallbackDelegate("Yes", this.Save),
                new CallbackDelegate("No", () => Task.FromResult(true)));

        private void Save()
        {
            // TODO: Save
        }
You could also change the CallbackDelegate.Callback to a Func<Task> instead of an Action and await the callback in the async call
           await alertService.ShowAsync(
                "Unsaved changes",
                "You have unsaved changes, do you want to save?",
                new CallbackDelegate("Yes", async() => await this.Save(this.content)),
                new CallbackDelegate("No", () => Task.FromResult(true)));

            this.IsSigningIn = false;
        }

        private Task Save(object content)
        {
            // TODO: Save
            return Task.FromResult(true);
        }
Does this do what you need? You can build out interfaces for each type of content you want to use with the alert and use a generic. This is mostly straight out of the Shopper example and works really well.

ShowAsync<ITermsOfServiceAlert>()

Then in the service implementation render a specific view that maps to the generic interface/type provided in the call.
Marked as answer by mrfusion on 12/22/2014 at 7:52 AM
Dec 22, 2014 at 3:51 PM
Thanks, this helps a lot.