Skip to main content

MVVM Column Generation

  • 5 minutes to read

This topic describes how to define columns in a ViewModel and display them in the GridControl. Use this technique when you need to change the set of GridControl columns dynamically based on the data source type or other custom condition in an MVVM scenario.

View Example: Data Grid - Bind to a Collection of Columns Specified in a ViewModel

Assign ViewModel Columns to the GridControl

This scenario demonstrates how to display information from an employee data model in the GridControl.

WinUI Grid - MVVM Columns

EmployeesDataModel
using System.Collections.ObjectModel;

namespace MVVM_Columns {
    public class Employee {
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string StateProvinceName { get; set; }
    }
    public class EmployeesDataModel {
        public static ObservableCollection<Employee> GetEmployees() {
            ObservableCollection<Employee> people = new ObservableCollection<Employee> {
                new Employee() { FirstName = "Bruce", LastName = "Cambell", StateProvinceName = "Missouri"},
                new Employee() { FirstName = "Cindy", LastName = "Haneline", StateProvinceName = "Oklahoma"},
                new Employee() { FirstName = "Andrea", LastName = "Deville", StateProvinceName = "Colorado"},
                new Employee() { FirstName = "Anita", LastName = "Ryan", StateProvinceName = "Colorado"}
            };
            return people;
        }
    }
}
  1. Create a class that describes a grid column:

    public class TextColumn : BindableBase {
        public TextColumn(string fieldname) {
            FieldName = fieldname;
        }
        public string FieldName { get; }
    }
    
  2. Specify a collection of columns in the ViewModel:

    public class ViewModel : ViewModelBase {
        public ViewModel() {
            Source = EmployeesDataModel.GetEmployees();
    
            Columns = new ObservableCollection<TextColumn>() {
                new TextColumn(nameof(Employee.FirstName)),
                new TextColumn(nameof(Employee.LastName)),
                new TextColumn(nameof(Employee.StateProvinceName))
            };
        }
        public ObservableCollection<Employee> Source { get; }
        public ObservableCollection<TextColumn> Columns { get; }
    }
    
  3. The GridControl generates columns based on column templates. Add a template that specifies a text column:

    <Page.Resources>
        <DataTemplate x:Key="TextColumn" x:DataType="local:TextColumn">
            <dxg:GridTextColumn FieldName="{x:Bind FieldName}"/>
        </DataTemplate>
    </Page.Resources>
    
  4. Assign the column collection to the DataControlBase.ColumnsSource property. Set the DataControlBase.ColumnTemplate property to a template that defines columns:

    <dxg:GridControl ItemsSource="{x:Bind ViewModel.Source}"
                     ColumnsSource="{x:Bind ViewModel.Columns}" 
                     ColumnTemplate="{StaticResource TextColumn}"/>
    

Add a ComboBox Column

If you want to specify extra column types (for example, to display a list of states in a column’s combo box), follow the steps below:

WinUI Grid - MVVM ComboBox Column

  1. Create a class that describes a combo box column:

    public class ComboBoxColumn : TextColumn {
        public ComboBoxColumn(string fieldname, IList items) : base(fieldname) {
            Items = items;
        }
        public IList Items { get; }
    }
    
  2. Add a combo box column to the ViewModel’s Columns collection and specify its data source:

    public class ViewModel : ViewModelBase {
        public ViewModel() {
            Source = EmployeesDataModel.GetEmployees();
            var States = Source.Select(x => x.StateProvinceName).Distinct().ToList();
    
            Columns = new ObservableCollection<TextColumn>() {
                new TextColumn(nameof(Employee.FirstName)),
                new TextColumn(nameof(Employee.LastName)),
                new ComboBoxColumn(nameof(Employee.StateProvinceName), States)
            };
        }
        public ObservableCollection<Employee> Source { get; }
        public ObservableCollection<TextColumn> Columns { get; }
    }
    
  3. Create a template that describes combo box columns:

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

Add a Template Selector to Choose among Multiple Templates

Your application now contains the TextColumn and ComboBoxColumn templates. Follow the steps below to choose a template based on the column type:

  1. Define a TypedDataTemplateSelector.
  2. Add column templates to the TypedDataTemplateSelector. The template selector returns a template for which the Key property matches the object type:

    <Page ...
        xmlns:dxg="using:DevExpress.WinUI.Grid" 
        xmlns:dx="using:DevExpress.WinUI.Core">
    
    <Page.Resources>
        <dx:TypedDataTemplateSelector x:Name="ColumnSelector">
    
            <DataTemplate x:Key="TextColumn" x:DataType="local:TextColumn">
                <dxg:GridTextColumn FieldName="{x:Bind FieldName}"/>
            </DataTemplate>
    
            <DataTemplate x:Key="ComboBoxColumn" x:DataType="local:ComboBoxColumn">
                <dxg:GridComboBoxColumn FieldName="{x:Bind FieldName}" ItemsSource="{x:Bind Items}"/>
            </DataTemplate>
        </dx:TypedDataTemplateSelector>
    </Page.Resources>
    

    You can also use a DataTemplateSelector descendant instead of the TypedDataTemplateSelector to choose a column template.

  3. Assign the template selector to the DataControlBase.ColumnTemplateSelector property:

    <dxg:GridControl ItemsSource="{x:Bind ViewModel.Source}"
                     ColumnsSource="{x:Bind ViewModel.Columns}" 
                     ColumnTemplateSelector="{StaticResource ColumnSelector}"/>