Search code examples
c#uwpdatagridwindows-community-toolkit

UWP GridView with dynamic names for columns


I'm trying to populate a GridView with some data, but am having no luck. Documentation tells me absolutely nothing about how the GridView maps data from the item source to the rows in the control. I've seen the textbox example where they create a class with properties, and this works fine:

public sealed partial class CollectionInfoPage: Page
{
    List<Test> m_ItemSource;
    
    class Test
    {
        public string s1 => "Hello";
        public string s2 => "World";
    }

    public async Task ModelUpdated(Model.Adaptor Adaptor)
    {
        await Common.RunUiTask(async () =>
        {
            m_ItemSource = new List<Test> { new Test(), new Test() };
        });
    }
}

XAML:

<Page
    x:Class="CollectionInfoPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="Transparent">

    <Grid>
        <controls:DataGrid x:Name="DataGrid" AutoGenerateColumns="True" ItemsSource="{x:Bind m_ItemSource}">
        </controls:DataGrid>
    </Grid>
</Page>

But my issue is that the column names are simply not known at compile, so I can't use this approach. I need to be able to specify the names of the columns at runtime and map the data to those specific columns, but here's where I am stuck. I can't figure out a way to do it. I can't find any documentation on how it maps data from a data set to the different columns. I can pretty much guess, but that does me no good.

I find this whole data binding approach to be terrible, inflexible, slow and difficult to use. Why isn't it possible to simply use an interface to do the data binding instead of taking objects?

Any clues on how to achieve this?

Solution: I defined a DataView class to use as an item source as follows:

class DataView
{
    public IEnumerable<string> Data { get; }

    public DataView(IEnumerable<string> Data)
    {
        this.Data = Data;
    }
}

I then used code to create the columns and create bindings to the data in the item source:

int n = 0;
var Cols = ColNames.Select((x, n) =>
{
    var Col = new DataGridTextColumn();
    Col.Header = x;
    Col.Binding = new Windows.UI.Xaml.Data.Binding();
    Col.Binding.Path = new PropertyPath("Data[" + n + "]");
    return Col;
}).ToList();

foreach (var Col in Cols)
    DataGrid.Columns.Add(Col);

XAML:

<Page
    x:Class="CollectionInfoPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:controls="using:Microsoft.Toolkit.Uwp.UI.Controls"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="Transparent">

    <Grid>
        <controls:DataGrid x:Name="DataGrid" AutoGenerateColumns="False" ItemsSource="{x:Bind m_GridItemSource}"/>
    </Grid>
</Page>

Then I just had to set my item source where each instance of DataView would contain one row of information.


Solution

  • I need to be able to specify the names of the columns at runtime and map the data to those specific columns.

    If you use AutoGenerateColumns, the columns header will generate base on the data model's property name automatically.

    You have many ways to specific the columns header. One is specific the DataGridColumn in the xaml with custom header.

    <controls:DataGridTextColumn Header="Rank" Binding="{Binding Rank}" Tag="Rank" />
    <controls:DataGridTextColumn Header="Height (m)" Binding="{Binding Height_m}" Tag="Height_m" />
    <controls:DataGridTextColumn Header="Range" Binding="{Binding Range}" Tag="Range" />
    <controls:DataGridTextColumn Header="Parent Mountain" Binding="{Binding Parent_mountain}" Tag="Parent_mountain" />
    

    Another is find the specific DataGridColumn from DataGrid.Columns property and edit the header in the code behind.

    private void DataGrid_Loaded(object sender, RoutedEventArgs e)
    {
        var colum = DataGrid.Columns[0] as DataGridColumn;
        colum.Header = "Test";
    }