Improving the accessibility of a UWP control

In this article we’ll show some techniques to improve the accessibility of a custom control in UWP. We’ll focus on three aspects:

  • Support for High Contrast themes,
  • Narrator compatibility, and
  • Keyboard input capability.

We’ll use the Radial Gauge control from Windows Community Toolkit as an example – the High Contrast and Narrator code in this article was recently merged into the source.

Supporting High Contrast themes

Adding Theme Dictionaries

With Settings –> Ease of access –> High Contrast, a Windows user can switch his color scheme to a small palette of contrasting colors. There may be medical reasons for this (like cataract or diabetic retinopathy) but it could also be set as to deal with working conditions (like direct sunlight on the screen, or insufficient lighting).

In a UWP app, all of the built-in native controls (the “XAML Common Controls”) respect this user setting and will update their UI. As an app developer you have to make sure

  • not to override that behavior of the native controls that you use, and
  • to provide a similar behavior for the XAML elements that you write yourself: pages and custom controls.

Basically this boils down to never hard coding colors.

When building a custom control, you should create a ThemeDictionary that includes an entry for ‘High Contrast’. Here’s a XAML snippet from the previous style definition from the Radial Gauge control. It came with direct assignment of colors:

<Style TargetType="local:RadialGauge"> 
   <Setter Property="NeedleBrush" 
           Value="{ThemeResource SystemControlBackgroundAccentBrush}" /> 
   <Setter Property="TrailBrush" 
           Value="{ThemeResource SystemControlBackgroundAccentBrush}" />  
   <Setter Property="TickBrush" 
           Value="{ThemeResource SystemControlForegroundBaseHighBrush}" />  
   <Setter Property="ScaleTickBrush" 
           Value="Transparent" /> 
   <!-- ... --/>
</Style>

The brushes were pulled from theme resources to support Light an Dark themes. That’s a good start, but it doesn’t cover the High Contrast scenario. You should stick to a restricted palette of only 8 system colors in the High Contrast section of the theme dictionary:

Key Initial default
SystemColorButtonFaceColor #FFF0F0F0
SystemColorButtonTextColor #FF000000
SystemColorGrayTextColor #FF6D6D6D
SystemColorHighlightColor #FF3399FF
SystemColorHighlightTextColor #FFFFFFFF
SystemColorHotlightColor #FF0066CC
SystemColorWindowColor #FFFFFFFF
SystemColorWindowTextColor #FF000000

To support High Contrast in the Radial Gauge, an extra layer of abstraction was added in the color brush assignments by means of a resource dictionary:

<ResourceDictionary.ThemeDictionaries>
    <ResourceDictionary x:Key="Default">
        <SolidColorBrush x:Key="RadialGaugeNeedleBrush"
                         Color="{ThemeResource SystemChromeHighColor}" />
        <SolidColorBrush x:Key="RadialGaugeTrailBrush"
                         Color="{ThemeResource SystemChromeHighColor}" />
        <SolidColorBrush x:Key="RadialGaugeScaleBrush"
                         Color="{ThemeResource SystemBaseMediumLowColor}" />
        <SolidColorBrush x:Key="RadialGaugeScaleTickBrush"
                         Color="{ThemeResource SystemBaseMediumLowColor}" />
        <SolidColorBrush x:Key="RadialGaugeTickBrush"
                         Color="{ThemeResource SystemBaseHighColor}" />
        <SolidColorBrush x:Key="RadialGaugeForeground"
                         Color="{ThemeResource SystemBaseHighColor}" />
    </ResourceDictionary>
    <ResourceDictionary x:Key="Light">
        <!-- ... -->
    </ResourceDictionary>
    <ResourceDictionary x:Key="Dark">
        <!-- ... -->
    </ResourceDictionary>
    <ResourceDictionary x:Key="HighContrast">
        <!-- ... -->
    </ResourceDictionary>
</ResourceDictionary.ThemeDictionaries>

The default style of the control was updated to refer to resources from that dictionary:

<Style TargetType="local:RadialGauge">
    <Setter Property="NeedleBrush"
            Value="{ThemeResource RadialGaugeNeedleBrush}" />
    <Setter Property="TrailBrush"
            Value="{ThemeResource RadialGaugeTrailBrush}" />
    <Setter Property="ScaleBrush"
            Value="{ThemeResource RadialGaugeScaleBrush}" />
    <Setter Property="ScaleTickBrush"
            Value="{ThemeResource RadialGaugeScaleTickBrush}" />
    <Setter Property="TickBrush"
            Value="{ThemeResource RadialGaugeTickBrush}" />
    <Setter Property="Foreground"
            Value="{ThemeResource RadialGaugeForeground}" />
    <!-- ... -->
</Style>

The resource dictionary has an entry for ‘High Contrast’ with only brushes out of the limited set of system colors:

<ResourceDictionary x:Key="HighContrast">
    <SolidColorBrush x:Key="RadialGaugeNeedleBrush"
                     Color="{ThemeResource SystemColorHotlightColor}" />
    <SolidColorBrush x:Key="RadialGaugeTrailBrush"
                     Color="{ThemeResource SystemColorHotlightColor}" />
    <SolidColorBrush x:Key="RadialGaugeScaleBrush"
                     Color="{ThemeResource SystemColorWindowColor}" />
    <SolidColorBrush x:Key="RadialGaugeScaleTickBrush"
                     Color="{ThemeResource SystemColorWindowColor}" />
    <SolidColorBrush x:Key="RadialGaugeTickBrush"
                     Color="{ThemeResource SystemColorWindowTextColor}" />
    <SolidColorBrush x:Key="RadialGaugeForeground"
                     Color="{ThemeResource SystemColorWindowTextColor}" />
</ResourceDictionary>

The control now supports High contrast themes, and we didn’t even need to change the code behind.

Ignoring local properties

When a developer places our control on a page, he can locally assign brushes to the control’s color settings – after all that’s why we decorated it with properties. It makes sense for the control to ignore these assignments when a High Contrast theme is applied. For that we need to detect whether we’re in High contrast mode or not, and find a way to get notified when the user enables or disables the feature.

This is were the AccessibilitySettings class enters the picture, with a HighContrast property and a HighContrastChanged event. When the app starts, and whenever the event fires, we update the color scheme if necessary. We first create an instance of the class:

private static readonly AccessibilitySettings ThemeListener = new AccessibilitySettings();

And then register an event handler. That handler may force a redraw of the control –to update the colors- so the control’s Visual Tree should be assembled (but not yet displayed) when we register it. That makes the OnApplyTemplate event the best choice to host this code:

ThemeListener.HighContrastChanged += ThemeListener_HighContrastChanged;

Let’s take a look at the code to change the color scheme. When the app starts, we create a cache to remember the local values for the different colors. All the brushes are defined as dependency properties. To detect whether a dependency property is locally overridden (in XAML or C#, but not by a theme dictionary), we can use ReadLocalValue. It returns the local value or the so-called UnSetValue that indicates that no value was assigned:

private SolidColorBrush _needleBrush;
private Brush _trailBrush;
private Brush _scaleBrush;
// More brushes ...
_needleBrush = ReadLocalValue(NeedleBrushProperty) as SolidColorBrush;
_trailBrush = ReadLocalValue(TrailBrushProperty) as SolidColorBrush;
_scaleBrush = ReadLocalValue(ScaleBrushProperty) as SolidColorBrush;
// More assignments ...

When we enter a High Contrast theme, we unbind the local values from the dependency properties so that the entries of the theme dictionary are used. And when we enter a non High Contrast theme, we reassign the local values from the cache to reinstall the original color scheme:

private void OnColorsChanged()
{
    if (ThemeListener.HighContrast)
    {
        // Apply High Contrast Theme.
        ClearBrush(_needleBrush, NeedleBrushProperty);
        ClearBrush(_trailBrush, TrailBrushProperty);
        ClearBrush(_scaleBrush, ScaleBrushProperty);
        // More of these ...
    }
    else
    {
        // Apply User Defined or Default Theme.
        RestoreBrush(_needleBrush, NeedleBrushProperty);
        RestoreBrush(_trailBrush, TrailBrushProperty);
        RestoreBrush(_scaleBrush, ScaleBrushProperty);
        // More of these ...
    }

    // ...
}

To clear and restore the brushes, we use ClearValue and SetValue respectively:

private void ClearBrush(Brush brush, DependencyProperty prop)
{
    if (brush != null)
    {
        ClearValue(prop);
    }
}

private void RestoreBrush(Brush source, DependencyProperty prop)
{
    if (source != null)
    {
        SetValue(prop, source);
    }
}

For a deeper dive into Dependency Properties, check this documentation.

In the small sample app that I wrote, I encapsulated all accessibility related code behind (there’s more to come further in this article) in a separate partial class file:

FileStructure

To get these file under the xaml.cs file in Visual Studio’s explorer, I tweaked the project file:

ProjectFile

That sample app has a page with three radial gauge controls. The one in the middle has a local assignment for some colors:

<controls:RadialGauge Unit="Mississippi"
                      Value="20"
                      NeedleBrush="MediumOrchid"
                      TrailBrush="Indigo"
/>

Here’s how that page looks like in a non High Contrast theme. On the left side of the screen there’s the Settings app; the right side is main page of the sample app:

HighContrast_Off

Here’s the app in High Contrast mode. Observe the limited color palette, and the fact that we successfully ignored the local colors for the middle gauge:

HighContrast

The UI Automation API

Another way to improve the accessibility of a custom UWP control is to provide screen reader support. Narrator lets you use your device to complete common tasks without mouse or touch input. It reads and interacts with elements on the screen, like text and buttons. Of course that only works if you stick to a protocol. That protocol is Microsoft UI Automation, an API in Windows that enables your apps to provide (and consume) programmatic information about its user interface. Providing programmatic access to most UI elements on the desktop enables assistive technology products, such as Narrator and Magnifier, to provide this information to the end users, e.g. by speech.

One of the most important components of Windows Automation is the so-called UI Automation Tree. This is the hierarchical representation of your desktop window, its child elements (open windows) and their UI components (menus, buttons, lists, …). The UI Automation Tree makes a difference between Control elements (interactive) and Content elements, but it makes no difference in technology: it works against all stacks: WinForms, Web, XAML, …

Supporting Narrator

Creating and registering an Automation Peer

An Automation Peer is a class that helps exposing the content of a UI element class to Windows UI Automation. It ensures that the UI Automation Tree does not have to rely on high-level assumptions, so it improves the service of components like Narrator and Magnifier.

To create an automation peer for a class, you just create a descendant from AutomationPeerFrameworkAutomationPeer is a good parent for a XAML control- and specify the described class (the ‘owner’) in the constructor:

public class RadialGaugeAutomationPeer : FrameworkElementAutomationPeer
{
    public RadialGaugeAutomationPeer(RadialGauge owner)
        : base(owner)
    { }

    // ...
}

To activate the automation peer, you also have to override the OnCreateAutomationPeer in the owner class (RadialGauge in our case):

protected override AutomationPeer OnCreateAutomationPeer()
{
    return new RadialGaugeAutomationPeer(this);
}

Basic overrides

There are two methods that you would probably override in every automation peer. The first one is GetChildrenCore(). It returns the list of child elements that you want to expose to UI Automation. In the case of the RadialGauge, we want to expose the control to as a single element and hide its details. So the override returns no children:

protected override IList<AutomationPeer> GetChildrenCore()
{
    return null;
}

The second important automation peer method is GetNameCore() which returns the main description of the component. If you want more than the fully qualified class name, then you should override it. Here’s what we did in the RadialGauge to make it return the class name and unit measure:

protected override string GetNameCore()
{
    var gauge = (RadialGauge)Owner;
    return "radial gauge. " + (string.IsNullOrWhiteSpace(gauge.Unit) ? "no unit specified. " : "unit " + gauge.Unit + ". ");
}

Tip: provide punctuation and pauses (blanks) in the result string to create a natural description. When Narrator reads the screen, it respects all of these.

After the name, Narrator tells the type of control. Custom is the default return value for GetAutomationControlTypeCore(), so this is actually an obsolete override:

protected override AutomationControlType GetAutomationControlTypeCore()
{
    return AutomationControlType.Custom;
}

I just want to point out that the type comes from the AutomationControlType enumeration. So you can not add your own here, but you can still expose a lot more details of your control.

Refining the Automation Peer

To find out more details about your control, Windows Automation will call GetPatternCore() a few times to see if it supports some protocols. One of these protocols is RangeValue which describes the controls that have a Value that is between a Minimum and a Maximum, controls such as … the RadialGauge.

To fulfill the protocol we have to implement the IRangeValueProvider interface:

public class RadialGaugeAutomationPeer :
    FrameworkElementAutomationPeer,
    IRangeValueProvider
{
    // ...
}
public double Value => ((RadialGauge)Owner).Value;
public double Minimum => ((RadialGauge)Owner).Minimum;
public double SmallChange => ((RadialGauge)Owner).StepSize;
public bool IsReadOnly => !((RadialGauge)Owner).IsInteractive;
// ...

Then we have to override GetPatternCore() to return a reference for each protocol that we implemented:

protected override object GetPatternCore(PatternInterface patternInterface)
{
    if (patternInterface == PatternInterface.RangeValue)
    {
        // Expose RangeValue properties.
        return this;
    }

    return base.GetPatternCore(patternInterface);
}

That’s is! Narrator will now say the description of our control, followed by the ValueRange information (Value, Minimum and Maximum). If you activate Narrator (in Settings) and TAB to the different gauges in the sample app, they will be nicely described. Here’s a –silent- screenshot:

Narrator

You can use the Inspect tool from the Windows SDK to discover and visualize all the information that Windows Automation is able to extract from your control:

Inspect_Narrator

Supporting keyboard input

A lot of users don’t use the mouse or a touch screen to interact with their computer, but only the keyboard. So it makes sense for a UWP control to accept keyboard input when it has the focus. The RadialGauge has a handler for the KeyDown event. Its value can be increased and decreased with the right and left arrow keys respectively. The Control key makes the difference between a large change of 5 units and a small change of 1:

private void RadialGauge_KeyDown(object sender, KeyRoutedEventArgs e)
{
    var step = 1;
    var ctrl = Window.Current.CoreWindow.GetKeyState(VirtualKey.Control);
    if (ctrl.HasFlag(CoreVirtualKeyStates.Down))
    {
        step = 5;
    }

    if (e.Key == VirtualKey.Left)
    {
        Value = Math.Max(Minimum, Value - step);
        e.Handled = true;
        return;
    }

    if (e.Key == VirtualKey.Right)
    {
        Value = Math.Min(Maximum, Value + step);
        e.Handled = true;
    }
}

This article described three techniques to improve the accessibility of a UWP Custom Control. If you want to know more on this topic, check this excellent starting page on the DevCenter.  You’ll quickly learn that spending some time and effort on accessibility makes your controls and apps better for everyone.

If you want to play with the code, the small sample app with the accessible RadialGauge lives here on GitHub.

Enjoy!

Advertisements

Drawing shapes with Windows Composition in UWP

This article explains how you can use the Composition API to display and animate geometric shapes in a UWP app. Before the April Windows 10 Update, this API was limited to drawing rectangles that you could fill with a brush or an image. The latest update does more than rectangles: it supports shapes from lines and ellipses to rounded rectangles and even SVG paths. This is good news for developers who want to use more of Windows Composition in their apps, e.g. for drawing controls or for animating content à la Lottie.

In this article I will not dive too much in detail on how to draw and animate Composition sprites in XAML. Please refer to one of my previous blog posts for an introduction to the core concepts. In this article we’ll stick to the new classes that were added to display shapes:

To demonstrate these new classes I built a Clock UserControl that’s made of ellipses and rounded rectangles drawn and animated by Windows Composition. This is how it looks like:
Standard

I didn’t have to start from scratch, I upgraded an existing project that used rectangles and images:
clock_original

Using the new API

The upgraded clock control is a custom user control that has

  • ellipses as background face and hour ticks,
  • rounded rectangles for hour and minute hands, and
  • a simple shape – a triangle- for the seconds hand.

Everything is done with the Composition API, the only XAML is

  • a ViewBox for easy calculations, with
  • an empty Canvas to host the visual:
<Viewbox Stretch="Uniform">
    <Canvas x:Name="Container"
            Background="Transparent"
            Height="200"
            Width="200">
    </Canvas>
</Viewbox>

The ContainerVisual that links the Composition elements to XAML is defined in the Loaded event of the hosting control, since that’s the first event where we have access to the Container canvas. For convenience, I still use an extension method to link the two worlds (maybe the new API has improvements here, which I didn’t notice):

_root = Container.GetVisual();
public static class UIElementExtensions
{
    public static ContainerVisual GetVisual(this UIElement element)
    {
        var hostVisual = ElementCompositionPreview.GetElementVisual(element);
        var root = hostVisual.Compositor.CreateContainerVisual();
        ElementCompositionPreview.SetElementChildVisual(element, root);
        return root;
    }
}

The Compositor is still used as a factory to create the rest of the instances.

Here’s the routine that draws the hour hand as a Win2D geometry and lifts it to the XAML stack – from CompositionRoundedRectangleGeometry to CompositionSpriteShape to ShapeVisual:

var roundedRectangle = _compositor.CreateRoundedRectangleGeometry();
roundedRectangle.Size = new Vector2(6.0f, 63.0f);
roundedRectangle.CornerRadius = new Vector2(3.0f, 3.0f);
_hourhandSpriteShape = _compositor.CreateSpriteShape(roundedRectangle);
_hourhandSpriteShape.FillBrush = _compositor.CreateColorBrush(Colors.DarkSlateGray);
_hourhandSpriteShape.Offset = new Vector2(97.0f, 40.0f);
_hourhandSpriteShape.CenterPoint = new Vector2(3.0f, 60.0f);
handShapeVisual = _compositor.CreateShapeVisual();
handShapeVisual.Size = new Vector2(200.0f, 200.0f);
handShapeVisual.Shapes.Add(_hourhandSpriteShape);
_root.Children.InsertAtTop(handShapeVisual);

The seconds hand is a triangle made out of three lines. Drawing it was a little bit harder than expected. The constructor of CompositionPath requires a IGeometrySource2D instance, which brings you pretty close to the Win2D primitives (and out of my enterprise XAML developer comfort zone). Fortunately I came across this blog post by Daren May where he shares an extension method on CanvasPathBuilder to build a path with lines:

public static class PathBuilderExtensions
{
    public static CanvasPathBuilder BuildPathWithLines(
        this CanvasPathBuilder builder,
        IEnumerable<Vector2> vectors,
        CanvasFigureLoop canvasFigureLoop)
    {
        var first = true;

        foreach (var vector2 in vectors)
        {
            if (first)
            {
                builder.BeginFigure(vector2);
                first = false;
            }
            else
            {
                builder.AddLine(vector2);
            }
        }

        builder.EndFigure(canvasFigureLoop);
        return builder;
    }

    public static CanvasPathBuilder BuildPathWithLines(
        this CanvasPathBuilder builder,
        IEnumerable<(float x, float y)> nodes,
        CanvasFigureLoop canvasFigureLoop)
    {
        var vectors = nodes.Select(n => new Vector2(n.x, n.y));
        return BuildPathWithLines(builder, vectors, canvasFigureLoop);
    }
}

Here’s the routine that draws the seconds hand from three line coordinates into a CompositionSpriteShape:

var canvasPathBuilder = new CanvasPathBuilder(new CanvasDevice());
canvasPathBuilder.BuildPathWithLines(new(float x, float y)[]
    {
        (0, 80),
        (3, 0),
        (6, 80)
    },
    CanvasFigureLoop.Closed);
var canvasGeometry = CanvasGeometry.CreatePath(canvasPathBuilder);
var compositionPath = new CompositionPath(canvasGeometry);
var pathGeometry = _compositor.CreatePathGeometry();
pathGeometry.Path = compositionPath;
_secondhandSpriteShape = _compositor.CreateSpriteShape(pathGeometry);

The –smooth- animations to redraw the hour, minute, and seconds hands act at CompositionObject level –the base class of all visual composition objects-, so there was no need to modify from the original clock here.

The old clock successfully upgraded from straight rectangles to rounded ones, ellipses, and lines. Let’s now get closer to real SVG paths.

Getting some help from CompositionProToolkit

When looking for more advanced extension methods for CanvasPathBuilder, I came across the CompositionProToolkit by Ratish Philip. This is a very rich collection of Win2D based helper classes and controls for Windows Composition. The helper classes allowed me to build a ‘pro’ version of the clock that comes with

  • ellipses for hour ticks and seconds hand,
  • an SVG-ish complex path as background face, and
  • a container shape for the hour and minute hands.

Here’s how that clock looks like:
Pro

Here’s how the seconds hand (just a circular dot) is drawn using one of the many extension methods on CanvasPathBuilder:

var canvasPathBuilder = new CanvasPathBuilder(new CanvasDevice());
canvasPathBuilder.AddCircleFigure(new Vector2(0.0f, 0.0f), 2.0f);
var canvasGeometry = CanvasGeometry.CreatePath(canvasPathBuilder);
var compositionPath = new CompositionPath(canvasGeometry);
var pathGeometry = _compositor.CreatePathGeometry();
pathGeometry.Path = compositionPath;
_secondhandSpriteShape = _compositor.CreateSpriteShape(pathGeometry);
_secondhandSpriteShape.FillBrush = _compositor.CreateColorBrush(Colors.Tomato);
_secondhandSpriteShape.Offset = new Vector2(100f, 5f);
_secondhandSpriteShape.CenterPoint = new Vector2(0.0f, 95.0f);

The soccer ball graphic at the end of the hour and minute hands is drawn from an SVG path. The background clock face uses the exact same code – with a different path of course. CompositionProToolkit comes with its own parser for the path mini language. Here’s how the path is transformed into a sprite:

pathData = "M12.255911,27.32522L8.0630484,27.526361z ... (rest of path omitted)";
var spriteShape = _compositor.CreateSpriteShape(pathData);
spriteShape.FillBrush = _compositor.CreateColorBrush(Colors.DarkSlateGray);
spriteShape.Offset = new Vector2(84.0f, 30f);

If you consider using Win2D or Windows Composition in your UWP apps, you should definitely take a look at CompositionProToolkit!

Using a CompositionContainerShape

This last version of the clock also comes with a nice use case for the new CompositionContainerShape. The hour and minute hands are each a ‘group’ of two sprites: a soccer ball and a line to the center of the clock. Both shapes are combined into one:

_hourContainerShape = _compositor.CreateContainerShape();
_hourContainerShape.CenterPoint = new Vector2(100f, 100f);
// ...
var spriteShape = _compositor.CreateSpriteShape(pathData);
// ...
_hourContainerShape.Shapes.Add(spriteShape);

var line = _compositor.CreateLineGeometry();
// ...
spriteShape = _compositor.CreateSpriteShape(line);
// ...
_hourContainerShape.Shapes.Add(spriteShape);

For the animation, we can now use that container as a target. So we don’t have to animate the individual shapes – very convenient:

_hourContainerShape.RotationAngleInDegrees = (float)now.TimeOfDay.TotalHours * 30;
_minuteContainerShape.RotationAngleInDegrees = now.Minute * 6;

The Sample Project

The sample project lives here on Github.

Enjoy!

Using a TreeView Control for Navigation in UWP

In this article we’ll show how to use a TreeView as the main navigation control in a UWP app. We will stay as close as possible to the Fluent Design principles and other current UI guidelines.

Our first approach was to rely on the NavigationView control, since it comes with a full user experience (navigation, hamburger, page title, secondary commands, responsive and fluent design). Unfortunately it is entirely focused on (read: limited to) a flat list of menu items. So we decided to design and build the entire user experience from scratch.

We admit that we expected to run into issues. None of the native UWP apps that could clearly use treeview based navigation –such as the Mail client and the OneNote app- actually have one. So we were a bit suspicious of the TreeView’s capabilities. Fortunately this was just unfounded fear.

We created a sample app with these main navigation components in a Shell page:

  • a SplitView,
  • a TreeView for primary navigation,
  • a Frame to host the pages on a background with parallax effect,
  • a Hamburger button, and
  • a CommandBar for secondary navigation.

Here’s how that app looks like:
TreeViewNavigation

Building the User Experience

Here’s the main XAML content of the Shell page:

<!-- SplitView -->
<SplitView x:Name="ShellSplitView">
    <SplitView.Pane>
        <Grid x:Name="SplitViewPane">
            <Grid.RowDefinitions>
                <RowDefinition Height="*" />
                <RowDefinition Height="auto" />
            </Grid.RowDefinitions>
            <!-- Navigation Tree -->
            <TreeView x:Name="NavigationTree"
                        ItemInvoked="TreeView_ItemInvoked"
                        Style="{StaticResource TreeViewStyle1}" />
            <!-- Command Bar -->
            <CommandBar>
                <!-- AppBarButtons -->
            </CommandBar>
        </Grid>
    </SplitView.Pane>
    <SplitView.Content>
        <Grid>
            <!-- Does not seem to work. -->
            <ParallaxView Source="{Binding ElementName=NavigationTree}"
                            VerticalShift="100">
                <!-- Background Image -->
            </ParallaxView>

            <!-- Navigation Frame -->
            <Frame x:Name="SplitViewFrame"
                    Navigated="SplitViewFrame_OnNavigated"/>
        </Grid>
    </SplitView.Content>
</SplitView>

<!-- Title Bar -->
<TextBlock x:Name="AppTitle" />

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

SplitView

We used a classic SplitView control to host the main components: the TreeView on the left and a Frame to hold the current page on the right. The SplitView Pane can be opened or closed with a Hamburger button that sits on top of it (z-index wise).

A SplitView supports four display modes for the Pane, but the NavigationView only uses three display modes for the menu:

  • Minimal: panel shows if needed and on top of content,
  • Compact: panel shows a narrow band of icons, and
  • Expanded: panel shows alongside the content.

We didn’t use icons in the TreeView’s ItemTemplate – and it would anyway be impossible to  to show a multi-level tree in a narrow vertical band. So we decided to skip compact mode in the visual states. As a result the SplitView Pane has always the same width (when visible). It will just toggle between Inline and Expanded according to the app’s width.

Following the guidelines for Acrylic, we made the navigation pane background

  • 60% in-app acrylic in overlay mode, and
  • 60% background acrylic in side-by-side mode.

When using acrylic brushes in your app, don’t forget to provide a fallback color. It’s the solid color that is shown when the composition brushes are not used – i.e. when the app loses focus, or when the user settings or the machine hardware prevent it. Here’s a screenshot of the app when it does not have the focus:

Unfocused

Here’s the definition of the corresponding brushes:

<!-- Splitview Pane -->
<AcrylicBrush x:Key="SplitViewInlineBackgroundBrush"
                BackgroundSource="HostBackdrop"
                TintOpacity="0.6"
                FallbackColor="#ffe6ff" />
<AcrylicBrush x:Key="SplitViewOverlayBackgroundBrush"
                BackgroundSource="Backdrop"
                TintOpacity="0.6"
                FallbackColor="#ffe6ff" />

And here’s how they are declaratively applied in the Visual State Manager:

<!-- VisualState to be triggered when window width is >=1007 effective pixels -->
<VisualState x:Name="Expanded">
    <VisualState.StateTriggers>
        <AdaptiveTrigger MinWindowWidth="1007" />
    </VisualState.StateTriggers>
    <VisualState.Setters>
        <!-- SplitView pane shows as overlay -->
        <Setter Target="ShellSplitView.DisplayMode"
                Value="Inline" />
        <Setter Target="ShellSplitView.PaneBackground"
                Value="{StaticResource SplitViewInlineBackgroundBrush}" />
    </VisualState.Setters>
</VisualState>

The Shell page -including the SplitView and its content- is blended into the title area:

// Blends the app into the title bar.
var coreTitleBar = CoreApplication.GetCurrentView().TitleBar;
coreTitleBar.ExtendViewIntoTitleBar = true;
var titleBar = ApplicationView.GetForCurrentView().TitleBar;
titleBar.ButtonBackgroundColor = Colors.Transparent;
titleBar.ButtonInactiveBackgroundColor = Colors.Transparent;
titleBar.ButtonForegroundColor = (Color)Application.Current.Resources["TitlebarButtonForegroundColor"];

For more info on this code and its consequences, check the TitleBar Customization documentation.

TreeView

The TreeView control is the obvious candidate for displaying a hierarchical menu. It is also one of the UWP controls that comes with built-in Reveal highlight effect, a lighting effect that highlights interactive elements, such as command bar buttons, listview items, or treeview nodes when the user moves the pointer near them. Check the bottom left part of the first screenshot in this article for an impression.

We tweaked the TreeViewItem style a little bit to decorate the selected node with a thin vertical bar, just like the NavigationView does (but without an animation). Here’s what we changed :

<!-- Added -->
<Grid x:Name="SelectionGrid"
        Width="4"
        Margin="8"
        HorizontalAlignment="Left" />

<VisualState x:Name="Selected">
    <VisualState.Setters>
        <!-- Changed -->
        <Setter Target="SelectionGrid.Background"
                Value="{ThemeResource TreeViewItemBackgroundSelected}" />
        <Setter Target="ContentPresenter.Foreground"
                Value="{ThemeResource TreeViewItemForegroundSelected}" />
        <Setter Target="ContentPresenterGrid.BorderBrush"
                Value="{ThemeResource TreeViewItemBorderBrushSelected}" />
    </VisualState.Setters>
</VisualState>

As part of the process, we also changed the different accent colors so that we don’t rely on the user’s personal accent color:

<Application.Resources>
    <ResourceDictionary>
        <ResourceDictionary.ThemeDictionaries>
            <!-- AccentColor overrides -->
            <!-- Shades of OrangeRed -->
            <ResourceDictionary x:Key="Default">
                <Color x:Key="SystemAccentColor">#ff4500</Color>
                <Color x:Key="SystemAccentColorLight1">#ff4500</Color>
                <Color x:Key="SystemAccentColorLight2">#ff8f66</Color>
                <Color x:Key="SystemAccentColorLight3">#b33000</Color>
                <Color x:Key="SystemAccentColorDark1">#ff4500</Color>
                <Color x:Key="SystemAccentColorDark2">#ff8f66</Color>
                <Color x:Key="SystemAccentColorDark3">#b33000</Color>
            </ResourceDictionary>
            <!-- ... -->

Here’s how all of this looks like – before and after the template and color changes:
WithoutVisualChanges

WithVisualChanges

Notice that we also moved the whole TreeView control some pixels to the left, to vertically align the chevron icons with the Hamburger button.

Hamburger button

We decided to give the Hamburger button the same size and padding as the system buttons (minimize, maximize, and close) and place it into the title bar in the top left corner. If you place input controls in the title bar, then you should override the default interactive part of the title bar (the part that responds to dragging and double clicking) – otherwise your control will not be reachable.

Here’s the XAML for the top left corner:

<!-- Hamburger Button -->
<Button x:Name="HamburgerButton"
        Margin="0"
        Width="48"
        Height="32"
        <!-- ... -->

<!-- Title Bar -->
<Grid x:Name="AppTitleBar"
        Background="Transparent"
        Height="32"
        VerticalAlignment="Top"
        HorizontalAlignment="Stretch">
    <TextBlock x:Name="AppTitle"
                xmlns:appmodel="using:Windows.ApplicationModel"
                Text="{x:Bind appmodel:Package.Current.DisplayName}"
                Style="{StaticResource CaptionTextBlockStyle}"
                IsHitTestVisible="False"
                Margin="48 8 0 0" />
</Grid>

Here’s how the default title bar zone is overridden:

Window.Current.SetTitleBar(AppTitleBar);

The code behind the Hamburger button is straightforward. It just needs to toggle IsPaneOpen on the SplitView:

/// <summary>
/// Toggles the SplitView pane.
/// </summary>
private void HamburgerButton_Click(object sender, RoutedEventArgs e)
{
    ShellSplitView.IsPaneOpen = !ShellSplitView.IsPaneOpen;
}

Secondary commands

For the secondary commands at the bottom of the navigation panel, we opted for a CommandBar with regular app bar buttons. It’s one of the controls that comes with the Reveal effect built in, and it has the correct size. We considered buttons of the same dimensions as the Hamburger and the system buttons, but this size is to small for buttons that are used often and that have more complex icons.

Navigation

Our focus of the sample app was mainly on the user experience, so we kept the functional part -the navigation itself- as simple as possible. All the related code is placed directly in the Shell page, albeit in a partial class file. In a real-world app you would build a Navigation Service and then find and call it using singletons, dependency injection, service locator, or any other kind of blood magic. For more info, please check my older blog post on SplitView navigation.

To separate at least some of the concerns, we did create a NavigationMenuItem class that allows to define a hierarchic navigation structure (with the destination page type and parameter, and a Children collection):
NavigationMenuItem

Here’s a part of the logical navigation menu in the sample app:

private ObservableCollection<NavigationMenuItem> MainMenu => 
    new ObservableCollection<NavigationMenuItem>
        {
            new NavigationMenuItem
            {
                Text = "Edged Weapons",
                NavigationDestination = typeof(BladesPage),
                Children = new ObservableCollection<MenuItem>
                {
                    new NavigationMenuItem
                    {
                        Text = "Arakh",
                        NavigationDestination = typeof(BladesPage),
                        NavigationParameter = "Arakh"
                    },
                    new NavigationMenuItem
                    {
                        Text = "Dragonglass",
                        NavigationDestination = typeof(BladesPage),
                        NavigationParameter = "Dragonglass"
                    },
                    // ...

The Navigate method uses this information to … well … navigate:

/// <summary>
/// Navigates to the specified source page type.
/// </summary>
public bool Navigate(Type sourcePageType, object parameter = null)
{
    return SplitViewFrame.Navigate(sourcePageType, parameter);
}

When the app starts up, TreeView nodes are created from the menu structure – with a little help from a recursive extension method:

internal static TreeViewNode AsTreeViewNode(this NavigationMenuItem menuItem)
{
    var result = new TreeViewNode
    {
        Content = menuItem
    };

    foreach (NavigationMenuItem subItem in menuItem.Children)
    {
        result.Children.Add(subItem.AsTreeViewNode());
    }

    return result;
}
/// <summary>
/// Populates the TreeView from the Menu.
/// </summary>
private void PopulateTreeView()
{
    // Populate the tree.
    foreach (var item in MainMenu)
    {
        NavigationTree.RootNodes.Add(item.AsTreeViewNode());
    }
}

Each node in the TreeView now has a NavigationMenuItem as its Content, so it knows which page to load and with which parameter. Here’s what happens when a node is clicked:

/// <summary>
/// Navigates to the corresponding treeview selection.
/// </summary>
private void TreeView_ItemInvoked(TreeView sender, TreeViewItemInvokedEventArgs args)
{
    if (args.InvokedItem is TreeViewNode node)
    {
        if (node.Content is NavigationMenuItem menuItem)
        {
            var target = menuItem.NavigationDestination;
            if (target != null)
            {
                Navigate(menuItem.NavigationDestination, menuItem.NavigationParameter);
            }
        }
    }
}

The secondary command buttons call the same method, but without a parameter:

private void SettingsButton_Tapped(object sender, Windows.UI.Xaml.Input.TappedRoutedEventArgs e)
{
    Navigate(typeof(SettingsPage));
}

Just to prove that the parameter from the NavigationMenuItem is correctly passed to the Page, here’s how the different pages in the sample app update their subtitle in the top right corner:

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

    base.OnNavigatedTo(e);
}

The Back Button

The hierarchical menu on the left -together with the secondary commands at the bottom- give the user immediate access to any page in the app. So personally we’re not in favor of providing a back button. But just for the sake of completeness, we also implemented it.

This implies a method to navigate back:

/// <summary>
/// Returns to the previous page.
/// </summary>
public void GoBack()
{
    if (SplitViewFrame.CanGoBack)
    {
        SplitViewFrame.GoBack();
    }
}

And you also need to make the system button visible:

/// <summary>
/// Shows the system back button.
/// </summary>
public void EnableBackButton()
{
    var navManager = SystemNavigationManager.GetForCurrentView();
    navManager.AppViewBackButtonVisibility = AppViewBackButtonVisibility.Visible;
    navManager.BackRequested -= (s, e) => GoBack();
    navManager.BackRequested += (s, e) => GoBack();
}

We placed the Hamburger button in the top left corner. When the system back button (dis)appears, we need to shift that button together with the text block holding the app title:

// Update the title bar when the back button (dis)appears or resizes.
Window.Current.CoreWindow.SizeChanged += (s, e) => UpdateAppTitle();
coreTitleBar.LayoutMetricsChanged += (s, e) => UpdateAppTitle();
/// <summary>
/// Updates the title bar when the back button (dis)appears or resizes.
/// </summary>
private void UpdateAppTitle()
{
    var full = (ApplicationView.GetForCurrentView().IsFullScreenMode);
    var left = (full ? 0 : CoreApplication.GetCurrentView().TitleBar.SystemOverlayLeftInset);
    HamburgerButton.Margin = new Thickness(left, 0, 0, 0);
    AppTitle.Margin = new Thickness(left + HamburgerButton.Width + 12, 8, 0, 0);
}

We also added the code to unselect the current TreeView node when the Back or a secondary command button is pressed. Since we weren’t successful in programmatically selecting and unselecting nodes, we applied the old trick of toggling the SelectionMode:

/// <summary>
/// Makes the TreeView lose its selection when there is no corresponding main menu item.
/// </summary>
/// <remarks>This works, but I don't know why...</remarks>
private void SplitViewFrame_OnNavigated(object sender, NavigationEventArgs e)
{
    NavigationTree.SelectionMode = TreeViewSelectionMode.None;
    NavigationTree.SelectionMode = TreeViewSelectionMode.Single;
}

We assumed it would be necessary to add some extra code to determine whether the navigation was triggered by the TreeView or not, before deciding to unselect the current node. It turns out that unconditionally toggling the selection mode seems to do the job already. But honestly: this feels like a coincidence.

NavigationTreeView, first of his name ?

We just created a great looking fluent design compatible TreeView based navigation UI. The development was easier than we expected, and the results look better than we expected. Some of you may be tempted to componentize all of this into a user experience just like the current NavigationView control. We think that this can be done with not too much extra effort, but personally we prefer the more granular control that we have in the sample app.

That sample app lives here on GitHub.

Enjoy!

Displaying Dynamic SQL Results in a UWP DataGrid

In this article we’ll show how you can display the result of any query against a SQL Server database directly in a UWP DataGrid. The grid control that we use is the new Windows Community Toolkit DataGrid. The companion sample app hosts 3 different TSQL queries (feel free to add your own), but only one instance of the DataGrid. Here’s how it looks like:

Grid_1

We’ll show you how to

  • connect to a SQL Server database from UWP,
  • read a dynamic TSQL statement into a DataTable,
  • modify the content of the DataTable progammatically – if needed,
  • bind the DataTable to a DataGrid, and
  • sort the grid on clicking a header column.

This type of dynamic data binding and visualization is particularly useful in the parts of an app that deal with administration, diagnostics, monitoring, or trouble shooting – where you don’t necessarily know upfront which queries to launch and don’t have the possibility (or resources, or priority) to define a full entity model and/or specialized UI controls.

Get that DataGrid

The DataGrid XAML Control is a port from the popular Silverlight control with the same name. Most -but not all- of the functionality has been migrated. This article does NOT focus on its full feature set, but only on dynamic data binding. Please check the official documentation and the source code of a complete sample app for more info. Its documentation is not yet fully uploaded, so some of the hyperlinks in this article will point to the (2011!) Silverlight version.

The DataGrid Control is distributed as a NuGet package within Windows Community Toolkit, currently as a prerelease :

Grid_NuGet

Here’s how the data grid is defined in the sample app. With no columns specified and AutoGenerateColumns to false it is ready for dynamic data binding:

<controls:DataGrid x:Name="ResultsGrid"
                    RowDetailsTemplate="{StaticResource DetailsTemplate}"
                    RowDetailsVisibilityMode="VisibleWhenSelected"
                    CanUserSortColumns="True"
                    Sorting="ResultsGrid_Sorting"
                    BorderThickness="2"
                    BorderBrush="DarkSlateGray"
                    AlternatingRowBackground="BlanchedAlmond"
                    GridLinesVisibility="All"
                    AutoGenerateColumns="False"
                    SelectionMode="Single" />

So let’s create some tabular data.

Populating a DataTable

DataTable is one of the core classes of the ADO.NET library. It represents an in-memory, database-agnostic structure of rows and strongly typed columns. An easy way to populate a DataTable is calling Fill() against a SqlDataAdapter that executes a SqlCommand on a SqlConnection. Here’s how this looks like in the sample app:

using (var connection = new SqlConnection(connectionString))
{
    await connection.OpenAsync();
    var command = connection.CreateCommand();
    command.CommandText = query;
    dataTable = new DataTable();

    using (var dataAdapter = new SqlDataAdapter(command))
    {
        dataAdapter.Fill(dataTable);
    }
}

Defining the DataGrid structure

In WPF, it would suffice to set AutoGenerateColumns to true and then run the following to populate the DataGrid from the DataTable:

ResultsGrid.ItemsSource = dataTable;

[Actually just data binding would even do the trick.]

The Silverlight -and hence UWP- version of the control doesn’t allow this, so there’s a tiny bit more programming required.

We’ll create a list of DataGridTextColumn instances -one for each column in the DataTable- and assign their Header text and and index Binding:

for (int i = 0; i < table.Columns.Count; i++)
{
        grid.Columns.Add(new DataGridTextColumn()
        {
            Header = table.Columns[i].ColumnName,
            Binding = new Binding { Path = new PropertyPath("[" + i.ToString() + "]") }
        });
}

If you would want to rename columns here, or skip some, or add some new ones, then this would be the place to do these changes.

Populating the DataGrid

The DataGrid is now ready to accept content. This content comes from an ObservableCollection that we populate with the ItemArray of each row in the DataTable. The collection is then set as ItemsSource of the DataGrid:

var collection = new ObservableCollection<object>();
foreach (DataRow row in table.Rows)
{
    collection.Add(row.ItemArray);
}

grid.ItemsSource = collection;

Here’s another view on the result. Same grid, same code, different query:

Grid_2

Tweaking the content

In the sample app, we manipulated the content of one of the columns. Some of the queries contain the text of a TSQL query, a long string that may contain visual formatting with tabs, carriage returns and series of blanks. That’s good to display in the details template – which is what we do. For a regular column it makes more sense to truncate the content, and get rid of the carriage returns.

Fortunately it’s easy to iterate through the rows of a DataTable and update one or more of its columns). Here’s how we remove obsolete white space from a string (using a Split() with StringSplitOptions.RemoveEmptyEntries), and truncate the SQL statements:

if (table.Columns.Contains("SQL Statement"))
{
    var column = table.Columns["SQL Statement"];

    foreach (DataRow row in table.Rows)
    {
        string sqlStatement = ((row[column] as string) ?? string.Empty).Trim();
        row[column] = string.Join(' ', sqlStatement.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries)).Substring(0, 80) + "...";
    }

    table.AcceptChanges();
}

The call to AcceptChanges() is not strictly necessary but we do it to save memory. The call locally commits the changes inside the DataTable and clears the –potentially bloated- row versioning information.

Here’s how the sample app looks like for a query that has a “SQL Statement” column. The manipulated value goes to a regular column and the full text appears in the details of the selected row:

Grid_3

Sorting

Last but not least, we implemented the canonical single-column-sorting-on-header-click behavior. This is only enabled when CanUserSort on the column or CanUserSortColumns on the DataGrid is set to true. In the current release, the DataGrid only covers the visual part: it displays an arrow in the column header according the current SortDirection. We have to implement the sort ourselves in a handler hooked to the Sorting event that exposes the column and the sort direction. We set that sort direction for the clicked column and reset I for the others:

var currentSortDirection = e.Column.SortDirection;

foreach (var column in ResultsGrid.Columns)
{
    column.SortDirection = null;
}

var sortOrder = "ASC";

if ((currentSortDirection == null || currentSortDirection == DataGridSortDirection.Descending))
{
    e.Column.SortDirection = DataGridSortDirection.Ascending;
}
else
{
    sortOrder = "DESC";
    e.Column.SortDirection = DataGridSortDirection.Descending;
}

Then we sort the content itself. We assign the Sort property of the DataTable’s DefaultView, transform the resulting DataView back to a DataTable, and update the binding:

var dataView = dataTable.DefaultView;
dataView.Sort = e.Column.Header + " " + sortOrder;
dataTable = dataView.ToTable();

RefreshContents(dataTable, ResultsGrid);

Code

The sample app lives here on GitHub. When playing with it, don’t forget to bring your own connection string to MainPage.xaml.cs.

Enjoy!

How to build a SQL Connection Dialog in UWP

This article explains how to build a dialog in UWP that sets up a connection from your app to any SQL Server database on your network or in the cloud. Since the Windows Fall Creators Update we can -finally- directly connect to SQL Server from UWP. In that SDK .net core was extended with a large portion of the object model of ye olde ADO.NET, hosted in the System.Data.SqlClient namespace. In this article we’ll use some of these to build a connection dialog to SQL Server, with the following features:

  • You can type a server name or select a recently use one.
  • You can select the security mode.
  • You can enter your credentials when SQL Authentication is used.
  • You can select the database from a list.
  • You have direct access to the connection string.
  • You can test the connection.

When the dialog closes, it will serve you with a validated connection string (unless you cancelled the operation).

Here’s how the dialog looks like in its default mode, with different input fields:

SqlDialogDefaultInput

And here’s how it looks like in direct access mode, where you can edit the connection string:

SqlDialogDirectInput

Basic structure

The SQL Connection Dialog is based on a ContentDialog. Under the title (which has a nice SVG Icon – thanks to TheNounProject) there’s a ProgressBar from which we toggle the IsIndeterminate property to indicate whether the control is busy – i.e. establishing a connection or fetching the database names. The first row in the input form is a custom editable combobox for the server name. Then come some regular input controls –ComboBox, TextBox, PasswordBox– for the security parameters. At the bottom of the input zone there’s a ComboBox that hosts the names of the databases. All text fields on the input form have their IsSpellCheckEnabled set to false – we assume you don’t want red squigglies under server names or user accounts.

All input fields are bound to properties of a SqlConnectionBuilder. As an example, here’s the code for the UserId field:

private SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
private string userId;

/// <summary>
/// Gets or sets the SQL user identifier.
/// </summary>
public string UserId
{
    get { return userId; }
    set
    {
        userId = value;
        builder.UserID = userId;
        OnPropertyChanged();
    }
}

And its corresponding XAML:

<TextBox Text="{x:Bind UserId, Mode=TwoWay}"
         IsEnabled="{x:Bind SqlSecurity, Mode=OneWay}" />

The ellipsis button in the top left corner sets the control in direct input mode, showing a large multi-row textbox to manipulate the connection string.

A ContentDialog can have maximum three buttons at the bottom, and we’re using all of these. The PrimaryButton allows to test the current connectionstring, the SecondaryButton returns a validated connectstring to the caller and closes the dialog, and the CloseButton … well … closes.

An Editable ComboBox

The name of the server to connect to can be chosen from a dropdown with recently used successful connections (stored in LocalSettings) or it can be typed in manually. Now UWP does not come with an editable ComboBox, so we needed to simulate one through an AutoSuggestBoxToggleButton combination. The toggle button opens the AutoSuggestBox’s dropdown. Here’s the XAML:

<AutoSuggestBox x:Name="SuggestBox"
                Style="{StaticResource AutoSuggestBoxNoSpellCheckingStyle}"
                Text="{x:Bind Server, Mode=TwoWay}"
                ItemsSource="{x:Bind MostRecentConnections}"
                VerticalAlignment="Stretch"
                IsSuggestionListOpen="{Binding IsChecked, ElementName=Toggle, Mode=TwoWay}"
                FontFamily="Segoe UI" />
<ToggleButton x:Name="Toggle"
                Grid.Column="1"
                VerticalAlignment="Stretch"
                Width="32"
                Background="Transparent"
                FontFamily="Segoe UI">
    <FontIcon Foreground="{ThemeResource ComboBoxDropDownGlyphForeground}"
                FontSize="12"
                FontFamily="Segoe MDL2 Assets"
                Glyph=""
                IsHitTestVisible="False" />
</ToggleButton>

A few classes from .NET SQL Server ecosystem were not ported to .net core. One of these is SqlDataSourceEnumerator, which allows to lookup SQL Server instances on the local network. So we can’t do this on UWP.

Maintaining the size

The UWP ContentDialog is quite stubborn when it comes to setting and resetting its size. It prefers to decide its height and width depending on its children and its host, and tends to ignore hard-coding values. We decided not to fight the dialog, and let it define its own size – based on the default look with the multiple input fields. But we don’t want the dialog to shrink when switching to direct input mode (with only one textbox). All the content is always in place so the dialog never feels the need to resize. We’re just playing with Opacity and Z-Order when switching the mode. A Grid has no ZIndex attached property for its content, so we reverse the Children collection instead. Here’s what happens when we switch from default mode to direct mode:

DefaultGrid.Opacity = 1;
DirectGrid.Opacity = 0;
Host.Children.Remove(DirectGrid);
Host.Children.Insert(0, DirectGrid);

Of course there’s similar code the other way around. It keeps the dialog the same size, and it freezes the mode switch button at its position, making it easy to rapidly check the connection string and come back.

Testing the connection

For testing the connection, we create a SqlConnection instance from the current connection string, and try to Open it:

using (SqlConnection con = new SqlConnection(builder.ConnectionString))
{
    await con.OpenAsync();
}

We’re updating the UI during the call (remember there’s a ProgressBar), so it’s a good idea to use OpenAsync here. The Using statement will make sure that we close the connection automatically.

When the connection is successful, we’ll show it to the user, with a MessageDialog:

SqlDialogTest

And when the connection is not successful, we display the error:

SqlDialogTestFail

Fetching the database list

The list of database names on a SQL Server Instance can only be fetched through a successful connection to the server. Knowing that every unsuccessful connection attempt waists at least a few seconds, we don’t want to try to connect on every property change. The list of databases is fetched in the combo box’s own DropDownOpened event:

<ComboBox x:Name="DatabaseComboBox"
          DropDownOpened="DatabaseComboBox_DropDownOpened"
          ItemsSource="{x:Bind Databases, Mode=OneWay}"
          SelectionChanged="Database_SelectionChanged" />

Here’s the code. We create a new SqlCommand on the open connection, execute it, and loop though the result with a SqlDataReader:

using (var connection = new SqlConnection(builder.ConnectionString))
{
    await connection.OpenAsync();

    using (var command = connection.CreateCommand())
    {
        command.CommandText = "SELECT [name] FROM sys.databases ORDER BY [name]";

        using (SqlDataReader reader = await command.ExecuteReaderAsync())
        {
            while (await reader.ReadAsync())
            {
                databases.Add(reader.GetString(0));
            }
        }
    }
}

Again, we used the asynchronous versions of the operations (ExecureReaderAsync and ReadAsync) to keep the UI responsive.

Achievement unlocked: connected

When the user clicks the Connect button, we validate the current connection string in just the same way as for the Test button. If the connection is successful, we store the current server name of top of the most recent servers – the list that appears in the editable combobox on top. Then we assign the dialog’s ConnectionString property for the caller to grab. As mentioned, the SqlConnectionDialog does not return a connection, but a validated connection string:

try
{
    using (SqlConnection con = new SqlConnection(builder.ConnectionString))
    {
        await con.OpenAsync();
    }

    MostRecentConnection = Server;
    ConnectionString = builder.ConnectionString;
}

How to use it

Here’s how a client app uses the dialog. It opens the dialog, waits for the result, checks whether the user cancelled or not, and finally grabs the connection string:

var dialog = new ConnectionDialog();
var result = await dialog.ShowAsync();

// User Cancelled
if (result == ContentDialogResult.None)
{
    return;
}

var connectionString = dialog.ConnectionString;

The sample client app uses the provided connection string to fetch the list of tables from the select database with their number of rows:

SqlDialogResult

The sample app with the SqlConnectionDialog lives here on GitHub.

Enjoy!

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!

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!