diff --git a/Semi.Avalonia.sln b/Semi.Avalonia.sln
index fe4a382..84e2505 100644
--- a/Semi.Avalonia.sln
+++ b/Semi.Avalonia.sln
@@ -19,6 +19,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semi.Avalonia.Demo.Web", "d
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Semi.Avalonia.Demo", "demo\Semi.Avalonia.Demo\Semi.Avalonia.Demo.csproj", "{D789AEDB-EBDF-4450-8E8E-B4A03FB257B0}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Semi.Avalonia.DataGrid", "src\Semi.Avalonia.DataGrid\Semi.Avalonia.DataGrid.csproj", "{8A90C292-8761-4F70-8E1F-EFC097FEADB3}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -41,6 +43,10 @@ Global
{D789AEDB-EBDF-4450-8E8E-B4A03FB257B0}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D789AEDB-EBDF-4450-8E8E-B4A03FB257B0}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D789AEDB-EBDF-4450-8E8E-B4A03FB257B0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {8A90C292-8761-4F70-8E1F-EFC097FEADB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {8A90C292-8761-4F70-8E1F-EFC097FEADB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {8A90C292-8761-4F70-8E1F-EFC097FEADB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {8A90C292-8761-4F70-8E1F-EFC097FEADB3}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/demo/Semi.Avalonia.Demo.Desktop/App.axaml b/demo/Semi.Avalonia.Demo.Desktop/App.axaml
index 3c2bebe..098f598 100644
--- a/demo/Semi.Avalonia.Demo.Desktop/App.axaml
+++ b/demo/Semi.Avalonia.Demo.Desktop/App.axaml
@@ -6,5 +6,6 @@
+
\ No newline at end of file
diff --git a/demo/Semi.Avalonia.Demo/Pages/DataGridDemo.axaml b/demo/Semi.Avalonia.Demo/Pages/DataGridDemo.axaml
new file mode 100644
index 0000000..cd5c9e8
--- /dev/null
+++ b/demo/Semi.Avalonia.Demo/Pages/DataGridDemo.axaml
@@ -0,0 +1,195 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demo/Semi.Avalonia.Demo/Pages/DataGridDemo.axaml.cs b/demo/Semi.Avalonia.Demo/Pages/DataGridDemo.axaml.cs
new file mode 100644
index 0000000..31caf37
--- /dev/null
+++ b/demo/Semi.Avalonia.Demo/Pages/DataGridDemo.axaml.cs
@@ -0,0 +1,508 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.ComponentModel;
+using System.Linq;
+using Avalonia;
+using Avalonia.Collections;
+using Avalonia.Controls;
+using Avalonia.Controls.Primitives;
+using Avalonia.Data;
+using Avalonia.Input;
+using Avalonia.Input.Raw;
+using Avalonia.Markup.Xaml;
+using Avalonia.Threading;
+
+namespace Semi.Avalonia.Demo.Pages;
+
+public partial class DataGridDemo : UserControl
+{
+ public DataGridDemo()
+ {
+ InitializeComponent();
+ var dataGridSortDescription = DataGridSortDescription.FromPath(nameof(Country.Region), ListSortDirection.Ascending, new ReversedStringComparer());
+ var collectionView1 = new DataGridCollectionView(Countries.All);
+ collectionView1.SortDescriptions.Add(dataGridSortDescription);
+ var dg1 = this.Get("dataGrid1");
+ dg1.IsReadOnly = true;
+ dg1.LoadingRow += Dg1_LoadingRow;
+ dg1.Sorting += (s, a) =>
+ {
+ var binding = (a.Column as DataGridBoundColumn)?.Binding as Binding;
+
+ if (binding?.Path is string property
+ && property == dataGridSortDescription.PropertyPath
+ && !collectionView1.SortDescriptions.Contains(dataGridSortDescription))
+ {
+ collectionView1.SortDescriptions.Add(dataGridSortDescription);
+ }
+ };
+ dg1.Items = collectionView1;
+
+ var dg2 = this.Get("dataGridGrouping");
+ dg2.IsReadOnly = true;
+
+ var collectionView2 = new DataGridCollectionView(Countries.All);
+ collectionView2.GroupDescriptions.Add(new DataGridPathGroupDescription("Region"));
+
+ dg2.Items = collectionView2;
+
+ var dg3 = this.Get("dataGridEdit");
+ dg3.IsReadOnly = false;
+
+ var list = new ObservableCollection
+ {
+ new Person { FirstName = "John", LastName = "Doe" , Age = 30},
+ new Person { FirstName = "Elizabeth", LastName = "Thomas", IsBanned = true , Age = 40 },
+ new Person { FirstName = "Zack", LastName = "Ward" , Age = 50 }
+ };
+ DataGrid3Source = list;
+
+ var addButton = this.Get