(presenting-data-on-a-grid)=
# Presenting data on a grid

:::{video} ../img/datagrid-example.webm
:figwidth: 35%
:align: right
:caption: Data grid with a button for adding new rows and sorting.
:::
We will integrate [`DataGrid`][datagrid-docs] – a GUI control component for presenting class data similar to the data represented on a spreadsheet.

## Creating a project

First create a new `Avalionia .NET App` with the name `DataGridExample`.

(datagrid-control-installation)=
## `DataGrid` control installation

In the following, we will install `Avalonia.Controls.DataGrid` in a C# project.

1. Open your project.
1. On the left vertical tool window bar, click {{nuget}} icon.

   :::{card} Troubleshooting 
   If you cannot find the icon:
   1. If you never used the {{nuget}} icon, it will be under the three dots `...` icon.
   2. Alternatively: On the `Explorer` {{AllIcons_expui_toolwindows_project}} tab, right-click on your project name and click {{nuget}} `Manage NuGet Packages`.
   :::

1. Search for `avalonia controls`.
1. Click `Avalonia.Controls.DataGrid`.
1. On the right, click {{AllIcons_expui_general_add}} icon. A new window will pop up.
1. Click `Install`.

   :::{card} Troubleshooting 
   If you get the error `Install failed ... detected package downgrade`:

   Then the `Avalonia.Controls.DataGrid` version does not match the base `Avalonia` framework version, e.g., if `DataGrid` is newer than the `Avalonia` template.
   
   [Upgrade the template](project:#upgrading-net-templates) or [upgrade .NET packages in the project](project:#upgrading-net-packages-in-the-project) in this case and try installing `DataGrid` again.
   :::
   
   You will get a message about `Avalonia.Controls.DataGrid ... was successfully installed to ...`.

Now you can close the NuGet window by clicking {{nuget}} again.

## Integrating a `DataGrid` into the project


1. <!-- application-styles-begin -->
   Open `App.axaml` and append the `<Styleinclude` line after `<FluentTheme>`. After the modification the `Styleinclude` line should look as follows:

   ```xml
   <Application.Styles>
       <FluentTheme />
       <StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml" />
   </Application.Styles>
   ```

   We need this, because `DataGrid` [uses additional styles](https://docs.avaloniaui.net/docs/reference/controls/datagrid#include-datagrid-styles) compared to the standard GUI elements.
   <!-- application-styles-end -->
1. Open `MainWindow.axaml.cs` and add the following class under the definition of `MainWindow` class.

   <!-- class-person-get-set-begin -->
   ```cs
   public class Person
   {
       public string FirstName { get; set; }
       public string LastName { get; set; }
   }
   ```
   
   Notice the `get` and `set` keywords for each field that we did not use before. These keywords introduce methods in the background that are used to *get* & *set* `FirstName` and `LastName`. We need them; otherwise the GUI framework does not show any data. For example, if you remove `{get; set;}` from `LastName`, then you will see no `LastName` field later when you run the program.
   
   We will call a field with `get` or `set` *property*.
   
   The reason for the behavior in the last paragraph could be that C# has a feature which allows to get the fields with get and set methods automatically, which in turn is used to show these data in DataGrid.
   
   <!-- According to Claude, by using them, we can get all properties using .NET Reflection. -->
   <!-- class-person-get-set-end -->

1. Open `MainWindow.axaml.cs`.
   <!-- mainwindow.axaml.cs-objectmodel-begin -->
   Add the following line on the top of the file:

   ```cs
   using System.Collections.ObjectModel;
   ```
   This library is required to make the `ObservableCollection` available. `ObservableCollection` is a special `List` that can communicate with the GUI when data in the list is modified.
   
   :::{warning}
   Use `ObservableCollection` instead of `List` if you plan to present a list on the GUI.
   :::
   <!-- mainwindow.axaml.cs-objectmodel-end -->
   
1. In `MainWindow.axaml.cs`, add the following property to the `MainWindow` class.

   :::{literalinclude} ../code-wi/DataGridExample/MainWindow.axaml.cs
   :language: cs
   :start-at: public ObservableCollection
   :end-at: ];
   :::

1. In `MainWindow.axaml.cs`, set the `DataContext` to `MainWindow` by adding a line to the constructor method `MainWindow()` as follows:

   :::{literalinclude} ../code-wi/DataGridExample/MainWindow.axaml.cs
   :language: cs
   :start-at: public MainWindow()
   :end-at: }
   :::
   
   The `DataContext` determines which properties (fields with get or set) are available in the XAML file. To make `People` available in the XAML file as the data source for the `DataGrid`, we require this file.

1. Open `MainWindow.axaml`. Add the following lines between `x:Class` and `Title` in the `Window` tag:

   ```xml
   xmlns:local="clr-namespace:YOUR-PROJECT-NAME"
   x:DataType="local:MainWindow"
   ```
   
   For example:
   
   :::{literalinclude} ../code-wi/DataGridExample/MainWindow.axaml
   :start-after: mc:Ignorable
   :end-before: <DataGrid
   :::

1. In `MainWindow.axaml` replace the text `Welcome to Avalonia!` with the following block:

   <!-- mainwindow-include-datagrid-begin -->
   ```xml
   <DataGrid Margin="20" ItemsSource="{Binding People}" 
             AutoGenerateColumns="True" 
             GridLinesVisibility="All"
             BorderThickness="1" BorderBrush="Gray">
   </DataGrid>
   ```
   
   These lines ensure the following:

   - `Binding People` binds `People` to the `DataGrid` so that the data on the GUI is updated automatically, when the data changes and vice-versa.
   - `AutoGenerateColumns` generates columns automatically by reading the properties of a class, e.g., `FirstName` and `LastName` in `Person`.
   - Other four attributes including `Margin` configure the style.

   For more information about the attributes, refer to [`DataGrid` documentation][datagrid-docs].
   <!-- mainwindow-include-datagrid-end -->
   
1. Run your project.
1. Try:

   - modifying data
   - changing sort order
 
1. If you would like to add additional logic, then append them to the `MainWindow()` method, e.g., `People.RemoveAt(0);` will remove an item from the top of the collection.

## Adding a button

We already integrated a button in section {ref}`getting-input-from-the-GUI`. Let us integrate a button that adds new data to the list:

1. Add a button to the XAML:

   :::{literalinclude} ../code-wi/DataGridExample/MainWindow.axaml
   :start-at: <StackPanel>
   :end-at: </StackPanel>
   :::
   
1. Add the click handler in the `MainWindow` class:

   :::{literalinclude} ../code-wi/DataGridExample/MainWindow.axaml.cs
   :language: cs
   :start-at: public void NewRowButton_OnClick(object? sender, RoutedEventArgs e)
   :end-before: public class Person
   :::
   
1. Run the project. The button should add new rows that you can edit.

You can find the whole project here:
- <repo-browse-prj:DataGridExample>
- <repo-download-prj:DataGridExample>
      
<!--
I recommend keeping a backup of the working project as a template and copying/migrating files from this project in new GUI projects using following steps:

NO, copy-paste does not work.
-->

:::{exercise} Person queue
Create a GUI app that can check a list of persons and puts them into two categories based on two buttons:
- ⬅️
- ➡️

You can augment your test code above so that you have one list above and two lists below that are horizontally aligned.
:::

<!-- used-resources-begin -->
## Related resources

- [`DataGrid`][datagrid-docs]

[datagrid-docs]: https://docs.avaloniaui.net/docs/reference/controls/datagrid
<!-- used-resources-end -->