Category Archives: MVVM

Stretching the WinUI 3 Expander control

In this article we take the new WinUI 3 Expander control through a couple of scenarios that you don’t find in the documentation. We have built a Windows Desktop app that demonstrates

  • an attempt to build a horizontal Expander,
  • grouping Expanders into an Accordion control, and
  • grouping Expanders into a Wizard control, using data templates and binding.

Here’s how that app looks like:

From the -excellent- design document we learn that the Expander control lets you show or hide less important content that’s related to a piece of primary content that’s always visible. The Header is always visible and behaves like a ToggleButton. It allows the user to expand and collapse the Content area with the secondary content. The Expander UI is commonly used when display space is limited and when information or options can be grouped together.

Most UI stacks and component libraries have an Expander control – in Bootstrap it’s called Collapsible Panel. In the WinUI ecosystem the Expander is relatively new. Its proposal came only at the end of 2020. The control was built to these specs, and eventually appeared in the latest releases of WinUI 3. Before, an expander control was available via Community Toolkit.

We decided to take this new control for a test drive through some scenarios.

Getting started with the Expander Control

Before starting to experiment with the Expander control, take your time to read its documentation and open the WinUI 3 Gallery app. Here’s how the Expander demo looks like – pretty basic:

Our own sample app starts with an Intro page. Here’s the XAML definition of the very first Expander – again pretty basic:

<Expander>
    <Expander.Header>
        <TextBlock Foreground="Red">Red</TextBlock>
    </Expander.Header>
    <Grid Background="Red"
            Height="100"
            Width="200"
            CornerRadius="4" />
</Expander>

As you see in the next composite screenshot, the Header width is not aligned to the width of the Content. This looks more like a Button with a Flyout than like a ‘classic’ Expander:

If you don’t like this default, you can give the control or the Header a Width, or place it in a parent Panel that has its HorizontalContentAlignment set to Stretch. Here’s the XAML of the second expander on the Intro page – the one with the orange. The header has a fixed width that corresponds to the width of the Content. We also added an icon in that Header:

<Expander Width="234">
    <Expander.Header>
        <Grid>
            <Grid.ColumnDefinitions>
                <ColumnDefinition Width="auto" />
                <ColumnDefinition Width="*" />
            </Grid.ColumnDefinitions>
            <BitmapIcon UriSource="/Assets/Orange.png"
                        ShowAsMonochrome="False"
                        Height="24" />
            <TextBlock Foreground="Orange"
                        HorizontalAlignment="Right"
                        VerticalAlignment="Center"
                        Grid.Column="1">Orange</TextBlock>
        </Grid>
    </Expander.Header>
    <Grid Background="Orange"
            Height="100"
            Width="200"
            CornerRadius="4" />
</Expander>

In the Intro page we stacked several expanders on top of each other – a common use of expanders that we elaborate on later in this article.

Building a Horizontal Expander

Unlike the ‘old’ WPF Expander, the WinUI 3 Expander only expands vertically: its ExpandDirection is limited to up and down. We made an attempt to implement a horizontal Expander – without altering the original XAML template. We hosted the control in a ContentControl that we submitted to a RenderTransform with a 90° Rotation to the left, and then rotated its Content back 90° to the right:

<ContentControl x:Name="HorizontalExpander"
                VerticalContentAlignment="Top">
    <ContentControl.RenderTransform>
        <RotateTransform Angle="270"
                            CenterX="117"
                            CenterY="117" />
    </ContentControl.RenderTransform>
    <Expander Width="234">
        <Grid Background="LightPink"
                Height="100"
                Width="200"
                CornerRadius="4">
            <BitmapIcon UriSource="/Assets/Heart.png"
                        ShowAsMonochrome="False"
                        VerticalAlignment="Center"
                        HorizontalAlignment="Center"
                        Height="80">
                <BitmapIcon.RenderTransform>
                    <RotateTransform Angle="90"
                                        CenterX="40"
                                        CenterY="40" />
                </BitmapIcon.RenderTransform>
            </BitmapIcon>
        </Grid>
    </Expander>
</ContentControl>

This is how the result looks like – it’s the pink one:

This horizontal expander looks functional, but it does not behave the same as the original. Under the hood, it has become a square control. We had to give the text box on its right a negative margin:

<TextBlock Text="Hello there"
            RelativePanel.RightOf="HorizontalExpander"
            Margin="-166 0 0 0" />

Also, it does not push its neighbors aside when expanding.

If you want to build a better WinUI 3 horizontal expander, you may get some inspiration in the source code of the Windows Community Toolkit UWP Expander. Just like its WPF and Silverlight ancestor, this one also expands to left and right.

Building an Accordion Control

A traditional usage of Expander controls is stacking some instances on top of each other to form an Accordion. Silverlight and WPF had one in their Toolkit, Syncfusion has an Accordion for UWP, and ComponentOne has one for WinUI.

Let’s see how far we can get with building an Expander-based WinUI 3 Accordion from scratch. [Spoiler alert: pretty far.]

Here are our requirements:

  • there should always be one accordion item open,
  • all other items should be closed, and
  • the accordion should always fill its vertical space.

Here’s how the AccordionPage in our sample app looks like:

The core XAML structure of the control consists of Expanders inside an ItemsControl inside some Panel to horizontally stretch them all, and decorated with a ScrollViewer:

<RelativePanel x:Name="Host"
                VerticalAlignment="Stretch"
                HorizontalAlignment="Stretch">
    <ScrollViewer x:Name="ScrollViewer">
        <ItemsControl x:Name="Accordion"
                        Width="480">
            <Expander HorizontalAlignment="Stretch"
                        HorizontalContentAlignment="Stretch">
                <!-- Header and Content here ... -->
            </Expander>
            <!-- More Expanders here ... -->
        </ItemsControl>
    </ScrollViewer>
</RelativePanel>

When the page opens, we add all expanders to a list, register an event handler, and open the first one by setting IsExpanded:

readonly List<Expander> _expanders = new();

private void AccordionPage_Loaded(object sender, RoutedEventArgs e)
{
    foreach (Expander expander in Accordion.Items)
    {
        _expanders.Add(expander);
        expander.Expanding += Expander_Expanding;
    }

    // Open the first one.
    ApplyScrollBar();
    _expanders[0].IsExpanded = true;
}

In the Expanding event handler, we loop through the list to close all expanders except the selected one. The open expander cannot be manually closed by clicking the header or the toggle button. It is forced to remain open by ‘locking’ its header:

private void Expander_Expanding(
     Expander sender, 
     ExpanderExpandingEventArgs args)
{
    foreach (var expander in _expanders)
    {
        // Close the others.
        expander.IsExpanded = sender == expander;

        // Force current to remain open by disabling the header.
        expander.IsLocked(sender != expander);
    }

    FillHeight(sender);
}

The IsLocked() in that code snippet is an extension method that we wrote:

public static void IsLocked(this Expander expander, bool locked)
{
    ((expander.Header as FrameworkElement).Parent as Control).IsEnabled = locked;
}

[Note: In a full-fledged Accordion control we would implement IsLocked as a dependency property.]

To ensure that the Accordion always fills its vertical space, we manipulate the height of the Content of the one open Expander. We couldn’t find a way to do this with stretching and binding, so we ended up calculating and explicitly setting the Height. When you look at the default XAML template there’s padding on top, padding on the bottom, and a border around the core content to consider in the calculation. So, when the app starts, we measure the height of the Accordion with all its expanders closed, and we store the total vertical padding around the content:

double _closedAccordionHeight;
readonly double _minimumContentHeight = 48;
double _contentPadding;

private void AccordionPage_Loaded(object sender, RoutedEventArgs e)
{
    _closedAccordionHeight = Accordion.ActualHeight;

    // ...

    _contentPadding = _expanders[0].Padding.Top + _expanders[0].Padding.Bottom + 2; // Border?

    Host.SizeChanged += Host_SizeChanged;
}

When the hosting panel is resized, and when a new expander opens, we calculate and set the height of the current item, respecting a minimum height for the content:

private void FillHeight(Expander expander)
{
    expander.SetContentHeight(Math.Max(
        _minimumContentHeight, 
        Host.ActualHeight - _closedAccordionHeight - _contentPadding));
}

Here’s the implementation of our SetContentHeight() extension method:

public static void SetContentHeight(this Expander expander, double contentHeight)
{
    (expander.Content as FrameworkElement).Height = contentHeight;
}

Here’s the event handler that’s fired when the page is resized;

private void Host_SizeChanged(object sender, SizeChangedEventArgs e)
{
    ApplyScrollBar();
    FillHeight(Current);
}

The ApplyScrollBar() call is there to ensure that the scroll bar does not pop up unnecessarily during the Expanding event where two of the expanders may be open simultaneously. Here’s the code:

private void ApplyScrollBar()
{
    if (Host.ActualHeight >= _closedAccordionHeight + _minimumContentHeight + _contentPadding)
    {
        ScrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Hidden;
    }
    else
    {
        ScrollViewer.VerticalScrollBarVisibility = ScrollBarVisibility.Auto;
    }
}

Here’s how the height adjustment looks like at runtime:

We did not refactor the code into a stand-alone custom control, but we believe that this would be a relatively easy task. A future ‘WinUI 3 Community Toolkit’ could have an Accordion control…

In meantime we’re taking our Expander test a step further, to build the core code for a Wizard control.

Building a Wizard Control

In this last Expander experiment we tried to figure out how Expanders deals with data templates and data binding. We reenacted the old ASP.NET Wizard control, designed to guide the user through a sequence of steps. It looks like an Accordion with extra controls in each expander’s Header (to display the status/result of the step) and a set of fixed controls in each expander’s Content (description of the step, buttons to move back and forth). Here’s how it looks like:

Here are some or our requirements. Most of these were at least partly implemented:

  • multiple steps may be open,
  • the control occupies all available horizontal space,
  • the result of a step appears in its header,
  • a description of the step appears in its content,
  • the content shows the navigation buttons,
  • the navigation button text depends on the position (e.g., no ‘go back’ in the first step),
  • steps may be defined as ‘execute only once’ (e.g., payment)

[Disclaimer: our code base was written to evaluate the Expander. The Wizard code is not complete and it contains mockups.]

We started with designing a set of lightweight bindable viewmodels for wizard and steps. Here’s a class diagram:

[Note: here’s how to add the Class Diagram feature to your Visual Studio 2022]

We used Microsoft MVVM Toolkit to implement change notification and ‘bindability’. Here’s part of the code for the WizardViewModel. It hosts a list of viewmodels for the Steps and the logic to determine the Next and Previous of a particular step:

internal partial class WizardViewModel : ObservableObject
{
    [ObservableProperty]
    private List<WizardStepViewModel> _steps = new();

    internal WizardStepViewModel NextStep(WizardStepViewModel step)
    {
        var stepIndex = _steps.IndexOf(step);
        if (stepIndex < _steps.Count - 1)
        {
            return _steps[stepIndex + 1];
        }

        return null;
    }

    internal WizardStepViewModel PreviousStep(WizardStepViewModel step)
    {
        var stepIndex = _steps.IndexOf(step);
        if (stepIndex > 0)
        {
            return _steps[stepIndex - 1];
        }

        return null;
    }
}

The WizardStepViewModel contains the properties and button labels to be displayed, as well as the commands behind the navigation buttons and whether these buttons should be enabled. When the ‘continue’ button is clicked, we validate and commit the current step and then navigate forward:

private void Next_Executed()
{
    if (!Commit())
    {
        return;
    }

    var next = _wizard.NextStep(this);

    if (next is null)
    {
        return;
    }

    IsActive = false;
    next.IsActive = true;
}

public bool Commit()
{
    // Validate and persist Model
    // ...

    // Update Status - Mockup
    Status = "Succes";

    // Return result
    return true;
}

The basic XAML structure of the Wizard is the same as in the Accordion: Panel, ScrollViewer, ItemsControl with Expanders. It just comes with more data binding and data templates:

<RelativePanel x:Name="Host"
                VerticalAlignment="Stretch"
                HorizontalAlignment="Stretch">
    <ScrollViewer x:Name="ScrollViewer"
                    HorizontalAlignment="Stretch"
                    HorizontalContentAlignment="Stretch">
        <ItemsControl x:Name="Wizard"
                        HorizontalAlignment="Stretch"
                        HorizontalContentAlignment="Stretch">
            <Expander HeaderTemplate="{StaticResource WizardHeaderTemplate}"
                        ContentTemplate="{StaticResource WizardContentTemplate}"
                        HorizontalAlignment="Stretch"
                        HorizontalContentAlignment="Stretch"
                        DataContext="{x:Bind ViewModel.Steps[0], Mode=OneWay}"
                        IsExpanded="{x:Bind ViewModel.Steps[0].IsActive, Mode=TwoWay}">
                <!-- Custom Content here ... -->
            </Expander>
            <!-- More Expanders here ... -->
        </ItemsControl>
    </ScrollViewer>
</RelativePanel>

Via a DataTemplate the Header of each Expander has an extra text block displaying the Status (e.g. execution status, result, validation error) of the corresponding step:

<DataTemplate x:Name="WizardHeaderTemplate"
                x:DataType="viewmodels:WizardStepViewModel">
    <Grid>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="auto" />
        </Grid.ColumnDefinitions>
        <TextBlock Text="{x:Bind Name, Mode=OneWay}" />
        <TextBlock Text="{x:Bind Status, Mode=OneWay}"
                    FontStyle="Italic"
                    Grid.Column="1" />
    </Grid>
</DataTemplate>

The content is also displayed through a data template. It has a description for the current step, buttons to move through the scenario, and a ContentPresenter for the core content:

<DataTemplate x:Name="WizardContentTemplate"
                x:DataType="viewmodels:WizardStepViewModel">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="auto" />
            <RowDefinition Height="auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*" />
            <ColumnDefinition Width="auto" />
        </Grid.ColumnDefinitions>

        <!-- Custom Content -->
        <ContentPresenter Content="{Binding Content, RelativeSource={RelativeSource TemplatedParent}}" />

        <!-- Default Content -->
        <TextBlock Text="{x:Bind Description, Mode=OneWay}"
                    Grid.Column="1" />
        <StackPanel Orientation="Horizontal"
                    HorizontalAlignment="Right"
                    VerticalAlignment="Bottom"
                    Grid.Row="1"
                    Grid.Column="1">
            <Button Command="{x:Bind PreviousCommand, Mode=OneWay}"
                    Content="{x:Bind PreviousLabel, Mode=OneWay}" />
            <Button Command="{x:Bind NextCommand, Mode=OneWay}"
                    Content="{x:Bind NextLabel, Mode=OneWay}"
                    Margin="10 0 0 0" />
        </StackPanel>
    </Grid>
</DataTemplate>

For custom content we went for simple images. Here’s an example:

<Expander HeaderTemplate="{StaticResource WizardHeaderTemplate}"
            ContentTemplate="{StaticResource WizardContentTemplate}"
            HorizontalAlignment="Stretch"
            HorizontalContentAlignment="Stretch"
            DataContext="{x:Bind ViewModel.Steps[1], Mode=OneWay}"
            IsExpanded="{x:Bind ViewModel.Steps[1].IsActive, Mode=TwoWay}"
            Margin="0 4 0 0">
    <BitmapIcon UriSource="/Assets/Seating.png"
                ShowAsMonochrome="False"
                VerticalAlignment="Stretch"
                HorizontalAlignment="Center"
                Height="240" />
</Expander>

When the page opens, we populate the main ViewModel:

var viewModel = new WizardViewModel();
viewModel.Steps = new List<WizardStepViewModel>()
    {
    new WizardStepViewModel(viewModel)
    {
        IsActive = true,
        AllowReturn = true,
        Name = "Your movie",
        Description = "Select a movie and a time"
    },
    new WizardStepViewModel(viewModel)
    {
        AllowReturn = true,
        Name = "Your seat",
        Description = "Select a row and a seat"
    },
    new WizardStepViewModel(viewModel)
    {
        AllowReturn = false,
        Name = "Yourself",
        Description = "Provide contact information"
    },
    new WizardStepViewModel(viewModel)
    {
        AllowReturn = false,
        Name = "Your money",
        Description = "Select a payment method and ... pay"
    }
};
DataContext = viewModel;

There’s no further code behind, except to stretch the Wizard horizontally:

private void Host_SizeChanged(object sender, SizeChangedEventArgs e)
{
    ScrollViewer.Width = Host.ActualWidth;
}

By leveraging the new Expander control we were able to build the foundations of a Wizard control with remarkably few lines of code. Here’s how our Wizard looks like in action:

Not too bad, right?

The Verdict

The Expander control is a great new member of the WinUI 3 ecosystem. It definitely passed our stress test. We identified some room for improvement however. Future versions of the control could benefit from

  • horizontal expansion out-of-the-box,
  • a more complete set of events (Expanded, Collapsing), and
  • an easy way to lock the Header in closed or open position.

Our sample app lives here on GitHub.

Enjoy!

Multi-Windowing in WinUI 3

In this article we take a look at multi-windowing in a WinUI 3 Desktop application. The ability for an app to control multiple windows -possibly over multiple monitors- is a key differentiator to Web development. We’ll demonstrate

  • opening multiple windows in a WinUI 3 app,
  • messaging between components using MVVM Toolkit and Dependency Injection, and
  • controlling window properties using WinUIEx.

We built a sample WinUI 3 Desktop app. This is how it looks like on Windows 10:

Here’s the list of the Nuget packages that we installed in the project:

The app comes with some Pages displayed in two Window classes:

  • the default Main Window (it’s called Shell in our app) is opened when the app starts, and
  • a DetailWindow is opened with every click on the launch button on our home page.

Most WinUI apps only have a main window. It’s often populated with a NavigationView menu and a Frame to host the different page types. Our sample app has a similar main window, but it will allow opening extra detail windows. Here’s the XAML structure of that detail window: unsurprisingly a Window with a Page.

<Window x:Class="XamlBrewer.WinUI3.MultiWindow.Sample.Views.DetailWindow">
    <Page x:Name="Page">

        <Page.DataContext>
            <viewmodels:DetailPageViewModel />
        </Page.DataContext>

        <Grid x:Name="Root">
            <!-- ... -->
        </Grid>
    </Page>
</Window>

Opening extra windows

To open a new window, just instantiate one and call Activate(). Here’s the core code behind our launch button:

private void LaunchButton_Click(object sender, RoutedEventArgs e)
{
    DetailWindow window = new();
    // ..
    window.Activate();

    Unloaded += (sender, e) => { window.Close(); };
}

All Window instances run on their own foreground thread. When you close the main window, the app does not stop. The detail windows remain open and keep the app running. That might not always be the desired behavior. In our sample app, we ensure that the satellite detail windows are closed together with the home page. We scheduled their Close() in an Unloaded event handler on the home page. This is convenient, but don’t consider this a best practice. Using an event handler implies that the home page is holding references to the detail windows – which will NOT be garbage collected when you close them. There are better ways of messaging available; we’ll come to that a bit later.

Controlling window properties

If an app requires multi-window support, some of the windows will host tools, extra controls, or dashboards. You may want to control the size and behavior of these. In the current state of WinUI 3 this involves low level interop with framework dll’s and dealing with window handles. Fortunately, Morten Nielsen already did the heavy lifting and shared it in the WinUIEx project -a set of WinUI 3 extension methods on windowing, authentication, and testing.

Here’s how we control the size, position, and the command bar behavior of our detail windows. All of these are WinUIEx extension method calls:

DetailWindow window = new();
window.SetIsMaximizable(false);
window.SetIsResizable(false);
window.SetWindowSize(360, 240);
window.CenterOnScreen();

Messaging

With your models, view, and services possibly scattered around multiple windows, you may want to consider a decent messaging infrastructure to allow communication between these components. In our sample app we didn’t want to keep a central list of detail windows. However, we do need to send a set of messages between them, and also to and from the home page and main window.

MVVM Toolkit is a great choice for this task. Let’s use it in a couple of scenarios.

Window to Window communication

Our main window has a button to switch the Theme. We broadcast the theme change to all other windows in the app. MVVM Toolkit Messenger is the right component for this job.

First, we defined a message class for transporting the theme after a change – a ValueChangedMessage:

public class ThemeChangedMessage : ValueChangedMessage<ElementTheme>
{
    public ThemeChangedMessage(ElementTheme value) : base(value)
    {
    }
}

Via MVVM Toolkit base classes and interfaces, Models and ViewModels have multiple ways of getting access to the Messager service. Since they rely on inheritance, most of these ways are not available to Window instances. We called a little help from our friends Microsoft.Extensions.DependencyInjection and the MVVM Toolkit Ioc service provider helper.

When our app starts, a call to ConfigureServices() ensures that there’s an inversion-of-control container with a Messenger instance available to all components of the app:

Ioc.Default.ConfigureServices
    (new ServiceCollection()
        .AddSingleton<IMessenger>(WeakReferenceMessenger.Default)
        .BuildServiceProvider()
    );

When the Theme changes, the main window fetches the Messenger instance with GetService() and broadcasts the message with Send(). It does not need to know if there are other windows or how much there are:

Ioc.Default.GetService<IMessenger>().Send(
    new ThemeChangedMessage(Root.ActualTheme)
);

Let’s take a look at the receiver. When a detail window instance starts, it approaches the Messenger to Register() a delegate to be called on an incoming ThemeChangedMessage:

Root.ActualThemeChanged += Root_ActualThemeChanged;

var messenger = Ioc.Default.GetService<IMessenger>();

messenger.Register<ThemeChangedMessage>(this, (r, m) =>
{
    Root.RequestedTheme = m.Value;
});

// Don't forget!
Closed += (sender, e) => { messenger.UnregisterAll(this); };

When a detail window is closed we call UnregisterAll() to ensure all further messages are ignored by it. It may take a while before it is garbage collected -especially with our Unloaded event handler in the home page- and we don’t want closed windows to crash our app.

Here’s the result of switching the theme from dark to light:

Here’s a second scenario for window-to-window communication. To visually keep track of the detail windows, our home page has a button that makes all of them move to the foreground. Here’s the message definition – a plain C# class without the need for a particular base class or interface:

public class RaiseMessage { }

Here’s the call behind the button on the home page:

private void RaiseButton_Click(object sender, RoutedEventArgs e)
{
    Ioc.Default.GetService<IMessenger>().Send(new RaiseMessage());
}

Here’s the registration and response of the detail windows – and an opportunity to demonstrate yet another useful WinUIEx extension:

messenger.Register<RaiseMessage>(this, (r, m) =>
{
    this.SetForegroundWindow();
});

This is how it looks like at runtime. Here’s a screenshot before the call:

And here’s the ‘after’ shot:

ViewModel to ViewModel communication

Our next use case is on ViewModel-to-ViewModel communication. We have more help from MVVM Toolkit, since this is right in its core business. Here’s the scenario: our detail windows are actually mining bitcoin diamonds. When diamonds are found, a NumberBox is filled for the quantity and a button pressed. The information will then be broadcasted to the ecosystem.

Here’s the core XAML structure of the page in the detail window:

<Page>
    <Page.DataContext>
        <viewmodels:DetailPageViewModel />
    </Page.DataContext>
    <Grid x:Name="Root">
        <!-- Some content omitted ... -->
        <RelativePanel VerticalAlignment="Top"
                        HorizontalAlignment="Right">
            <NumberBox x:Name="DiamondsBox"
                        Header="Diamonds"
                        Value="{x:Bind ViewModel.Diamonds, Mode=TwoWay}" />
            <Button RelativePanel.Below="DiamondsBox"
                    Command="{x:Bind ViewModel.DiamondsFound}">Collect</Button>
        </RelativePanel>
    </Grid>
</Page>

Here’s the message definition – a ValueChangedMessage that transports an integer:

public class AssetsChangedMessage : ValueChangedMessage<int>
{
    public AssetsChangedMessage(int value) : base(value)
    { }
}

Here’s how (part of) the detail page ViewModel looks like:

public partial class DetailPageViewModel : 
    ObservableRecipient
{
    [ObservableProperty]
    private int diamonds;

    public DetailPageViewModel()
    {
        DiamondsFound = new RelayCommand(DiamondsFound_Executed);

        Messenger.Register(this);
    }

    // This code will be generated by the field attribute.
    // public int Diamonds
    // {
    //     get => diamonds
    //     set => SetProperty(ref diamonds, value);
    // }

    public ICommand DiamondsFound { get; }

    private void DiamondsFound_Executed()
    {
        Messenger.Send(new AssetsChangedMessage(Diamonds));
    }
}

Via inheritance from ObservableRecipient, the ViewModel gets access to change notification helpers (from ObservableObject) and to the messaging infrastructure – a Messenger property. Notice that the diamonds field is decorated with ObservableProperty. This conveniently generates the corresponding getter and setter. Via an ICommand property that is backed up by a RelayCommand, the method that sends the message is bound to the button in the View – all according to the MVVM pattern.

Here’s the message registering and receiving part in the ViewModel of the home page:

public partial class HomePageViewModel : 
    ObservableRecipient, 
    IRecipient<AssetsChangedMessage>
{
    [ObservableProperty]
    private int wealth;

    public HomePageViewModel()
    {
        Messenger.Register(this);
    }

    // This method is auto-registered by implementing the interface.
    public void Receive(AssetsChangedMessage message)
    {
        Wealth += message.Value;
    }
}

This ViewModel also inherits from ObservableRecipient. On top of that it implements IRecipient<T>. This conveniently auto-registers the Receive<TMessage>() method to the corresponding incoming message type. The home page updates its stock – the Wealth property. This property is also generated through the ObservableProperty attribute on the field variable.

For the sake of completeness (and definitely also for fun) we added another ViewModel-to-ViewModel messaging scenario. When a detail ViewModel finds a diamond, all of its colleagues get excited for a few seconds. Here’s the corresponding code:

public partial class DetailPageViewModel : 
    ObservableRecipient, 
    IRecipient<AssetsChangedMessage>
{
    [ObservableProperty]
    private bool isExcited;

    public DetailPageViewModel()
    {
        DiamondsFound = new RelayCommand(DiamondsFound_Executed);

        Messenger.Register(this);
    }

    public void Receive(AssetsChangedMessage message)
    {
        IsExcited = true;
        CoolDown();
    }

    private void DiamondsFound_Executed()
    {
        Messenger.Unregister<AssetsChangedMessage>(this); // Don't react to own message.
        Messenger.Send(new AssetsChangedMessage(Diamonds));
        Messenger.Register(this);
    }
}

There’s no need to go into details here: it’s all stuff we just covered before. Here’s how the result looks like.

For the record: the animation in the home page is not Lottie based. Lottie is not yet available for WinUI 3. In mean time we have to help ourselves with animated GIFs.

Multi-windowing is an important feature in Windows development. WinUI 3 desktop apps that use multi-windowing will definitely benefit from the MVVM Toolkit Messenger and from the WinUIEx extension methods.

Our sample app lives here on GitHub.

Enjoy!

A Dialog Service for WinUI 3

I this article we build an MVVM DialogService in a WinUI 3 Desktop application. It comes with the following features:

  • Message Dialog, Confirmation Dialog, Input Dialog
  • Works with {Binding} and {x:Bind}
  • Bindable to Command and Event Handler
  • Callable from View, ViewModel, Service and more
  • Theme-aware

Here’s a screenshot from the corresponding demo app:

The core class of the dialog service is based on the ModalView static class that we built long ago in a similar solution for UWP. It programmatically creates ContentDialog instances for which you can provide the title, and the content of all buttons. Here’s an extract of the original code:

var dialog = new ContentDialog
{
    Title = title,
    PrimaryButtonText = yesButtonText,
    SecondaryButtonText = noButtonText,
    CloseButtonText = cancelButtonText
};

When you run this code in a WinUI 3 Desktop app, nothing really happens. You get a “This element is already associated with a XamlRoot, it cannot be associated with a different one until it is removed from the previous XamlRoot.” exception:

The screenshot above comes from a click event handler. The app crashes and tell you what’s wrong, and that’s OK. However, when using a Command to open the dialog, the binding engine swallows the exception, and the app continues to run without the dialog opening. It’s not a bug -it’s what the binding engine does- but it smells like a bug, so people logged issues in WinUI and people logged issues in Prism.

When programmatically instantiating a ContentDialog in WinUI 3 -even in a View or Page- you need to provide a XamlRoot. We decided to transform our static methods into extension methods for FrameworkElement. Not only does this class come with a XamlRoot property, it also has a RequestedTheme that we can pass to ensure that the dialog follows the app’s theme. Here’s the set of methods to open a message box – a content dialog with a title, a piece of text (the message), and one single button (the classic OK button). It returns no result.

public static async Task MessageDialogAsync(
     this FrameworkElement element, 
     string title, 
     string message)
{
    await MessageDialogAsync(element, title, message, "OK");
}

public static async Task MessageDialogAsync(this FrameworkElement element, string title, string message, string buttonText)
{
    var dialog = new ContentDialog
    {
        Title = title,
        Content = message,
        CloseButtonText = buttonText,
        XamlRoot = element.XamlRoot,
        RequestedTheme = element.ActualTheme
    };

    await dialog.ShowAsync();
}

For each of the dialog types, the call to the extension method in the ModalView class is more or less the same. Only the return type is different: void, bool, nullable bool, string, …

For the first test, we create a button in the View, and call a classic event handler:

<Button Content="Message Dialog"
        Click="MessageBox_Click" />

In the event handler, we use the page itself (this) as the Framework element to pass. Here’s the call:

private async void MessageBox_Click(object sender, RoutedEventArgs e)
{
    await this.MessageDialogAsync("All we are saying:", "Give peace a chance.", "Got it");
}

Here’s how the result looks like:

Our second dialog type is the ConfirmationDialog, with a title and two (Yes/No) or three (Yes/No/Cancel) buttons. Here’s the main extension method for this one:

public static async Task<bool?> ConfirmationDialogAsync(
     this FrameworkElement element, 
     string title, 
     string yesButtonText, 
     string noButtonText, 
     string cancelButtonText)
{
    var dialog = new ContentDialog
    {
        Title = title,
        PrimaryButtonText = yesButtonText,
        SecondaryButtonText = noButtonText,
        CloseButtonText = cancelButtonText,
        XamlRoot = element.XamlRoot,
        RequestedTheme = element.ActualTheme
    };
    var result = await dialog.ShowAsync();

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

    return (result == ContentDialogResult.Primary);
}

To test it, we added a ViewModel, set it as DataContext to the View, and added a button:

<Button Content="2-Button Confirmation Dialog"
        Command="{Binding ConfirmationCommandYesNo}" />

Our next step was finding an appropriate Framework element to pass – not easy in a ViewModel that is unaware of the View it’s bound to. In UWP we could use Window.Current (and its Content). In WinUI 3 Window.Current is still in the API but always returns null. As an alternative we declared a MainRoot property in our application class. It refers to the Content element of the main window of our app, that we traditionally named ‘Shell’. Here’s the declaration and initialization:

public partial class App : Application
{
    private Shell shell;

    public static FrameworkElement MainRoot { get; private set; }

    protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
    {
        shell = new Shell();
        shell.Activate();
        MainRoot = shell.Content as FrameworkElement;
    }
}

When you’re building a multi-window app, you will probably need to change that logic. Anyway, all ViewModels and Services now have access to a Framework element to pass to the dialog service. Here’s how the ViewModel from our sample app opens a 2-Button Confirmation Dialog via an AsyncRelayCommand:

public ICommand ConfirmationCommandYesNo => new AsyncRelayCommand(ConfirmationYesNo_Executed);

private async Task ConfirmationYesNo_Executed()
{
    var confirmed = await App.MainRoot.ConfirmationDialogAsync(
            "What Pantone color do you prefer?",
            "Freedom Blue",
            "Energizing Yellow"
        );
}

This is how the dialog looks like:

The overload (extension) method with three buttons, opens the dialog that you saw in the first screenshot. There’s no need to paste the code here, it’s similar to the previous one.

Let’s jump to another dialog type: an Input Dialog to request a string from the user. In this scenario, we programmatically set a TextBox as Content of the Dialog, and return its text when the Dialog closes:

public static async Task<string> InputStringDialogAsync(
     this FrameworkElement element, 
     string title, 
     string defaultText, 
     string okButtonText, 
     string cancelButtonText)
{
    var inputTextBox = new TextBox
    {
        AcceptsReturn = false,
        Height = 32,
        Text = defaultText,
        SelectionStart = defaultText.Length
    };
    var dialog = new ContentDialog
    {
        Content = inputTextBox,
        Title = title,
        IsSecondaryButtonEnabled = true,
        PrimaryButtonText = okButtonText,
        SecondaryButtonText = cancelButtonText,
        XamlRoot = element.XamlRoot,
        RequestedTheme = element.ActualTheme
    };

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

This time we use {x:Bind} to a command in the View:

<Button Content="String Input Dialog"
        Command="{x:Bind ViewModel.InputStringCommand}" />

Here’s the code in the ViewModel:

public ICommand InputStringCommand => new AsyncRelayCommand(InputString_Executed);

private async Task InputString_Executed()
{
    var inputString = await App.MainRoot.InputStringDialogAsync(
            "How can we help you?",
            "I need ammunition, not a ride.",
            "OK",
            "Forget it"
        );
}

And the result:

The last type of input dialog in this article, is a multi-line text input dialog – typically one that you would use to collect comments or remarks:

public static async Task<string> InputTextDialogAsync(
     this FrameworkElement element, 
     string title, 
     string defaultText)
{
    var inputTextBox = new TextBox
    {
        AcceptsReturn = true,
        Height = 32 * 6,
        Text = defaultText,
        TextWrapping = TextWrapping.Wrap,
        SelectionStart = defaultText.Length
    };
    var dialog = new ContentDialog
    {
        Content = inputTextBox,
        Title = title,
        IsSecondaryButtonEnabled = true,
        PrimaryButtonText = "Ok",
        SecondaryButtonText = "Cancel",
        XamlRoot = element.XamlRoot,
        RequestedTheme = element.ActualTheme
    };

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

For the sake of completeness, we’ll bind it to an event handler in the ViewModel:

<Button Content="Text Input Dialog"
        Click="{x:Bind ViewModel.InputText_Click}" />

Here’s the code in the ViewModel:

public async void InputText_Click(object sender, RoutedEventArgs e)
{
    var inputText = await App.MainRoot.InputTextDialogAsync(
            "What would Faramir say?",
            "“War must be, while we defend our lives against a destroyer who would devour all; but I do not love the bright sword for its sharpness, nor the arrow for its swiftness, nor the warrior for his glory. I love only that which they defend.”\n\nJ.R.R. Tolkien"
        );
}

And this is what it looks like at runtime:

With just a handful lines of code, we built a dialog service for WinUI 3 Desktop applications. Our sample solution lives here in GitHub. Feel free to add your own dialogs, like for numeric or date input.

Enjoy!

Drawing charts and diagrams with OxyPlot in WinUI 3

In this article we demonstrate how to use H.OxyPlot to draw diagrams and charts in a WinUI 3 .NET 6 desktop application. We created a sample app with different pages that cover:

  • the ‘Hello World’ diagram,
  • an overview of many of the supported charts,
  • working with an interactive model, and
  • instant theming support.

Here’s how the app looks like:

It’s our first Visual Studio 2022 .NET 6 app. In some of these pages we test drive the new version of the Microsoft MVVM Toolkit, but our focus remains on OxyPlot.

OxyPlot

OxyPlot is an Open Source .NET library for drawing diagrams and charts on multiple platforms. It comes with a core object model for models, series, axes, annotations etcetera. Further there’s a RenderContext / PlotView pair for each UI technology: WPF, WinForms, SkiaSharp, UWP, Xamarin, … Windows Phone and Silverlight. The different PlotView implementations are relatively simple – a Canvas and a crosshair Tracker. The complexity of the UI platform specific items lies in the RenderContext implementations that physically draw the PlotModel’s colors, brushes, fonts, lines, ellipses, and geometries. Take a look at the renderers for WinUI 3, Windows Forms, Skia, and WPF.

The OxyPlot team is currently very busy refactoring its core code. They’re not focusing on creating PlotViews or RenderContexts for the more recent .NET UI platforms like WinUI 3. This is where H.OxyPlot comes in. H.OxyPlot is a project that provides UWP, WinUI 3, and Uno support for the latest OxyPlot core library. It combines the (abandoned) OxyPlot UWP code with the current version of the WPF code. It tries to match the latter as closely as possible in behavior, while adding some extra features – like Dark Theme support. It’s this version that we use in our sample app. Here’s an overview of that app’s dependencies:

Hello World

The HomePage of our sample app shows OxyPlot usage in its simplest form. The page exposes a PlotModel property: a model consisting of two axes and one line series with just a handful of points:

public PlotModel Model { get; private set; } = new PlotModel
{
    Title = "Hello WinUI 3",
    PlotAreaBorderColor = OxyColors.Transparent,
    Axes =
    {
        new LinearAxis { Position = AxisPosition.Bottom },
        new LinearAxis { Position = AxisPosition.Left },
    },
    Series =
    {
        new LineSeries
        {
            Title = "LineSeries",
            MarkerType = MarkerType.Circle,
            Points =
            {
                new DataPoint(0, 0),
                new DataPoint(10, 18),
                new DataPoint(20, 12),
                new DataPoint(30, 8),
                new DataPoint(40, 15),
            }
        }
    }
};

The PlotModel is bound to a PlotView in the page declaration:

<oxy:PlotView Model="{x:Bind Model}" />

That’s all there is! Here’s how the Hello World page looks like:

Model Gallery

We mentioned that the OxyPlot core components are being refactored, and that the WinUI 3 controls are brand new. That sounds like thin ice to walk on, no?  Well, to get an idea of the state of both projects and their usability in production quality apps, we decided to create a page with as much different models as possible. All of these diagrams were straight copied from the official OxyPlot samples repository. We just removed titles and legends.

Welcome to the Model Gallery page. Here’s how it looks like:

The page has its own ViewModel that exposes different PlotModel properties. The ViewModel is declared as DataContext of the page – classic MVVM style:

<Page.DataContext>
    <viewmodels:MultiPageViewModel x:Name="ViewModel" />
</Page.DataContext>

All PlotModels are declaratively bound to PlotView instances:

<oxyplot:PlotView Model="{x:Bind ViewModel.BarSeriesModel}" />

Here’s the ViewModel code for the PieSeries model property:

pieSeriesModel = new PlotModel(); 

pieSeriesModel.PlotAreaBorderColor = OxyColors.Transparent;

    dynamic seriesP1 = new PieSeries 
    { 
        StrokeThickness = 2.0, 
        InsideLabelPosition = 0.8, 
        AngleSpan = 360, 
        StartAngle = 0 
    };

seriesP1.Slices.Add(new PieSlice("Africa", 1030) 
    { 
        IsExploded = false, 
        Fill = OxyColors.PaleVioletRed 
    });
seriesP1.Slices.Add(new PieSlice("Americas", 929) 
    { 
        IsExploded = true 
    });
seriesP1.Slices.Add(new PieSlice("Asia", 4157) 
    { 
        IsExploded = true 
    });
seriesP1.Slices.Add(new PieSlice("Europe", 739) 
    { 
        IsExploded = true 
    });
seriesP1.Slices.Add(new PieSlice("Oceania", 35) 
    { 
        IsExploded = true 
    });

pieSeriesModel.Series.Add(seriesP1);

All diagrams are defined and created in exactly the same way, and the corresponding PlotViews are hooked in a VariableSizedWrapGrid. When the window is resized, we let all items move smoothly to their new position with a RepositionThemeTransition:

<GridViewItem.Transitions>
    <TransitionCollection>
        <RepositionThemeTransition />
    </TransitionCollection>
</GridViewItem.Transitions>

We did not observe any annoying performance or rendering issue that would make us decide not to use OxyPlot or H.OxyPlot in a production app. So, on to another test.

Interactive Model

Sometimes we need to display a chart with ever changing, dynamic data. Our sample app has a page to simulate this. The Interactive Model page has a PlotView populated with three StemSeries picturing differently parameterized normal distributions. With a Slider below the diagram, you can change the mean and the variance of the distributions. It will update all series in more or less real time. Here’s how it looks like in action:

Again, we use a ViewModel. Here are its main properties:

public PlotModel Model => model;

public double Variance
{
    get => variance;
    set
    {
        SetProperty(ref variance, value);
        updateSeries();
    }
}

The page controls are bound to these properties:

<oxyplot:PlotView Model="{x:Bind ViewModel.Model}"
                    Background="Transparent" />
<Slider Value="{x:Bind ViewModel.Variance, Mode=TwoWay}"
        Minimum="-5"
        Maximum="5"
        StepFrequency=".25"
        Margin="20 0 10 0"
        Grid.Row="1" />

Observe that the PlotView uses {x:Bind} with its default mode: OneTime. There’s no need to use classic data binding here. The PlotModel knows its PlotView and can broadcast its updates itself. It does not do this automatically – you’ll need an InvalidatePlot() call, like the one at the end of the following method. The call has a true parameter to indicate that the data was changed (and things like axes minima and maxima need to be recalculated):

private void updateSeries()
{
    model.Series.Clear();
    model.Series.Add(CreateNormalDistributionSeries(-5, 5, Variance, 0.2));
    model.Series.Add(CreateNormalDistributionSeries(-5, 5, -5, 5 + Variance));
    model.Series.Add(CreateNormalDistributionSeries(-5, 5, 5, 5 - Variance));

    model.InvalidatePlot(true);
}

In scenarios with very high update frequency of very large data series, you may not get the performance that you would like. In that case we advise you to look for (or develop) an OxyPlot PlotView/RenderContext pair that’s based on SkiaSharp. The SkiaSharp WPF rendering pretends to be 100 times faster than the WPF Canvas. We did not find a WinUI 3 compatible version of such a view…

Instant Theming

OxyPlot was developed way before Dark Theme or instant theming were a thing: models natively come a white background and black titles, axis lines and labels. If you want to change that, you need to recolor everything individually. H.OxyPlot (the WinUI 3 / Uno version) on the other hand adds a nice feature: it has with built-in support for a Dark Theme, and it uses a great trick for this. When the RequestedTheme is dark, the RenderContext reverses black and white by swapping the cached brushes for these colors:

if (color == OxyColors.Black)
{
    color = OxyColors.White;
}
else if (color == OxyColors.White)
{
    color = OxyColor.FromArgb(255, 32, 32, 32);
}

This looks too simply to be true, but it works surprisingly well for most of the diagram types.

In our sample app we wanted to take it a step further and support instant theme switching. We created a page with a copy of the Hello World model, and some other ones.

OxyPlot comes with its own color classes. This should not come as a surprise, since every .NET UI stack also has its own. But for XAML developers, that means that you cannot (at least directly) rely on Theme resource dictionaries. And even if the brushes and colors were compatible, XAML theming, styling, and templating would still not work. The PlotView is a Canvas on the outside, but apart from the crosshair Tracker there’s no XAML in it. Here’s a screenshot of the Live Visual Tree:

We couldn’t resist, and actually did some XAML theming. We replaced the black crosshair Tracker lines by the system accent color:

<oxy:PlotView.DefaultTrackerTemplate>
    <ControlTemplate>
        <oxy1:TrackerControl Position="{Binding Position}"
                                LineStroke="{ThemeResource SystemColorControlAccentBrush}"
                                Background="GhostWhite">
            <oxy1:TrackerControl.Content>
                <TextBlock Text="{Binding}"
                            Margin="5" />
            </oxy1:TrackerControl.Content>
        </oxy1:TrackerControl>
    </ControlTemplate>
</oxy:PlotView.DefaultTrackerTemplate>

And we like wat we see – in Dark as well as Light themes:

Let’s go back to instant theming. When the Theme is changed, we change the color of all text elements in the Hello World model, as well as the color of the axes and their ticks:

var foreground = theme == ElementTheme.Light 
    ? OxyColor.FromRgb(32, 32, 32) 
    : OxyColors.WhiteSmoke;

helloWorldmodel.TextColor = foreground;
foreach (var axis in helloWorldmodel.Axes)
{
    axis.TicklineColor = foreground;
    axis.AxislineColor = foreground;
}

We refactored this code into an extension method that toggles the color of all non-transparent items of these types:

public static void ApplyTheme(this PlotModel plotModel, ElementTheme theme)
{
    var foreground = theme == ElementTheme.Light 
        ? OxyColor.FromRgb(32, 32, 32)
        : OxyColors.WhiteSmoke;

    if (plotModel.TextColor != OxyColors.Transparent)
    {
        plotModel.TextColor = foreground;
    }

    foreach (var axis in plotModel.Axes)
    {
        if (axis.TicklineColor != OxyColors.Transparent)
        {
            axis.TicklineColor = foreground;
        }
        if (axis.AxislineColor != OxyColors.Transparent)
        {
            axis.AxislineColor = foreground;
        }
    }

    plotModel.InvalidatePlot(false);
}

We made this extension method run against all the models in the Model Gallery page. It may need some fine tuning but so far, we are happy with the result – check the screenshots above.

There’s more in a diagram than texts and axes: some of the series might also need some theming support. Most of the bars, lines, columns, dots, and areas look very good if you apply a decent color scheme. If the series’ shapes have a fill color and borders or decorations (e.g., an area series has a fill, upper border and lower border), then you may need some extra code for theming support.

A first example is the boxplot chart in the lower left corner. We assigned it two matching colors (a light and a dark) and swap these when the theme changes:

var series = boxPlotModel.Series[0] as BoxPlotSeries;
if (theme == ElementTheme.Light)
{
    series.Fill = OxyColors.LightSteelBlue;
    series.Stroke = OxyColors.LightSlateGray;
}
else
{
    series.Fill = OxyColors.LightSlateGray;
    series.Stroke = OxyColors.LightSteelBlue;
}

Another way of blending the shapes of a series better into the theme, is make them semi-transparent. As a bonus there’s no extra code to execute when the theme changes. That’s what we did to the pie chart in the lower right corner of the test page:

foreach (var slice in (pieChartModel.Series.First() as PieSeries).Slices)
{
    var color = slice.ActualFillColor;
    slice.Fill = OxyColor.FromArgb(90, color.R, color.G, color.B);
}

Here’s how all of this looks like:

For the sake of completion, here’s how the instant theming support is implemented. When the page is loaded and all controls are accessible, we apply the current theme:

public InteractivePage()
{
    InitializeComponent();
    Loaded += Page_Loaded;
}

private void Page_Loaded(object sender, RoutedEventArgs e)
{
    ApplyTheme(ActualTheme);
}

private void ApplyTheme(ElementTheme theme)
{
    ViewModel.Model.ApplyTheme(theme);
}

As long as the page is active, we listen to ActualThemeChanged and react appropriately:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    ActualThemeChanged += Page_ActualThemeChanged;
    base.OnNavigatedTo(e);
}

protected override void OnNavigatedFrom(NavigationEventArgs e)
{
    ActualThemeChanged -= Page_ActualThemeChanged;
    base.OnNavigatedFrom(e);
}

private void Page_ActualThemeChanged(FrameworkElement sender, object args)
{
    ApplyTheme(sender.ActualTheme);
}

The exact same logic is applied in the Model Gallery page and the Interactive Model page. And of course, we also tested everything in an app that’s more representative than our small sample. Here’s the result:

It’s one of the many pages to which we also applied our Master-Detail pattern. We added an area chart that supports instant theming. Since this type of chart does not allow to select an item (like a bar, a column, or a pie slice) we decorated it with a RectangleAnnotation to highlight the selected zone.

The Verdict

In our sample app we demonstrated how to use H.OxyPlot to draw diagrams and charts in a WinUI 3 .NET 6 desktop application. The project definitely meets enterprise development standards, and it sets the bar high for third party paid components.

Our sample app lives here on GitHub.

Enjoy!

Building a Master-Detail page with WinUI 3 and MVVM

In this article we show how to build a Master-Detail page in a WinUI 3 Desktop application. The Master-Detail (a.k.a. List-Details) pattern that we’ll build is inspired by the popular mail client UI. It comes with

  • a (filterable) list pane on the left,
  • a details pane on the right,
  • a command bar on top,
  • a command bar on mouse-over within in the list item, and
  • a dialog for editing and adding items.

We will make maximum use of the more recent WinUI 3 controls for the desktop, compiled bindings (x:Bind), and Microsoft MVVM Toolkit. If you’re already into Visual Studio 2022, then we propose go for the brand-new .NET Community Version of this MVVM Toolkit– it’s the same code but in a different namespace.

We built a small sample Win32 Desktop app, here’s how that looks like:

It uses Windows App SDK and MVVM Toolkit:

Here’s a class diagram of our pattern. We did not make any assumptions on the Model, also the generic ViewModel and the XAML structure of the View were designed with reusability in mind:

Model

The Model in our sample app implements INotifyPropertyChanged to facilitate data binding. It represents a character in a small screen series, it is called Character and it inherits from ObservableObject. We defined its fields and decorated them with ObservableProperty attributes. These attributes trigger the new MVVM source code generator for the properties – which also explains why the class is defined as partial:

public partial class Character : ObservableObject
{
    [ObservableProperty]
    private string name;

    [ObservableProperty]
    private string kind;

    [ObservableProperty]
    private string description;

    [ObservableProperty]
    private string imagePath;

    // ...
}

Our sample app uses the Name of the Character as identifier (primary key).

ViewModel

The pièce-de-résistance of our solution is a generic 100% reusable ViewModel class that pragmatically deals with most of the logic in a master-detail pattern. It hosts an ObservableCollection of Model instances and knows

  • which one is selected,
  • how to filter the list,
  • how to add, remove, and update items, and
  • it notifies all of this to its environment.

Here’s the part of its code that deals with the collection, maintaining the current selection, and filtering. We decided to expose only one Items collection and apply the filtering on that one – the Views can then remain bound to the Items property. The underlying field variable always refers to the unfiltered collection. Depending on your own use cases (e.g. if your View needs access to items that are filtered away), you may want to make the field protected instead of private.

The only thing that the generic base class does not know, is how to lookup items. That’s why ApplyFilter() is abstract:

public abstract partial class MasterDetailViewModel<T> : ObservableObject
{
    private readonly ObservableCollection<T> items = new();

    public ObservableCollection<T> Items =>
        filter is null
            ? items
            : new ObservableCollection<T>(items.Where(i => ApplyFilter(i, filter)));

    public T Current
    {
        get => current;
        set
        {
            SetProperty(ref current, value);
            OnPropertyChanged(nameof(HasCurrent));
        }
    }

    public string Filter
    {
        get => filter;
        set
        {
            var current = Current;

            SetProperty(ref filter, value);
            OnPropertyChanged(nameof(Items));

            if (current is not null && Items.Contains(current))
            {
                Current = current;
            }
        }
    }

    public bool HasCurrent => current is not null;

    public abstract bool ApplyFilter(T item, string filter);

    // ...

}

The base ViewModel hosts a set of virtual methods for adding, deleting, and replacing items. They work whether or not the collection is filtered, and all raise the CollectionChanged event so that the Views can update through databinding:

public virtual T AddItem(T item)
{
    items.Add(item);
    if (filter is not null)
    {
        OnPropertyChanged(nameof(Items));
    }

    return item;
}

public virtual T UpdateItem(T item, T original)
{
    var hasCurrent = HasCurrent;

    var i = items.IndexOf(original);
    items[i] = item; // Raises CollectionChanged.
            
    if (filter is not null)
    {
        OnPropertyChanged(nameof(Items));
    }

    if (hasCurrent && !HasCurrent)
    {
        // Restore Current.
        Current = item;
    }

    return item;
}

public virtual void DeleteItem(T item)
{
    items.Remove(item);

    if (filter is not null)
    {
        OnPropertyChanged(nameof(Items));
    }
}

The ViewModel in our sample app inherits from this MasterDetailViewModel<T> and overrides the ApplyFilter() method. When filtering Characters by a search string, we look it up in the Name, Kind, and Description properties. In most apps the search logic is independent from the View and its ViewModel, so it makes sense to implement it in the Model. We indeed pass the search logic to the Character:

public partial class HomePageViewModel : MasterDetailViewModel<Character>
{
    // ...

    public override bool ApplyFilter(Character item, string filter)
    {
        return item.ApplyFilter(filter);
    }

}

Here’s the ApplyFilter() method in the Model:

public bool ApplyFilter(string filter)
{
    return Name.Contains(filter, StringComparison.InvariantCultureIgnoreCase)
        || Kind.Contains(filter, StringComparison.InvariantCultureIgnoreCase)
        || Description.Contains(filter, StringComparison.InvariantCultureIgnoreCase);
}

The ViewModel is set as DataContext to the View:

<Page.DataContext>
    <viewmodels:HomePageViewModel x:Name="ViewModel" />
</Page.DataContext>

Let’s connect some more dots by taking a look at that View.

View

Our Master-Detail page consists of an AutoSuggestBox, a CommandBar, a ListView and a handful of Grid elements. If you’re on UWP, then you may consider bringing a ListDetailsView control in the equation.

On the left side lives a ListView which is bound to the Items and the Current of the ViewModel. For each item, the ‘master’ information (Name and Kind) is hosted in a RelativePanel. To allow compiled data binding within the data template, we specified the target class via x:DataType. The mouse-over status is monitored via PointerEntered and PointerExited event handlers:

<ListView x:Name="CharacterListView"
            ItemsSource="{x:Bind ViewModel.Items, Mode=OneWay}"
            SelectedItem="{x:Bind ViewModel.Current, Mode=TwoWay}">
    <ListView.ItemTemplate>
        <DataTemplate x:DataType="models:Character">
            <UserControl PointerEntered="ListViewItem_PointerEntered"
                         PointerExited="ListViewItem_PointerExited">
                <RelativePanel Background="Transparent">
                    <!-- more ... -->

Whenever the Current changes, we ensure it becomes visible:

private void ViewModel_PropertyChanged(
    object sender, 
    System.ComponentModel.PropertyChangedEventArgs e)
{
    if (e.PropertyName == "Current" && ViewModel.HasCurrent)
    {
        CharacterListView.ScrollIntoView(ViewModel.Current);
    }
}

Use Cases

Filtering

We already covered the Model and ViewModel parts of filtering. The visual part is an AutoSuggestBox that we blended into the command bar on top:

<AutoSuggestBox x:Name="SearchBox"
                QuerySubmitted="SearchBox_QuerySubmitted"
                QueryIcon="Find" />

Its input is passed to the ViewModel:

private void SearchBox_QuerySubmitted(
    AutoSuggestBox sender, 
    AutoSuggestBoxQuerySubmittedEventArgs args)
{
    ViewModel.Filter = args.QueryText;
}

You already know the rest of the story: the search logic is further passed to the Model, and the ViewModel’s Items collection becomes filtered:

public ObservableCollection<T> Items =>
    filter is null
        ? items
        : new ObservableCollection<T>(items.Where(i => ApplyFilter(i, filter)));

Here’s the app with a filter applied:

Commands

Our Master-Detail solution has a CommandBar on top, hosting the usual suspects (Add, Edit, Delete) but there’s also room for custom commands (like Duplicate). Some of these are always actionable, some only when there’s a Current. The data template of the items list on the left has also room for commands: commands that act on the list element that has mouse focus – whether or not it is the Current. These are grouped into a panel called HoverButtons, of which the visibility is managed by a VisualStateManager:

<RelativePanel>
    <VisualStateManager.VisualStateGroups>
        <VisualStateGroup x:Name="HoveringStates">
            <VisualState x:Name="HoverButtonsHidden" />
            <VisualState x:Name="HoverButtonsShown">
                <VisualState.Setters>
                    <Setter Target="HoverButtons.Visibility"
                            Value="Visible" />
                </VisualState.Setters>
            </VisualState>
        </VisualStateGroup>
    </VisualStateManager.VisualStateGroups>
    <!-- more ... -->
private void ListViewItem_PointerEntered(object sender, PointerRoutedEventArgs e)
{
    if (e.Pointer.PointerDeviceType is PointerDeviceType.Mouse or PointerDeviceType.Pen)
    {
        VisualStateManager.GoToState(sender as Control, "HoverButtonsShown", true);
    }
}

private void ListViewItem_PointerExited(object sender, PointerRoutedEventArgs e)
{
    VisualStateManager.GoToState(sender as Control, "HoverButtonsHidden", true);
}

Duplicate & Delete

The Duplicate and Delete commands appear in multiple places in the View. Thanks to the XamlUICommand and StandardUICommand classes, we were able to define these only once:

<Page.Resources>
    <XamlUICommand x:Name="DuplicateCommand"
                   Command="{x:Bind ViewModel.DuplicateCommand}"
                   Description="Create a clone of this character"
                   Label="Clone">
        <XamlUICommand.IconSource>
            <SymbolIconSource Symbol="Copy" />
        </XamlUICommand.IconSource>
        <XamlUICommand.KeyboardAccelerators>
            <KeyboardAccelerator Key="D"
                                 Modifiers="Control" />
        </XamlUICommand.KeyboardAccelerators>
    </XamlUICommand>
    <StandardUICommand x:Name="DeleteCommand"
                       Kind="Delete"
                       Command="{x:Bind ViewModel.DeleteCommand}"
                       Description="Remove this character" />
</Page.Resources>

Here’s how these UI commands are used in buttons on the top command bar, with the Current as parameter:

<AppBarButton Command="{StaticResource DuplicateCommand}"
              CommandParameter="{x:Bind ViewModel.Current.Name, Mode=OneWay}"
              IsEnabled="{x:Bind ViewModel.HasCurrent, Mode=OneWay}" />
<AppBarButton Command="{x:Bind DeleteCommand}"
              CommandParameter="{x:Bind ViewModel.Current.Name, Mode=OneWay}"
              IsEnabled="{x:Bind ViewModel.HasCurrent, Mode=OneWay}" />

Here are the same commands in the HoverButtons panel. They’re bound to the Model under the mouse cursor:

<StackPanel x:Name="HoverButtons"
            Orientation="Horizontal"
            Visibility="Collapsed">
    <AppBarButton IsCompact="True"
                  Command="{StaticResource DuplicateCommand}"
                  CommandParameter="{x:Bind Name}" />
    <AppBarButton IsCompact="True"
                  Command="{StaticResource DeleteCommand}"
                  CommandParameter="{x:Bind Name}" />
</StackPanel>

Inside the ViewModel the Delete and Duplicate commands are RelayCommand instances:

public ICommand DuplicateCommand => new RelayCommand<string>(DuplicateCommand_Executed);

public ICommand DeleteCommand => new RelayCommand<string>(DeleteCommand_Executed);

When they’re executed, they update the ViewModel’s collection via the helper methods:

private void DeleteCommand_Executed(string parm)
{
    if (parm is not null)
    {
        var toBeDeleted = Items.FirstOrDefault(c => c.Name == parm);
        DeleteItem(toBeDeleted);
    }
}

private void DuplicateCommand_Executed(string parm)
{
    var toBeDuplicated = Items.FirstOrDefault(c => c.Name == parm);
    var clone = toBeDuplicated.Clone();
    AddItem(clone);
    if (Items.Contains(clone))
    {
        Current = clone;
    }
}

Here’s an animation that shows these commands in action. We duplicate an item that’s not the Current, then select the clone and scroll it into view:

Insert & Update

The Insert and Update scenarios require some extra UI: the View has a ContentDialog that’s opened for entering a new Character, or for editing an existing one. RelativePanel is a great host for this type of content:

<ContentDialog x:Name="EditDialog"
                PrimaryButtonText="Update"
                CloseButtonText="Cancel">
    <RelativePanel HorizontalAlignment="Stretch">
        <TextBox x:Name="Name"
                    Header="Name"
                    Text="{Binding Name, Mode=TwoWay}" />
        <TextBox x:Name="Kind"
                    Header="Kind"
                    Text="{Binding Kind, Mode=TwoWay}"
                    RelativePanel.RightOf="Name"
                    RelativePanel.AlignRightWithPanel="True" />
        <TextBox x:Name="ImagePath"
                    Header="Path to Image"
                    Text="{Binding ImagePath, Mode=TwoWay}"
                    RelativePanel.Below="Name"
                    RelativePanel.AlignLeftWith="Name"
                    RelativePanel.AlignRightWith="Kind" />
        <TextBox x:Name="Description"
                    Header="Description"
                    Text="{Binding Description, Mode=TwoWay}"
                    TextWrapping="Wrap"
                    RelativePanel.Below="ImagePath"
                    RelativePanel.AlignLeftWith="ImagePath"
                    RelativePanel.AlignRightWith="ImagePath" />
    </RelativePanel>
</ContentDialog>

Here’s how the dialog looks like. We know: there’s room for improvement in the color scheme:

It should not come as a surprise that we’re using RelayCommands again:

public ICommand NewCommand => new AsyncRelayCommand(OpenNewDialog);

public ICommand EditCommand => new AsyncRelayCommand(OpenEditDialog);

Both commands configure and open the context dialog, which is bound to a Character that’s not in the official Items collection – to prevent side effects and facilitate the Cancel operation:

private async Task OpenNewDialog()
{
    EditDialog.Title = "New Character";
    EditDialog.PrimaryButtonText = "Insert";
    EditDialog.PrimaryButtonCommand = InsertCommand;
    EditDialog.DataContext = new Character();
    await EditDialog.ShowAsync();
}

private async Task OpenEditDialog()
{
    EditDialog.Title = "Edit Character";
    EditDialog.PrimaryButtonText = "Update";
    EditDialog.PrimaryButtonCommand = UpdateCommand;
    var clone = ViewModel.Current.Clone();
    clone.Name = ViewModel.Current.Name;
    EditDialog.DataContext = clone;
    await EditDialog.ShowAsync();
}

Another set of RelayCommands is bound to the Close button of the dialog, and update ViewModel’s items collection:

private void Update()
{
    ViewModel.UpdateItem(EditDialog.DataContext as Character, ViewModel.Current);
}

private void Insert()
{
    var character = ViewModel.AddItem(EditDialog.DataContext as Character);
    if (ViewModel.Items.Contains(character))
    {
        ViewModel.Current = character;
    }
}

Reusability

Here are the steps to take if you want to apply the proposed Master-Detail implementation in your own MVVM app:

  1. Copy/paste the generic MasterDetailViewModel.
  2. Make your own subclass of it, with your own Model.
  3. Use the XAML structure and bindings of our View, but applied to your own Model.

That’s all.  We successfully did this exercise a couple of times in a relatively big app, with a few variations in the scenario, such as a read-only collection, no search, and insert only via selection from another list. Here’s how that app looks like:

It took a few iterations, but we’re pretty confident that this pattern and its component are reusable.

Our sample app lives here on GitHub.

Enjoy!

Pagination with Entity Framework Core and Microsoft MVVM in WinUI 3

In this article we present a LINQ-based pagination solution for list controls as well as a pager user interface, all in a WinUI 3 application. We use SQLite as data provider and a Windows Community Toolkit DataGrid as host control, but the solution applies to any Entity Framework Core (EF Core) store and any WinUI 3 ItemsControl. For the implementation of the MVVM pattern, we chose the usual suspect: Microsoft MVVM Toolkit.

Here’s how the sample page looks like:

This is a new XAML page that we added to the sample app for our previous article on using the DataGrid in WinUI 3.

The heart of our pagination infrastructure is PaginatedList<T> – a subclass of List<T>.

It holds a specific page from a query result as a list of items can be bound to any ItemsControl as ItemsSource. A PaginatedList<T> instance not only embeds the (partial) result of the query but it also exposes the current page number, and the total number of pages for the whole query (quite convenient for a pager UI). The page size (number of records wanted) is not a property but is provided to the constructor.

Here’s how the class definition looks like:

public class PaginatedList<T> : List<T>
{
    public int PageIndex { get; private set; }

    public int PageCount { get; private set; }

    private PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
    {
        PageIndex = pageIndex;
        PageCount = (int)Math.Ceiling(count / (double)pageSize);
        AddRange(items);
    }

    public static async Task<PaginatedList<T>> CreateAsync(
	IQueryable<T> source, 
	int pageIndex, 
	int pageSize)
    {
        int count = await source.CountAsync();
        List<T> items = await source
	.Skip((pageIndex - 1) * pageSize)
	.Take(pageSize)
	.ToListAsync();

        return new PaginatedList<T>(items, count, pageIndex, pageSize);
    }
}

We’re using some of the asynchronous EF Core IQueryable Extensions here, but feel free to add a synchronous version if that better fits your use case.

The PaginatedList is used inside the XAML page’s ViewModel (a Microsoft MVVM Toolkit’s ObservableObject) to produce a List<Mountain> to which the DataGrid is bound. The ViewModel also exposes the current page number and the total number of pages. These are used by the pager on top. Here’s the ViewModel’s code:

public class PaginationPageViewModel : ObservableObject
{
    private int _pageSize = 10;
    private int _pageNumber;
    private int _pageCount;
    private List<Mountain> _mountains;

    public int PageNumber
    {
        get => _pageNumber;
        private set => SetProperty(ref _pageNumber, value);
    }

    public int PageCount
    {
        get => _pageCount;
        private set => SetProperty(ref _pageCount, value);
    }

    public List<Mountain> Mountains
    {
        get => _mountains;
        private set => SetProperty(ref _mountains, value);
    }

    private async Task GetMountains(int pageIndex, int pageSize)
    {
        using MountainDbContext dbContext = new();
        PaginatedList<Mountain> pagedMountains = await PaginatedList<Mountain>.CreateAsync(
            dbContext.Mountains
                .OrderBy(m => m.Rank),
            pageIndex,
            pageSize);
        PageNumber = pagedMountains.PageIndex;
        PageCount = pagedMountains.PageCount;
        Mountains = pagedMountains;
    }
}

Here’s how the ‘current page of Mountains’ is bound to the DataGrid:

<ctWinUI:DataGrid x:Name="DataGrid"
                    ItemsSource="{x:Bind ViewModel.Mountains, Mode=OneWay}"
                    AutoGenerateColumns="False"
                    CanUserSortColumns="False"
                    SelectionMode="Single"
                    IsReadOnly="True"
                    RowDetailsVisibilityMode="Collapsed">
    <ctWinUI:DataGrid.Columns>
        <ctWinUI:DataGridTextColumn Header="Rank"
                                    Binding="{Binding Rank}" />
        <ctWinUI:DataGridComboBoxColumn Header="Mountain"
                                        Binding="{Binding Name}" />
        <!-- More columns -->
    </ctWinUI:DataGrid.Columns>
</ctWinUI:DataGrid>

The ViewModel also exposes 4 commands -implementors of IAsyncRelayCommand– to allow refreshing the DataGrid with a new logical page:

public IAsyncRelayCommand FirstAsyncCommand { get; }

public IAsyncRelayCommand PreviousAsyncCommand { get; }

public IAsyncRelayCommand NextAsyncCommand { get; }

public IAsyncRelayCommand LastAsyncCommand { get; }

We would have loved to use one of the new WinUI Pager controls for user interface, but tody these are available in WinUI 2 only. Therefor we brewed our own CommandBar-based pagination control with the canonical navigation buttons to the first, previous, next, and last pages:

<CommandBar DefaultLabelPosition="Right">
    <AppBarButton ToolTipService.ToolTip="First"
                    Icon="Previous"
                    Command="{x:Bind ViewModel.FirstAsyncCommand, Mode=OneWay}" />
    <AppBarButton ToolTipService.ToolTip="Previous"
                    Icon="Back"
                    Command="{x:Bind ViewModel.PreviousAsyncCommand, Mode=OneWay}" />
    <AppBarElementContainer>
        <TextBlock Text="Page" />
    </AppBarElementContainer>
    <AppBarElementContainer>
        <TextBlock Text="{x:Bind ViewModel.PageNumber, Mode=OneWay}" />
    </AppBarElementContainer>
    <! -- And so on ... -->

Here’s how the AsyncRelayCommands are initialized in the ViewModel with their respective Execute and CanExecute logic:

FirstAsyncCommand = new AsyncRelayCommand(
    async () => await GetMountains(1, _pageSize),
    () => _pageNumber != 1
);
PreviousAsyncCommand = new AsyncRelayCommand(
    async () => await GetMountains(_pageNumber - 1, _pageSize),
    () => _pageNumber > 1
);
NextAsyncCommand = new AsyncRelayCommand(
    async () => await GetMountains(_pageNumber + 1, _pageSize),
    () => _pageNumber < _pageCount
);
LastAsyncCommand = new AsyncRelayCommand(
    async () => await GetMountains(_pageCount, _pageSize),
    () => _pageNumber != _pageCount
);

Each time a new page is fetched and displayed, we need to tell the commands that their CanExecute property was updated so that the corresponding buttons in the pager will enable or disable themselves:

FirstAsyncCommand.NotifyCanExecuteChanged();
PreviousAsyncCommand.NotifyCanExecuteChanged();
// And so on ...

To improve the user experience, we enhanced PaginatedList<T> to implement some edge cases. When the result of the full query is empty (page count is zero), we set the current page number to zero:

int count = await source.CountAsync();
if (count == 0)
{
   // No results -> return page 0.
   return new PaginatedList<T>(new List<T>(), 0, 0, pageSize);
}

This is how the pager then looks like:

When the requested page is out of range (has no records) then PaginatedList<T> returns the last page:

List<T> items = await source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
if (items.Count == 0)
{
    // Requested page is out of range -> return last page.
    pageIndex = (int)Math.Ceiling(count / (double)pageSize);
    items = await source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
}

return new PaginatedList<T>(items, count, pageIndex, pageSize);

Here’s how the pager looks like when we try to navigate to page 1000:

Last but not least, we allow the user to change the page size. We first added the property and a list of possible values to the ViewModel:

public List<int> PageSizes => new() { 5, 10, 20, 50, 100 };

public int PageSize
{
    get => _pageSize;
    set
    {
        SetProperty(ref _pageSize, value);
        Refresh();
    }
}

Then we added a ComboBox and a TextBlock to the AppBar, via AppBarElementContainer instances:

<AppBarSeparator />
<AppBarElementContainer VerticalContentAlignment="Center">
    <ComboBox ItemsSource="{x:Bind ViewModel.PageSizes}"
                SelectedItem="{x:Bind ViewModel.PageSize, Mode=TwoWay}" />
</AppBarElementContainer>
<AppBarElementContainer VerticalContentAlignment="Center">
    <TextBlock Text="rows per page"
                Margin="8 0" />
</AppBarElementContainer>

Here’s how that part of the UI looks like:

paginationpagesize

Finally we implemented the Refresh() method – triggered by a change of the PageSize property. We simply navigate to the first page. Before that we make sure that the command can execute by setting the page number to zero:

private void Refresh()
{
    _pageNumber = 0;
    FirstAsyncCommand.Execute(null);
}

With a handful lines of code and a little help from our friends Microsoft MVVM Toolkit and EF Core we just implemented a WinUI 3 pagination use case. Our sample app lives here on GitHub.

Enjoy!

Data Validation with the Microsoft MVVM Toolkit

In this article we will walk through a handful of scenarios and techniques for input validation with the ObservableValidator class of Microsoft MVVM Toolkit. We will cover

  • canonical property validation,
  • comparing two (or more) properties,
  • comparing the new value of a property to its previous one,
  • delaying the validation, and
  • preventing to assign an invalid value to a property.

We created a sample app in UWP, it looks like this:

ComparingProperties

Input Validation in UWP

The System.Component.DataAnnotations is almost as old as the .NET Framework itself. It contains attribute classes to decorate ViewModels and Models with metadata for validation purposes – among other. These attributes describe property validation rules. When the rules are broken, the instance exposes its validation errors via its INotifyDataError members. This validation technique is intensively used in several ASP.NET frameworks and also made its way to Silverlight and WPF. Until very recently, data validation in UWP did not get much attention from Microsoft. Most development teams embedded third-party solutions such as Prism, Template10, or Calcium into their UWP apps, or rolled their own custom solution.

With Microsoft MVVM toolkit there now is a Microsoft provided alternative, and it’s even ready for Reunion. Now that we’re talking about the future: WinUI 3 controls will come with templates that react upon the INotifyDataErrorInfo status of the (View)Model that they are bound to. There is currently a bug that inhibits a deeper dive into this. Nevertheless, the WinUI3 Controls Gallery app already contains a sample page. It’s currently broken, but it reveals a glimpse of the near future of input validation in WinUI:

WinuiControlsGallery

That’s not a spectacular screenshot, so for reference here’s an example of similar control templates in WPF:

customerrortemplateWPF

While the UI parts of UWP Data Validation are not yet ready for prime time, the supporting Microsoft MVVM Toolkit is fully operational. Welcome to ObservableValidator.

ObservableValidator

The great official documentation teaches us that

‘The ObservableValidator is a base class implementing the INotifyDataErrorInfo interface, providing support for validating properties exposed to other application modules. It also inherits from ObservableObject, so it implements INotifyPropertyChanged and INotifyPropertyChanging as well. It can be used as a starting point for all kinds of objects that need to support both property change notifications and property validation.’.

Microsoft MVVM Toolkit is fully developed in the open, the source code for ObservableValidator is right here. Models and ViewModels that require validation just need to inherit from it, like this:

public class Suspect : ObservableValidator
{
    private string _name;
    private string _socialSecurityNumber;

    // ... there's more
}

The instance will expose its error status through its INotifyDataErrorInfo members. The Views in our sample app have bindings to ErrorsChanged and HasErrors. Check our previous blog post to see how this was done. Were’ reusing its ‘error popup’ approach. Here’s its XAML definition:

<SymbolIcon Symbol="ReportHacked"
            Foreground="Red"
            Visibility="{x:Bind ViewModel.Suspect.HasErrors, Mode=OneWay}">
    <ToolTipService.ToolTip>
        <TextBlock Text="{x:Bind ViewModel.Suspect.Errors, Mode=OneWay}"
                    Foreground="Red" />
    </ToolTipService.ToolTip>
</SymbolIcon>

Using existing Validation Attributes

The System.Component.DataAnnotations namespace hosts a huge list of attributes available for the validation of individual properties: required, minimum and maximum length, range, Enum, Regex … These cover most of the usual suspects. There are not too much XAML examples on the market, so when you search for sample code, you’ll probably end up in ASP.NET MVC projects. Don’t worry about that: MVC Models are very similar to MVVM ViewModels.

To implement validation in a class, it suffices to decorate its properties with one or more of these validation attributes and call one of its SetProperty() overloads with true as the third parameter in the property setter. Here’s how our sample app evaluates whether Keyser Söze has a required Name of minimum length and has his SocialSecurityNumber checked against a regular expression:

[Required(
	ErrorMessage = "Name is Required")]
[MinLength(
	2, 
	ErrorMessage = "Name should be longer than one character")]
public string Name
{
    get => _name;
    set => SetProperty(ref _name, value, true);
}

[RegularExpression(
	@"^(?!000)(?!666)(?!9)\d{3}([- ]?)(?!00)\d{2}\1(?!0000)\d{4}$", 
	ErrorMessage = "Invalid Social Security Number.")]
public string SocialSecurityNumber
{
    get => _socialSecurityNumber;
    set => SetProperty(ref _socialSecurityNumber, value, true);
}

The validation is triggered whenever the property gets a new value. All we need in the View is a two-way binding to the property:

<TextBox Text="{x:Bind ViewModel.Suspect.Name, Mode=TwoWay}"
            PlaceholderText="Name" />
<TextBox Text="{x:Bind ViewModel.Suspect.SocialSecurityNumber, Mode=TwoWay}"
            PlaceholderText="Social Security Number" />

Here’s how our sample app reacts to an evil social security number:

ValidationAttributes

The regular expression correctly refuses a number that starts with 666.

Rolling your own Validation Attributes

It’s easy to roll your own reusable validation attribute: inherit from ValidationAttribute and provide your own implementation of the IsValid() method. The method gets the ValidationContext injected as a parameter, exposing the whole instance that is being validated, not only the decorated property. This allows you to write validation rules over more than one property, like:

  • property a should be less than property b, or
  • if property a has a value, then property b becomes required.

Allow us to mention that there already *is* a validation attribute that compares two properties -the CompareAttribute– but it only checks for equality. It’s used in the confirmation fields for email addresses and passwords (and probably nowhere else)

Our sample app contains a validation rule to compare two dates. It is used in the common ‘if the EndDate is filled out it then should come after StartDate’ scenario.

Let’s first show how the GreaterThan attribute is applied in the ViewModel class:

[GreaterThan (
	nameof(StartDate), 
	"End date should come after start date.")]
public DateTime? EndDate
{
    get => _endDate;
    set { 
        SetProperty(ref _endDate, value, true);
    }
}

Here’s the implementation of the validation attribute itself. It can be reused across applications:

public sealed class GreaterThanAttribute : ValidationAttribute
{
    private string _errorMessage;

    public GreaterThanAttribute(string propertyName, string errorMessage)
    {
        PropertyName = propertyName;
        _errorMessage = errorMessage;
    }

    public string PropertyName { get; }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value == null)
        {
            return ValidationResult.Success;
        }

        var instance = validationContext.ObjectInstance;
        var otherValue = instance.GetType().GetProperty(PropertyName).GetValue(instance);

        if (((IComparable)value).CompareTo(otherValue) > 0)
        {
            return ValidationResult.Success;
        }

        return new ValidationResult(_errorMessage);
    }
}

Since we’ve created a dependency between the values of Start- and EndDate in the ViewModel, we need to make sure that the validation is triggered by both properties’ changes. That’s why we make a call to ValidateProperty() in the setter of StartDate:

public DateTime? StartDate
{
    get => _startDate;
    set { SetProperty(ref _startDate, value, true);
        ValidateProperty(EndDate, nameof(EndDate));
    }
}

In the View, we bound both properties to a Date in a CalendarDatePicker:

<CalendarDatePicker Date="{x:Bind ViewModel.NoDelorean.GetStartDate(), BindBack=ViewModel.NoDelorean.SetStartDate, Mode=TwoWay}"
                    PlaceholderText="Start Date" />
<CalendarDatePicker Date="{x:Bind ViewModel.NoDelorean.GetEndDate(), BindBack=ViewModel.NoDelorean.SetEndDate, Mode=TwoWay}"
                    PlaceholderText="End Date" />

[We used {x:Bind} with function bindings to plug in a transformation between the DateTime of the properties and the DateTimeOffset values of the controls.]

Here’s how an invalid instance looks like in the sample app:

ComparingProperties

Also, please note that this rule does not apply to all ViewModels. Sometimes it should be possible to go back in time:

delorean

Using a CustomValidation method

Not every validation rule should be cast in a universally reusable validation attribute class. For a local rule, you can get away with writing a static validation method and applying it to a property via a CustomValidation attribute. Our sample app uses this technique to compare a new value of a property to its old value. The ViewModel represents a CountDown class – the validation rule ensures that we don’t ‘count up’.

The values to compare must have their own fields:

private int _value = 10;
private int _previousValue;

We created a static method that takes the property’s type (int in our Counter case) and a ValidationContext, and returns the result of the validation as a ValidationResult:

public static ValidationResult ValidateValue(
	int value, 
	ValidationContext context)
{
    var instance = (Countdown)context.ObjectInstance;
    var isValid = value < instance._previousValue;

    if (isValid)
    {
        return ValidationResult.Success;
    }

    return new ValidationResult("We're not supposed to count up.");
}

Again the validation context gives us access to the instance being validated, so it allows us to compare the new counter value to the old. Here’s how the rule is applied to the Value property:

[CustomValidation(typeof(Countdown), nameof(ValidateValue))]
public int Value
{
    get => _value;
    set
    {
        _previousValue = _value;
        SetProperty(ref _value, value, true);
    }
}

And this is how violating the counter rule looks like in the sample app:

ComparingNewToOld

Delaying Validation

Up until now, we always triggered the validation on the assignment of the property. This is not needed or wanted in every scenario or for every property. The third parameter in the SetProperty() call was always set to true in the previous examples. In our next sample, the validation will be triggered by a button click, so we start with a false in the property setter:

[Required(
	ErrorMessage = "Name is Required")]
[MinLength(
	2, 
	ErrorMessage = "Name should be longer than one character")]
public string Name
{
    get => _name;
    set => SetProperty(ref _name, value, false);
}

[RegularExpression(
	@"^(?!000)(?!666)(?!9)\d{3}([- ]?)(?!00)\d{2}\1(?!0000)\d{4}$", 
	ErrorMessage = "Invalid Social Security Number.")]
public string SocialSecurityNumber
{
    get => _socialSecurityNumber;
    set => SetProperty(ref _socialSecurityNumber, value, false);
}

For the sake of simplicity we validate all properties together – there’s a call for this: ValidateAllProperties(). The call is hooked to the button with an instance of MVVM Toolkit’s RelayCommand:

public ICommand ValidateCommand => 
	new RelayCommand(() => ValidateAllProperties());

Here’s the binding:

<Button Content="Validate"
        Command="{x:Bind ViewModel.SuspectWithDelayedValidation.ValidateCommand, Mode=OneWay}" />

And this is how the result looks like in the app:

DelayedValidation

There is a Try

In the previous examples we started the validation after the assignment of a property. We were continuously breaking an ancient object oriented programming principle:

it should not be possible to bring a properly encapsulated object into an invalid state via the public interface.

In some scenarios or parts of your app (e.g. in the Models) you should indeed prevent the assignment of invalid values to properties. Fortunately Microsoft MVVM Toolkit comes with TrySetProperty() … there *is* a Try.

notry

TrySetProperty inspects the new value that you (try to) assign, and only succeeds when that value is a valid one. On the upside you’ll never have instances in an invalid state, on the downside you need to provide you own Errors store – instances will never have ‘official’ errors in their INotifyDataErrorInfo members.

In our sample app we decorated the ViewModel with alternative members – a list of ValidationResult instances to store the errors per property and a Boolean that returns whether that list has members:

public class NotYoda : ObservableValidator
{
    private List<ValidationResult> _errors = new List<ValidationResult>();

    public string Errors => string.Join(Environment.NewLine, from ValidationResult e in _errors select e.ErrorMessage);

    // Since HasErrors is not virtual:
    public bool ErrorsHaveI => Errors.Length > 0;

    // More, there is...
}

In the property setters we first clean up the previous messages for the property, then call the TrySetProperty, use its output parameter to update our custom error list, and then notify the custom error property changes:

[Required(
	ErrorMessage = "Name is Required")]
[MinLength(
	2, 
	ErrorMessage = "Name should be longer than one character")]
public string Name
{
    get => _name;
    set
    {
        _errors.RemoveAll(v => v.MemberNames.Contains(nameof(Name)));

        TrySetProperty(ref _name, value, out IReadOnlyCollection<ValidationResult> errors);

        _errors.AddRange(errors);
        OnPropertyChanged(nameof(Errors));
        OnPropertyChanged(nameof(ErrorsHaveI));
    }
}

[RegularExpression(
	@"^(?!000)(?!666)(?!9)\d{3}([- ]?)(?!00)\d{2}\1(?!0000)\d{4}$", 
	ErrorMessage = "Invalid Social Security Number.")]
public string SocialSecurityNumber
{
    get => _socialSecurityNumber;
    set
    {
        _errors.RemoveAll(v => v.MemberNames.Contains(nameof(SocialSecurityNumber)));

        TrySetProperty(ref _socialSecurityNumber, value, out IReadOnlyCollection<ValidationResult> errors);

        _errors.AddRange(errors);
        OnPropertyChanged(nameof(Errors));
        OnPropertyChanged(nameof(ErrorsHaveI));
    }
}

In the View, the bindings for the properties are not different from the other samples:

<TextBox Text="{x:Bind ViewModel.NotYoda.SocialSecurityNumber, Mode=TwoWay}"
            PlaceholderText="Social Security Number" />
<TextBlock Text="{x:Bind ViewModel.NotYoda.SocialSecurityNumber, Mode=TwoWay}" />

The error icon is of course bound to the custom error properties:

<SymbolIcon Symbol="ReportHacked"
            Foreground="Red"
            Visibility="{x:Bind ViewModel.NotYoda.ErrorsHaveI, Mode=OneWay}"
            HorizontalAlignment="Right">
    <ToolTipService.ToolTip>
        <TextBlock Text="{x:Bind ViewModel.NotYoda.Errors, Mode=OneWay}"
                    Foreground="Red" />
    </ToolTipService.ToolTip>
</SymbolIcon>

Our sample app displays the current (valid) value next to the input boxes:

TrySetProperty

The shape of things to come

If you compare the different ViewModels in our sample app, you’ll definitely observe the copy/paste patterns around most of the calls to SetProperty() and OnPropertyChanged(). In a future version, MVVM Toolkit will be enhanced with property attributes that will generate the source code for these – as illustrated in this tweet from the author:

WhatsNext

We’ll keep you informed on this. In mean time, our sample app lives here on GitHub.

Enjoy!

A lap around the Microsoft MVVM Toolkit

In this article we’ll walk through a UWP sample app to experiment with the features of the new Microsoft.Toolkit.Mvvm package that is part of the Microsoft Community Toolkit.

MVVM

MVVM is a software architectural pattern introduced by Microsoft in 2005 to support the development of XAML apps, originally in WPF and later on in Silverlight. MVVM stays relevant to the more recent XAML environments such as UWP, Xamarin (MAUI), the Uno platform, and WinUI.

In a nutshell MVVM allows you to separate UI logic from business logic by dividing your code into

  • Models that represent data or information – think ‘domain objects’ or ‘entities’,
  • Views that represent the structure and layout that the user interacts with – think ‘pages’ and ‘controls’, and
  • ViewModels that express Model data and business logic through methods, properties and commands that are accessible to the Views,  preferably via declarative data-binding.

MVVMCore

MVVM requires the strong data-binding capabilities that we see in XAML through its {Binding} and {x:Bind} markup extensions. The pattern was also implemented in other technology stacks such as AngularJS/Angular.io, Ext JS and Vue.js.

Microsoft started recommending MVVM for XAML 15 years ago and they still do. Oddly enough, they only provided ‘bindable controls’ and never came up with official helper classes (like base classes that implement INotifyPropertyChanged or ICommand) or an MVVM development framework (with event aggregation, messaging, or viewmodel location). The only exception is the Composite Application Library, which later grew into Prism. Regarding MVVM, Microsoft developers always had to rely on (Open Source) third parties, of which the most popular (in alphabetical order) are/were Caliburn, MVVM Light, and Prism.

We had great experiences with each of these frameworks. However -except for Prism- none of them are still actively maintained. We assume that the ecosystem for XAML apps is evolving too rapidly for the maintainers of larger Open Source libraries. Indeed none of the existing MVVM frameworks was ever designed with .NET Core, Uno, MAUI or WinUI 3 in mind…

Maybe it’s time for a new wind to blow.

Enter Windows MVVM Toolkit

The Microsoft.Toolkit.Mvvm package is a modern, fast, and modular MVVM library that is part of the Windows Community Toolkit (WCT). The package targets .NET Standard 2.* so it can be used on

  • any .NET app platform: UWP, WinForms, WPF, Xamarin, Uno, WinUI and more, and on
  • any .NET runtime: .NET Native, .NET Core, .NET Framework, or Mono.

Its API surface is the same in all environments, making it perfect for building shared libraries.

The object model and terminology are definitely inspired by MVVM Light and the development team is aiming to provide a smooth migration path from MVVM Light to the new toolkit. Apart from functionality WCT Toolkit also pays high attention to performance. Many benchmarks like this one (on the Messenger class) are continuously run during the development:

perf2

The package comes with documentation and a nice sample app. WCT MVVM is currently in ‘preview 4’ so everything is still work in progress.

A UWP Sample App

A few months ago this GitHub issue revealed the initial feature set of Microsoft MVVM Toolkit, preliminary documentation, and even an operational preview package. We decided to create a little UWP app to test that package. Here’s how the home page looks like – standard clean WinUI 2.5:

HomePage

We’ll use this app throughout this article to explore the features of the new MVVM Toolkit in town.

Core classes

The Building Blocks page of the sample app illustrates the core classes for data binding and commanding:

BuildingblocksPage

ObservableObject

ObservableObject provides a base class for types to implement INotifyPropertyChanged and INotifyPropertyChanging – typically Models and ViewModels. You may have encountered similar helpers in other frameworks under the name ObservableBase, BindableBase, or NotifyPropertyChanged. Typically such a class comes with an easy way to raise the PropertyChanged event in property Setters, and that’s exactly what SetProperty() does.

Here’s what it looks like in one of our Model classes:

public class SuperHero : ObservableObject
{
    private string _name;

    public string Name
    {
        get => _name;
        set => SetProperty(ref _name, value);
    }

    // ...
}

Here’s another example from the sample app, this time for a ViewModel – the one that is bound to the BuildingBlocksPage View:

public class BuildingBlocksPageViewModel : ObservableObject
{
    private SuperHero _superHero;

    public SuperHero SuperHero
    {
        get => _superHero;
        set => SetProperty(ref _superHero, value);
    }

    // ...

}

Here’s how the ViewModel is declaratively bound to the View as its DataContext:

<Page.DataContext>
    <viewModels:BuildingBlocksPageViewModel x:Name="ViewModel" />
</Page.DataContext>

When we change a property on the SuperHero, the UI will update:

ViewModel.SuperHero.Nemesis = _nemeses[rnd.Next(0, 5)];

When the property that you want to monitor is a Task<T>, then you can use SetPropertyAndNotifyOnCompletion() instead of SetProperty(). It wil set the property but with the help of an internal TaskNotifier<T> it will delay the notification to when the task was completed:

private TaskNotifier<string> _saveTheUniverseTask;

public Task<string> SaveTheUniverseTask
{
    get => _saveTheUniverseTask;
    private set
    {
        SetPropertyAndNotifyOnCompletion(ref _saveTheUniverseTask, value);
    }
}

There’s even an overload that allows you to register a callback for when the task finishes. Here’s the ViewModel code triggered by the save button on the sample page. It assigns a new value to the task, and then starts it:

public async Task SaveTheUniverse()
{
    SaveTheUniverseTask = new Task<string>(
        () =>
        {
            Task.Delay(2000).Wait();

            if (rnd.Next(2) > 0)
            {
                return $"We're doomed, I lost my {SuperHero.Tool}.";
            }

            return "We're saved ... this time.";
        }
    );

    _saveTheUniverseTask.Start();
}

The task was made observable, so you can monitor its status and result. A simple binding suffices to follow up on the Status [note: The sample app has some extra code to reveal more statuses than ‘initial’ and ‘completed’.]:

<Run Text="{x:Bind ViewModel.SaveTheUniverseTask.Status, Mode=OneWay}" />

We needed to add some extra code to bind to the Result of the task, since UWP XAML does not allow you to bind to a generic. Here’s a ‘wrapper’ property for the result:

public string SaveTheUniverseTaskResult => 
	_saveTheUniverseTask.Status == TaskStatus.RanToCompletion 
	? _saveTheUniverseTask.Result 
	: "(hold your breath)";

And again, the declarative binding:

<Run Text="{x:Bind ViewModel.SaveTheUniverseTaskResult, Mode=OneWay}" />

RelayCommand

The RelayCommand class – a.k.a. DelegateCommand in some frameworks- and its generic and asynchronous siblings provide a default implementation of the ICommand interface. It’s used to declaratively bind events in UI controls to methods in the viewmodel. In the sample app the BuildingBlocksPageViewModel has the following method to toggle its data provider:

private void SwitchDataProvider()
{
    if (_dataProvider is RedDataProvider)
    {
        DataProvider = new BlueDataProvider();
    }
    else
    {
        DataProvider = new RedDataProvider();
    }

    SuperHero = _dataProvider.SuperHero();
}

To trigger this method from a button, we declare a property of the ICommand type:

public ICommand SwitchDataProviderCommand { get; }

Then we let a RelayCommand refer to it:

SwitchDataProviderCommand = new RelayCommand(SwitchDataProvider);

In the view we can now bind the button’s Command property to the ViewModel’s property of type ICommand:

<Button Command="{x:Bind ViewModel.SwitchDataProviderCommand}" />

AsyncRelayCommand extends the ICommand behavior with support for asynchronous operations. Here’s an example of an asynchronous data provider switch:

private async Task SwitchDataProviderAsync()
{
    await Task.Delay(1000);

    SwitchDataProvider();
}

The corresponding property is defined as IAsyncRelayCommand:

public IAsyncRelayCommand SwitchDataProviderAsyncCommand { get; }

And assigned to an AsyncRelayCommand instance:

SwitchDataProviderAsyncCommand = new AsyncRelayCommand(SwitchDataProviderAsync);

IAsyncRelayCommand inherits from ICommand, so you can bind it to a Button’s Command:

<Button Command="{x:Bind ViewModel.SwitchDataProviderAsyncCommand}" />

The ExecutionTask property gives you access to the underlying Task, so you can monitor it. There’s also a convenient IsRunning property that indicates when the task is … running. You can use this to display a busy indicator, such as a ProgressRing. Here’s how such binding looks like:

<winui:ProgressRing
   IsActive="{x:Bind 
	ViewModel.SwitchDataProviderAsyncCommand.IsRunning, 
	FallbackValue=False, 
	Mode=OneWay}" />

ObservableValidator

ObservableValidator is an ObservableObject that hosts a default implementation of INotifyDataErrorInfo. This enables Models and ViewModels to implement validation rules and expose validation results. INotifyDataErrorInfo used to be an important interface some years ago, when all out-of-the-box controls in e.g. Windows Forms, ASP.NET Forms, and WPF came with error templates that implemented the UI part of this interface. It looks like the built-in class comments in the validation annotation attributes are still living in that era – not really reassuring for today’s .NET Client developers:

RegexValidator

In the modern XAML stacks you need to do all of the control templating manually or rely on a third-party control library. But here’s the good news: rumor has it that WinUI 3 will bring back error templates for XAML controls, so we welcome this WCT MVVM Toolkit’s ObservableValidator very much.

The ObservableValidator class comes with

  • members that implement the INotifyDataErrorInfo interface (HasErrors, GetErrors(), ErrorsChanged, …), 
  • overloads of SetProperty() that allow you to specify whether or not validation should run on the assignment of a new property value,
  • a TrySetProperty() that does not assign the value if it induces validation errors, and 
  • support for all kinds of validation attributes from the System.Component.DataAnnotations namespace.

Here’s the StudyGroup class from our sample app. Its properties are decorated with validation rules on requirement, length, numerical range, and regular expression pattern:

public class StudyGroup : ObservableValidator
{
    [Required(ErrorMessage = "Topic is Required")]
    [MinLength(2, ErrorMessage = "Topic should be longer than one character")]
    public string Topic
    {
        get => _topic;
        set => SetProperty(ref _topic, value, true);
    }

    [Range(2009, 2015, ErrorMessage = "Class should be from 2009 to 2015")]
    public int Class
    {
        get => _class;
        set => SetProperty(ref _class, value, true);
    }

    [RegularExpression(@".*[pP]aintball.*", ErrorMessage = "Hobbies should contain 'Paintball'.")]
    public string Hobbies
    {
        get => _hobbies;
        set => SetProperty(ref _hobbies, value, true);
    }

    // ...
}

Many more validation attributes exist. Some allow you to compare the new value of a property to the old one, or compare the value of two properties within the instance. It’s also very easy to roll your own custom validation attribute.

Using the INotifyDataErrorInfo members a View can react appropriately to the the validation state of its ViewModels. In our sample app we decided to let StudyGroup expose an Errors string with the concatenated list of validation errors. A symbol icon with a tooltip displays the messages:

<SymbolIcon Symbol="ReportHacked"
            Foreground="Red"
            Visibility="{x:Bind ViewModel.StudyGroup.HasErrors, Mode=OneWay}"
            HorizontalAlignment="Right"
            Margin="0 4">
    <ToolTipService.ToolTip>
        <TextBlock Text="{x:Bind ViewModel.StudyGroup.Errors, Mode=OneWay}"
                    Foreground="Red" />
    </ToolTipService.ToolTip>
</SymbolIcon>

Here’s how it looks like in action:

Validation

Messaging

In an MVVM application, ViewModels as well as other components may need to communicate with each other in a loosely coupled way. Most MVVM libraries have a Messenger infrastructure for this purpose.

MVVMMessenger

Microsoft MVVM Toolkit’s messaging API allows independent modules to exchange information via messages through a publish/subscribe mechanism. Our sample app hosts two scenario’s. Both scenario’s have different View/ViewModel pairs (a.k.a. “Modules”) on the same page. They are unaware of each other, but need to exchange information about the current ‘theme’.

Here’s the first messaging scenario:

  • the ViewModel of the Shell hosts the current ‘Theme’ (just a color),
  • when the sample page opens, all modules request the current theme and adapt their UI,
  • a ‘Theme Module’ allows to update this current theme through a ToggleSwitch,
  • the other modules update their UI immediately, and
  • the ‘Theme’ is not a global variable (and neither is the ShellViewModel).

Here’s how the page looks like:

MessengerPage

Messages

In this scenario we use two of the message base classes that come with MVVM Toolkit. Here’s the definition of the message that is sent by the theme module when the theme changed, a ValueChangedMessage<T>:

public class ThemeChangedMessage : ValueChangedMessage<Theme>
{
    public ThemeChangedMessage(Theme value) : base(value)
    {
    }
}

Here’s the definition of the message that all modules use to get the current theme, a RequestMessage<T>:

public class ThemeRequestMessage : RequestMessage<Theme>
{
}

ObservableRecipient and Messenger

All ViewModels in this scenario inherit from ObservableRecipient  (which was called ViewModelBase in Preview 1) to get access to an IMessenger instance via the Messenger property.

Here’s the ShellViewModel from our sample app. It switches to the default theme, and starts listening for the ThemeRequestMessage (which it answers with the theme) and the ThemeChangedMessage (which updates the theme). In a production app, the theme would probably be persisted in the Settings.

Here’s the definition of the ShellViewModel:

public class ShellViewModel : ObservableRecipient
{
    private Theme _theme = Theme.Default;

    public ShellViewModel()
    {
        Messenger.Register<ThemeRequestMessage>(this, m =>
        {
            m.Reply(_theme); 
        });

        Messenger.Register<ThemeChangedMessage>(this, m =>
        {
            _theme = m.Value;
        });
    }
}

Here’s the call that the theme-aware modules make to fetch the current theme when they are displayed:

_theme = Messenger.Send<ThemeRequestMessage>();

Just like the ShellViewModel they also subscribe to the ThemeChangedMessage to update:

Messenger.Register<ThemeChangedMessage>(this, m =>
   {
      // ...
   }

Sending a message is as easy as calling Send() on the Messenger:

Messenger.Send(new ThemeChangedMessage(_theme));

Under the hood, the messaging infrastructure is using classic .NET Events and Delegates. It’s important that ViewModels unsubscribe their callbacks when they stop listening for messages. The IMessenger interface comes with several Unregister() members, of which this one is the most radical:

Messenger.UnregisterAll(this);

Generally a ViewModel should only send messages and respond to messages when it is in an operational state – i.e. when it is bound to a loaded View. This operational state can be defined with the IsActive property. When the property becomes true, the OnActivated() method is called, so that’s the appropriate place to register your callbacks:

protected override void OnActivated()
{
    base.OnActivated();

    Messenger.Register<ThemeChangedMessage>(this, m =>
    {
        // ...
    });
}

When IsActive becomes false, OnDeactivated() is called. Its default implementation unregisters all callbacks, so in most cases you need to do … nothing.

Here’s how a View in the sample app marks out the operational state of its ViewModel:

private void ColorModule_Loaded(object sender, RoutedEventArgs e)
{
    _colorModuleViewModel.IsActive = true;
}

private void ColorModule_Unloaded(object sender, RoutedEventArgs e)
{
    _colorModuleViewModel.IsActive = false;
}

The default Messenger in the current version of WCT MVVM uses WeakReferences under the hood, so dangling ViewModels with non-unregistered callbacks will eventually be cleaned up by the Garbage Collector. If you’re sure that your app nicely unregisters all messaging handlers, then you may switch to the original (faster!) Messenger that relies on strong references. Here’s how to do this:

public class ShellViewModel : ObservableRecipient
{
    public ShellViewModel() : base(StrongReferenceMessenger.Default)
    {}

    // ...

}

Message Tokens

Some apps may host groups of modules that listen to different variations of messages. Message tokens can be very useful to reduce the communication overhead. Here’s a screenshot of our sample app – the page is based of a real-life scenario and shows four modules:

  • one that broadcasts all pillow-and-blanket war casualties,
  • one that is only interested in pillow victims,
  • one that is only interested in blanket victims, and
  • one that is interested in all casualty messages.

MessengerWithTokenPage

The message itself is empty – it’s good to see that no specific base class is needed:

public class CasualtyMessage // No specific base class needed.
{
}

When a casualty is reported, the broadcast module sends the message, and uses the victim’s party (pillow or blanket) as token:

Messenger.Default.Send<CasualtyMessage, string>(new CasualtyMessage(), "pillow");
// OR
Messenger.Default.Send<CasualtyMessage, string>(new CasualtyMessage(), "blanket");

The next to modules register a callback for CasualtyMessage, but only for their own token:

Messenger.Register<CasualtyMessage, string>(this, "blanket", m => { OnCasualtyMessageReceived(); });

The fourth module registers twice, once for each token. It’s impossible to receive tokenized messages without tokenized registration. So in out sample app, the following will not receive a single message:

// Does not see the messages with a token.
Messenger.Register<CasualtyMessage>(this, m => { OnCasualtyMessageReceived(); });

In our sample app –and many other- Enumations would make an ideal candidate for tokens. After all: the use of hardcoded strings is risky business. Unfortunately token classes are required to implement IEquatable<T> as you see in the definition:

void Register<TMessage, TToken>(object recipient, TToken token, Action<TMessage> action)
            where TMessage : class
            where TToken : IEquatable<TToken>;

This constraint rules out the use of Enumerations. So we created the Party class that looks and feels like an Enum, but implements the interface:

public sealed class Party : IEquatable<Party>
{
    public static readonly Party Pillow = new Party(1, nameof(Pillow));
    public static readonly Party Blanket = new Party(2, nameof(Blanket));

    public string Name { get; private set; }

    public int Id { get; private set; }

    private Party(int id, string name)
    {
        Id = id;
        Name = name;
    }

    public bool Equals(Party other)
    {
        return Id == other.Id;
    }
}

The class can be used as token, instead of String, and we ended up with a much better maintainable code:

Messenger.Register<CasualtyMessage, Party>(this, Party.Blanket, m => { OnCasualtyMessageReceived(); });
Messenger.Default.Send<CasualtyMessage, Party>(new CasualtyMessage(), Party.Pillow);

Dependency Injection

In an MVVM architecture your code base is divided into Views, ViewModels, and Models. Any other reusable code should be grouped into Services. There should be a way to locate and call these services from any component.

MVVMServices

ServiceLocator and DependencyInjection are two (related) common patterns to implement this. These patterns are independent from MVVM, but having a service container available in an MVVM app is useful/required in many cases, like

  • when you want to cache expensive ViewModels instead of recreating them every time a View is instantiated, or
  • when you want to access the messenging infrastructure from a non-ObservableRecipient (like a View).

WCT MVVM comes with an Ioc helper class around the IServiceProvider interface. It supports creating and using a service container, but does not come itself with another service provider. This allows you to stick to your favorite dependency injection framework: Autofac, Ninject, Castle Windsor, Microsoft.Extensions.DependencyInjection or any other. Our sample app uses that last one – very popular in ASP.NET Core:

DependencyInjectionNuGet

Here’s a screenshot of the test page, it demonstrates fetching services from the container, and constructor injection:

InversionOfControlPage

When the app starts up, it registers its services in the container (the Messenger instance, an expensive ViewModel, a service for Logging, and one for Dialogs) and builds the provider:

Ioc.Default.ConfigureServices
        (new ServiceCollection()
            .AddSingleton<IMessenger>(WeakReferenceMessenger.Default)
            .AddSingleton<ILoggingService, DebugLoggingService>()
            .AddSingleton<ColorModuleViewModel>()
            .AddSingleton<ModalView>()
            .BuildServiceProvider()
        );

At runtime, the app can now pull services out of the container with a call to one of the GetService() family members. Here’s how the expensive ViewModel is fetched by a View:

var viewModel = Ioc.Default.GetService<ColorModuleViewModel>();

And here’s a View getting a reference to the Messenger to send a message:

Ioc.Default.GetService<IMessenger>()
	.Send<CasualtyMessage, Party>(new CasualtyMessage(), Party.Pillow);

For the sake of completeness (and unrelated to MVVM) the sample app also demonstrates constructor injection. Here’s how a ViewModel fetches the Logging and the Dialog services when it’s instantiated:

public ColorModuleViewModel(ILoggingService loggingService, ModalView modalView)
{
    // ...
}

Wait, there’s more!

The new WCT MVVM library is familiar, fast, and flexible. But there’s more coming your way than just the NuGet package. In the near future you may also expect:

In mean time our sample UWP app 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!

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!