Category Archives: MVVM

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!