Blog

We believe there is something unique at every business that will ignite the fuse of innovation.

Introduction

Since modern smartphones began emerging in 2007, screen sizes have increased substantially. Between 2007 and 2012, average screen size increased from 3 inches to 4 inches, but only took two years to increase to 5 inches between 2012 and 2014. With larger screens, comes UI elements that, historically, have been more popular on traditional web pages. The modal dialog, or popup as it’s commonly referred, is a design component found in iOS, Android and Windows Phone apps. This post will demonstrate a scalable popup implementation for Windows Phone 8.1.

Windows Phone 8.1 contains a Popup class, described by MSDN as “a general purpose container for hosting UIElements on top of existing content”. Our example implementation will focus on Windows Phone 8.1, but the Popup class is available for Windows apps as well.

While the Popup class is sufficient to create the popup display, it makes sense to standardize the construction of these popups fostering code reuse.  To accomplish this, we’ll create a class that can be called to instantiate a popup object that meets our app requirements, which dictate the popup contain a title bar section and an area for dynamic content.

For the sake of brevity, all referenced code is available in the PopupSample.zip file at the end of this post. Some key methods are shown for clarity.

Prerequisites

This demo’s underlying project contains a navigation service class, NavigationService, handling events related to app navigation. Its implementation is included in the PopupSample.zip file.

The Messenger service, which is part of the MvvmLight library, is referenced in this demonstration as well.

ModalPopup Class

The previously mentioned Popup class will serve as the base class for the standardized popup. The class level variables and constructor are defined as follows.

private Popup _overlay;
private Popup _mainPopup;
private AppBar _appBar;
private UIElement _content;

public ModalPopup(string title, UIElement content)
{
  CreatePopup(title, content);
}

The _overlay variable provides a visual separation between the screen content and the content we wish to display on the popup, while the _mainPopup object contains styling and content information. It also handles dimming the background content, while limiting user interaction to popup content.

An AppBar object is included to represent the application bar used in our app, since we want control over its visibility when showing/hiding our popups. The _content variable represents a Windows RT base class (UIElement), which allows type variability for visual components we include in popup content.

The parameters, title and content, inside the ModalPopup constructor represent the title text and UI content to be displayed on the popup. The CreatePopup method builds the actual popup. Let’s take a closer look at the method implementation.

CreatePopup and Related Methods

private void CreatePopup(string title, UIElement content)
{
  _content = content;

  var frame = Window.Current.Content as Frame;
  var frameWidth = frame != null ? frame.ActualWidth : Constants.DefaultScreenWidth;
  var frameHeight = frame != null ? frame.ActualHeight : Constants.DefaultScreenHeight;
  var page = frame.Content as Page;

  _appBar = page.BottomAppBar;
  if (_appBar != null && _appBar.Visibility == Visibility.Visible)
    _appBar.Visibility = Visibility.Collapsed;
  else
    _appBar = null;

  _overlay = CreateOverlay(frameWidth, frameHeight);
  var popupContent = CreatePopupContent(title, content);

  _mainPopup = new Popup
  {
    Style = Application.Resources[StyleResource.PopupStyle],
    Child = popupContent,
    IsOpen = true
  };

  _mainPopup.HorizontalOffset = (frameWidth - _mainPopup.ActualWidth) / 2;
  _originalVerticalOffset = _mainPopup.VerticalOffset;

  InputPane.GetForCurrentView().Showing += Popup_Showing;
  InputPane.GetForCurrentView().Hiding += Popup_Hiding;
}

The implementation, while straight-forward, accomplishes several tasks.

  • Retrieves height and width of the user’s device; If unavailable, constant values are used
  • Collapse application bar
  • Create popup overlay
  • Create title and content
  • Assembles all visual components into a new Popup object
  • Set position of popup
  • Handling Navigation Events

Common practice dictates our implementation include functionality to handle navigation actions like closing the popup and clicking the device’s Back button. The following code should be added to the constructor to handle both events.

Messenger.Default.Register(this, Close)
NavigationService.BackNavigationRequested += NavigationService_BackNavigationRequested;

The first line registers an action (Close) that can be performed on the popup (this). The second line adds an event handler that closes the popup when the Back button is tapped. Both methods are shown below.

public void Close()
{
  if (_mainPopup == null || _overlay == null)
    return;

  _mainPopup.IsOpen = false;
  _overlay.IsOpen = false;

  if (_appBar != null)
    _appBar.Visibility = Visibility.Visible;

  _mainPopup = _overlay = null;

  Messenger.Default.Unregister(this, Close);
  NavigationService.BackNavigationRequested -= NavigationService_BackNavigationRequested;

  InputPane.GetForCurrentView().Showing -= Popup_Showing;
  InputPane.GetForCurrentView().Hiding -= Popup_Hiding;
}

private void NavigationService_BackNavigationRequested(object sender, BackNavigationRequestedEventArgs e)
{
  if (e.Handled)
    return;

  Close();
  e.Handled = true;
}

Positioning Using the InputPane Class

Unlike traditional web applications, a smartphone keyboard will often cover screen content when it’s displayed. Since the Popup class does not account for this, additional logic is needed to ensure the interactive elements of the popup aren’t hidden by an activated keyboard.

The last two lines of the CreatePopup and Close methods utilize the InputPane class. MSDN describes this class as “a UI element that appears when the user performs an action that requires them to enter information, such as tapping a search box or an entry field in a form”.

When opening a popup, the app is registered to receive notifications when the InputPane is going to be displayed (or hidden). If the input keyboard is visible, for instance, the vertical offset is adjusted so that the inputs are in view. The referenced methods, Popup_Showing and Popup_Hiding, are shown here:

private void Popup_Showing(InputPane sender, InputPaneVisibilityEventArgs args)
{
  var focusedControl = FocusManager.GetFocusedElement() as Control;

  if (focusedControl == null || _mainPopup == null)
    return;

  var frame = (Window.Current.Content as Frame);
  var relativeLocation = frame.TransformToVisual(focusedControl).TransformPoint(new Point(0, 0));
  var keyboardHeight = frame.ActualHeight - args.OccludedRect.Height;
  var distanceFromBottom = relativeLocation.Y - focusedControl.ActualHeight + frame.ActualHeight;

  if (distanceFromBottom > keyboardHeight)
    return;

  _mainPopup.VerticalOffset = _originalVerticalOffset -
  Math.Min(keyboardHeight - distanceFromBottom + 20, (_mainPopup.Child as FrameworkElement).ActualHeight);
}

private void Popup_Hiding(InputPane sender, InputPaneVisibilityEventArgs args)
{
  if (_mainPopup == null)
    return;

  _mainPopup.VerticalOffset = _originalVerticalOffset;
}

Similarly, when the popup is closed, the receipt of these notifications is no longer needed, so the references to methods are removed from the InputPane.

In Action

To create a popup instance, a call to the Popup constructor is all that’s needed.

new PopupControl(“Title Text”, new BasicPopupContent(“This is where the main content is displayed.”);

 

 

The BasicPopupContent class accepts a string for the content in this example, but other constructors can be added that accept other types of content objects like a grid or data template.

Conclusion

The native Popup class includes a rich set of methods and properties that can be leveraged to extend its functionality. For instance, accounting for AppBar visibility, handling navigation events, and ensuring interactive content is not hidden by the keyboard are features developers can implement to promote a robust and pleasant user experience. This extended version of the Popup class yields an extensible and scalable user control which can be easily modified to suit different application requirements.

Attachments