WinUI Data Grid in MVVM Applications: Column Generation, Selection Binding, and other MVVM Techniques

WPF Team Blog
13 June 2022
MVVM is a well-known foundation for flexible and well-structured applications for such platforms as WinUI and WPF. That’s why we pay special attention to MVVM usage scenarios when developing our suite of controls. In this blog post, we will show you how to use the WinUI Data Grid with some basic MVVM scenarios.

Create a View Model and Bind the Data Grid to Data

Let's start with a simple view model that contains data for the Data Grid.
Create a WinUI application and install the DevExpress WinUI Controls suite. Refer to the following help topic for a complete tutorial: DevExpress WinUI Controls - Get Started.
This example uses a compile-time generated view model to keep its code compact and clean. Install the DevExpress.Mvvm.CodeGenerators NuGet package to obtain access to view model generators.
Create a view model with the Source property that retrieves data from the data model and can be used as the GridControl's binding source:
using DevExpress.Mvvm.CodeGenerators;
// ...
[GenerateViewModel]
public partial class MainViewModel {
    public MainViewModel() {
        Source = ProductsDataModel.GetProducts();
    }

    [GenerateProperty]
    ObservableCollection<Product> source;
}
In the Window class, create a ViewModel property that returns the created view model. This property allows you to use the x:Bind technique to bind the Data Grid to view model properties. The x:Bind is a relatively new markup extension that uses type information at compile-time to optimize the binding.
public sealed partial class MainWindow : Window {
    public MainViewModel ViewModel { get; } = new MainViewModel();
    public MainWindow() {
        this.InitializeComponent();
    }
}
Bind the Data Grid's ItemsSource property to the Source collection of the view model:
<Window
    x:Class="WinUIMVVMGrid.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:WinUIMVVMGrid"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
    xmlns:dxg="using:DevExpress.WinUI.Grid"
    mc:Ignorable="d">
    <Grid>
        <dxg:GridControl ItemsSource="{x:Bind ViewModel.Source}"/>
    </Grid>
</Window>

Generate Data Grid Columns from your View Model

The Data Grid offers flexible APIs to generate and configure columns according to the MVVM pattern. This technique allows you to change the set of grid columns dynamically, based on the data source type or other custom logic in your view model.
Create classes that describe grid columns. In this example, we use columns that display text values, a column with the ComboBox editor, and a column that displays DateTime values:
using DevExpress.Mvvm;
// ...
public class TextColumn : BindableBase {
    public TextColumn(string fieldname) {
        FieldName = fieldname;
    }
    public string FieldName { get; }
}
public class ComboBoxColumn : TextColumn {
    public ComboBoxColumn(string fieldname, IList items) : base(fieldname) {
        Items = items;
    }
    public IList Items { get; }
}
public class DateColumn : TextColumn {
    public DateColumn(string fieldname) : base(fieldname) { }
}
Add the Columns collection to the view model and populate it with grid columns you want to display:
[GenerateViewModel]
public partial class MainViewModel {
    public MainViewModel() {
        Source = ProductsDataModel.GetProducts();

        IList Countries = Source.Select(x => x.Country).Distinct().ToList();
        Columns = new ObservableCollection<TextColumn>() {
            new TextColumn(nameof(Product.ProductName)),
            new ComboBoxColumn(nameof(Product.Country), Countries),
            new TextColumn(nameof(Product.UnitPrice)),
            new DateColumn(nameof(Product.OrderDate))
        };
    }

    [GenerateProperty]
    ObservableCollection<Product> source;

    [GenerateProperty]
    ObservableCollection<TextColumn> columns;
}
The Data Grid generates its columns based on templates. Specify a template for each column type and create a template selector that returns a column template based on a column type:
xmlns:dx="using:DevExpress.WinUI.Core"

<Grid.Resources>
    <dx:TypedDataTemplateSelector x:Name="ColumnTemplateSelector">
        <DataTemplate x:DataType="local:TextColumn" x:Name="TextColumn">
            <dxg:GridTextColumn FieldName="{x:Bind FieldName}"/>
        </DataTemplate>

        <DataTemplate x:DataType="local:ComboBoxColumn" x:Name="ComboBoxColumn">
            <dxg:GridComboBoxColumn FieldName="{x:Bind FieldName}" ItemsSource="{x:Bind Items}"/>
        </DataTemplate>

        <DataTemplate x:DataType="local:DateColumn" x:Name="DateColumn">
            <dxg:GridDateColumn FieldName="{x:Bind FieldName}"/>
        </DataTemplate>
    </dx:TypedDataTemplateSelector>
</Grid.Resources>
In the code snippet above, we use TypedDataTemplateSelector, which allows you to choose templates without writing a single line of C# code. You can specify a standard template selector instead.
Bind the ColumnsSource property to the view model's Column collection and assign the template selector to the ColumnTemplateSelector property:
<dxg:GridControl ItemsSource="{x:Bind ViewModel.Source}" 
                 NavigationStyle="Cell" 
                 ColumnsSource="{x:Bind ViewModel.Columns}" 
                 ColumnTemplateSelector="{StaticResource ColumnTemplateSelector}"/>

Obtain Selected Items in your View Model

The Data Grid allows you to bind the collection of its selected items to a property in your view model.
Add a collection for selected items to the view model and initialize it in the view model's constructor. You can also modify this collection, and the Data Grid will reflect such changes:
[GenerateViewModel]
public partial class MainViewModel {
    public MainViewModel() {
        Source = ProductsDataModel.GetProducts();
        // ...
        Selection = new ObservableCollection<Product>() {Source.ElementAt(0)};
    }
    // ...
    [GenerateProperty]
    ObservableCollection<Product> selection;
}
Enable Multiple Row Selection mode and bind the Data Grid's SelectedItems property to the Selection collection:
<dxg:GridControl ...
                 SelectionMode="RowExtended"
                 SelectedItems="{x:Bind ViewModel.Selection, Mode=TwoWay}"/>
This is all you need to do to synchronize the Data Grid's selected items with a view model property.

Use UI Services to Invoke a Message Box from your View Model

Now we'll describe how to show a message box with the selected rows according to the MVVM technique. In this case, we'll use our UI Services to avoid accessing visual elements from the view model.
Add MessageBoxService to the view and bind its ServiceClient property to the view model:
<dxg:GridControl ...
                 SelectionMode="RowExtended"
                 SelectedItems="{x:Bind ViewModel.Selection, Mode=TwoWay}">
    <dx:Interaction.Behaviors>
        <dx:MessageBoxService ServiceClient="{x:Bind ViewModel}"/>
    </dx:Interaction.Behaviors>
</dxg:GridControl>
Add the ImplementISupportUIServices parameter to the GenerateViewModel attribute and create a command that uses MessageBoxService to display selected items:
[GenerateViewModel(ImplementISupportUIServices=true)]
public partial class MainViewModel {
    // ...
    IMessageBoxService MessageBoxService => GetUIService<IMessageBoxService>();
    
    [GenerateCommand]
    async void ShowSelectedRows() {
        string Text = "";
        foreach (Product product in Selection) {
            Text += product.ProductName + "\r\n";
        }
        await MessageBoxService.ShowAsync(Text, "Selected Products");
    }
    bool CanShowSelectedRows() => Selection.Any();
}
Subscribe to the Selection collection's CollectionChanged event to refresh the ShowSelectedRows command's CanExecute state each time a user selects or unselects a row:
[GenerateViewModel(ImplementISupportUIServices=true)]
public partial class MainViewModel {
    public MainViewModel() {
        // ...
        Selection.CollectionChanged += (s, e) => ShowSelectedRowsCommand.RaiseCanExecuteChanged();
    }
    // ...
}
Add a button that executes the ShowSelectedRows command:
<Grid>
    <Grid.RowDefinitions>
        <RowDefinition/>
        <RowDefinition Height="35"/>
    </Grid.RowDefinitions>
    <!-- ... -->
    <Button Grid.Row="1" 
            HorizontalAlignment="Stretch" 
            Content="Show Selected Products" 
            Command="{x:Bind ViewModel.ShowSelectedRowsCommand}"/>
</Grid>

Use the EventToCommand Behavior to Assign Data Grid events to View Model Commands

The Data Grid allows you to use the EventToCommand behavior to associate Data Grid events with the view model commands. You can use this behavior to initialize a new row in the view model.
Specify the NewItemRowPosition property to show the New Item Row. This row allows users to add new rows to the Data Grid.
Add an EventToCommand behavior that executes a view model command instead of the AddingNewRow event:
<dxg:GridControl ...
                 NewItemRowPosition="Top">
    <dx:Interaction.Behaviors>
        <dx:EventToCommand EventName="AddingNewRow"  
                           PassEventArgsToCommand="True"
                           Command="{x:Bind ViewModel.AddingNewRowCommand}"/>
    </dx:Interaction.Behaviors>
</dxg:GridControl>
Create a command that adds a new object with predefined values to the data source when a user starts to edit the New Item Row:
[GenerateCommand]
void AddingNewRow(AddingNewEventArgs e) =>
    e.NewObject = new Product {ProductName = "", Country = "Austria", UnitPrice = 0, OrderDate = System.DateTime.Today};

Recap

In this post, we have described how to use our MVVM techniques with the WinUI Data Grid. You have learned how to:
  • Bind the Data Grid's ItemsSource property to a collection in a view model.
  • Specify grid columns in a view model and display them in the Data Grid.
  • Access the Data Grid's selected items from the view model.
  • Convert the Data Grid's events to commands in a view model.
The application created in this post is available on GitHub: WinUI Data Grid in MVVM Scenarios.
Note that v22.1 also offers new great features for the WinUI Data Grid. See What’s New for complete information.

Free DevExpress Products - Get Your Copy Today

The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the DevExpress Support Center at your convenience. We'll be happy to follow-up.