Fill DataGrid with only the Data from DataTable WinUI3 MVVM

61 Views Asked by At

The DataTable is populated with the data from a database.

 var query = $"SELECT * FROM {databaseTable.Name}";
 OracleCommand oracleCommand = new OracleCommand(query, _connection);
 OracleDataAdapter oracleDataAdapter = new OracleDataAdapter(oracleCommand);
 DataTable data = new DataTable();
 oracleDataAdapter.Fill(data);
 return data;

I want to populate a DataGrid with the DataTable trough Itemssource. We don't want to specify any objects (classes). We want to fill the Grid with only the informations we get from the DataTable

<control:DataGrid x:Name="gridView1" Margin="5" ItemsSource="{Binding DataTable}" GridLinesVisibility="All" AlternatingRowBackground="DarkBlue">

At the moment I have a ListView that shows me all the Tables that we read out separately from the database

 <ListView x:Name="listView" ItemsSource="{Binding DatabaseTables, Mode=TwoWay}" DisplayMemberPath="Name" SelectedItem="{Binding DatabaseTable, Mode=TwoWay}">

and by selecting a table we want all the entries shown in the Grid with the expecting columns from the DataTable

I tried to look around, but couldn't find a solution.

1

There are 1 best solutions below

0
Andrew KeepCoding On BEST ANSWER

This should work:

MainPageViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using System.Data;
using System.Threading.Tasks;

namespace DataTableExample;

// This class needs to be "partial" 
// for the CommunityToolkit.NuGet package source generators.
public partial class  MainPageViewModel : ObservableObject
{
    [ObservableProperty]
    // "Items" property will be auto-generated for you.
    private ObservableCollection<object>? _items;

    [RelayCommand]
    // "LoadItemsCommand" will be auto-generated for you.
    private async Task LoadItems()
    {
        DataTable dataTable = await LoadItemsFromDataBase();

        ObservableCollection<object> items = new();

        foreach (DataRow row in dataTable.Rows)
        {
            items.Add(row.ItemArray);
        }

        Items = items;
    }

    private Task<DataTable> LoadItemsFromDataBase()
    {
        return Task.Run(() =>
        {
            DataTable dataTable = new();
            dataTable.Columns.Add("Id", typeof(int));
            dataTable.Columns.Add("Name", typeof(string));
            dataTable.Columns.Add("Price", typeof(decimal));
            dataTable.Rows.Add(1, "Product 1", 10.0);
            dataTable.Rows.Add(2, "Product 2", 20.0);
            dataTable.Rows.Add(3, "Product 3", 30.0);
            return dataTable;
        });
    }
}

MainPage.xaml.cs

using Microsoft.UI.Xaml.Controls;

namespace DataTableExample;
public sealed partial class MainPage : Page
{
    public MainPage()
    {
        this.InitializeComponent();
    }

    private MainPageViewModel ViewModel => new();
}

MainPage.xaml

<Page
    x:Class="DataTableExample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:DataTableExample"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:toolkit="using:CommunityToolkit.WinUI.UI.Controls"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    mc:Ignorable="d">

    <Grid RowDefinitions="Auto,*">
        <Button
            Grid.Row="0"
            Command="{x:Bind ViewModel.LoadItemsCommand}"
            Content="Load items" />
        <toolkit:DataGrid
            Grid.Row="1"
            AutoGenerateColumns="False"
            ItemsSource="{x:Bind ViewModel.Items, Mode=OneWay}">
            <toolkit:DataGrid.Columns>
                <toolkit:DataGridTextColumn
                    Binding="{Binding [0]}"
                    Header="ID" />
                <toolkit:DataGridTextColumn
                    Binding="{Binding [1]}"
                    Header="Name" />
                <toolkit:DataGridTextColumn
                    Binding="{Binding [2]}"
                    Header="Price" />
            </toolkit:DataGrid.Columns>
        </toolkit:DataGrid>
    </Grid>

</Page>

NOTE:

I'm using the CommunityToolkit.Mvvm NuGet package for the MVVM design.

UPDATE:

I came up with another solution that might be a bit closer to your requirements. This way you won't need to declare each column in XAML.

DataGridExtensions.cs

using CommunityToolkit.WinUI.UI.Controls;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Data;
using System.Collections.ObjectModel;

namespace DataTableExample;

public static class DataGridExtensions
{
    public static readonly DependencyProperty ColumnsProperty =
        DependencyProperty.RegisterAttached(
            "Columns",
            typeof(ObservableCollection<Column>),
            typeof(DataGridExtensions),
            new PropertyMetadata(
                new ObservableCollection<Column>(),
                OnColumnsPropertyChanged));

    public static ObservableCollection<Column> GetColumns(DependencyObject obj)
    {
        return (ObservableCollection<Column>)obj.GetValue(ColumnsProperty);
    }

    public static void SetColumns(DependencyObject obj, ObservableCollection<Column> value)
    {
        obj.SetValue(ColumnsProperty, value);
    }

    private static void OnColumnsPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        if (d is not DataGrid dataGrid ||
            e.NewValue is not ObservableCollection<Column> columns)
        {
            return;
        }

        dataGrid.Columns.Clear();

        foreach (Column column in columns)
        {
            Binding binding = new()
            {
                Path = new PropertyPath(column.Binding)
            };

            dataGrid.Columns.Add(
                new DataGridTextColumn
                {
                    Header = column.Header,
                    Binding = binding,
                });
        }
    }

    public class Column
    {
        public string? Header { get; set; }

        public string? Binding { get; set; }
    }
}

MainPageViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using System.Collections.ObjectModel;
using System.Data;
using System.Threading.Tasks;

namespace DataTableExample;

public partial class  MainPageViewModel : ObservableObject
{
    [ObservableProperty]
    private ObservableCollection<object>? _items;

    [ObservableProperty]
    private ObservableCollection<DataGridExtensions.Column>? _columns;

    [RelayCommand]
    private async Task LoadItems()
    {
        DataTable dataTable = await LoadItemsFromDataBase();

        ObservableCollection<DataGridExtensions.Column> columns = new();
        ObservableCollection<object> items = new();

        int index = 0;

        foreach (DataColumn column in dataTable.Columns)
        {
            columns.Add(
                new DataGridExtensions.Column {
                    Header = column.ColumnName,
                    Binding = $"[{index++}]"
                });
        }

        foreach (DataRow row in dataTable.Rows)
        {
            items.Add(row.ItemArray);
        }

        Columns = columns;
        Items = items;
    }

    private Task<DataTable> LoadItemsFromDataBase()
    {
        return Task.Run(() =>
        {
            DataTable dataTable = new();
            dataTable.Columns.Add("Id", typeof(int));
            dataTable.Columns.Add("Name", typeof(string));
            dataTable.Columns.Add("Price", typeof(decimal));
            dataTable.Rows.Add(1, "Product 1", 10.0);
            dataTable.Rows.Add(2, "Product 2", 20.0);
            dataTable.Rows.Add(3, "Product 3", 30.0);
            return dataTable;
        });
    }
}

and...

MainPage.xaml

<Page
    x:Class="DataTableExample.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:local="using:DataTableExample"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:toolkit="using:CommunityToolkit.WinUI.UI.Controls"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"
    mc:Ignorable="d">

    <Grid RowDefinitions="Auto,*">
        <Button
            Grid.Row="0"
            Command="{x:Bind ViewModel.LoadItemsCommand}"
            Content="Load items" />
        <toolkit:DataGrid
            Grid.Row="1"
            local:DataGridExtensions.Columns="{x:Bind ViewModel.Columns, Mode=OneWay}"
            AutoGenerateColumns="False"
            ItemsSource="{x:Bind ViewModel.Items, Mode=OneWay}" />
    </Grid>

</Page>