Printing with the UWP Community Toolkit

In this article we describe some advanced printing scenarios on top of the PrintHelper from the UWP Community Toolkit. This helper is specialized in interacting with the built-in print dialogs, and in printing one UI element on one page. The latter sounds pretty limited, but in the next couple of paragraphs we’ll show things like

  • wrapping the helper in an MVVM service,
  • adding a header and/or a footer to the print pages,
  • adding page numbers to the print pages,
  • printing a list of items, each on its own page, and
  • printing a more complex report.

That should suffice for most UWP apps that have printing on their list of requirements – basically all apps that don’t need dynamic page breaks.

As usual, there’s a sample app on GitHub. It comes with several pages with a large ‘Print’ button:

 ContentPage

Here are the main classes in that sample app:

  • Content pages: the app’s pages that provide content and layout of the information to be printed,
  • PrintService: a class that provides printing-as-a-service in an MVVM architecture,
  • PrintPage: a helper class that prepares the print content (a FrameworkElement) with header, footer, and page numbering, and
  • PrintHelper: the toolkit’s print helper that knows how to print a FrameworkElement on a single page.

Here’s a class diagram of the core print engine:

ClassDiagram1

Creating an MVVM PrintService

The Print Service is the central place to which the different content pages send the elements to be printed:

  • one header, one footer, and one place to put the page for the entire job, and
  • one main content per individual print page.

The service registers the handlers for the system print dialog events (to deal with success, failure, or cancellation), manipulates the content through the smart PrintPage (see further), and hands the result over to the toolkit PrintHelper.

Printing XAML –with or without the PrintHelper- requires the XAML elements to be rendered on an invisible panel in your Visual Tree before they’re moved to separate print preview pages in the system print dialog. Your app needs to provide that invisible panel.

In an MVVM environment the so-called Shell (the page that hosts the navigation panel) is an ideal place to host this panel. We’ll call it the PrintingContainer:

    <Grid>
        <!-- Required for the PrintService -->
        <Grid x:Name="PrintingContainer"
              Opacity="0" />

        <!-- SplitView -->
        <SplitView x:Name="SplitView"
        <!-- ... -->
        </SplitView>

        <!-- Hamburger Button -->
        <Button x:Name="HamburgerButton">
        <!-- ... -->
        </Button>
    </Grid>

When the app starts, it provides this field to the central print service:

// Initialize Printing Service.
PrintService.PrintingContainer = PrintingContainer;

The PrintService class takes a Header, Footer and PageNumbering property. For the rest it host all the code for working with the UWP Community Toolkit’s PrintHelper class that you find in the official documentation. In the Print method you see that the service creates a PrintPage (see further) for each page to be sent to the PrintHelper. The print service protects the content pages from all that knowledge.

Here’s the whole class:

public class PrintService
{
    private static Panel _printingContainer;

    private PrintHelper _printHelper;
    private List<FrameworkElement> _content = new List<FrameworkElement>();
    private FrameworkElement _header;
    private FrameworkElement _footer;
    private PageNumbering _pageNumbering = PageNumbering.None;

    public PrintService()
    {}

    public static Panel PrintingContainer
    {
        set { _printingContainer = value; }
    }

    public FrameworkElement Header
    {
        set { _header = value; }
    }

    public FrameworkElement Footer
    {
        set { _footer = value; }
    }

    public PageNumbering PageNumbering
    {
        set { _pageNumbering = value; }
    }

    public void AddPrintContent(FrameworkElement content)
    {
        _content.Add(content);
    }

    public void Print()
    {
        _printHelper = new PrintHelper(_printingContainer);

        PrintPage.StartPageNumber = 1;
        foreach (var content in _content)
        {
            var page = new PrintPage(content, _header, _footer, _pageNumbering);
            _printHelper.AddFrameworkElementToPrint(page);
        }

        _printHelper.OnPrintFailed += printHelper_OnPrintFailed;
        _printHelper.OnPrintSucceeded += printHelper_OnPrintSucceeded;
        _printHelper.OnPrintCanceled += printHelper_OnPrintCanceled;

        _printHelper.ShowPrintUIAsync("Print Sample");
    }

    private void printHelper_OnPrintCanceled()
    {
        ReleasePrintHelper();
    }

    private void printHelper_OnPrintSucceeded()
    {
        Toast.ShowInfo("Printed.");
        ReleasePrintHelper();
    }

    private void printHelper_OnPrintFailed()
    {
        Toast.ShowError("Sorry, printing failed.");
        ReleasePrintHelper();
    }

    private void ReleasePrintHelper()
    {
        _printHelper.Dispose();
    }
}

Here’s how a successful print notification looks like, in the sample app:

Notification

Adding headers and footers

The main job of the PrintPage class in the sample app is to transform all print content for a single page (main content, header, footer, page numbering) into a single FrameworkElement, since the toolkit’s PrintHelper only accepts single FrameworkElement instances to be printed.

The PrintPage takes all print content and lays it out in a 3-row Grid (header – main beef – content):

Grid _printArea;

public PrintPage(FrameworkElement content, FrameworkElement header = null, FrameworkElement footer = null, PageNumbering pageNumbering = PageNumbering.None)
{
    _printArea = new Grid();
    _printArea.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
    _printArea.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
    _printArea.RowDefinitions.Add(new RowDefinition() { Height = GridLength.Auto });
    _printArea.Children.Add(new Canvas() { Width = 10000 }); // Horizontally stretches the Grid.

    Content = _printArea;

    AddContent(content);
    Header = header;
    Footer = footer;
}

When the Header is assigned, it is immediately cloned -because it will possibly be used on multiple page instances- and placed in the top row of the grid:

public FrameworkElement Header
{
    set
    {
        if (value != null)
        {
            var header = value.DeepClone();
            Grid.SetRow(header, 0);
            _printArea.Children.Add(header);
        }
    }
}

The header and footer are cloned because they will appear on each page in the print job and a UI element can only have one parent. I would have preferred to use a XamlWriter to transform the element into a XAML string, and then a XamlReader to instantiate a clone from this XAML. Unfortunately XamlWriter does not exist in UWP, so we have to rely on reflection.

The DeepClone method that we’re using, is all over the internet – it’s impossible to find the original source. It checks the origin type, creates a new instance from it, and then recursively clones its properties (except the Name) and children.

Here’s how the first content page in the sample app talks to the print service – instantiate, assign header and footer, add print pages, call Print method:

var service = new PrintService();

// Define Header, Footer, and Page Numbering.
service.Header = new TextBlock() { Text = "Header", Margin = new Thickness(0, 0, 0, 20) };
service.Footer = new TextBlock() { Text = "Footer", Margin = new Thickness(0, 20, 0, 0) };
service.PageNumbering = PageNumbering.TopRight;

// Add three pages.
service.AddPrintContent(new TextBlock() { Text = "Hello World!" });
service.AddPrintContent(new Image() { Source = new BitmapImage(new Uri("ms-appx:///Assets/world.png")) });
service.AddPrintContent(new TextBlock() { Text = "Goodbye World!" });

service.Print();

Adding page numbering

Once we know how to add a header and a footer to the print pages, adding page numbers is trivial. Let’s start with defining an enumeration for page number positions:

public enum PageNumbering
{
    None,
    TopLeft,
    TopMiddle,
    TopRight,
    BottomLeft,
    BottomMidle,
    BottomRight
}

Each time we add a new print page to the print job with AddContent(), we check if a page number is required. If so, we increment the value and place a TextBlock in the appropriate place (be careful: the page number will appear in row 0 or 2 of the print page grid, so it may clash with header or footer – it’s up to you to avoid this):

public void AddContent(FrameworkElement content)
{
    Grid.SetRow(content, 1);
    _printArea.Children.Add(content);

    if (PageNumbering != PageNumbering.None)
    {
        _pageNumber += 1;
        var pageNumberText = new TextBlock() { Text = _pageNumber.ToString() };

        switch (PageNumbering)
        {
            case PageNumbering.None:
                break;
            case PageNumbering.TopLeft:
                Grid.SetRow(pageNumberText, 0);
                pageNumberText.Margin = new Thickness(0, 0, 0, 20);
                _printArea.Children.Add(pageNumberText);
                break;
            case PageNumbering.TopMiddle:
                Grid.SetRow(pageNumberText, 0);
                pageNumberText.Margin = new Thickness(0, 0, 0, 20);
                pageNumberText.HorizontalAlignment = HorizontalAlignment.Stretch;
                pageNumberText.HorizontalTextAlignment = TextAlignment.Center;
                _printArea.Children.Add(pageNumberText);
                break;
            case PageNumbering.TopRight:
                Grid.SetRow(pageNumberText, 0);
                Grid.SetColumn(pageNumberText, 1);
                pageNumberText.Margin = new Thickness(0, 0, 0, 20);
                pageNumberText.HorizontalAlignment = HorizontalAlignment.Stretch;
                pageNumberText.HorizontalTextAlignment = TextAlignment.Right;
                _printArea.Children.Add(pageNumberText);

                break;
            case PageNumbering.BottomLeft:
                Grid.SetRow(pageNumberText, 2);
                pageNumberText.Margin = new Thickness(0, 20, 0, 0);
                _printArea.Children.Add(pageNumberText);
                break;
            case PageNumbering.BottomMidle:
                Grid.SetRow(pageNumberText, 2);
                pageNumberText.Margin = new Thickness(0, 20, 0, 0);
                pageNumberText.HorizontalAlignment = HorizontalAlignment.Stretch;
                pageNumberText.HorizontalTextAlignment = TextAlignment.Center;
                _printArea.Children.Add(pageNumberText);
                break;
            case PageNumbering.BottomRight:
                Grid.SetRow(pageNumberText, 2);
                pageNumberText.Margin = new Thickness(0, 20, 0, 0);
                _printArea.Children.Add(pageNumberText);
                break;
            default:
                break;
        }
    }
}

Here’s how all of this looks like at runtime in the sample app:

HeaderFooterPageNumbers

Printing a list on separate pages

Maybe your app needs to print a collection of items, typically displayed through some kind of ItemsControl. The UWP Community Toolkit’s PrintHelper can’t do dynamic page breaks, so we’ll start each item on a new page.

The print content instances are created from a DataTemplate, like this:

<Page.Resources>
    <DataTemplate x:Name="MoonTemplate">
        <StackPanel>
            <TextBlock Text="{Binding Name}"
                        FontSize="48"
                        Margin="60" />
            <Image Source="{Binding ImagePath}"
                    Margin="120 0" />
        </StackPanel>
    </DataTemplate>
</Page.Resources>

To print the collection, just iterate through it and create a ContentControl for each item, load the data template in its ContentTemplate, assign the DataContext to update all bindings, and add the new content to the print job:

var service = new PrintService();
service.Header = new TextBlock { Text = "List of items with page break" };
service.PageNumbering = PageNumbering.BottomMidle;

foreach (var moon in Moons)
{
    // The secret is to NOT use an ItemsControl.
    var cont = new ContentControl();
    cont.ContentTemplate = Resources["MoonTemplate"] as DataTemplate;
    cont.DataContext = moon;
    service.AddPrintContent(cont);
}

service.Print();

Here’s how this looks like in the sample app:

ListItems

Printing a report

Maybe your app has a more or less complex dashboard page, with different texts and images, and controls like gauges and diagrams. That page may have a background image, light text on a dark background, and/or a horizontal layout. So it’s probably not a very good idea to emulate a screenshot and send that page as-is to a printer. For printed reports we expect a white background, probably no background images, and a vertical (portrait) layout. But we still want to reuse as much as possible XAML elements in both ‘pages’. So we’re going to rely on data templates again.

Some data templates can be very simple, like this one that just displays an image:

<DataTemplate x:Name="ImageTemplate">
    <Image Source="{Binding ImagePath}"
            Stretch="Uniform"
            Margin="0" />
</DataTemplate>

But they can also be a bit more complex, like this grid that hosts a list of characteristics:

<DataTemplate x:Name="CharacteristicsTemplate">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
        </Grid.RowDefinitions>
        <TextBlock Text="Planet"
                    FontWeight="SemiBold"
                    HorizontalAlignment="Right"
                    Margin="0 0 10 10" />
        <TextBlock Text="{Binding Planet}"
                    Margin="10 0 0 10"
                    Grid.Column="1" />
        <TextBlock Text="Mass"
                    FontWeight="SemiBold"
                    HorizontalAlignment="Right"
                    Margin="0 0 10 10"
                    Grid.Row="1" />
        <TextBlock Text="{Binding Mass}"
                    Margin="10 0 0 10"
                    Grid.Row="1"
                    Grid.Column="1" />
        <TextBlock Text="Albedo"
                    FontWeight="SemiBold"
                    Margin="0 0 10 10"
                    HorizontalAlignment="Right"
                    Grid.Row="2" />
        <TextBlock Text="{Binding Albedo}"
                    Margin="10 0 0 10"
                    Grid.Row="2"
                    Grid.Column="1" />
        <TextBlock Text="Orbital Eccentricity"
                    FontWeight="SemiBold"
                    HorizontalAlignment="Right"
                    Margin="0 0 10 10"
                    Grid.Row="3" />
        <TextBlock Text="{Binding OrbitalEccentricity}"
                    Margin="10 0 0 10"
                    Grid.Row="3"
                    Grid.Column="1" />
    </Grid>
</DataTemplate>

Here’s the horizontal layout of the dashboard page in the sample app. It gets its details from the different data templates:

<ListView SelectionMode="None"
            ScrollViewer.HorizontalScrollMode="Enabled"
            ScrollViewer.IsHorizontalRailEnabled="True"
            ScrollViewer.HorizontalScrollBarVisibility="Auto"
            Grid.Row="1">
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <StackPanel Orientation="Horizontal" />
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
    <Grid Height="500"
            Width="500">
        <ContentControl DataContext="{x:Bind SelectedMoon}"
                        ContentTemplate="{StaticResource ImageTemplate}" />
    </Grid>
    <Grid Height="500"
            Width="500">
        <ContentControl DataContext="{x:Bind SelectedMoon}"
                        ContentTemplate="{StaticResource DescriptionTemplate}" />
    </Grid>
    <Grid Height="500"
            Width="250">
        <ContentControl DataContext="{x:Bind SelectedMoon}"
                        ContentTemplate="{StaticResource CharacteristicsTemplate}" />
    </Grid>
    <Grid Height="500"
            Width="500">
        <ContentControl DataContext="{x:Bind Moons}"
                        ContentTemplate="{StaticResource BarChartTemplate}" />
    </Grid>
</ListView>

Here’s the template for the vertical print page. It reuses as much of these data templates as possible, but it hangs these in a container with a different look. The data template for the report is in the page’s resources:

<DataTemplate x:Name="ReportTemplate">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <TextBlock DataContext="{Binding SelectedMoon}"
                    Text="{Binding Name}"
                    FontSize="36"
                    Margin="0 10"
                    Grid.ColumnSpan="2" />
        <ContentControl DataContext="{Binding SelectedMoon}"
                        ContentTemplate="{StaticResource ImageTemplate}"
                        Margin="0 0 10 10"
                        Grid.Row="1" />
        <TextBlock DataContext="{Binding SelectedMoon}"
                    Text="{Binding Description}"
                    TextWrapping="Wrap"
                    Margin="10 0 0 10"
                    Grid.Row="1"
                    Grid.Column="1" />
        <ContentControl DataContext="{Binding SelectedMoon}"
                        ContentTemplate="{StaticResource CharacteristicsTemplate}"
                        HorizontalAlignment="Center"
                        Margin="0 0 10 10"
                        Grid.Row="2" />
        <ContentControl RequestedTheme="Dark"
                        DataContext="{Binding Moons}"
                        ContentTemplate="{StaticResource BarChartTemplate}"
                        HorizontalAlignment="Center"
                        Margin="0 0 0 0"
                        Grid.ColumnSpan="2"
                        Grid.Row="3" />
    </Grid>
</DataTemplate>

I also added an example of reusing a more advanced control – a Telerik BarChart. I had to hard code the color scheme (blue with a black border) for the bars, because by default they became white or transparent in the print page:

<DataTemplate x:Name="BarChartTemplate">
    <telerikChart:RadCartesianChart>
        <telerikChart:RadCartesianChart.VerticalAxis>
            <telerikChart:LinearAxis Minimum="0"
                                        Maximum="1"
                                        Title="Albedo"
                                        FontFamily="Segoe UI" />
        </telerikChart:RadCartesianChart.VerticalAxis>
        <telerikChart:RadCartesianChart.HorizontalAxis>
            <telerikChart:CategoricalAxis />
        </telerikChart:RadCartesianChart.HorizontalAxis>
        <telerikChart:BarSeries ItemsSource="{Binding}">
            <telerikChart:BarSeries.DefaultVisualStyle>
                <Style TargetType="Border">
                    <Setter Property="Background"
                            Value="DodgerBlue" />
                    <Setter Property="BorderBrush"
                            Value="Black" />
                    <Setter Property="BorderThickness"
                            Value="1" />
                </Style>
            </telerikChart:BarSeries.DefaultVisualStyle>
            <telerikChart:BarSeries.CategoryBinding>
                <telerikChart:PropertyNameDataPointBinding PropertyName="Name" />
            </telerikChart:BarSeries.CategoryBinding>
            <telerikChart:BarSeries.ValueBinding>
                <telerikChart:PropertyNameDataPointBinding PropertyName="Albedo" />
            </telerikChart:BarSeries.ValueBinding>
        </telerikChart:BarSeries>
    </telerikChart:RadCartesianChart>
</DataTemplate>

Here are the two pages, in the sample app:

DashboardToReport

In this article we showed some easy tricks to create more complex print jobs on top of the UWP Community Toolkit. If your app needs more complexity than this, then I would suggest to take a look at my dynamic page-break solution or let your app expose its information through PDF or HTML.

The sample app lives here on GitHub.

Enjoy!

Advertisements

How to enumerate all installed fonts on UWP

In this article we’ll show you how a UWP app can detect and enumerate all the installed fonts on the device, and how to apply a selected font to a UI element.

It’s all extremely easy – if you know which NuGet package to use:

Win2D_UWP

Win2D is a Windows Runtime API wrapper on Direct2D that is specialized in rendering 2D graphics using hardware acceleration.

Win2D comes with API calls and XAML helper controls for:

  • manipulating and rendering bitmaps,
  • manipulating vector graphics,
  • applying color effects and filters,
  • converting text to geometry, et cetera.

If you want to dive deeper into Win2D then checkout these features and UWP samples.

Once you’re referencing the Win2D.UWP NuGet package, enumerating the installed fonts on the device is literally a one-liner. Just call the GetSystemFontFamilies on the CanvasTextFormat class. It returns the (localized!) font names as an array of strings:

public List<string> Fonts
{
    get
    {
        return CanvasTextFormat.GetSystemFontFamilies().OrderBy(f => f).ToList();
    }
}

To display these in a ComboBox, assign the list to its ItemsSource:

<ComboBox x:Name="FontsCombo"
            ItemsSource="{x:Bind Fonts}"
            SelectionChanged="FontsCombo_SelectionChanged">
    <!---... -->
</ComboBox>

When the ComboBox’s selection changes, we update the font of a TextBox. Provide the font name string to the FontFamily constructor to set the FontFamily dependency property:

private void FontsCombo_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
    TextEditor.FontFamily = new FontFamily(FontsCombo.SelectedValue.ToString());
}

In the ComboBox’s item template, I used x:Bind to provide both the FontFamily and Text for the TextBlock (don’t forget to specify the item type with x:DataType).

<ComboBox.ItemTemplate>
    <DataTemplate x:DataType="x:String">
        <TextBlock FontFamily="{x:Bind}"
                    Text="{x:Bind}" />
    </DataTemplate>
</ComboBox.ItemTemplate>

Done!

Here’s how all of this looks like at runtime:

ScreenShot

The sample app lives here on GitHub.

Enjoy!

Everything about the UWP ParallaxView Control

In this article we dive into the UWP ParallaxView control, an important player in Fluent Design that allows you to create a feeling of depth, perspective, and movement in your layered UI.

I created a small UWP app to demonstrate

  • defining a vertical parallax effect,
  • defining a horizontal parallax effect,
  • fine-tuning the animations, and
  • declaratively and programmatically adapting the UI to the Windows release.

I’m not going to spend too much time in introductions, but if you insist: there’s this great article on docs.microsoft.com. It also hosts this picture that says it all:

parallax_v2

To achieve  a parallax effect, all you need is

  • a (faster) scrolling foreground: any element with a ScrollViewer will do,
  • a (slower) scrolling background: any UIElement, but typically an image, and
  • a ParallaxView control to glue these together.

The ParallaxView control adjusts the size of the background to always remain in view, and registers a Composition Animation that moves the background when you scroll through the foreground element.

Vertical parallax

Here’s the canonical ParallaxView setup:

  • it’s wrapped around the background Image,
  • its Source property refers to the ListView in the foreground, and
  • its VerticalShift property defines the range in pixels (and the axis) of the parallax motion:
<ParallaxView Source="{x:Bind ForegroundElement}"
                VerticalShift="100">

    <!-- Background element -->
    <Image x:Name="BackgroundImage"
            Source="ms-appx:///Assets/cyberpunk.jpg"
            Stretch="UniformToFill" />
</ParallaxView>

<!-- Foreground element -->
<ListView x:Name="ForegroundElement"
            ScrollViewer.VerticalScrollBarVisibility="auto">
    <!-- ListView style and content ... -->
</ListView>

Here’s how this looks like in the sample app:

VerticalParallax

This page uses a double parallax effect: the white text in the center is animated too. I used a smaller VerticalShift value so it appears to dangle between the foreground and the background:

<ParallaxView Source="{x:Bind ForegroundElement}"
                VerticalShift="50">
    <TextBlock Text="Bladerunner 2049" />
</ParallaxView>

This is just to illustrate that the background element does not need to be an image, and that multiple ParallaxView instances can be tied to the same source.

Horizontal Parallax

If you have a horizontally scrolling app –such as the Weather app- then you can also achieve a parallax effect, just set the HorizontalShift property. Here’s three controls simulating the sea:

<ParallaxView Source="{x:Bind ForegroundElement}"
                HorizontalShift="33"
                Margin="0 0 -33 180">
    <Image Source="ms-appx:///Assets/waves.png"
            Stretch="UniformToFill"
            Height="200"
            VerticalAlignment="Bottom" />
</ParallaxView>

<ParallaxView Source="{x:Bind ForegroundElement}"
                HorizontalShift="66"
                Margin="-66 0 0 100">
    <Image Source="ms-appx:///Assets/waves.png"
            Stretch="UniformToFill"
            Height="200"
            VerticalAlignment="Bottom" />
</ParallaxView>

<ParallaxView Source="{x:Bind ForegroundElement}"
                HorizontalShift="100">
    <Image x:Name="BackgroundImage"
            Source="ms-appx:///Assets/waves.png"
            Stretch="UniformToFill"
            Height="200"
            VerticalAlignment="Bottom" />
</ParallaxView>

Here’s how this looks like in the sample app:

HorizontalParallax

By the way: this is a nice use case for playing with negative shift values. Feel free to install the sample app and give it a try…

Fine-tuning the shift animations

In most cases it suffices to setting the vertical or horizontal shift to create a decent parallax effect. In other cases you may want to adjust the animations to achieve pixel-perfectness or realistic behavior. The sample app comes with a configuration page where you can test the other relevant properties of the ParallaxView control: VerticalSourceStartOffset, VerticalSourceEndOffset, VerticalSourceOffsetKind, MaxVerticalShiftRatio, and IsVerticalShiftClamped. For the sake of completeness: all of these have also a a Horizontal counterpart.

This is how the configuration page looks like:

ConfigurationPage

Sometimes it’s hard to see the impact of changing one of the parameters. This is especially the case when the background image automatically resizes itself (e.g. when it’s Stretched to Uniform or UniformToFill, which is … well… always).

For the edge cases I created an alternative background with a non-stretched ruler from 0 to 100. To enable is, just switch the comments in the configuration view:

<ParallaxView x:Name="Parallax"
                Source="{x:Bind ForegroundElement}">
    <Image x:Name="BackgroundImage"
            Source="ms-appx:///Assets/mountains.jpg"
            Stretch="UniformToFill"
            HorizontalAlignment="Center" />
    <!--<Image x:Name="BackgroundImage"
            Source="ms-appx:///Assets/ruler.jpg"
            Stretch="None" />-->
</ParallaxView>

The page looks less sexy like this, but it’s a lot more useful. Here’s a standard vertical parallax with a VerticalShift of 200 pixels:

ConfigurationDetails_1

In this configuration, you lose part of the background. The start and the end of the ruler (the 0 and 100 marker) are off-screen and only become visible when scrolling by touch – because of the built-in inertia animations. If you’re using a mouse, these parts of the background will never show.

Fortunately, with VerticalSourceStartOffset and VerticalSourceEndOffset and their horizontal counterparts, you can move the start and the end position of the background element. When we move the vertical source start offset to 100 pixels, the zero marker appears on top:

ConfigurationDetails_2

Likewise, setting the vertical source end offset to –100 reveals the 100 marker when you scroll to the bottom of the foreground (note: that was using Stretch.Uniform on the background):

ConfigurationDetails_3

Please observe that this behavior of the ParallaxView is only a side effect (pun intended Smile). These properties actually specify the vertical scroll offset at which parallax motion starts and ends. This would allow you to do small adjustments in the foreground element without starting an animation on the background – I assume.

Unfortunately the settings only have an effect against a background image that does not stretch (i.e. almost never) and only when you start scrolling from the top or the bottom.

Here’s how this looks like in practice – there’s a small delay in the background animation:

VerticalSourceStartOffset

Use MaxVerticalShiftRatio (with a range from 0 to 1) to limit the background scrolling speed. If you set it to 0.2 in the sample app and scroll down, you’ll see that you can’t reach the end of the ruler anymore:

ConfigurationDetails_4

I assume that the IsVerticalShiftClamped property switches the vertical shift on and off, but I see no effect in the sample app.

The VerticalSourceOffsetKind is even more mysterious. The documentation states that it can be Absolute or Relative, and that’s it. I can’t see the logic in the state changes – yet. I do observe some unexpected shifts from time to time, like this:

ConfigurationDetails_5

We want more

If you want more control over the parallax effect than what’s provided by the ParallaxView, then you have to roll the animations yourself. Here are two fine examples:

Targeting pre-FCU versions

The ParallaxView is an easy way to get some of that Fluent Design into your app. It was introduced with the Fall Creators Update (a.k.a. FCU, or Build 16299), and your apps may be targeting earlier releases. Don’t worry: it’s easy to make the ParallaxView an optional component in your UI. With the same code base, you can present  a static background image for the users on an earlier release, and use the parallax effect for the ones on a newer release.

Let me prove that to you. The Conditional XAML page in the sample app is divided in three zones:

  • on the left is the canonical implementation that requires FCU,
  • the image in the middle uses declarative conditional XAML, while
  • the right hand part uses programmatic conditional XAML.

Here’s how it looks like at run-time:

ConditionalPage

Declarative conditional XAML

If you’re targeting Creators Update (Build 15063) or higher, then you can use Conditional XAML –a markup extension on top of IsApiContractPresent -to configure the UI based on the release.

Here’s how to use it. First declare the conditional XAML namespaces to see whether or not you’re running on Fall Creators Update (that’s UniversalApiContract 5) or higher:

xmlns:PriorToFcu="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractNotPresent(Windows.Foundation.UniversalApiContract,5)"
xmlns:FcuOrHigher="http://schemas.microsoft.com/winfx/2006/xaml/presentation?IsApiContractPresent(Windows.Foundation.UniversalApiContract,5)"

Now you can create conditional elements in your XAML, like this:

<!-- Declarative conditional XAML expressions. -->
<FcuOrHigher:ParallaxView Source="{x:Bind ForegroundElement}"
                            VerticalShift="100">
    <Image Source="ms-appx:///Assets/beach.jpg" />
</FcuOrHigher:ParallaxView>
<PriorToFcu:Image Source="ms-appx:///Assets/beach.jpg" />

These will be evaluated at run-time to spawn the appropriate XAML.

Programmatic conditional XAML

Alternatively you can call the primitives on which the markup extension is built. I’m not sure it’s a good idea to call IsApiContractPresent all over the place, so I created this nice helper class –with room for extension- in a MVVM Versioning service. It allows me to evaluate Sdk.SupportsParallaxView:

public static class Sdk
{
    public static bool SupportsParallaxView
    {
        get
        {
            return ApiInformation.IsApiContractPresent("Windows.Foundation.UniversalApiContract", 5);
        }
    }
}

For the XAML part, just draw the image. Don’t forget to give it a Name:

<!-- Programmatic conditional XAML expressions. -->
<Image x:Name="BeachImage"
        Source="ms-appx:///Assets/beach.jpg" />

The Grid that hosts the elements should also get a name – I called it ContentGrid. Here’s the code that detects the support for ParallaxView and modifies the XAML programmatically. It executes in the Loaded event, when the Visual Tree was populated and is ready for interaction:

private void ConditionalPage_Loaded(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    if (Sdk.SupportsParallaxView)
    {
        // Insert ParallaxView.
        var parallax = new ParallaxView();
        parallax.Source = ForegroundElement;
        parallax.VerticalShift = 100;
        Grid.SetColumn(parallax, 2);
        ContentGrid.Children.Remove(BeachImage);
        parallax.Child = BeachImage;
        ContentGrid.Children.Insert(0, parallax);
    }
}

I definitely prefer the programmatic approach here, because

  • I can adapt the XAML not only based on the Windows Release, but also on hardware specifications, or on user preferences, and
  • it comes with decent XAML Designer support.

This is how the page looks at design time. All conditional XAML elements are simply ignored:

XamlDesigner

Call to action

You see: nothing prevents you from using ParallaxView in your apps to bring in some Fluent Design. Just make sure you don’t exaggerate…

The code

The ParallaxView sample app lives here on GitHub.

Enjoy!

An Extended Splash Screen with Lottie Animations in UWP

In this article we demonstrate how to use Lottie animations in the extended splash screen of a UWP application. A Lottie animation is a programmable animated graphic that can be rendered into your XAML.

Here’s how the companion sample app starts up, with its splash screen transitioning into an look-alike page that displays an animation, whilst executing some longer-running code:

AppStart

Creating an extended splash screen

Sometimes an app needs to execute some longer-running code on startup. Especially the very first time after an install or an upgrade, you may want to connect to the backend, create a user profile, populate a local SQLite database, and defeat the Klingons, the Sith and/or the Daleks.

The long-running code in my sample app is less ambitious:

await Task.Delay(10000);

It is common to handle this scenario with a so-called Extended Splash Screen. This is a page that looks like the native splash screen and that executes the longer-running code, before eventually navigating to the main page. Typically an extended splash screen is decorated with a progress ring to indicate that the app does not hang.

The nice official documentation for creating an extended the splash screen is right here. I started with a copy of the sample code from there, and replaced the progress ring by a Lottie animation.

In a nutshell, here’s the routine to create an extended splash screen:

  • get the position of the SplashScreen image from the LaunchActivatedEventArgs,
  • navigate to an extended splash screen page that displays the same image at the same location,
  • execute the long running code in the Dismissed event handler of the original splash screen, and finally
  • navigate to the ‘real’ homepage or shell.

Adding Lottie capabilities

Lottie is a library to render animations. It uses JSON files that were generated by the Bodymovin extension of Adobe After Effects, a Special Effects editor to create picture and SVG animations. Lottie has implementations for iOS, Android, and the Web (React Native). It is supported by a broad community and has won a number of awards as design and development tool. It seems fair to assume that Lottie is becoming one of the standards for stand-alone animations in apps.

For Native Windows developers, Lottie is now available as LottieUWP, a NuGet package written by Alexandre Chohfi. To look it up from Visual Studio, you need to check the ‘Include prerelease’ box in NuGet Package Manager:

LottieUWP_NuGet

Adding Lottie files

LottieFiles is a great resource to get some free JSON files to experiment with:

LottieFiles

That’s where I found the beer bubbles animation.

Add the downloaded JSON file(s) to your project, and don’t forget to change their Build Action to Content:

Json_Build_Action

Displaying Lottie animations

The best way to get some insight into LottieUWP and its API is viewing this #ifdef episode on Channel 9. For the sample app, I just added a LottieAnimationView in the extended splash screen, with an auto-played, infinite-looping animation, nicely positioned over the beer glass background:


That’s all that’s needed to host and run the animation. I just modified the Speed – programmatically, since assigning a value declaratively in XAML is not very well supported in the current LottieUWP version (after all it *is* a pre-release):

// Adjust the speed of the Lottie animation.
SplashScreenAnimation.Speed = 3;

The end-user may resize the window or rotate his device when the app is starting. So when the background image is repositioned we also have to update the size and location of the LottieAnimationView:

// Position the extended splash screen image in the same location as the system splash screen image.
private void PositionImage()
{
    extendedSplashImage.SetValue(Canvas.LeftProperty, splashImageRect.X);
    extendedSplashImage.SetValue(Canvas.TopProperty, splashImageRect.Y);
    extendedSplashImage.Height = splashImageRect.Height;
    extendedSplashImage.Width = splashImageRect.Width;

    // Position the Lottie animation.
    var factor = splashImageRect.Width / 620;
    SplashScreenAnimation.SetValue(Canvas.LeftProperty, splashImageRect.X + 100 * factor);
    SplashScreenAnimation.SetValue(Canvas.TopProperty, splashImageRect.Y + 150 * factor);
    SplashScreenAnimation.Width = 180 * factor;
}

Tweaking the JSON files

Visual Studio allows you to unminify the JSON file, this will reveal a structure of layers with the strokes and shapes and their use in animations and transformations. I assume that it’s unlikely that we’re going to apply changes manually in there. Nevertheless there are some ways to slightly tweak the files.

In LottieFile’s preview page you can visualize the selected JSON against different background colors. This helps you to reveal the color scheme that is used in the animation. Here you can see that the filter animation uses plain white to fill the circles – so they’re not transparent:

LottiePreview

The button in the top right corner launches the Lottie Editor:

LottieEditor

This page allows you to change the color of some elements. The corresponding changes to the JSON files are logged in the browser’s Console.

Other use cases

The main page of the sample app shows how to decorate a button with a (more or less) subtle animation to draw the user’s attention. This is how it looks like:

AppMain

Please note that the animated GIF on this page loops infinitely, unlike the animation in the UWP app.

Again, we used a LottieAnimationView, but we set the RepeatCount to 2 and the RepeatMode to Reverse to obtain a brief symmetric animation:


And again, we adjusted the speed programmatically:

// Adjust the speed of the animation.
// Does not work declaratively for now.
MainpageAnimation.Speed = 3F;

Use PlayAnimation to (re)play the Lottie animation:

// Replay the animation.
MainpageAnimation.PlayAnimation();

I just added this call to verify whether the button is still clickable when the animation is running on top of it. Fortunately this is the case.

if you want see more Lottie API examples, please check Corrado’s blog post on the subject.

GitHub Repo N° 50

The sample app lives here on GitHub in my 50th public repo.

Enjoy!

Creating human-friendly texts on UWP with Humanizer

This article highlights some of the many features of the Humanizer for .NET project which also runs in UWP. It’s a project from the .NET Foundation that comes with a lot of helper functions for generating human-readable strings from … well … less human-readable expressions, for multiple cultures. Here’s an overview of the functionality:

  • Humanize and dehumanize strings
  • Truncate strings
  • Format strings
  • Humanize and dehumanize enumerations
  • Humanize dates, times, and time spans
  • Pluralize and singularize
  • Transforming numbers to words
  • Transforming numbers to ordinal words
  • Transforming to and from Roman numerals
  • … and more

I used many features of this package in many apps in several .NET technologies (console apps, WPF, and WCF). I’m a huge fan of the relative date and time functions, because after all:

Your 3 books will be delivered in 10 minutes.” sounds a lot better than ‘”Item: book; Quantity:3; ETA 2018-06-18T13:45:30”.

This extremely useful functionality is available as source on GitHub and as a NuGet package. I created a small app to test the package in UWP.

All you need to do is include the namespace:

using Humanizer;

Then you can call the many functions in the package. Most of these are implemented as extension methods. Here’s an overview of some expressions and their result:

“Can_return_title_Case”.Humanize(LetterCasing.Title) Can Return Title Case
“Pascal case input string is turned into sentence”.Dehumanize() PascalCaseInputStringIsTurnedIntoSentence
“Long text to truncate”.Truncate(10) Long text…
DateTime.UtcNow.AddHours(-30).Humanize() yesterday
TimeSpan.FromMilliseconds(1299630020).Humanize(3, collectionSeparator: null) 2 weeks, 1 day, and 1 hour
“Man”.Pluralize() Men
“process”.ToQuantity(2) 2 processes
“dollar”.ToQuantity(2, “C2”, new CultureInfo(“en-US”)) $2.00 dollars
5.Ordinalize() 5th
3501.ToWords() three thousand five hundred and one
21.ToOrdinalWords() twenty-first
“MMXVIII”.FromRoman().ToString() 2018

Seeing is believing, so here’s how these calls look like in the sample UWP app:

Humanizer_HomePage

The main page of the sample app allows you to set a time using Dean Chalk’s radial UWP TimePicker control. It then displays the selected time, and its Humanized value:

Humanizer_MainPage

I’m not diving further into Humanizer’s features and code here, since it is well documented. My UWP sample app lives here on GitHub.

Enjoy!

App tile pinning in UWP

In this article we walk through the different APIs for pinning and unpinning a UWP app to different destinations, and show how to bundle these calls into a single AppTile class that provides a ‘pinning service’ including:

  • pinning to the taskbar,
  • pinning to the start menu, and
  • enumerating, pinning and unpinning secondary tiles.
public class AppTile
{
    // ...
}

We also describe the impact of pinned tiles to the application launch process and how to debug your app in this scenario.

This article comes with a sample app on GitHub that looks like this:

ScreenShot

The AppTile class or service makes it easier to use the down-to-earth API’s in UWP apps that use MVVM and data binding. Here’re the class diagram. I’m assuming that its member names are sufficiently self-explanatory:

AppTileClass

Pinning to the taskbar

Users can manually pin your app to the taskbar, but you can also friendly hint this from your app and then pin programmatically. All the details for this are nicely explained in the official documentation, so allow me to stay brief on the details.

Service API

Not every device that runs your app comes with a recent OS and/or a taskbar. Before calling pinning features, you have to ensure that there is a Windows TaskBarManager (via ApiInformation.IsTypePresent) and that the taskbar itself is present and accessible (through IsPinningAllowed):

public static bool IsPinToTaskBarEnabled 
	=> ApiInformation.IsTypePresent("Windows.UI.Shell.TaskbarManager") 
	   && TaskbarManager.GetDefault().IsPinningAllowed;

When you programmatically pin your app to the taskbar, a dialog will pop up to ask for the user’s consent. To avoid an embarrassing experience, it makes sense to first discover whether the app is already pinned. This is done with a call to IsCurrentAppPinnedAsync():

public async static Task<bool?> IsPinnedToTaskBar()
{
    if (IsPinToTaskBarEnabled)
    {
        return await TaskbarManager.GetDefault().IsCurrentAppPinnedAsync();
    }
    else
    {
        return null;
    }
}

Eventually a call to RequestPinCurrentAppAsync() will launch the pinning process:

public static async Task<bool?> RequestPinToTaskBar()
{
    if (IsPinToTaskBarEnabled)
    {
        return await TaskbarManager.GetDefault().RequestPinCurrentAppAsync();
    }
    else
    {
        return null;
    }
}

Usage

The AppTile API is designed for easy use in any UWP app, but maybe the IsPinnedToTaskBar() method will pose a challenge. I can imagine that you may want to access this asynchronous member as a property or from inside a synchronous method (e.g. in data binding). I did not code a synchronous version inside the AppTile class, since it feels like an anti-pattern. There are more than enough asynchronous methods in a page or control’s life cycle, so you should use these to do calls that are asynchronous by nature.

Nevertheless, with Task.Run<T> you could easily create a synchronous wrapper that executes on the UI thread (a requirement for the call). Here’s the corresponding code snippet from the main page of the sample app:

var isPinned = Task.Run<bool?>(() => AppTile.IsPinnedToTaskBar()).Result;

Using this wrapper, the hosting UI element can define bindable properties and commands:

private bool PinToTaskBar_CanExecute()
{
    var isPinned = Task.Run<bool?>(() => AppTile.IsPinnedToTaskBar()).Result;
    return !isPinned ?? false;
}

private async void PinToTaskBar_Executed()
{
    await AppTile.RequestPinToTaskBar();
}

Here are the XAML snippets for status field and command button:

<TextBlock Text="{x:Bind IsPinnedToTaskBar()}" />

<AppBarButton Icon="Pin"
              Label="Pin to Task Bar"
              IsCompact="False"
              Command="{x:Bind PinToTaskBarCommand }" />

Here’s how the system dialog to get the user’s consent looks like:

PinToTaskbar_Dialog

A couple of years ago, Windows 8.* came with different icons for ‘Pin to Start’ and ‘Pin to Taskbar’ using pins that pointed in the appropriate direction:

Windows8_Experience

Windows 10 uses the same horizontal pin icon for both, and a diagonal one for unpinning. Inside your own app you may choose other icons of course. The Symbol Enum has a Pin and an UnPin icon.

Pinning to the start menu

It should not come as a surprise that the Start Menu Pinning API is similar to the Taskbar Pinning API. All documentation is here. Again, we made a wrapper to detect whether the service is enabled. It uses ApiInformation.IsTypePresent, and checks whether your app is allowed through SupportsAppListentry in the Windows StartScreenManager. To verify if your app is allowed you need to look up its main tile as an AppListEntry from GetAppListEntriesAsync:

public static bool IsPinToStartMenuEnabled
{
    get
    {
        if (ApiInformation.IsTypePresent("Windows.UI.StartScreen.StartScreenManager"))
        {
            return Task.Run<bool>(() => IsPinToStartMenuSupported()).Result;
        }

        return false;
    }
}

private static async Task<bool> IsPinToStartMenuSupported()
{
    AppListEntry entry = (await Package.Current.GetAppListEntriesAsync())[0];
    return StartScreenManager.GetDefault().SupportsAppListEntry(entry);
}

To detect whether you main tile is already pinned to the start menu, you have to look up the app list entry again from your Package [note to self: there’s room for a helper method in the AppTile class] and then ask it to the StartScreenManager with ContainsAppListEntryAsync:

public static async Task<bool?> IsPinnedToStartMenu()
{
    if (IsPinToStartMenuEnabled)
    {
        AppListEntry entry = (await Package.Current.GetAppListEntriesAsync())[0];
        return await StartScreenManager.GetDefault().ContainsAppListEntryAsync(entry);
    }
    else
    {
        return null;
    }
}

The request to programmatically pin is done through RequestAddAppListEntryAsync():

public static async Task<bool?> RequestPinToStartMenu()
{
    if (IsPinToStartMenuEnabled)
    {
        AppListEntry entry = (await Package.Current.GetAppListEntriesAsync())[0];
        return await StartScreenManager.GetDefault().RequestAddAppListEntryAsync(entry);
    }
    else
    {
        return null;
    }
}

Here’s the corresponding system dialog:

PinToStart_Dialog

Observe the complete difference in style with the taskbar system dialog…

A word (or four) about unpinning

By default, all developer’s pinned tiles are automatically unpinned when you compile the app after a source code change. I didn’t find a switch to alter this behavior.

Programmatically unpinning from start menu or taskbar is not included in the official API’s.

You-Shall-Not-Unpin

Secondary tiles

A secondary tile allows your users not only to startup the app, but also to directly navigate to a specific page or specific content. All the official info is right here.

Pinning secondary tiles

When you pin a secondary tile, the app (or the user) needs to give it a name. The name of the secondary tile serves as an identifier for the specific page or content that it is deep-linked to. In most cases you can grab this from the current context, the sample app just asks the user for it:

SecondaryTileNameDialog

There are some restrictions in the string identifier for a secondary tile: there’s a maximum length of 2048 (that’’s still long enough to hold a simple serialized object) and it doesn’t like some characters, like spaces or exclamation marks. The AppTile service comes with a method to clean this up:

private static string SanitizedTileName(string tileName)
{
    // TODO: complete if necessary...
    return tileName.Replace(" ", "_").Replace("!","");
}

The API for pinning secondary tiles is available in all Windows 10 versions and is enabled on all devices, so there’s no call required to check if it’s enabled. Before pinning a secondary tile, you should verify whether it exists already with SecondaryTile.Exists, and then you call one of the constructors of SecondaryTile. If you want the tile name to be displayed with ShowNameOnSquare150x150Logo (which is a horrible property name), then you have to make sure to provide a tile image that comes with free space at the bottom. Fortunately the SecondaryTile class can deal with this – you can even use a different image for each secondary tile. Actually there is a lot more that you can do with secondary tiles, but I’m just focusing on the pinning part here. With RequestCreateAsync the tile is created:

public async static Task<bool> RequestPinSecondaryTile(string tileName)
{
    if (!SecondaryTile.Exists(tileName))
    {
        SecondaryTile tile = new SecondaryTile(
            SanitizedTileName(tileName),
            tileName,
            tileName,
            new Uri("ms-appx:///Assets/Square150x150SecondaryLogo.png"),
            TileSize.Default);
        tile.VisualElements.ShowNameOnSquare150x150Logo = true;
        return await tile.RequestCreateAsync();
    }

    return true; // Tile existed already.
}

Here’s the corresponding system dialog to get the user’s consent:

Pin_SecondaryTile_Dialog

Here’s a screenshot from a start screen with the main tile and some secondary tiles from the sample app:

StartMenu

This is the code from the main page, it uses the Dialog Service:

private async void PinSecondaryTile_Executed()
{
    var tileName = await ModalView.InputStringDialogAsync("Pin a new tile.", "Please enter a name for the new secondary tile.", "Go ahead.", "Oops, I changed my mind.");
    if (!string.IsNullOrEmpty(tileName))
    {
        await AppTile.RequestPinSecondaryTile(tileName);
    }
}

Enumerating secondary tiles

The FindAllAsync static method on SecondaryTile returns all secondary tile instances for your app, so you could use it to detect if there are any:

public async static Task<bool> HasSecondaryTiles()
{
    var tiles = await SecondaryTile.FindAllAsync();
    return tiles.Count > 0;
}

or to enumerate their identities:

public async static Task<List<string>> SecondaryTilesIds()
{
    var tiles = await SecondaryTile.FindAllAsync();
    var result = new List<string>();
    foreach (var tile in tiles)
    {
        if (!string.IsNullOrEmpty(tile.TileId))
        {
            result.Add(tile.TileId);
        }
    }

    return result;
}

Here’s the sample app displaying the list of secondary tiles:

SecondaryTiles_Enum

Unpinning secondary tiles

With RequestDeleteAsync you can unpin a secondary tile. You have to provide its identity:


public async static Task<bool> RequestUnPinSecondaryTile(string tileName)
{
    if (SecondaryTile.Exists(tileName))
    {
        return await new SecondaryTile(tileName).RequestDeleteAsync();
    }

    return true; // Tile did not exist.
}

Unpinning does not require the user’s consent, so there’s no dialog involved.

Launching the App from a pinned tile

When the user clicks a pinned tile, your app is launched. If the launch came from a secondary tile, then its identity is reflected in the startup arguments.

UWP does not support running multiple instances of the same app. This means that when the user clicks a tile, the OnLaunched of the running instance of the app may be called. You have to prepare for that – I had to change my ActivationService for this:

  • When the app was already running (ApplicationExecutionState.Running) and there are no startup arguments (main tile or taskbar) then we may simply ignore the call.
  • If there are startup arguments then the launch was done through a secondary tile. In that case, we navigate to the tile-specific content. In the sample app, this means navigating to the Home page.

Here’s the code for this scenario:

public async Task LaunchAsync(LaunchActivatedEventArgs e)
{
    if (e.PreviousExecutionState == ApplicationExecutionState.Running)
    {
        if (string.IsNullOrEmpty(e.Arguments))
        {
            // Launch from main tile, when app was already running.
            return;
        }

        // Launch from secondary tile, when app was already running.
        Navigation.Navigate(typeof(HomePage), e.Arguments);
        return;
    }

    // Default Launch.
    await DefaultLaunchAsync(e);
}

When the app was not running during the launch, we do the standard initializations, and afterwards navigate to the tile-specific content if necessary. Notice that the Navigation Service passes the startup arguments to the target page:

private async Task DefaultLaunchAsync(LaunchActivatedEventArgs e)
{
    // Custom pre-launch service calls.
    await PreLaunchAsync(e);

    // Navigate to shell.
    Window.Current.EnsureRootFrame().NavigateIfAppropriate(typeof(Shell), e.Arguments).Activate();

    // Custom post-launch service calls.
    await PostLaunchAsync(e);

    // Navigate to details.
    if (!string.IsNullOrEmpty(e.Arguments))
    {
        Navigation.Navigate(typeof(HomePage), e.Arguments);
    }
}

The target page gets the startup arguments in its OnNavigatedTo handler and can act on it. The sample app just stores the tile name in a field:

private string navigationParameter = string.Empty;

public string NavigationParameter => navigationParameter;

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    if ((e != null) && (e.Parameter != null))
    {
        navigationParameter = e.Parameter.ToString();
    }

    base.OnNavigatedTo(e);
}

A text block is bound to the value of that parameter:

<Run Text="{x:Bind NavigationParameter}" />

Here’s how this looks like at runtime:

LaunchFromSecondaryTile

Debugging launch from a tile

To debug an app-launch from a tile, go to Debug/Other Debug/Targets/Debug Installed Package:

DebugSecondaryTileMenu

Select your app from the list, check the do-not-launch-but-debug-when-it-starts box, and press the start button:

 DebugSecondaryTile

When you click on one the pinned tiles, you’ll see your breakpoints getting hit:

DebugSecondaryTileBreakpoint

Code

That’s it! The AppTile services and the sample app live here on GitHub.

Enjoy!

A Fluent Button Flyout for UWP

In this article we describe how to build an elegantly designed and animated fluent ToggleButton-Popup combination for use in UWP apps as an alternative for the less fluent Button Flyout. The code is not mine, I merely reused code and components from the Continuity framework by Justin Liu. This frameworks contains a lot of helpers for implementing Fluent Design in your apps without needing Windows 10 Fall Creators Update.

The ToggleButton-Popup combo is an excellent widget to display extra information such as a help text.

Here’s how all of this may look like (styling is up to you of course). It starts with a stand-alone round ‘more’ button, with hover animations. When pressed, the button rotates toward the opening flyout, while changing its shape to look like a ‘close’ button that becomes connected to the content that was just revealed. In the mean time, the flyout itself opens with a scale animation as if it grew out of the button:

FluentFlyoutButton

Closing the button and dismissing the flyout come with reverse animations.

I’ve been looking for ways to animate opening and closing Flyout or ContentDialog instances. I went through many of the XAML animations techniques, but could not find a way. Flyout and ContentDialog don’t come with Visual States, so Visual Transitions are not an option. Although its name seems promising, PopInThemeAnimation is not applicable. And if it were, it doesn’t come with a scale animation (only opacity and translation).

I almost gave up, and tried to to accept that flyouts would always open and close like this:

DefaultFlyoutButton

Frankly, I don’t like the animations. But what’s worse: you can not even see which of the buttons was actually pressed…

Then I came across this sample Kliva design with exactly the user experience I was looking for:

ContinuityPanel

I decided to copy the relevant code into my own solution under Features/Continuity to get some Fluent-As-A-Service:

ContinuityClasses

The Kliva demo is not using a Button with its Flyout, but a ToggleButton with ‘just’ a Grid. My only mission was to replace the Grid with a Popup. After a few iterations, I came up with the following setup, which even allows the Popup to have IsLightDismissEnabled on.:

  • A toggle button with storyboarded animations is placed in a container
  • A Popup is placed next to it with
  • The content of the Popup (not the Popup itself) is given implicit Show and Hide Composition API animations.

Here’s the corresponding XAML definition:

<!-- Toggle Button -->
<continuity:CircularToggleButton x:Name="TheToggle"
                                    CheckedBackground="{StaticResource HighlightBrush}"
                                    CheckedCornerRadius="6 0 0 6"
                                    FontFamily="Segoe UI">
    <continuity:CircularToggleButton.CheckedContent>
        <ContentControl Margin="3"
                        Style="{StaticResource IconCloseStyle}" />
    </continuity:CircularToggleButton.CheckedContent>
    <ContentControl Style="{StaticResource IconMoreStyle}" />
</continuity:CircularToggleButton>
<!-- 'Flyout' -->
<Popup x:Name="ThePopup"
        IsOpen="{Binding IsChecked, ElementName=TheToggle, Mode=TwoWay}"
        IsLightDismissEnabled="False"
        HorizontalOffset="{Binding ActualWidth, ElementName=TheToggle}"
        VerticalOffset="-20">
    <Grid x:Name="TheGrid"
            Visibility="{Binding IsOpen, ElementName=ThePopup}">
        <!-- Content -->
    </Grid>
</Popup>

That’s it! Let’s dive into some details.

An animated circular toggle button

The CircularToggleButton adds a few dependency properties to ToggleButton, like content, background color and corner radius for the different states:

public sealed class CircularToggleButton : ToggleButton
{
    public static readonly DependencyProperty CheckedContentProperty =
        DependencyProperty.Register(
            "CheckedContent",
            typeof(object),
            typeof(CircularToggleButton),
            new PropertyMetadata(null));

    public object CheckedContent
    {
        get => GetValue(CheckedContentProperty);
        set => SetValue(CheckedContentProperty, value);
    }

    // ..
}

It also comes with a circular custom style and some nice visual effects and animations.

Here’s how the PointerOver VisualState pumps up the size of the content using a RenderTransform with a CompositeTransform on ScaleX and ScaleY:

<VisualState x:Name="PointerOver">
    <VisualState.Setters>
        <Setter Target="CheckedContentPresenter.(UIElement.RenderTransform).(CompositeTransform.ScaleX)"
                Value="1.1" />
        <Setter Target="CheckedContentPresenter.(UIElement.RenderTransform).(CompositeTransform.ScaleY)"
                Value="1.1" />
        <Setter Target="UncheckedContentPresenter.(UIElement.RenderTransform).(CompositeTransform.ScaleX)"
                Value="1.1" />
        <Setter Target="UncheckedContentPresenter.(UIElement.RenderTransform).(CompositeTransform.ScaleY)"
                Value="1.1" />
        <Setter Target="BackgroundVisual.Opacity"
                Value="0.9" />
    </VisualState.Setters>
</VisualState>

Here’s the VisualTransition with the StoryBoard on CornerRadius that turns the round button into its ‘connected’ shape when checked:

<VisualTransition GeneratedDuration="0:0:0.25"
                    To="Checked">
    <Storyboard>
        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(Border.CornerRadius)"
                                        Storyboard.TargetName="BackgroundVisual">
            <DiscreteObjectKeyFrame KeyTime="0"
                                    Value="{Binding CheckedCornerRadius, RelativeSource={RelativeSource TemplatedParent}}" />
        </ObjectAnimationUsingKeyFrames>
    </Storyboard>
</VisualTransition>

If you want to know more about storyboarded animations, this circular toggle button style definition is a nice tutorial. Storyboarded animations are a way to change a dependency property over time. When there’s a visual impact of this, the animation may or may not require the UI thread. While the SDK’s evolve, more and more of these animations are internally rewritten to run on the composition thread. So there’s no need for you give up readable declarative Storyboards in XAML in favor of dark imperative code against the Composition API. But for the second part of this article, we’ll use the latter…

An animated Popup

The Popup control is a XAML container that allows you to place content on top of other content. You can not animate the Popup itself but fortunately you can animate its content. As promised we’ll use Composition animations for this. These are 60-frames-per-second animations that run independent of the UI thread. They’re a bit harder to write, but there are a lot of helpers available, like these from UWP Community Toolkit.

Here’s the Continuity helper that starts it all:

TheGrid.EnableFluidVisibilityAnimation(
    centerPoint: new Vector3(0.0f, 40.0f, 0.0f), 
    showFromScale: 0.2f, 
    hideToScale: 0.2f, 
    showDuration: 400, 
    hideDuration: 400);

Here’s part of its implementation.

A time based Composition API animation of the type Vector2KeyFrameAnimation is created with the Scale of the Visual as Target:

if (!showFromScale.Equals(1.0f))
{
    showeScaleAnimation = compositor.CreateVector2KeyFrameAnimation();
    showeScaleAnimation.InsertKeyFrame(0.0f, new Vector2(showFromScale));
    showeScaleAnimation.InsertKeyFrame(1.0f, Vector2.One);
    showeScaleAnimation.Duration = TimeSpan.FromMilliseconds(showDuration);
    showeScaleAnimation.DelayTime = TimeSpan.FromMilliseconds(showDelay);
    showeScaleAnimation.Target = "Scale.XY";
}

The different animations (scale, position, opacity, …) are placed together in an AnimationGroup (one for hide and one for show):

var showAnimationGroup = compositor.CreateAnimationGroup();
// ...
if (showeScaleAnimation != null)
{
    showAnimationGroup.Add(showeScaleAnimation);
}

These animation groups are then implicitly hooked to the Popup’s content, using SetImplicitShowAnimation and SetImplicitHideAnimation:

ElementCompositionPreview.SetImplicitShowAnimation(element, showAnimationGroup);

Under the hood, a lot of different animation techniques were used to create this user experience, but I love the result! And while it’s probably possible to forge this into a single custom control, I’m not sure if it would add much value….

The code

The ‘fluent button flyout’ sample lives here on Github, the inspiring Continuity by Justin Liu is right here.

Enjoy!