New to Telerik UI for .NET MAUI? Start a free 30-day trial

.NET MAUI DataGrid Data Binding: Binding to Dynamic Data

As of Telerik UI for .NET MAUI 6.8.0, the .NET MAUI DataGrid supports binding to any type that implements the standard IDynamicMetaObjectProvider DLR interface, such as DynamicObject and ExpandoObject.

Using Dynamic Data enables you to filter, group, and sort the data inside the DataGrid both through the UI and programmatically.

Executing Dynamic Data on iOS and MacCatalyst

When you develop applications for Apple devices and plan to bind the DataGrid to dynamic data, you must consider the security restrictions set by Apple. These restrictions disallow the execution of dynamically generated code on a device. As a result, apps that use DynamicObject or ExpandoObject will crash in a release build for iOS and Mac Catalyst with a System.ExecutionEngineException. To prevent the exception, use the MtouchInterpreter as recommended by Microsoft.

<PropertyGroup Condition="'$(Configuration)|$(RuntimeIdentifier)'=='Release|maccatalyst-arm64'">
    <MtouchInterpreter>-all,+Telerik.Maui.Controls.Compatibility.dll</MtouchInterpreter>
</PropertyGroup>

For more information about these limitations and the suggested solution, see Microsoft's Mono interpreter on iOS and Mac Catalyst article.

Binding to DynamicObject

The following example shows how to implement a DynamicObject class with Dynamic Language Runtime (DLR) and Common Language Runtime (CLR) fields and bind it to the DataGrid. The standard CLR properties registered in the dynamic type must be exposed through the DLR API.

The model shows a class that derives from DynamicObject and contains a CLR property called Id. When the DataGrid auto-generates its columns, the TryGetMember method of the DynamicObject class will be used to fetch the values for each column. You must also implement a specific logic in the method to allow RadDataGrid to work with both CLR and DLR data.

1. Add the model for the DataGrid:

public class MyDynamicObject : DynamicObject, INotifyPropertyChanged
{
    private int id;
    private readonly IDictionary<string, object> data = new Dictionary<string, object>();

    // CLR property 
    public int Id
    {
        get => this.id;
        set 
        {
            if (this.id != value)
            {
                this.id = value;
                OnPropertyChanged("Id");
            }
        }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    public override IEnumerable<string> GetDynamicMemberNames() => data.Keys;

    public override bool TryGetMember(GetMemberBinder binder, out object result)
    {
        if (binder.Name == nameof(this.Id))
        {
            result = this.Id;
        }
        else
        {
            result = this[binder.Name];
        }

        return true;
    }

    public override bool TrySetMember(SetMemberBinder binder, object value)
    {
        this[binder.Name] = value;
        return true;
    }

    public object this[string columnName]
    {
        get
        {
            if (data.ContainsKey(columnName))
            {
                return data[columnName];
            }
            return null;
        }
        set
        {
            if (!data.ContainsKey(columnName))
            {
                data.Add(columnName, value);
                OnPropertyChanged(columnName);
            }
            else
            {
                if (data[columnName] != value)
                {
                    data[columnName] = value;
                    OnPropertyChanged(columnName);
                }
            }
        }
    }

    private void OnPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

2. Define the ViewModel:

public class ViewModel
{
    public ViewModel()
    {
        this.Data = new ObservableCollection<MyDynamicObject>();
        for (int i = 0; i < 30; i++)
        {
            var row = new MyDynamicObject() { Id = i };
            for (int j = 0; j < 5; j++)
            {
                row[string.Format("Column {0}", j)] = string.Format("Cell {0} {1}", i, j);
            }

            this.Data.Add(row);
        }
    }

    public ObservableCollection<MyDynamicObject> Data { get; set; }
}

3. Define the RadDataGrid:

<telerik:RadDataGrid ItemsSource="{Binding Data}">
    <telerik:RadDataGrid.BindingContext>
        <local:ViewModel />
    </telerik:RadDataGrid.BindingContext>
</telerik:RadDataGrid>

4. Add the telerik namespace:

xmlns:telerik="http://schemas.telerik.com/2022/xaml/maui"

This is the result:

Telerik UI for .NET MAUI DataGrid DynamicObject

Binding to ExpandoObject

1. Define the RadDataGrid control in XAML:

<telerik:RadDataGrid ItemsSource="{Binding Data}">
    <telerik:RadDataGrid.BindingContext>
        <local:ViewModel />
    </telerik:RadDataGrid.BindingContext>
</telerik:RadDataGrid>

2. Add the telerik namespace:

xmlns:telerik="http://schemas.telerik.com/2022/xaml/maui"

3. Define the ViewModel:

public class ViewModel
{
    public ViewModel()
    {
        this.Data = new ObservableCollection<ExpandoObject>();
        for (int i = 0; i < 30; i++)
        {
            dynamic item = new ExpandoObject();
            item.Country = "Country " + i;
            item.Capital = "Capital " + i;
            item.Population = i * 10000;

            this.Data.Add(item);
        }
    }

    public ObservableCollection<ExpandoObject> Data { get; set; }
}

This is the result:

Telerik UI for .NET MAUI DataGrid ExpandoObject

Additional Resources

See Also

In this article