Turn any UserControl into a pleasing Dialog/Flyout in Windows 8

Windows 8 comes with a somewhat extended Message box functionality – you can control the text on the message box and the content of the buttons.

However, for Flyouts and arbitrary content, there’s no easy mechanism to use. WinRT does, however, provide a Popup class that can be used to easily overlay information on your page.

This is where the PopupHelper library comes to the rescue. It can take any UserControl and turn it into a dialog-like element on your app complete with animation and other relevant UI changes. One place where dialogs and popups differ is with regard to how they treat the UI thread. Whereas in the “old-world”, a popup would be essentially a blocking call, in Windows 8, you can’t really block the UI thread. Instead, the classes use the new awaitable mechanisms in .NET 4.5 to make this a little easier to handle.

Show me how I use it!

So say you have a message box in which you want to show a link to the user – say an error message with “more information” type link on it.

 

image

When showing the message box, the library animates the UI to flip into view and the background to “dim” and the UI under it to unusable (since it’s covering it).

The code is fairly simple.

Step 1: Design your XAML

Create a UserControl and add to it the following XAML:

<Grid HorizontalAlignment="Stretch" Background="Gold" VerticalAlignment="Center">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" AreScrollSnapPointsRegular="True" Margin="20">
<TextBlock Text="You can read more here:" FontSize="20" Style="{StaticResource HeaderTextStyle}"/>
<HyperlinkButton Content="http://socialebola.wordpress.com"/>
<Button Content="Close" Click="Button_Click_1" Margin="0,20,0,0" HorizontalAlignment="Right"/>
</StackPanel>
</Grid>

The important bits here are that HorizontalAlignment is stretch and VerticalAlignment is Center to get that “band” look of Windows 8.

Step 2: Implement an inner class that inherits from PopupHelper.

Now, for the code. First off, you define the class that will let you show the popup. Note that this is not mandatory – it’s here for convenience. You can always just call the vanilla (non-templated) PopupHelper class to create it:

public class Popup : PopupHelper<SimplePopup>
{
}

This creates a PopupHelper class that knows how to show the SimplePopup UserControl (see below on how to call it)

Step 3: Implement the IPopupControl interface

public sealed partial class SimplePopup : UserControl, IPopupControl
{
public SimplePopup()
{
this.InitializeComponent();
}

private Popup m_popup;

public void SetParent(PopupHelper parent)
{
m_popup = (Popup)parent;
}

public void Closed(CloseAction action)
{

}

public void Opened()
{

}

private void Button_Click_1(object sender, RoutedEventArgs e)
{
m_popup.CloseAsync();
}

}

The interface has just 3 methods:

  • SetParent(): This will be called when the popup is just about to show and tells the control who the PopupHelper showing it is.
  • Opened(): Called after the popup is done animating in.
  • Closed(): Called after the popup is done doing the close animation.

Note that SetParent() is the only method implemented – it stores the popup as a member variable.

Finally – the button on the popup needs to close it. That’s done by calling into the PopupHelper derivative and calling CloseAsync() on it.

Step 4: Using the popup

In your code, when you want to raise the event, all you need to do is this:

var popup = new SimplePopup.Popup();
popup.ShowAsync();

That’s about it! Note that I could have just easily written this code (and avoided the inner-class):

var popup = new PopupHelper(new SimplePopup());
popup.ShowAsync();

It’s a matter of choice how you want to work it.

How about returning values from dialogs?

The infrastructure also supports dialog that return data. This can be easily done with what you’ve seen so far, but there are a few small things added to make it even easier.

In this case, we’ll use a different example that shows a more traditional popup right smack in the middle of the page:

image

When defining your Popup class as an inner class, use PopupHelperWithResult<>:

public class Popup : 
PopupHelperWithResult<NetworkCredential, SimpleResultPopup>
{
}

In this case, the result returned is a NetworkCredential and the UserControl name is SimpleResultPopup.

Everything else about this is the same, except for what happens when a user taps one of the buttons (cancel or login):

private void cancel_Click(object sender, RoutedEventArgs e)
{
m_popup.Result = null;
var dummy = m_popup.CloseAsync();
}

private void login_Click(object sender, RoutedEventArgs e)
{
m_popup.Result = new System.Net.NetworkCredential(userName.Text, password.Text);
var dummy = m_popup.CloseAsync();
}

As you can see, when cancelling, we simply set the .Result poperty to null, whereas when the user actually taps “Login”, we set it to a NetworCredential instance.

Using the popup is also fairly simple – you call into it, awaiting the result, and use it to do what’s needed:

private void Button_Click_2(object sender, RoutedEventArgs e)
{
ShowSimplePopupWithResult();
}

private async void ShowSimplePopupWithResult()
{
var popup = new SimpleResultPopup.Popup();
var credentials = await popup.ShowAsync();
text.Text = credentials == null ?
"User cancelled" :
String.Format("User logging in as:{0}", credentials.UserName);
}

How about flyouts?

You can use the same infrastructure described to create Windows 8 Flyouts that look great.

The only difference is in how you define your PopupHelper class and the XAML design:

<UserControl
x:Class="PopupHelpers.Demo.SimpleFlyout"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:PopupHelpers.Demo"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
VerticalAlignment="Stretch"
HorizontalAlignment="Right"
Width="346"
d:DesignHeight="300"
d:DesignWidth="400">

This is a standard definition of an inherited UserControl. The interesting bits are in VerticalAlignment and HorizontalAlignment. The first is set to Stretch since the Flyout needs to take the entire available vertical screen size. The latter is set to Right so that it’s “docked” to the charms bar.

The code for defining the Flyout:

public class Flyout : PopupHelper<SimpleFlyout>
{
public override PopupSettings Settings
{
get
{
return PopupSettings.Flyout;
}
}
}

The Flyout class overrides the Settings property and returns settings that tell the Popup to behave like a flyout.

The result looks just like any Flyout:

image

Note that there’s another difference between how Flyouts and Popups handle themselves. Flyouts are dismissed by simply tapping anywhere on the screen real-estate that does not belong to the Flyout while Popups need to be explicitly dismissed.

Odds and ends

You can create your own Popup behaviors by creating your own PopupSettings instances.

These are the settings you can currently control:

  • AnimationDuration : This is the oveall duration of the animation. Both for Flyouts and regular popups, it’s set to 350ms.
  • OverlayToControlAnimationRatio: This is the difference between how long it takes the overlay to animate and how long it takes the popup to animate. In the case of a regular popup, this is 0.7 (the control starts animating in a delay of 30% of the total animation time). For Flyouts, this is 1 (since there’s no noticeable animation happening on the overaly)
  • OverlayOpacity: This governs how opaque the overlay is. For Popups this is set at 0.5 and for Flyouts at 0.
  • Animation: The type of animation to apply when showing and closing the popup (enum flags)
  • OverlayDismissal: Whether clicking or tapping on the overlay will dismiss the dialog. True for Flyouts, false for regular popups.

The Soft Keyboard (SIP), when showing, will cause the popup to reposition itself on the page.

You can download full source code and a demo project here.

About these ads
This entry was posted in Dev, Windows8 and tagged , , , , , , , , , . Bookmark the permalink.

15 Responses to Turn any UserControl into a pleasing Dialog/Flyout in Windows 8

  1. Pingback: Windows 8 Developer Links – 2012-10-15 | Dan Rigby

  2. Hiren says:

    User control with listbox or listview in touch mode scrolling not working

    • SocialEbola says:

      Hi Hiren,

      It seems like there’s a bug with using plane projection on ListView/FlipView and others. In those cases, you can tweak the settings to not do the twist animation and the crash will go away.

      • YJ says:

        Yes. the default FlipControl animation definitely made a crash.
        wouldn’t be nice if it has only simple fade?

      • SocialEbola says:

        That’s apparently a bug with Windows. If you have one of the list controls (flipview, listview, gridview etc) and you apply a projection transformation to it, it will die.

  3. Alan Feekery says:

    PopupHelper library? I can’t seem to find it on MSDN. Where do I get it from?

  4. YJ says:

    Thanks for the great control :)
    It saves me lots of time.

  5. youngjaekim says:

    This component is really good to use. Thank you.
    Can you tell me why secondary monitor does not show popup location properly?
    I connected my laptop to external monitor and the popup showed well in laptop monitor but did not in external monitor. weird…

  6. glennp says:

    I’m using a flyout successfully in full-screen mode, but in snapped view, no flyout appears, even when I set its width to under 320 pixels, the standard snap width. If I press the button to invoke the flyout in snapped mode (possibly several times), then when I go back to full screen mode, the flyout appears (one instance for each press). Suggestions to get the flyout to appear when snapped?

    Also, if the app is snapped to the left, and I then want the flyout to come from the left edge, what changes should i make? Thanks

  7. glennp says:

    I gave up trying to reuse the flyout in snapped view. Instead, just added a standard snap-view-sized page with the same controls and content (plus back button), and chose that at invocation time if in snapped mode. Some code duplication thusly, but I can live with it.

  8. microstrip says:

    Can you suggest me on the License Terms of your library? e.g. GPLv3
    I would like to use your library with my commercial application; therefore, I want to ensure that the license align with my company policy.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s