Category Archives: UWP

Data validation in UWP

In this article we describe how to implement validation of an object –typically an Entity, a Model, or a ViewModel instance- in a UWP app. We’ll use the Template10 Validation core classes for this.

The validation of an instance includes:

  • validating individual fields,
  • comparing two (or more) fields to each other,
  • comparing a newly assigned value to its original value,
  • verifying whether one or more fields have changed, and
  • undoing all changes to the instance.

I built a small sample app to illustrate all of this. Here’s how the main page looks like: on the left there’s a list of company cars. If one of these is selected, its details appear in the middle and we show an Edit button in the top right corner:

List

When the Edit button is clicked, an edit form pops open inside a content dialog. All fields of the ViewModel are editable. At the bottom of the dialog there are some switches that indicate the state of the instance (whether it was changed in the dialog, and whether it is valid) and a button to reset all fields to their original value.

If the object breaks any of the validation rules, then a list of errors appears at the bottom. As long as there are validation errors, the Save button is disabled:

Screenshot

I think this is a quite common use case, especially in enterprise environments. Here’s how I built it.

Template 10 Validation

The Template 10 Validation project by Jerry Nixon covers all of these scenarios with Property<T> and ValidationModelBase as its core classes. I copied these classes with their helpers and interfaces into my own project (under Services/Validation):

classes

I know it would have been easier to just reference the Template 10 Validation NuGet package. But I initially wanted to identify the smallest possible set of classes that cover my requirements and then make an old school Portable Class Library or a more modern .NET Standard Library from it. Unfortunately that was too ambitious. The current implementation relies on IObservableMap<K,V> from the Windows.Foundations.Collection namespace which –as its name implies- is not platform independent. If ObservableDictionary<K,V> could be rewritten without members from this namespace, then I think it would be possible to create a .NET Standard version of the code, that could then be used in environments such as Xamarin…

I kept all the code as is, except for the JsonIgnore and NotMapped attributes on some properties. These were removed to further reduce dependencies. Here’s an overview of the seven classes and interfaces that make up the Validation service:

ValidationClassDiagram

The core class is ValidatableModelBase, which holds the default implementation for an editable data object that supports validation:

  • an implementation of INotifyPropertyChanged,
  • an internal collection of Property<T> instances that store the new and original values of all its properties,
  • a Validator Action<T> delegate to hold your validation logic,
  • a Validate method to trigger the validation,
  • an Errors collection to hold the validation messages,
  • IsValid and isDirty properties to inspect the state, and
  • a Revert method to undo all changes.

Let’s see how this works in practice.

A simple Model

My UWP sample app contains the following class to hold Company Car information. Instances of this Model class are bound to the list at the left, and to the read-only details. The Model class uses the change propagation that comes with my own compact MVVM library and I will stick to this:

public class CompanyCar : BindableBase
{
    private string _brand;
    private string _type;
    private string _body;
    // ...

    public string Brand
    {
        get { return _brand; }
        set { SetProperty(ref _brand, value); }
    }

    public string Type
    {
        get { return _type; }
        set { SetProperty(ref _type, value); }
    }

    public string Body
    {
        get
        {
            return _body;
        }
        set
        {
            SetProperty(ref _body, value);
            OnPropertyChanged(nameof(BodyIcon));
        }
    }

    // ...
}

An editable and validatable ViewModel

To create the corresponding ViewModel class that will be bound to the edit dialog, you must plug ValidatableModelBase somewhere in its inheritance chain and define the properties in such a way that they are automatically hooked to the underlying Property<T> instances:

public class CompanyCarViewModel : ValidatableModelBase
{
    public string Brand { get { return Read<string>(); } set { Write(value); } }

    public DateTime ProductionDate { get { return Read<DateTime>(); } set { Write(value); } }

    public double Mileage { get { return Read<double>(); } set { Write(value); } }

    // ...
}

Of course you also need to provide the validation logic. The validation delegate typically iterates through a set of rules. Any validation rule that is not met by the instance, adds a message in the Errors collection of the corresponding property. Here are some validation rules from the sample app:

  • Brand and Type are mandatory
  • Date of first use should come after date of production
  • Mileage may not decrease

Validations on a single field can be done in many ways with many frameworks, most of which rely on data annotation through validation attributes on the properties. Comparing two fields in a validation rule is a lot more complex (but not impossible, there’s an example of using Prism for this right here). I am not aware of a library that allows to compare the changed value of a field to its original value, except for the bloated self-tracking entities. Nevertheless this is a requirement that I often encounter (typically with counters and dates). I believe that the Template 10 Validation classes provide an excellent service in an elegant way and with minimal impact on your app.

Here’s the code for the validation rules in the sample app:

private void Validation_Executed(CompanyCarViewModel c)
{
    if (string.IsNullOrEmpty(c.Brand))
    {
        c.Properties[nameof(c.Brand)].Errors.Add("Brand is mandatory.");
    }

    if (string.IsNullOrEmpty(c.Type))
    {
        c.Properties[nameof(c.Type)].Errors.Add("Type is mandatory.");
    }

    // Compare two properties.
    if (c.FirstUseDate < c.ProductionDate)
    {
        // Unfortunately errors have to be assigned to a property.
        c.Properties[nameof(c.FirstUseDate)].Errors.Add("Date of first use should come after date of production.");
    }

    // Compare with original value.
    if (c.Mileage < (c.Properties[nameof(c.Mileage)] as Property<double>).OriginalValue)
    {
        c.Properties[nameof(c.Mileage)].Errors.Add("Turning back the mileage is illegal.");
    }
}

The ViewModel is ready now, so let’s provide edit functionality in its most common form: as a modal edit dialog.

The Edit Dialog

According to the dialogs and flyouts recommendations, a ContentDialog would be the ideal control for this in UWP. If you’re planning to use content dialogs for more than just messages and confirmation, then you would want to override some of the size restrictions in the default style. Here’s how to set the maximum width from the default of 548 pixels (which is way to small for an edit form) to 800, in app.xaml:

<x:Double x:Key="ContentDialogMaxWidth">800</x:Double>

In editable fields, don’t forget to set the BindingMode to TwoWay (it’s not the default):

<TextBox Header="Brand" Text="{Binding Brand, Mode=TwoWay}" />

Here’s the XAML for the status bar at the bottom, displaying the validation errors:

<ItemsControl ItemsSource="{Binding Errors, Mode=OneWay}">
    <ItemsControl.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding}" />
        </DataTemplate>
    </ItemsControl.ItemTemplate>
</ItemsControl>

And here’s how the entire edit form looks like in action:

Screenshot

When the Edit button is clicked, you need to create the ViewModel from the currently selected Model. You can use any kind of Mapper for this, or conversion operators, or you can do it in a constructor like this:

public CompanyCarViewModel(Models.CompanyCar model)
{
    this.Brand = model.Brand;
    this.Type = model.Type;
    this.Body = model.Body;
    this.ProductionDate = model.ProductionDate;
    this.FirstUseDate = model.FirstUseDate;
    this.PowerUnit = model.PowerUnit;
    this.Emission = model.Emission;
    this.Mileage = model.Mileage;
    this.Driver = model.Driver;
    this.Validator = that => { Validation_Executed(that as CompanyCarViewModel); };

    // Not needed: ValidatableModelBase constructor does this.
    // this.PropertyChanged += (o, e) => Val(o as CompanyCar);
}

Of course you also need to provide the code to update the Model with the –validated- data when the Save button was pressed:

internal void Update(Models.CompanyCar model)
{
    model.Brand = this.Brand;
    model.Type = this.Type;
    model.Body = this.Body;
    model.ProductionDate = this.ProductionDate;
    model.FirstUseDate = this.FirstUseDate;
    model.PowerUnit = this.PowerUnit;
    model.Emission = this.Emission;
    model.Mileage = this.Mileage;
    model.Driver = this.Driver;
}

Here’s the code to open the dialog and to make sure that the primary button is disabled as long as there are validation errors:

// Prepare ViewModel.
var companyCarViewModel = new CompanyCarViewModel(ViewModel.SelectedCompanyCar);
companyCarViewModel.PropertyChanged += (obj, ev) => EditDialog.IsPrimaryButtonEnabled = companyCarViewModel.IsValid;
companyCarViewModel.Validate();
EditDialog.DataContext = companyCarViewModel;

// Process Dialog.
var result = await EditDialog.ShowAsync();

Here’s how the Model is updated when the dialog is closed through the primary button:

var result = await EditDialog.ShowAsync();
if (result == ContentDialogResult.Primary)
{
    // Update model.
    companyCarViewModel.Update(ViewModel.SelectedCompanyCar);
}

Here’s how to reset the ViewModel to its original values:

private void ResetButton_Click(object sender, Windows.UI.Xaml.RoutedEventArgs e)
{
    (EditDialog.DataContext as CompanyCarViewModel).Revert();
}

Wait, there’s more

Validation checks are not restricted to edit forms. You could for example validate all the (view)models in the UI, to show your end users which objects need some attention. Just bind the IsValid property to an element in the data template, like in this example from the Template10 Validation sample app:

Template10ValidationMain

That sample project also shows how to decorate the controls in the edit form with error indicators, like this:

Template10Validation

This requires some extra behaviors and control templates from the NuGet package. I don’t have these in my own sample app, because I was looking for reusable logic, not UI.

The code

My own sample app lives here on GitHub.

Enjoy!

Advertisements

An Adaptive Menu Bar for UWP

This article demonstrates how to build an adaptive page header for UWP apps. The header contains a title, a horizontal tab-like menu bar, and an optional logo. On a wide screen all of these elements are positioned next to each other. When the screen narrows, the sizes of the title and the menu are fluidly scaled down. When the screen becomes too narrow, the tab control moves underneath the title in a fluid animation. Warning: there’s no rocket science involved, just some restyling and composition black magic.

Main menu and navigation

There is a consensus that he main navigation UI in a UWP app should be vertical menu on the left. Some time ago I wrote a blog post on how to build such navigation based on the SplitView control. Windows 10 Fall Creators Update introduces a new control for this: the NavigationView. It brings all of the top level navigation look-and-feel (menu, hamburger button, link to Settings, navigation service) in one control. For a good example on how to use it, create a Windows Template Studio Navigation Pane project and look at its source code.

In the sample project that I built for this article, I have reused the main menu UI and the Navigation service from the mentioned blog post: the so-called Shell page has a main menu on the left and a Frame that hosts the active user page on the right.

Secondary navigation

For commanding and secondary navigation UWP apps generally use horizontal menus or command bars. Some candidates for this are controls such as the different app bars, the UWP Toolkit Menu (which also supports vertical orientation), a future Ribbon that was promised in a recent Windows Community Standup and a lot of other controls that you may find in the field.

I decided to brew my own control: a light-weight horizontal menu that looks like the familiar Tab control. I put it together with the page title and an optional logo in a UserControl to be used as page header. The same header will appear on top of each content page that belongs to the same top level menu item. I did not introduce another Frame control and stayed close to the Pane-Header-Content paradigm of the already mentioned NavigationView.

Sample app

I built a small sample app with 12 content pages, unevenly spread over two main menu items. Here’s how the page header looks like with a title, 7 menu items (that’s what I target as a maximum) and no fixed logo:

AnimalsPage

Here’s a page with its title, a 5-items tab and a fixed logo at the right – that’s the default configuration in most of the apps that I’m currently building:

OthersPage
 

Building a lightweight tab control

The Tab control is nothing more than a styled ListView: a horizontal list of items, of which one can be selected:

<ListView x:Name="Menu"
            SelectionChanged="Menu_OnSelectionChanged"
            Style="{StaticResource MenuListViewStyle}"
            ItemContainerStyle="{StaticResource MenuListViewItemStyle}"
            ItemTemplate="{StaticResource MenuItemTemplate}"
            HorizontalAlignment="Left"
            Margin="20 10 10 0" />

In its custom Style we visually attach the tabs (items) to the content below by aligning the WrapGrid in the ItemsPanelTemplate to the bottom.

<Style x:Key="MenuListViewStyle"
        TargetType="ListView">
    <Setter Property="ItemsPanel">
        <Setter.Value>
            <ItemsPanelTemplate>
                <WrapGrid Orientation="Horizontal"
                            HorizontalAlignment="Right"
                            VerticalAlignment="Bottom" />
            </ItemsPanelTemplate>
        </Setter.Value>
    </Setter>
</Style>

Through the custom ItemContainerStyle we ensure that background colors of selected and non-selected tabs correspond to the background colors of header and content.

<Style x:Key="MenuListViewItemStyle"
        TargetType="ListViewItem">
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="ListViewItem">
                <ListViewItemPresenter SelectedBackground="{StaticResource PageBackgroundBrush}"
                                        SelectedPointerOverBackground="{StaticResource TenPercentLighterBrush}"
                                        PointerOverBackground="{StaticResource TenPercentDarkerBrush}"
                                        ContentTransitions="{TemplateBinding ContentTransitions}"
                                        HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"
                                        VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
                                        ContentMargin="{TemplateBinding Padding}" />
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Finally the ItemTemplate makes the items look like menu buttons, with an SVG icon (of any size, unlike the standard AppBarButton) and a text:

<DataTemplate x:Key="MenuItemTemplate">
    <StackPanel Orientation="Vertical"
                Height="72"
                Width="80"
                Padding="4 4 4 0">
        <Border Background="Transparent"
                ToolTipService.ToolTip="{Binding Text}">
            <Path x:Name="Glyph"
                    Data="{Binding Glyph}"
                    VerticalAlignment="Center"
                    HorizontalAlignment="Center"
                    Height="40"
                    Width="40"
                    Fill="{StaticResource PageForegroundBrush}"
                    Stretch="Uniform" />
        </Border>
        <TextBlock Text="{Binding Text}"
                    Margin="0 4 0 0"
                    Foreground="{StaticResource PageForegroundBrush}"
                    VerticalAlignment="Center"
                    HorizontalAlignment="Center" />
    </StackPanel>
</DataTemplate>

This results in a clean UI that looks more or less like the familiar Tab control, but only works for a limited (<8) number of menu items. It you want more options in the same space, then I would suggest to restyle an instance of the UWP Toolkit Carousel to make a ‘rolling tab control’.

Let’s dive into the behavior. When you click a menu item, the selection changed event handler calls the navigation service in the exact same way as in the left hand main menu:

private void Menu_OnSelectionChanged(
	object sender, 
	SelectionChangedEventArgs e)
{
    if (e.AddedItems.First() is MenuItem menuItem 
	&& menuItem.IsNavigation)
    {
        Navigation.Navigate(menuItem.NavigationDestination);
    }
}

You navigate within the content frame to a new content page. That page contains the same page header (or another!). On the menu in the page header, the appropriate tab is selected:

/// <summary>
/// Highlights the (first) menu item that corresponds to the page.
/// </summary>
/// <param name="pageType">Type of the page.</param>
public void SetTab(Type pageType)
{
    // Lookup destination type in menu(s)
    var item = (from i in Menu.Items
                where (i as MenuItem).NavigationDestination == pageType
                select i).FirstOrDefault();
    if (item != null)
    {
        Menu.SelectedItem = item;
    }
    else
    {
        Menu.SelectedIndex = -1;
    }
}

Here’s the Tab Control in action:

TabNavigation

 

Making it Adaptive and Fluid

Initially, the title and the tab control each get half of the width of the page (minus the logo). This positions the first tab of the menu always at the same place, which gives a nice consistent UI. For a reasonable title and a submenu with a reasonable number of items, half the screen width should suffice. To deal with less reasonable content, each control is wrapped in a ViewBox that will stretch (only) down if needed.

<!-- Title -->
<GridViewItem VerticalAlignment="Stretch"
                VerticalContentAlignment="Center"
                HorizontalAlignment="Stretch"
                HorizontalContentAlignment="Left">
    <Viewbox x:Name="Title"
                Stretch="Uniform"
                StretchDirection="DownOnly"
                HorizontalAlignment="Left"
                VerticalAlignment="Center">
        <TextBlock Foreground="{StaticResource PageForegroundBrush}"
                    FontSize="48"
                    FontWeight="Light"
                    VerticalAlignment="Top"
                    HorizontalAlignment="Left"
                    Margin="48 8 0 0">
            <Run Text="Others" />
        </TextBlock>
    </Viewbox>
</GridViewItem>

<!-- Navigation -->
<GridViewItem HorizontalAlignment="Stretch"
                HorizontalContentAlignment="Stretch"
                VerticalAlignment="Stretch"
                VerticalContentAlignment="Bottom"
                Margin="0"
                Padding="0">
    <Viewbox x:Name="MenuBar"
                Stretch="Uniform"
                StretchDirection="DownOnly"
                HorizontalAlignment="Right"
                VerticalAlignment="Bottom"
                Margin="0">
        <ListView x:Name="Menu"
                    SelectionChanged="Menu_OnSelectionChanged"
                    Style="{StaticResource MenuListViewStyle}"
                    ItemContainerStyle="{StaticResource MenuListViewItemStyle}"
                    ItemTemplate="{StaticResource MenuItemTemplate}"
                    HorizontalAlignment="Left"
                    Margin="20 10 10 0" />
    </Viewbox>
</GridViewItem>

When the screen becomes too narrow, the elements are placed underneath each other. Most implementations for this scenario rely on a Visual State Trigger that changes the Orientation of a StackPanel. Unfortunately a StackPanel is not good in stretching its children, and I’m not sure whether its orientation change can be animated (Maybe it can, I just didn’t try it out). Instead we decided to place the title and menu as GridViewItems in a GridView with a WrapGrid as ItemsPanelTemplate. You can hook implicit animations to these items when their offset changes – more details in this blog post. The stretching and positioning of the GridView’s items are controlled by aligning the ItemWidth of the inner WrapGrid to theGridView’s own ActualWidth. I decided to use a SizeChanged event handler for this, but this might also be done through an element binding.

private void GridView_SizeChanged(object sender, SizeChangedEventArgs e)
{
    if (_itemsPanel == null)
    {
        return;
    }

    // Only react to change in Width.
    if (e.NewSize.Width != e.PreviousSize.Width)
    {
        AdjustItemTemplate();
    }
}

private void ItemsPanel_Loaded(object sender, RoutedEventArgs e)
{
    // Avoid walking the Visual Tree on each Size change.
    _itemsPanel = sender as WrapGrid;

    // Initialize item template.
    AdjustItemTemplate();
}

private void AdjustItemTemplate()
{
    if (ActualWidth > 800)
    {
        // Two rows.
        _itemsPanel.ItemWidth = ActualWidth / 2;
        _itemsPanel.MinWidth = ActualWidth;
        MenuBar.Margin = new Thickness(0, 0, 64, 0);
        Title.Margin = new Thickness(0);
    }
    else
    {
        // One row.
        _itemsPanel.ItemWidth = ActualWidth;
        _itemsPanel.Width = ActualWidth;
        MenuBar.Margin = new Thickness(0);
        Title.Margin = new Thickness(0, 0, 64, 0);
    }
}

By using a GridView to host the UI elements, I was able to reuse the animation from a previous blog post. [Well, almost: I removed the rotation, because you don’t want the tab control to look like a prancing pony when the screen resizes.]  Using the Composition API, we define an ImplicitAnimationCollection for the Offset, and apply it to the Visual for each of the GridView’s items:

        public static void RegisterImplicitAnimations(this ItemsControl itemsControl)
        {
            var compositor = ElementCompositionPreview.GetElementVisual(itemsControl as UIElement).Compositor;

            // Create ImplicitAnimations Collection. 
            var elementImplicitAnimation = compositor.CreateImplicitAnimationCollection();

            // Define trigger and animation that should play when the trigger is triggered. 
            elementImplicitAnimation["Offset"] = CreateOffsetAnimation(compositor);

            foreach (SelectorItem item in itemsControl.Items)
            {
                var elementVisual = ElementCompositionPreview.GetElementVisual(item);
                elementVisual.ImplicitAnimations = elementImplicitAnimation;
            }
        }

        private static CompositionAnimationGroup CreateOffsetAnimation(Compositor compositor)
        {
            // Define Offset Animation for the Animation group
            Vector3KeyFrameAnimation offsetAnimation = compositor.CreateVector3KeyFrameAnimation();
            offsetAnimation.InsertExpressionKeyFrame(1.0f, "this.FinalValue");
            offsetAnimation.Duration = TimeSpan.FromSeconds(.2);

            // Define Animation Target for this animation to animate using definition. 
            offsetAnimation.Target = "Offset";

            // Add Animation to Animation group. 
            CompositionAnimationGroup animationGroup = compositor.CreateAnimationGroup();
            animationGroup.Add(offsetAnimation);

            return animationGroup;
        }

The Menu’s constructor declares the default menu items (the hosting page can override this, if needed) and registers the animations:

public OthersMenu()
{
    this.InitializeComponent();

    // Populate Menu.
    Menu.Items.Add(new MenuItem() {
        Glyph = Icon.GetIcon("AquariusIcon"),
        Text = "Aquarius",
        NavigationDestination = typeof(AquariusPage) });
    // More menu items ...

    // Animate Menu.
    GridView.RegisterImplicitAnimations();
}

That’s it! The content page should only host the user control in it’s XAML and does not need any code behind.

Here’s what all of this looks like in action:

MenuAnimation

The sample project lives here on GitHub.

Enjoy!

An elementary Dialog Service for UWP

In this article we present a Service that provides the elementary Modal Views in a UWP application. The dialogs and flyouts guidance learns us that dialogs are UI overlays that provide contextual app information and block interaction with the app window until they are explicitly dismissed. That makes them different from Flyouts, Tooltips or Toast Notifications, which are easily dismissed or even close automatically. Dialogs are typically used in the following scenarios, and these are exactly the views that are exposed by the ModalView service:

  • to display an important message to the user,
  • to request for confirmation from the user,
  • or to request long and short text input from the user.

Dialogs should be callable from anywhere inside your app’s code base: from a View, from a UserControl, from a ViewModel, from Business Logic, etcetera. In an MVVM(ish) architecture functionality like this typically ends up in a so-called Service. Here’s where I located the service in my Visual Studio solution, and the class diagram that shows its API:

ServiceAPI

The ModalView service comes with a small sample app that calls most of the methods in its API. Here’s how that sample app looks like:

MainPage

Basically we need a control to display some text(s), one, two, or three buttons, an optional input field, and an overlay to cover the app window to make it inaccessible. Well, that’s exactly what ContentDialog does. By the way: try to get rid of the old Windows 8 MessageDialog.

To open a message box, al you need to do is provide values for the Title, the Content (subTitle) and the CloseButtonText of the ContentDialog, call ShowAsyc(), and … sit and wait:

public static async Task MessageDialogAsync(
	string title, 
	string message, 
	string buttonText)
{
    var dialog = new ContentDialog
    {
        Title = title,
        Content = message,
        CloseButtonText = buttonText
    };

    await dialog.ShowAsync();
}

That’s exactly what the ModalView service does, and it provides an overload with a default value (“OK”) for the close button text. Here’s the service call from the sample app:

await ModalView.MessageDialogAsync(
	"Ready to go?", 
	"Place a cat, a flask of poison, and a radioactive source in a sealed box.", 
	"Got it");

And this is how it looks like:

MessageBox

The call to open a confirmation box is almost identical. There are only two differences:

  • instead of a single button, there will be two (yes, no) or three (yes, no, cancel), and
  • the caller would want to know which of these buttons was pressed.

All we need to do is provide values for PrimaryButtonText and SecondaryButtonText. You don’t need to specify IsSecondaryButtonEnabled (and the corresponding setting for the other buttons). If you don’t specify a text, the button will not appear. Here’s the service’s implementation of ConfirmationDialogAsync, which returns a nullable boolean to represent the yes/no/cancel response from the user, translated from ContentDialogResult:

public static async Task<bool?> ConfirmationDialogAsync(
	string title, 
	string yesButtonText, 
	string noButtonText, 
	string cancelButtonText)
{
    var dialog = new ContentDialog
    {
        Title = title,
        //IsPrimaryButtonEnabled = true,
        PrimaryButtonText = yesButtonText,
        SecondaryButtonText = noButtonText,
        CloseButtonText = cancelButtonText
    };
    var result = await dialog.ShowAsync();

    if (result == ContentDialogResult.None)
    {
        return null;
    }

    return (result == ContentDialogResult.Primary);
}

Again, there are overloads that use default values for the buttons: the not highly original “Yes”, “No”, and “Cancel”.

Here’s how the sample app opens a two-button confirmation dialog:

private bool? confirmed;

confirmed = await ModalView.ConfirmationDialogAsync(
        "Are you planning to open the box?",
        "Sure",
        "No, thanks"
   );

Here’s the result:

2ButtonConfirmation

When the app window becomes too small in height or width, the ContentDialog automagically snaps to the sides, like this:

Stretched

Here’s a call for a three-button confirmation dialog. It also shows how to create a multi-line title:

confirmed = await ModalView.ConfirmationDialogAsync(
        "So, what's the status the cat?\nHint: use Quantum Mechanics.",
        "It's alive",
        "It's dead",
        "It's both"
    );

And this is how it looks like at runtime:

3ButtonConfirmation

To transform a content dialog into an input dialog, it suffices to replace the default text Content by an input control, such as a TextBox. The ModalView Service provides two input dialogs: one for a string value, and one for a larger text value. Feel free to add your own versions to request for a number, a date, or a selection from a list.

Here’s the implementation of InputStringDialogAsync, it requests for a string without line breaks (hence value for AcceptsReturn). You can provide a default value for the response, and we will place the caret at the end through SelectionStart:

public static async Task<string> InputStringDialogAsync(
	string title, 
	string defaultText, 
	string okButtonText, 
	string cancelButtonText)
{
    var inputTextBox = new TextBox
    {
        AcceptsReturn = false,
        Height = 32,
        Text = defaultText,
        SelectionStart = defaultText.Length,
        BorderThickness = new Thickness(1),
        BorderBrush = new SolidColorBrush((Color)Application.Current.Resources["CustomDialogBorderColor"])
    };
    var dialog = new ContentDialog
    {
        Content = inputTextBox,
        Title = title,
        IsSecondaryButtonEnabled = true,
        PrimaryButtonText = okButtonText,
        SecondaryButtonText = cancelButtonText
    };

    if (await dialog.ShowAsync() == ContentDialogResult.Primary)
    {
        return inputTextBox.Text;
    }
    else
    {
        return string.Empty;
    }
}

Here’s the call from the sample app:

private string inputString;

inputString = await ModalView.InputStringDialogAsync(
        "How do you want to call this phenomenon?",
        "Verschränkung",
        "Claim",
        "Forget it"
    );

And here’s the corresponding UI:

StringInput

The InputTextDialogAsync method listens for a possibly longer text input, and looks very similar to the previous one. The input TextBox is higher, accepts line feeds, and does TextWrapping:

public static async Task<string> InputTextDialogAsync(
	string title, 
	string defaultText, 
	string okButtonText, 
	string cancelButtonText)
{
    var inputTextBox = new TextBox
    {
        AcceptsReturn = true,
        Height = 32 * 6,
        Text = defaultText,
        TextWrapping = TextWrapping.Wrap,
        SelectionStart = defaultText.Length,
        Opacity = 1,
        BorderThickness = new Thickness(1),
        BorderBrush = new SolidColorBrush((Color)Application.Current.Resources["CustomDialogBorderColor"])
    };
    var dialog = new ContentDialog
    {
        Content = inputTextBox,
        Title = title,
        IsSecondaryButtonEnabled = true,
        PrimaryButtonText = okButtonText,
        SecondaryButtonText = cancelButtonText
    };

    if (await dialog.ShowAsync() == ContentDialogResult.Primary)
    {
        return inputTextBox.Text;
    }
    else
    {
        return string.Empty;
    }
}

By the way: if you try to use WrapWholeWords as a value forTextBox.TextWrapping an exception is thrown. You can guess what my first assignment was…

Here’s the call from the sample app:

var inputText = await ModalView.InputTextDialogAsync(
        "What whas your point actually?",
        "Some large string containing line break (\n) characters."
    );

And the corresponding UI:

TextInput

Did you observe that I slightly pimped the default style of the ContentDialog? To add some contrast –and to get rid of the default AccentColor– I applied a custom BorderBrush to the dialog itself and to the input TextBoxes. I also added a DropShadowPanel from UWP Community Toolkit around it. The new Style is available in a ResourceDictionary (don’t forget to register it in your app.xaml). Here are the upgraded parts:


<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:toolkit="using:Microsoft.Toolkit.Uwp.UI.Controls">

    <!-- Custom style for Windows.UI.Xaml.Controls.ContentDialog -->
    <Color x:Key="CustomDialogBorderColor">#FFF05F66</Color>
    <SolidColorBrush x:Key="CustomDialogBorderBrush"
                     Color="#FFF05F66" />
    <Color x:Key="CustomDialogBackgroundColor">#FF4F2316</Color>
    <SolidColorBrush x:Key="CustomDialogBackgroundBrush"
                     Color="#FF4F2316" />

    <!-- From  \(Program Files)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\10.0.10586.0\Generic  -->
    <Style TargetType="ContentDialog">
        <!-- .. -->
            <Setter.Value>
                <ControlTemplate TargetType="ContentDialog">
                    <Border x:Name="Container">
                        <!-- ... -->
                        <Grid x:Name="LayoutRoot">
                            <!-- Added -->
                            <toolkit:DropShadowPanel Color="{StaticResource CustomDialogBorderColor}"
                                                     BlurRadius="20">
                                <!-- Modified Background and BorderBrush -->
                                <Border x:Name="BackgroundElement"
                                        Background="{StaticResource CustomDialogBackgroundBrush}"
                                        FlowDirection="{TemplateBinding FlowDirection}"
                                        BorderThickness="{ThemeResource ContentDialogBorderWidth}"
                                        BorderBrush="{StaticResource CustomDialogBorderBrush}"
                                        MaxWidth="{TemplateBinding MaxWidth}"
                                        MaxHeight="{TemplateBinding MaxHeight}"
                                        MinWidth="{ThemeResource ContentDialogMinWidth}"
                                        MinHeight="{ThemeResource ContentDialogMinHeight}">
                                    <!-- .. -->
                                </Border>
                            </toolkit:DropShadowPanel>
                        </Grid>
                    </Border>
                </ControlTemplate>
            </Setter.Value>
        </Setter>
    </Style>

</ResourceDictionary>

This is how the InputStringDialog looks like, without the customization:

StringInputDefaultStyle

Feel free to add your company’s or app’s logo to the template, and/or a background image. Here’s an example of ModalView in a real Store app:

RealAppSample

Be aware: if you retemplate built-in controls you have to closely follow up and verify your styles against all the new SDK-releases. Future native ContentDialogs may have a different structure or visual states, or will make use of new features like the AcrylicBrush (for the overlay) or RevealBrush on the button(s). I’m pretty sure you wouldn’t want to miss these.

For the sake of completeness, here’s the XAML code behind the sample app’s buttons. It uses different types of commanding and binding, just to illustrate its architectural neutrality:

<!-- Click Event Handler in the View -->
<Button Content="Message Dialog"
        Click="MessageBox_Click" />
<!-- Binding to Command in ViewModel -->
<Button Content="2-Button Confirmation Dialog"
        Command="{Binding ConfirmationCommandYesNo}" />
<!-- Binding to Command in ViewModel -->
<Button Content="3-Button Confirmation Dialog"
        Command="{Binding ConfirmationCommandYesNoCancel}" />
<!-- x:Bind to Command in ViewModel -->
<Button Content="String Input Dialog"
        Command="{x:Bind ViewModel.InputStringCommand}" />
<!-- x:Bind to Method in ViewModel -->
<Button Content="Text Input Dialog"
        Click="{x:Bind ViewModel.InputText_Click}" />

The ModalView Service and its sample app live in this GitHub repo.

Enjoy!

A subtle(r) TextBox for UWP

I’m not a huge fan of the standard text input control in most platforms. Not in Windows Forms, not in HTML, not on any of the XAML platforms. The UWP TextBox control is not an exception. In its default style, a TextBox is a huge, sterile bunch of white pixels staring at you. It waits for your input, and even after you provided a value, it remains a a huge, sterile bunch of white pixels staring at you. When a TextBox asks for your name and you type ‘Tim Smith’, it still keeps enough bordered white space to hold ‘Hubert Blaine Wolfeschlegelsteinhausenbergerdorff, Sr.’ When a TextBox asks for your city and you type ‘Rome’ or ‘Paris’, it still keeps enough bordered white space to hold ‘Llanfairpwllgwyngyllgogerychwyrndrobwllllantysiliogogogoch’.

Enough philosophy, I think I made my point: let’s build a better TextBox.

This article presents the SubtleTextBox control (pending registration). It’s a TextBox control for UWP that looks like a regular TextBox (white and bordered) when it has the focus, but looks more subtle (hence its name) when the user is paying attention to other controls on the view. When the SubtleTextBox does not have the focus, it looks more (or entirely) like a TextBlock – with a transparent background and no border. When the user is moving through the input controls in ‘slow’ mode -by clicking or pressing the tab key- the transition between the two states is smoothly animated. In ‘fast’ mode -hovering the mouse- the transition goes without animation to give immediate feedback to the user. Here’s how the control looks like in a small sample app. This app displays some views with different types of textboxes. The ones that fade away smoothly when losing focus are SubtleTextBox instances:
SubtleTextBoxFull

Building the SubtleTextBox

Here’s how SubtleTextBox was built. I started with deriving a class from TextBox, adding a field to hold its current state:

/// <summary>
/// TextBox that looks like a TextBlock when not editing.
/// </summary>
public class SubtleTextBox : TextBox
{
    private bool isInTextBlockMode = false;

    // ...
}

The difference between TextBox-Style and TextBlock-Style lies in the Opacity of the control’s Background and BorderBrush. The Opacity of these elements in TextBoxStyle is 1, which is hardcoded as HighOpacity. The Opacity of these elements in pure TextBlockStyle is 0, but you may want to configure this to any value between 1 and 0, to apply a faded style. I created a dependency property called LowOpacity for this. That’s not the world’s best name for it -since it reveals implementation details- but it’s still better than the semantically correct ‘ReverseSubtleness’:

/// <summary>
/// Registers the LowOpacity dependency property.
/// </summary>
public static readonly DependencyProperty LowOpacityProperty = DependencyProperty.Register(
    "LowOpacity", 
    typeof(double), 
    typeof(SubtleTextBox), 
    new PropertyMetadata(0.0));

/// <summary>
/// Gets or sets the lowest opacity for border and background.
/// </summary>
/// <value>The low opacity.</value>
/// <remarks>This is the value used in TextBlock mode.</remarks>
public double LowOpacity
{
    get { return (double)GetValue(LowOpacityProperty); }
    set { SetValue(LowOpacityProperty, value); }
}

When the control is Loaded, we make sure to give the control its own SolidColorBrush instance of Background and BorderBrush. Otherwise we’ll simultaneously animate ALL text boxes (subtle and regular ones) on the view. If you want to see that show, just put the assignments in comment…

When the control appears on screen, it will look like a regular TextBox -to hint the user that it’s for input- and then it fades away to its TextBlock state:

/// <summary>
/// Initializes a new instance of the <see cref="SubtleTextBox"/> class.
/// </summary>
public SubtleTextBox()
{
    Loaded += SubtleTextBox_Loaded; ;
    timer.Interval = TimeSpan.FromSeconds(2);
    timer.Tick += Timer_Tick;
}

Here are the internal methods to switch visual state:

/// <summary>
/// Makes the control look like a read-only TextBlock.
/// </summary>
public void ApplyTextBlockStyle()
{
    if (isInTextBlockMode)
    {
        return;
    }

    isInTextBlockMode = true;
    Animate(HighOpacity, LowOpacity);
}

/// <summary>
/// Makes the control look like a regular TextBox.
/// </summary>
public void ApplyTextBoxStyle()
{
    if (!isInTextBlockMode)
    {
        return;
    }

    isInTextBlockMode = false;
    Animate(LowOpacity, HighOpacity);
}

They are called when the control retrieves and loses focus:

protected override void OnGotFocus(RoutedEventArgs e)
{
    timer.Stop();
    ApplyTextBoxStyle();
    base.OnGotFocus(e);
}

protected override void OnLostFocus(RoutedEventArgs e)
{
    ApplyTextBlockStyle();
    base.OnLostFocus(e);
}

The transition is made of two simultaneous DoubleAnimations (Opacity of Background and Opacity of BorderBrush) in a StoryBoard. Typically story boards are defined in XAML. If you create these programmatically you can hook them in the visual tree with Storyboard.SetTarget and Storyboard.SetTargetProperty. Also don’t forget to activate EnableDependentAnimation, or you’ll see nothing:

private void Animate(double from, double to)
{
    var storyboard = new Storyboard();

    var animation = new DoubleAnimation
    {
        From = from,
        To = to,
        Duration = new Duration(TimeSpan.FromMilliseconds(Duration)),
        EnableDependentAnimation = true
    };
    Storyboard.SetTarget(animation, BorderBrush);
    Storyboard.SetTargetProperty(animation, nameof(BorderBrush.Opacity));
    storyboard.Children.Add(animation);

    animation = new DoubleAnimation
    {
        From = from,
        To = to,
        Duration = new Duration(TimeSpan.FromMilliseconds(Duration)),
        EnableDependentAnimation = true
    };
    Storyboard.SetTarget(animation, Background);
    Storyboard.SetTargetProperty(animation, nameof(Background.Opacity));
    storyboard.Children.Add(animation);

    storyboard.Begin();
}

I could have manipulated and animated the controls ‘official’ VisualStates, but these are not supposed to be animated.

Here’s how to use SubtleTextBox in XAML:

<controls:SubtleTextBox PlaceholderText="Subtle TextBox 0 %" />
<controls:SubtleTextBox PlaceholderText="Subtle TextBox 10 %" 
                        LowOpacity=".1" />

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

SubtleTextBoxShort

I use the SubtleTextBox in some of my views to host the non-mandatory input fields. But there’s also another use case:

Extending a Slider

A long time ago in a galaxy far away –called Windows 8- I wrote an ‘EnhancedSlider‘ control. It was a Slider that came with a TextBox to allow the user to manually adjust its value. I used it successfully in some apps. Today, the need for such a control is even higher: on the Windows 10 Universal Platform we can not make any assumption anymore on screen sizes. The user (or the hardware) may make a view so narrow that any Slider control would become inaccurate. A Slider can still be used to get in close range of the intended value, but it makes sense to allow keyboard input to let the user enter the exact final  value.

I actually created SubtleTextBox for this purpose. It comes with a behavior that I didn’t mention yet. The control it can ‘flash’: it can switch to TextBox mode and get back to TextBlock mode to get the attention of the user. That way it can be used as an extension to input controls such as a Slider, or a RadialGauge in interactive mode.

When the value of the slider has changed (through manipulation or two-way binding), we can call the SuggestInput method to indicate the user that there is an alternative input control bound to the same value.

Here’s how the method is implemented:


private DispatcherTimer timer = new DispatcherTimer();

/// <summary>
/// Briefly makes the control look like a regular TextBox.
/// </summary>
public void SuggestInput()
{
    ApplyTextBoxStyle();
    timer.Start();
}

private void Timer_Tick(object sender, object e)
{
    timer.Stop();
    ApplyTextBlockStyle();
}

Here’s how the controls are hooked to each other in a sample view:

<controls:SubtleTextBox x:Name="ValueBox"
                        Text="{x:Bind Model.SomeValue, Mode=TwoWay}"
                        LowOpacity=".1"
                        InputScope="Number"
                        HorizontalAlignment="Right"
                        TextAlignment="Right"
                        Margin="0 40 0 0" />
<Slider x:Name="ValueSlider"
        Maximum="500"
        Value="{x:Bind Model.SomeValue, Mode=TwoWay}"
        ValueChanged="ValueSlider_ValueChanged" />
private void ValueSlider_ValueChanged(object sender, RangeBaseValueChangedEventArgs e)
{
    ValueBox.SuggestInput();
}

Here’s how this looks like in the sample app – the TextBox that decorates the top Slider is a regular one, the two others are of the subtle flavor:
SubtleTextBoxSlider

Please consider that all controls in this sample view are bound to the same value in the ViewModel. So the two subtle textboxes flash together, which is not very … subtle. In a real app, it looks a lot better. Here’s an example. The textbox that decorates the slider, and the textbox for the optional notes are ‘subtle’ versions:
SubtleTextBoxInReal

The SubtleTextBox and its sample app live here on GitHub.

Enjoy!

A Strength Indicator control for UWP

In this article we present a lightweight, flexible UWP XAML control that’s specialized in visually representing a ‘strength’ – a number from 0 to 5. By default it looks like the well-known 5-vertical-bar indicators that display WIFI or any other network strength. But this one is fully configurable: its colors, shapes, and maximum value can be set declaratively and programmatically.

Here’s a screenshot from a sample client app. The image on the left is the control in its default style, the image on the right uses custom colors and shapes. Each controls’ Value can be changed by using the sliders underneath:

MainPage

The control is intended to look crisp at any size, although I presume it will be displayed in a small size in most use cases. It relies on the Path control – the  XAML representation of vector graphics that looks nice in any resolution and size. Based on its Value, the StrenghtIndicator will display one Path instance either out of a list you provide it with, or out of its default list.

It comes with the following dependency properties:

Maximum int Gets or sets the highest possible Value.
Value int Gets or sets the current Value. Constrained to the range from 0 to Maximum.
Fill Brush Gets or sets the Fill property for the displayed paths.
Stroke Brush Gets or sets the Stroke property for the displayed paths.

The list of Paths that you want to display is provided through a regular property:

Paths PathCollection Gets or sets the list of Path Data strings to display.

StrengthIndicator is implemented as a UserControl. Its XAML part is nothing more than a Path control embedded in a ViewBox;


<UserControl x:Class="XamlBrewer.Uwp.Controls.StrengthIndicator"
             ...
             d:DesignHeight="200"
             d:DesignWidth="200">
    <Viewbox HorizontalAlignment="Stretch"
             VerticalAlignment="Stretch">
        <Path Height="200"
              Width="200"
              x:Name="Shape"
              Fill="{x:Bind Fill, Mode=OneWay}"
              Stroke="{x:Bind Stroke, Mode=OneWay}"
              VerticalAlignment="Stretch"
              HorizontalAlignment="Stretch"
              Stretch="Uniform" />
    </Viewbox>
</UserControl>

To make it easy to provide the list of vector images through XAML, I created a separate (empty) class to host the list of Data elements for the paths:

public class PathCollection : List<string> { }

When the control is instantiated, it loads its default set of images that correspond to its default Value range from 0 to 5. That’s just to make sure that there is always an image available. After the control is Loaded (constructed, added to the object tree, declarative bindings resolved, and ready for interaction), we run through the ValueChanged routine to give it its initial look:

 

public StrengthIndicator()
{
    InitializeComponent();
    Paths = DefaultPaths;
    Loaded += StrengthIndicator_Loaded;
}

private void StrengthIndicator_Loaded(object sender, RoutedEventArgs e)
{
    OnValueChanged(this);
}

private static PathCollection DefaultPaths
{
    get
    {
        return new PathCollection
        {
            "m 72.772,54.758 ... data omitted ...0,2 z",
            "m 67.784,67.871 ... data omitted ... 2.288 z",
            "m 67.784,67.871 ... data omitted ... 2.288 z",
            "m 67.784,67.871 ... data omitted ... 2.288 z",
            "m 67.784,67.871 ... data omitted ... 2.288 z",
            "m 67.784,67.871 ... data omitted ... 2.288 z"
        };
    }
}

When the value changes, we first make sure that it stays within the range, and then load new path data in the controls’ core Path. That control is found by navigating with the VisualTreeHelper. Feeding the path data is not so straightforward, since the control expects a Geometry and we’re providing just a string. There are some frameworks available to parse a Geometry to and from a String, but as a lazy developer I decided to delegate this work the the XAML binding engine. To update the path, we create a new Binding, assign the Data as a string to its Source, and use SetBinding to hook it to the controls’ Data dependency property.

Here’s the whole routine:

public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(
    "Value", 
    typeof(int), 
    typeof(StrengthIndicator), 
    new PropertyMetadata(0, OnValueChanged));

private static void OnValueChanged(DependencyObject d)
{
    var indicator = d as StrengthIndicator;

    if (indicator == null) return;

    if (indicator.Value > indicator.Maximum)
    {
        indicator.Value = indicator.Maximum;
    }

    if (indicator.Value < Minimum)
    {
        indicator.Value = Minimum;
    }

    // var shape = (indicator.Content as Viewbox).Child; // Straightforward version.
    var shape = indicator.FindChild<Windows.UI.Xaml.Shapes.Path>("Shape");

    if (shape == null) return;

    var binding = new Binding
    {
        Source = indicator.Paths[indicator.Value + indicator._pathIndex]
    };

    BindingOperations.SetBinding(shape, Windows.UI.Xaml.Shapes.Path.DataProperty, binding);
}

To use the StrenghtIndicator in XAML, start with adding the namespace:

<Page xmlns:controls="using:XamlBrewer.Uwp.Controls" />

Then drop the element in a host of your choice. All properties have a default value, you probably just need to define a binding to its Value. Here’s the definition of the left control on the main page of the sample app:

<controls:StrengthIndicator Value="{Binding Path=Value, ElementName=Slider, Mode=TwoWay}" />

Here’s how to declaratively assign values to the colors (Fill and Stroke) and the list of shapes. Just make sure that the number of images equals the Maximum value plus one, because there’s no internal validation for this (yet):

<controls:StrengthIndicator Value="{Binding Path=Value, ElementName=Slider2, Mode=TwoWay}"
                            Fill="#8882A8"
                            Stroke="#141426">
    <controls:StrengthIndicator.Paths>
        <controls:PathCollection>
            <x:String>M83.4,20.7c0-8.5-2 ... data omitted ...</x:String>
            <x:String>M89.8,21.8c0.1-0.4 ... data omitted ...</x:String>
            <x:String>M89.8,21.8c0.1-0.4 ... data omitted ...</x:String>
            <x:String>M89.8,21.8c0.1-0.4 ... data omitted ...</x:String>
            <x:String>M84.9,21.1c0.1-0.4 ... data omitted ...</x:String>
            <x:String>M84.3,21.8c0.1-0.4 ... data omitted ...</x:String>
        </controls:PathCollection>
    </controls:StrengthIndicator.Paths>
</controls:StrengthIndicator>

If you want to reuse a set of shapes (like I did in the thumbnail controls at the bottom of the page) then you can store a path collection in a resource dictionary:

<Page.Resources>
    <controls:PathCollection x:Key="GlassPaths">
        <x:String>M83.4,20.7c0-8.5-2 ... data omitted ...</x:String>
        <x:String>M89.8,21.8c0.1-0.4 ... data omitted ...</x:String>
        <x:String>M89.8,21.8c0.1-0.4 ... data omitted ...</x:String>
        <x:String>M89.8,21.8c0.1-0.4 ... data omitted ...</x:String>
        <x:String>M84.9,21.1c0.1-0.4 ... data omitted ...</x:String>
        <x:String>M84.3,21.8c0.1-0.4 ... data omitted ...</x:String>
    </controls:PathCollection>
</Page.Resources>

You can then refer to this resource in multiple StrengthIndicator instances:

<controls:StrengthIndicator Value="5"
                            Paths="{StaticResource GlassPaths}"
                            Fill="#8882A8"
                            Stroke="#141426" />

Here’s a screenshot of the Gallery page of the sample app. It shows some more ‘advanced’ usages:

GalleryPage

The indicator on the left changes colors: it evolves from green to red when its value increases. All it takes is a custom ValueConverter:

<controls:StrengthIndicator Value="{Binding Path=Value, ElementName=Slider, Mode=TwoWay}" 
                            Stroke="{Binding Path=Value, ElementName=Slider, Converter={StaticResource IntToBrushConverter}}" 
                            Fill="{Binding Path=Value, ElementName=Slider, Converter={StaticResource IntToBrushConverter}}" />

The control on the right has interactive behavior: you can change its value by swiping left or right on it. There’s a Grid on top of it with the appropriate ManipulationMode. Here’s the XAML for this compact Rating Control:

<Grid ManipulationMode="TranslateX" 
      ManipulationCompleted="StrengthIndicator_ManipulationCompleted" 
      Background="Transparent"> 
    <controls:StrengthIndicator x:Name="InteractiveIndicator" 
                                Paths="{StaticResource DicePaths}" 
                                Fill="#8882A8" 
                                Stroke="#141426" /> 
</Grid> 

And here’s the implementation of ManipulationCompleted:


private void StrengthIndicator_ManipulationCompleted(object sender, Windows.UI.Xaml.Input.ManipulationCompletedRoutedEventArgs e)
{
    if (e.Cumulative.Translation.X > 30)
    {
        InteractiveIndicator.Value++;
    }
    else if (e.Cumulative.Translation.X < -30)
    {
        InteractiveIndicator.Value--;
    }
}

Since the control has a more or less square shape, I added a page with the SquareOfSquares test container, so you can see the StrenghtIndicator in different sizes and colors:

SquaresPage

This page also demonstrates how to programmatically create instances of the StrenghtIndicator control:

square.Content = new XamlBrewer.Uwp.Controls.StrengthIndicator()
{
    Height = square.ActualHeight - 8,
    Width = square.ActualWidth - 8,
    Margin = new Windows.UI.Xaml.Thickness(4),
    Stroke = new SolidColorBrush(square.RandomColor()),
    Fill = new SolidColorBrush(square.RandomColor()),
    Value = random.Next(0, 6),
    Paths = new PathCollection
    {
        "M83.4,20.7c0-8.5-25.6-9.1-33.4-... data omitted ...",
        "M89.8,21.8c0.1-0.4,0.2-0.7,0.2-... data omitted ...",
        "M89.8,21.8c0.1-0.4,0.2-0.7,0.2-... data omitted ...",
        "M89.8,21.8c0.1-0.4,0.2-0.7,0.2-... data omitted ...",
        "M84.9,21.1c0.1-0.4,0.2-0.8,0.2-... data omitted ...",
        "M84.3,21.8c0.1-0.4,0.2-0.7,0.2-... data omitted ..."
    }
};

For the sake of completeness, here’s a screenshot of the control inside a templated column of a RadDataGrid (look at the Flocculaton column) :

RadGridPage

StrenghtIndicator is a UWP control, so it should run perfectly on all Windows 10 devices. I can confirm it runs smoothly on my phone…

The control and its sample client live here on GitHub.

Enjoy!

Getting started with the Telerik RadDataGrid on UWP

This article explains how to get started with the Telerik RadDataGrid for UWP. RadDataGrid is a tabular control with a long history (in different technology stacks including Windows Forms, WPF, and HTML) and a rich API. It displays a list of items and supports customizable columns, single and multi-column sorting, data editing, grouping, selection, filtering and so on. XAML-wise you may consider RadDataGrid as the successor of the DataGrid in WPF.

In this article we’ll focus on templating, sorting, filtering, and grouping. I built a small sample that displays a list of racing drivers with some of their properties (name, team, nationality, experience, …). As an end user of this app you can

  • change the order of the columns,
  • select which columns to display or hide,
  • sort the list on column values,
  • filter the list on column values and,
  • group list entries on column values.

Here’s how the app looks like:

Overview

RadDataGrid is part of the Telerik UI for UWP toolkit. Its source code is available for free (!) here on GitHub, the binaries are available through NuGet. For an overview of the RadDataGrid features –but without source code- check the corresponding Telerik web page. You can also download their sample Store App. It looks like this, but unfortunately again, there’s no source code available:

TelerikStoreApp

The official documentation for Telerik UI for UWP is here, and the starting point for the RadDataGrid documentation is right here. As already mentioned, the focus of this article is data visualisation (read-only). If you’re looking for an example of editing and validation, then you better check this example from Microsoft.

Off we go

First you need to install the NuGet package in your app:

NuGetPackage

On the page that’s going to host the RadDataGrid, define the necessary namespaces:

<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      ...
      xmlns:grid="using:Telerik.UI.Xaml.Controls.Grid"
      xmlns:core="using:Telerik.Data.Core">

Then you just create a RadDataGrid XAML element on it. For a quick result, you can set AutoGenerateColumns to true. A default column will be created for each property in the ItemsSource’s item class.

A transparent background and no border or gridlines give the control a nice Windows 10-look. I also prefer to always display the column resize handlers – the double vertical lines in the column header. They look good and they’re really handy on a touch screen. For more details, check the properties and configurations and the visual structure documentation pages.

Here’s my initial control definition for the racing drivers grid:

<grid:RadDataGrid ItemsSource="{x:Bind ViewModel.Drivers}"
                    AutoGenerateColumns="True"
                    Background="Transparent"
                    BorderBrush="Transparent"
                    GridLinesVisibility="None"
                    CanUserChooseColumns="True"
                    ColumnResizeHandleDisplayMode="Always">

Here’s how it looks like at runtime:

AutoGenerateColumns

Default Column types

If you’re not auto-generating the columns, then you need to specify the column list yourself. RadDataGrid comes with different column types, including a set of types that allow visualization as well as editing and validation of String, Boolean, Date, Image, Numerical and Time properties, and for selection from a ComboBox. Here’s an example of a DataGridTextColumn and a DataGridDateColumn, each bound to a property in the itemssource.

As you see, you can apply your own header text, and formatting:

<grid:RadDataGrid.Columns>
    <grid:DataGridTextColumn PropertyName="Name"
                                Header="Name" />
    <!-- ... -->
    <grid:DataGridDateColumn PropertyName="MostRecentVictory"
                                Header="Last win"
                                CellContentFormat="{}{0:d}" />
    <!-- ... -->
</grid:RadDataGrid.Columns>

Template Columns

If you want complete control over the look and feel of a column, then you have to use a DataGridTemplateColumn. This allows you to provide your own data template. Here’s an example of two templated columns – the first one uses a Rating Control to visualize the the experience level (from an enumeration), the second one uses an icon to represent a Boolean value:

<grid:DataGridTemplateColumn Header="Experience">
    <grid:DataGridTemplateColumn.CellContentTemplate>
        <DataTemplate>
            <controls:Rating Maximum="5"
                                Value="{Binding ExperienceAsNumber}"
                                EmptyImage="ms-appx:///Assets/RatingIcons/wreath_empty.png"
                                FilledImage="ms-appx:///Assets/RatingIcons/wreath_full.png"
                                IsInteractive="False"
                                ItemHeight="24" />
        </DataTemplate>
    </grid:DataGridTemplateColumn.CellContentTemplate>
</grid:DataGridTemplateColumn>
<!-- No customization possible :-(-->
<!--<grid:DataGridBooleanColumn Header="Active"
            PropertyName="IsStillActive" />-->
<grid:DataGridTemplateColumn Header="Active">
    <grid:DataGridTemplateColumn.CellContentTemplate>
        <DataTemplate>
            <Path Data="{StaticResource SteeringWheelIcon}"
                    VerticalAlignment="Center"
                    HorizontalAlignment="Center"
                    Height="20"
                    Width="20"
                    Fill="{Binding IsStillActive, Converter={StaticResource BooleanToBrushConverter}}"
                    Stretch="Uniform" />
        </DataTemplate>
    </grid:DataGridTemplateColumn.CellContentTemplate>
</grid:DataGridTemplateColumn>

Sorting

According to the classic paradigm (and hence the user’s expectations) columns can be sorted by clicking on the column header. When clicking on the header of a column that is already sorted, the rows will iterate to the next stage: from sorted ascending, to sorted descending, and back to its original order (I love this last ‘unsort’ stage, it demonstrates the maturity of the API).

The built-in default column types enable sorting out of the box: they know to which property they’re bound –through PropertyName– and the underlying data types are all IComparable.

For templated columns, you need to explicitly declare that the user can sort the column via CanUserSort. Since such a column isn’t necessarily bound to a property, you have to provide a SortDescriptor yourself. Here’s how I defined that the column with the steering wheel icon can be sorted on the IsStillActive property of the racing driver class:

<grid:DataGridTemplateColumn Header="Active"
                                CanUserSort="True">
    <grid:DataGridTemplateColumn.SortDescriptor>
        <core:PropertySortDescriptor PropertyName="IsStillActive" />
    </grid:DataGridTemplateColumn.SortDescriptor>
    <grid:DataGridTemplateColumn.CellContentTemplate>
        <!-- ... -->
    </grid:DataGridTemplateColumn.CellContentTemplate>
</grid:DataGridTemplateColumn>

As you would expect, a sorted column is decorated with a triangle in its header. Here’s the sample data grid, reverse alphabetically sorted by the Name column:

Sorting

Filtering

Columns headers can be decorated with a control that allows filtering. Here’s how to enable this feature:

<grid:RadDataGrid ItemsSource="{x:Bind ViewModel.Drivers}"
                    AutoGenerateColumns="False"
                    UserFilterMode="Enabled">

With UserFilterMode enabled, all non-templated columns are decorated with a filter icon. When that icon is clicked, a data type specific mini UI pops open, and allows the user to specify a filter to apply on the column values. When the filter is activated, the rows that don’t comply disappear, and the column header is underlined to visually indicate that a filter is in place. Here’s how all of this looks like in the sample app. We’re filtering the drivers with their name ending on ‘son’:

Filtering

As mentioned, the filter UI is specific to the data type of the column. Here’s a screenshot of the date filter on the ‘most recent victory’ property:

FilterOnDate

If you want, you can even create your own custom filter UI control. And for the sake of completeness: filters can also be applied programmatically.

Grouping

The most powerful feature in the RadDataGrid is grouping: the ability to let the user group the data by column values. All the user needs to do is drag a column header to the grouping bar at the left.

Again it is a feature that you have to enable:

<grid:RadDataGrid ItemsSource="{x:Bind ViewModel.Drivers}"
                    ...
                    UserGroupMode="Enabled">

You can then selectively turn it of again, e.g. for columns with relatively unique values, like the Name in the sample app:

<grid:DataGridTextColumn PropertyName="Name"
                            Header="Name"
                            CanUserGroup="False" />

For template-type columns, you need to specify the property name and the group name through a GroupDescriptor. Here’s how I allow grouping on the values in the Active column:

<grid:DataGridTemplateColumn Header="Active">
    <grid:DataGridTemplateColumn.GroupDescriptor>
        <core:PropertyGroupDescriptor PropertyName="ActiveDescription"
                                        DisplayContent="Active" />
    </grid:DataGridTemplateColumn.GroupDescriptor>
    <!-- ... -->
</grid:DataGridTemplateColumn>

I did not not want to show true and false in the group header, so I created a new property in the racing driver class. This is not the only property that I defined just for the data grid, but after all, that’s what ViewModels are made for:

public string ActiveDescription => IsStillActive ? "Active" : "Retired";

When the icon on top of the grouping bar is clicked, a panel slides open that allows to change the nesting order, sort the group headers, and remove columns from the grouping hierarchy. Here’s how the UI looks like in the sample app. We group the drivers by nationality, and then ungroup again:

Grouping

When user grouping is enabled, it also makes sense to enable column selection. After all, the user will want to hide the columns on which the data is grouped on – their values are already displayed in the group headers. Column selection is enabled through the CanUserChooseColumns property. It displays a triangular button in the top right corner that triggers a panel with a checkbox for all the columns:

ColumnSelection

Combining the features

All RadDataGrid features (sorting, grouping, filtering) can be combined. Here’s the grid from the sample app, sorted and filtered by name and grouped by nationality of the driver:

GroupedFiltering

Theming

Every part of the RadDataGrid is themeable, but some parts are easier than other. Telerik provides the classic light and dark theme resources (different shades of grey, and an accent color). They also provide a UserThemeResource markup extension that enables you to easily override resources. Just create your own theme and register it, like this:

<ResourceDictionary.MergedDictionaries>
    <!-- Theme colors and icons -->
    <ResourceDictionary Source="Services/Icons/Icons.xaml" />
    <ResourceDictionary Source="Services/Theming/Theme.xaml" />
    <ResourceDictionary>
        <telerik:UserThemeResources x:Key="TelerikLightResources"
                                    LightResourcesPath="ms-appx:///Services/Theming/Telerik/TelerikTheme.xaml" />
    </ResourceDictionary>
</ResourceDictionary.MergedDictionaries>

In your custom theme, just override the Telerik named brushes:

<Color x:Key="TelerikSelectedColor">#B1560F</Color>
<Color x:Key="TelerikHighlightedColor">#75390A</Color>

<SolidColorBrush x:Key="TelerikGridHeaderBackgroundBrush"
                    Color="#7A6F41" />
<SolidColorBrush x:Key="TelerikGridGroupHeaderBackgroundBrush"
                    Color="#7A6F41" />
<SolidColorBrush x:Key="TelerikGridServiceColumnBackgroundBrush"
                    Color="#7A6F41" />
<SolidColorBrush x:Key="TelerikGridHeaderForegroundBrush"
                    Color="White" />
<SolidColorBrush x:Key="TelerikGridGroupHeaderForegroundBrush"
                    Color="White" />
<SolidColorBrush x:Key="TelerikGridServiceColumnForegroundBrush"
                    Color="White" />

Some of the theme assets are images, like the buttons to open and close panels (because it’s hard to draw a triangle in XAML?). If you want to create custom versions of these, then just download these from the source code in GitHub:

TelerikAssets

DefaultCloseButton

You can then modify the images (or replace them entirely) and refer to these in your resources dictionary:

<BitmapImage x:Key="TelerikGridColumnChooserOpenMouseOver"
                UriSource="ms-appx:///Services/Theming/Telerik/Assets/column_chooser_btn_themed.png" />
<BitmapImage x:Key="TelerikGridColumnChooserCloseMouseOver"
                UriSource="ms-appx:///Services/Theming/Telerik/Assets/close_btn_themed.png" />
<BitmapImage x:Key="TelerikGridFilterFlyoutExpanderIcon"
                UriSource="ms-appx:///Telerik.UI.Xaml.Grid.UWP/Assets/FilterFlyout/ic_arrow_down_white.png" />

Here’s a custom panel button in the sample app (it’s the triangle in the upper right corner):

ThemedCloseButton

Personal Experience

I’ve been using this control for a while now. Here’s a screenshot from one of the RadDataGrids that I’m building in another app. It shows a list of possible ingredients for brewing a beer:

AppSample

I have no idea which criteria the user is going to apply to find the ingredient(s) he’s looking for. But I’m sure that the control’s capabilities of presenting the data in a clear way and allowing him to sort, filter, and group the rows on any column or combination of columns will rapidly narrow down the search list.

Here are my personal observations on the Telerik UI controls for UWP, and the RadDataGrid in particular:

Thumbs up Thumbs down
Very rich and mature API and implementation. Shallow reference documentation.
Good quickstart documentation. Lack of detailed sample projects.
It’s for free! Occasional bugs*.

(*) I relatively often need to work around a “Layout cycle detected” error when using more advanced templated columns.

RadDataGrid for UWP is a tremendously powerful and useful control that you may consider for a large number of scenarios.

Source Code

My sample app lives here in GitHub.

Enjoy!

A Radial Range Indicator control for UWP

In this article we present a Radial Range Indicator control for use in XAML-based UWP apps. RadialRangeIndicator is a circular control for graphically displaying a Range of values (from a minimum to a maximum) inside a scale, e.g. in a Dashboard. Circular and square controls are excellent citizens of adaptive user interfaces, since they scale easily and are independent of the screen orientation. RadialRangeIndicator is derived from PercentageRing and RadialGauge.

Here’s how a RadialRangeIndicator looks like:

RadialRangeIndicator

Its main constituents are

  • the scale, i.e. the background arc,
  • the range, i.e. the foreground arc, and
  • the text.

All properties are implemented as dependency properties so you can bind to these in every way you like. Every change is immediately reflected in the UI. The control does not crash on unexpected values: it survives assignments like a maximum angle over 360 degrees, or any minimum value that is greater than its corresponding maximum. [note to self: add exception handling to the string.Format call that generates the Text Smile]

Here’s the list of configurable properties:


Scale related
ScaleMinimum double Gets or sets the minimum value of the scale.
ScaleMaximum double Gets or sets the maximum value of the scale.
ScaleWidth double Gets or sets the width of the scale, in percentage of the radius.
ScaleMinimumAngle int Gets or sets the start angle of the scale, which corresponds with the ScaleMinimum value, in degrees.
ScaleMaximumAngle int Gets or sets the end angle of the scale, which corresponds with the ScaleMaximum value, in degrees.
ScaleBrush Brush Gets or sets the brush for the scale.
ScaleStartCap PenLineCap Gets or sets the StrokeStartCap for the Scale.
ScaleEndCap PenLineCap Gets or sets the StrokeEndCap for the Scale.
Range related    
RangeMinimum double Gets or sets the minimum value for the range.
RangeMaximum double Gets or sets the maximum value for the range.
RangeStepSize double Gets or sets the rounding interval for the range values. If the StepSize is zero, no rounding is applied.
RangeBrush Brush Gets or sets the brush for the range.
RangeStartCap PenLineCap Gets or sets the StrokeStartCap for the Range.
RangeEndCap PenLineCap Gets or sets the StrokeEndCap for the Range.
Text related    
TextBrush Brush

Gets or sets the brush for the displayed value range.

TextStringFormat string Gets or sets the text string format. Use {0} and {1} to display range minimum and maximum.

The core of the control’s default style template is a ViewBox with two Path instances with configurable PenLineCaps:

<ControlTemplate TargetType="local:RadialRangeIndicator">
    <Border Background="{TemplateBinding Background}"
            BorderBrush="{TemplateBinding BorderBrush}"
            BorderThickness="{TemplateBinding BorderThickness}">
        <Viewbox>
            <Grid x:Name="PART_Container"
                    Height="200"
                    Width="200"
                    Background="Transparent">

                <!-- Scale -->
                <Path Name="PART_Scale"
                        Stroke="{TemplateBinding ScaleBrush}"
                        StrokeThickness="{TemplateBinding ScaleWidth}"
                        StrokeStartLineCap="{TemplateBinding ScaleStartCap}"
                        StrokeEndLineCap="{TemplateBinding ScaleEndCap}" />

                <!-- Range -->
                <Path Name="PART_Range"
                        Stroke="{TemplateBinding RangeBrush}"
                        StrokeThickness="{TemplateBinding ScaleWidth}"
                        StrokeStartLineCap="{TemplateBinding RangeStartCap}"
                        StrokeEndLineCap="{TemplateBinding RangeStartCap}" />

                <!-- Value -->
                <StackPanel VerticalAlignment="Center"
                            HorizontalAlignment="Center">
                    <TextBlock Name="PART_Text"
                                Foreground="{TemplateBinding TextBrush}"
                                FontSize="20"
                                FontWeight="SemiBold"
                                TextAlignment="Center" />
                </StackPanel>
            </Grid>
        </Viewbox>
    </Border>
</ControlTemplate>

The code-behind populates each of these Paths with an ArcSegment in a PathGeometry or a full circle EllipseGeometry. Here’s the code for the Range:

if (radialRangeIndicator.RangeMaximumValueAngle - radialRangeIndicator.NormalizedMinAngle == 360)
{
    // Draw full circle.
    var eg = new EllipseGeometry
    {
        Center = new Point(Radius, Radius),
        RadiusX = Radius - (radialRangeIndicator.ScaleWidth / 2)
    };

    eg.RadiusY = eg.RadiusX;
    range.Data = eg;
}
else
{
    range.StrokeStartLineCap = radialRangeIndicator.RangeStartCap;
    range.StrokeEndLineCap = radialRangeIndicator.RangeEndCap;

    // Draw arc.
    var pg = new PathGeometry();
    var pf = new PathFigure
    {
        IsClosed = false,
        StartPoint = radialRangeIndicator.ScalePoint(radialRangeIndicator.RangeMinimumValueAngle, middleOfScale)
    };

    var seg = new ArcSegment
    {
        SweepDirection = SweepDirection.Clockwise,
        IsLargeArc = radialRangeIndicator.RangeMaximumValueAngle > (180 + radialRangeIndicator.RangeMinimumValueAngle),
        Size = new Size(middleOfScale, middleOfScale),
        Point =
            radialRangeIndicator.ScalePoint(
                Math.Min(radialRangeIndicator.RangeMaximumValueAngle, radialRangeIndicator.NormalizedMaxAngle), middleOfScale)
    };

    pf.Segments.Add(seg);
    pg.Figures.Add(pf);
    range.Data = pg;
}

For more info on the algorithms and calculations, please read the article on the Percentage Ring. After all, this Radial Range Indicator is the very same control, but with a variable start point for the Range.

The Gallery page of the sample app shows some more advanced usages and styles of the RadialRangeIndicator:

Gallery

On the left, you see that a series of Radial Gauge Indicators can be used perfectly to indicate ranges inside (or outside) the scale of a Radial Gauge.

In the middle you see how to define a custom string format for the Text:

<controls:RadialRangeIndicator ScaleMinimumAngle="-150"
                                ScaleMaximumAngle="150"
                                ScaleBrush="Silver"
                                TextStringFormat="{}{0}-{1} Å"
                                TextBrush="{StaticResource PageForegroundBrush}" />

The instance in the middle also demonstrates how a DropShadowPanel control can be used inside a control’s template. There’s a white one around the Scale to smoothen the entire control, and a yellow one to add a glow effect to the Range:

<!-- Scale -->
<toolkit:DropShadowPanel Color="White">
    <Path Name="PART_Scale"
            Stroke="{TemplateBinding ScaleBrush}"
            StrokeThickness="{TemplateBinding ScaleWidth}"
            StrokeStartLineCap="{TemplateBinding ScaleStartCap}"
            StrokeEndLineCap="{TemplateBinding ScaleEndCap}" />
</toolkit:DropShadowPanel>

<!-- Range -->
<toolkit:DropShadowPanel Color="Yellow"
                            BlurRadius="20">
    <Path Name="PART_Range"
            Stroke="{TemplateBinding RangeBrush}"
            StrokeThickness="{TemplateBinding ScaleWidth}"
            StrokeStartLineCap="{TemplateBinding RangeStartCap}"
            StrokeEndLineCap="{TemplateBinding RangeStartCap}" />
</toolkit:DropShadowPanel>

Here’s an example of Radial Range Indicators in a more realistic app. They have a DropShadowPanel around the Scale, and a BackDrop underneath the Text to blur the background:

HopDetails

I also added a page with Radial Range Indicators inside a Simple Perfect Square. This gives an overview of the control in many different sizes and configurations, and allows to assess the performance when having multiple instances of it on the same page. On top of that, it’s also colorful and fun to look at:

SquareOfSquares

If you want to start using the control, it’s available on NuGet. If you want to dive in its source code, it’s on GitHub.

Enjoy!