ソースを参照

Remove DataGrid from the main repository (#18401)

Julien Lebosquain 7 ヶ月 前
コミット
07a76145f4
80 ファイル変更36 行追加31891 行削除
  1. 3 0
      .gitmodules
  2. 1 13
      Avalonia.sln
  3. 0 1
      samples/AppWithoutLifetime/AppWithoutLifetime.csproj
  4. 1 4
      samples/ControlCatalog/App.xaml
  5. 0 5
      samples/ControlCatalog/App.xaml.cs
  6. 0 1
      samples/ControlCatalog/ControlCatalog.csproj
  7. 0 256
      samples/ControlCatalog/Models/Countries.cs
  8. 0 41
      samples/ControlCatalog/Models/Country.cs
  9. 0 37
      samples/ControlCatalog/Models/GDPValueConverter.cs
  10. 0 31
      samples/ControlCatalog/Models/GDPdLengthConverter.cs
  11. 7 10
      samples/ControlCatalog/Pages/DataGridPage.xaml
  12. 10 94
      samples/ControlCatalog/Pages/DataGridPage.xaml.cs
  13. 0 1
      samples/GpuInterop/GpuInterop.csproj
  14. 0 1
      samples/Sandbox/Sandbox.csproj
  15. 0 1
      samples/SingleProjectSandbox/SingleProjectSandbox.csproj
  16. 1 0
      src/Avalonia.Controls.DataGrid
  17. 0 18
      src/Avalonia.Controls.DataGrid/Automation/Peers/DataGridAutomationPeer.cs
  18. 0 22
      src/Avalonia.Controls.DataGrid/Automation/Peers/DataGridCellAutomationPeer.cs
  19. 0 22
      src/Avalonia.Controls.DataGrid/Automation/Peers/DataGridColumnHeaderAutomationPeer.cs
  20. 0 23
      src/Avalonia.Controls.DataGrid/Automation/Peers/DataGridColumnHeadersPresenterAutomationPeer.cs
  21. 0 14
      src/Avalonia.Controls.DataGrid/Automation/Peers/DataGridDetailsPresenterAutomationPeer.cs
  22. 0 20
      src/Avalonia.Controls.DataGrid/Automation/Peers/DataGridRowAutomationPeer.cs
  23. 0 22
      src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj
  24. 0 4380
      src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs
  25. 0 1369
      src/Avalonia.Controls.DataGrid/Collections/DataGridGroupDescription.cs
  26. 0 324
      src/Avalonia.Controls.DataGrid/Collections/DataGridSortDescription.cs
  27. 0 233
      src/Avalonia.Controls.DataGrid/Collections/IDataGridCollectionView.cs
  28. 0 6236
      src/Avalonia.Controls.DataGrid/DataGrid.cs
  29. 0 153
      src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs
  30. 0 274
      src/Avalonia.Controls.DataGrid/DataGridCell.cs
  31. 0 71
      src/Avalonia.Controls.DataGrid/DataGridCellCollection.cs
  32. 0 57
      src/Avalonia.Controls.DataGrid/DataGridCellCoordinates.cs
  33. 0 334
      src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs
  34. 0 204
      src/Avalonia.Controls.DataGrid/DataGridClipboard.cs
  35. 0 1198
      src/Avalonia.Controls.DataGrid/DataGridColumn.cs
  36. 0 581
      src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs
  37. 0 878
      src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs
  38. 0 1790
      src/Avalonia.Controls.DataGrid/DataGridColumns.cs
  39. 0 710
      src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs
  40. 0 364
      src/Avalonia.Controls.DataGrid/DataGridDisplayData.cs
  41. 0 106
      src/Avalonia.Controls.DataGrid/DataGridEnumerations.cs
  42. 0 190
      src/Avalonia.Controls.DataGrid/DataGridError.cs
  43. 0 70
      src/Avalonia.Controls.DataGrid/DataGridFillerColumn.cs
  44. 0 541
      src/Avalonia.Controls.DataGrid/DataGridLength.cs
  45. 0 1106
      src/Avalonia.Controls.DataGrid/DataGridRow.cs
  46. 0 471
      src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs
  47. 0 57
      src/Avalonia.Controls.DataGrid/DataGridRowGroupInfo.cs
  48. 0 224
      src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs
  49. 0 3045
      src/Avalonia.Controls.DataGrid/DataGridRows.cs
  50. 0 470
      src/Avalonia.Controls.DataGrid/DataGridSelectedItemsCollection.cs
  51. 0 147
      src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs
  52. 0 287
      src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs
  53. 0 44
      src/Avalonia.Controls.DataGrid/DataGridValueConverter.cs
  54. 0 569
      src/Avalonia.Controls.DataGrid/EventArgs.cs
  55. 0 25
      src/Avalonia.Controls.DataGrid/Extensions.cs
  56. 0 850
      src/Avalonia.Controls.DataGrid/IndexToValueTable.cs
  57. 0 358
      src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs
  58. 0 436
      src/Avalonia.Controls.DataGrid/Primitives/DataGridColumnHeadersPresenter.cs
  59. 0 141
      src/Avalonia.Controls.DataGrid/Primitives/DataGridDetailsPresenter.cs
  60. 0 43
      src/Avalonia.Controls.DataGrid/Primitives/DataGridFrozenGrid.cs
  61. 0 214
      src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs
  62. 0 5
      src/Avalonia.Controls.DataGrid/Properties/AssemblyInfo.cs
  63. 0 69
      src/Avalonia.Controls.DataGrid/Range.cs
  64. 0 598
      src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml
  65. 0 376
      src/Avalonia.Controls.DataGrid/Themes/Simple.xaml
  66. 0 160
      src/Avalonia.Controls.DataGrid/Utils/CellEditBinding.cs
  67. 0 22
      src/Avalonia.Controls.DataGrid/Utils/DataGridHelper.cs
  68. 0 32
      src/Avalonia.Controls.DataGrid/Utils/KeyboardHelper.cs
  69. 0 585
      src/Avalonia.Controls.DataGrid/Utils/ReflectionHelper.cs
  70. 0 60
      src/Avalonia.Controls.DataGrid/Utils/TreeHelper.cs
  71. 0 172
      src/Avalonia.Controls.DataGrid/Utils/ValidationUtil.cs
  72. 12 4
      src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj
  73. 1 1
      src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml
  74. 0 22
      tests/Avalonia.Controls.DataGrid.UnitTests/Avalonia.Controls.DataGrid.UnitTests.csproj
  75. 0 226
      tests/Avalonia.Controls.DataGrid.UnitTests/Collections/ComparerTests.cs
  76. 0 133
      tests/Avalonia.Controls.DataGrid.UnitTests/Collections/DataGridSortDescriptionTests.cs
  77. 0 177
      tests/Avalonia.Controls.DataGrid.UnitTests/DataGridRowTests.cs
  78. 0 20
      tests/Avalonia.Controls.DataGrid.UnitTests/Utils/ReflectionHelperTests.cs
  79. 0 1
      tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj
  80. 0 41
      tests/Avalonia.LeakTests/ControlTests.cs

+ 3 - 0
.gitmodules

@@ -4,3 +4,6 @@
 [submodule "src/Markup/Avalonia.Markup.Xaml/XamlIl/xamlil.github"]
 	path = src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
 	url = https://github.com/kekekeks/XamlX.git
+[submodule "src/Avalonia.Controls.DataGrid"]
+	path = src/Avalonia.Controls.DataGrid
+	url = https://github.com/AvaloniaUI/Avalonia.Controls.DataGrid.git

+ 1 - 13
Avalonia.sln

@@ -170,14 +170,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PlatformSanityChecks", "sam
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.ReactiveUI.UnitTests", "tests\Avalonia.ReactiveUI.UnitTests\Avalonia.ReactiveUI.UnitTests.csproj", "{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.DataGrid", "src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj", "{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Dialogs", "src\Avalonia.Dialogs\Avalonia.Dialogs.csproj", "{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.FreeDesktop", "src\Avalonia.FreeDesktop\Avalonia.FreeDesktop.csproj", "{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}"
 EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Controls.DataGrid.UnitTests", "tests\Avalonia.Controls.DataGrid.UnitTests\Avalonia.Controls.DataGrid.UnitTests.csproj", "{351337F5-D66F-461B-A957-4EF60BDB4BA6}"
-EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Themes.Fluent", "src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj", "{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless", "src\Headless\Avalonia.Headless\Avalonia.Headless.csproj", "{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC}"
@@ -301,6 +297,7 @@ EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.RenderTests.WpfCompare", "tests\Avalonia.RenderTests.WpfCompare\Avalonia.RenderTests.WpfCompare.csproj", "{9AE1B827-21AC-4063-AB22-C8804B7F931E}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Win32.Automation", "src\Windows\Avalonia.Win32.Automation\Avalonia.Win32.Automation.csproj", "{0097673D-DBCE-476E-82FE-E78A56E58AA2}"
+EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XEmbedSample", "samples\XEmbedSample\XEmbedSample.csproj", "{255614F5-CB64-4ECA-A026-E0B1AF6A2EF4}"
 EndProject
 Global
@@ -497,10 +494,6 @@ Global
 		{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68}.Release|Any CPU.Build.0 = Release|Any CPU
-		{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{3278F3A9-9509-4A3F-A15B-BDC8B5BFF632}.Release|Any CPU.Build.0 = Release|Any CPU
 		{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{4D55985A-1EE2-4F25-AD39-6EA8BC04F8FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -509,10 +502,6 @@ Global
 		{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA}.Release|Any CPU.Build.0 = Release|Any CPU
-		{351337F5-D66F-461B-A957-4EF60BDB4BA6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{351337F5-D66F-461B-A957-4EF60BDB4BA6}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{351337F5-D66F-461B-A957-4EF60BDB4BA6}.Release|Any CPU.Build.0 = Release|Any CPU
 		{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
 		{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{C42D2FC1-A531-4ED4-84B9-89AEC7C962FC}.Release|Any CPU.ActiveCfg = Release|Any CPU
@@ -754,7 +743,6 @@ Global
 		{D775DECB-4E00-4ED5-A75A-5FCE58ADFF0B} = {9B9E3891-2366-4253-A952-D08BCEB71098}
 		{AF915D5C-AB00-4EA0-B5E6-001F4AE84E68} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 		{4D36CEC8-53F2-40A5-9A37-79AAE356E2DA} = {86C53C40-57AA-45B8-AD42-FAE0EFDF0F2B}
-		{351337F5-D66F-461B-A957-4EF60BDB4BA6} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
 		{8C89950F-F5D9-47FC-8066-CBC1EC3DF8FC} = {FF237916-7150-496B-89ED-6CA3292896E7}
 		{B859AE7C-F34F-4A9E-88AE-E0E7229FDE1E} = {FF237916-7150-496B-89ED-6CA3292896E7}
 		{909A8CBD-7D0E-42FD-B841-022AD8925820} = {8B6A8209-894F-4BA1-B880-965FD453982C}

+ 0 - 1
samples/AppWithoutLifetime/AppWithoutLifetime.csproj

@@ -9,7 +9,6 @@
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
   </ItemGroup>

+ 1 - 4
samples/ControlCatalog/App.xaml

@@ -28,11 +28,8 @@
       </ResourceDictionary.ThemeDictionaries>
 
       <!-- Styles attached dynamically depending on current theme (simple or fluent) -->
-      <FluentTheme x:Key="FluentTheme">
-      </FluentTheme>
+      <FluentTheme x:Key="FluentTheme" />
       <SimpleTheme x:Key="SimpleTheme" />
-      <StyleInclude x:Key="DataGridFluent" Source="avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml" />
-      <StyleInclude x:Key="DataGridSimple" Source="avares://Avalonia.Controls.DataGrid/Themes/Simple.xaml" />
       <StyleInclude x:Key="ColorPickerFluent" Source="avares://Avalonia.Controls.ColorPicker/Themes/Fluent/Fluent.xaml" />
       <StyleInclude x:Key="ColorPickerSimple" Source="avares://Avalonia.Controls.ColorPicker/Themes/Simple/Simple.xaml" />
     </ResourceDictionary>

+ 0 - 5
samples/ControlCatalog/App.xaml.cs

@@ -18,7 +18,6 @@ namespace ControlCatalog
         private FluentTheme? _fluentTheme;
         private SimpleTheme? _simpleTheme;
         private IStyle? _colorPickerFluent, _colorPickerSimple;
-        private IStyle? _dataGridFluent, _dataGridSimple;
         
         public App()
         {
@@ -35,8 +34,6 @@ namespace ControlCatalog
             _simpleTheme = (SimpleTheme)Resources["SimpleTheme"]!;
             _colorPickerFluent = (IStyle)Resources["ColorPickerFluent"]!;
             _colorPickerSimple = (IStyle)Resources["ColorPickerSimple"]!;
-            _dataGridFluent = (IStyle)Resources["DataGridFluent"]!;
-            _dataGridSimple = (IStyle)Resources["DataGridSimple"]!;
             
             SetCatalogThemes(CatalogTheme.Fluent);
         }
@@ -83,13 +80,11 @@ namespace ControlCatalog
             {
                 app._themeStylesContainer[0] = app._fluentTheme!;
                 app._themeStylesContainer[1] = app._colorPickerFluent!;
-                app._themeStylesContainer[2] = app._dataGridFluent!;
             }
             else if (theme == CatalogTheme.Simple)
             {
                 app._themeStylesContainer[0] = app._simpleTheme!;
                 app._themeStylesContainer[1] = app._colorPickerSimple!;
-                app._themeStylesContainer[2] = app._dataGridSimple!;
             }
 
             if (shouldReopenWindow)

+ 0 - 1
samples/ControlCatalog/ControlCatalog.csproj

@@ -26,7 +26,6 @@
   <ItemGroup>
     <ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj" />

+ 0 - 256
samples/ControlCatalog/Models/Countries.cs

@@ -1,256 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-using System.Linq;
-
-namespace ControlCatalog.Models
-{
-    public static class Countries
-    {
-        static IEnumerable<Country> GetCountries()
-        {
-            yield return new Country("Afghanistan", "ASIA (EX. NEAR EAST)", 31056997, 647500, 48, 0, 23.06, 163.07, 700, 36, 3.2, 46.6, 20.34);
-            yield return new Country("Albania", "EASTERN EUROPE", 3581655, 28748, 124.6, 1.26, -4.93, 21.52, 4500, 86.5, 71.2, 15.11, 5.22);
-            yield return new Country("Algeria", "NORTHERN AFRICA", 32930091, 2381740, 13.8, 0.04, -0.39, 31, 6000, 70, 78.1, 17.14, 4.61);
-            yield return new Country("American Samoa", "OCEANIA", 57794, 199, 290.4, 58.29, -20.71, 9.27, 8000, 97, 259.5, 22.46, 3.27);
-            yield return new Country("Andorra", "WESTERN EUROPE", 71201, 468, 152.1, 0, 6.6, 4.05, 19000, 100, 497.2, 8.71, 6.25);
-            yield return new Country("Angola", "SUB-SAHARAN AFRICA", 12127071, 1246700, 9.7, 0.13, 0, 191.19, 1900, 42, 7.8, 45.11, 24.2);
-            yield return new Country("Anguilla", "LATIN AMER. & CARIB", 13477, 102, 132.1, 59.8, 10.76, 21.03, 8600, 95, 460, 14.17, 5.34);
-            yield return new Country("Antigua & Barbuda", "LATIN AMER. & CARIB", 69108, 443, 156, 34.54, -6.15, 19.46, 11000, 89, 549.9, 16.93, 5.37);
-            yield return new Country("Argentina", "LATIN AMER. & CARIB", 39921833, 2766890, 14.4, 0.18, 0.61, 15.18, 11200, 97.1, 220.4, 16.73, 7.55);
-            yield return new Country("Armenia", "C.W. OF IND. STATES", 2976372, 29800, 99.9, 0, -6.47, 23.28, 3500, 98.6, 195.7, 12.07, 8.23);
-            yield return new Country("Aruba", "LATIN AMER. & CARIB", 71891, 193, 372.5, 35.49, 0, 5.89, 28000, 97, 516.1, 11.03, 6.68);
-            yield return new Country("Australia", "OCEANIA", 20264082, 7686850, 2.6, 0.34, 3.98, 4.69, 29000, 100, 565.5, 12.14, 7.51);
-            yield return new Country("Austria", "WESTERN EUROPE", 8192880, 83870, 97.7, 0, 2, 4.66, 30000, 98, 452.2, 8.74, 9.76);
-            yield return new Country("Azerbaijan", "C.W. OF IND. STATES", 7961619, 86600, 91.9, 0, -4.9, 81.74, 3400, 97, 137.1, 20.74, 9.75);
-            yield return new Country("The Bahamas", "LATIN AMER. & CARIB", 303770, 13940, 21.8, 25.41, -2.2, 25.21, 16700, 95.6, 460.6, 17.57, 9.05);
-            yield return new Country("Bahrain", "NEAR EAST", 698585, 665, 1050.5, 24.21, 1.05, 17.27, 16900, 89.1, 281.3, 17.8, 4.14);
-            yield return new Country("Bangladesh", "ASIA (EX. NEAR EAST)", 147365352, 144000, 1023.4, 0.4, -0.71, 62.6, 1900, 43.1, 7.3, 29.8, 8.27);
-            yield return new Country("Barbados", "LATIN AMER. & CARIB", 279912, 431, 649.5, 22.51, -0.31, 12.5, 15700, 97.4, 481.9, 12.71, 8.67);
-            yield return new Country("Belarus", "C.W. OF IND. STATES", 10293011, 207600, 49.6, 0, 2.54, 13.37, 6100, 99.6, 319.1, 11.16, 14.02);
-            yield return new Country("Belgium", "WESTERN EUROPE", 10379067, 30528, 340, 0.22, 1.23, 4.68, 29100, 98, 462.6, 10.38, 10.27);
-            yield return new Country("Belize", "LATIN AMER. & CARIB", 287730, 22966, 12.5, 1.68, 0, 25.69, 4900, 94.1, 115.7, 28.84, 5.72);
-            yield return new Country("Benin", "SUB-SAHARAN AFRICA", 7862944, 112620, 69.8, 0.11, 0, 85, 1100, 40.9, 9.7, 38.85, 12.22);
-            yield return new Country("Bermuda", "NORTHERN AMERICA", 65773, 53, 1241, 194.34, 2.49, 8.53, 36000, 98, 851.4, 11.4, 7.74);
-            yield return new Country("Bhutan", "ASIA (EX. NEAR EAST)", 2279723, 47000, 48.5, 0, 0, 100.44, 1300, 42.2, 14.3, 33.65, 12.7);
-            yield return new Country("Bolivia", "LATIN AMER. & CARIB", 8989046, 1098580, 8.2, 0, -1.32, 53.11, 2400, 87.2, 71.9, 23.3, 7.53);
-            yield return new Country("Bosnia & Herzegovina", "EASTERN EUROPE", 4498976, 51129, 88, 0.04, 0.31, 21.05, 6100,null, 215.4, 8.77, 8.27);
-            yield return new Country("Botswana", "SUB-SAHARAN AFRICA", 1639833, 600370, 2.7, 0, 0, 54.58, 9000, 79.8, 80.5, 23.08, 29.5);
-            yield return new Country("Brazil", "LATIN AMER. & CARIB", 188078227, 8511965, 22.1, 0.09, -0.03, 29.61, 7600, 86.4, 225.3, 16.56, 6.17);
-            yield return new Country("British Virgin Is.", "LATIN AMER. & CARIB", 23098, 153, 151, 52.29, 10.01, 18.05, 16000, 97.8, 506.5, 14.89, 4.42);
-            yield return new Country("Brunei", "ASIA (EX. NEAR EAST)", 379444, 5770, 65.8, 2.79, 3.59, 12.61, 18600, 93.9, 237.2, 18.79, 3.45);
-            yield return new Country("Bulgaria", "EASTERN EUROPE", 7385367, 110910, 66.6, 0.32, -4.58, 20.55, 7600, 98.6, 336.3, 9.65, 14.27);
-            yield return new Country("Burkina Faso", "SUB-SAHARAN AFRICA", 13902972, 274200, 50.7, 0, 0, 97.57, 1100, 26.6, 7, 45.62, 15.6);
-            yield return new Country("Burma", "ASIA (EX. NEAR EAST)", 47382633, 678500, 69.8, 0.28, -1.8, 67.24, 1800, 85.3, 10.1, 17.91, 9.83);
-            yield return new Country("Burundi", "SUB-SAHARAN AFRICA", 8090068, 27830, 290.7, 0, -0.06, 69.29, 600, 51.6, 3.4, 42.22, 13.46);
-            yield return new Country("Cambodia", "ASIA (EX. NEAR EAST)", 13881427, 181040, 76.7, 0.24, 0, 71.48, 1900, 69.4, 2.6, 26.9, 9.06);
-            yield return new Country("Cameroon", "SUB-SAHARAN AFRICA", 17340702, 475440, 36.5, 0.08, 0, 68.26, 1800, 79, 5.7, 33.89, 13.47);
-            yield return new Country("Canada", "NORTHERN AMERICA", 33098932, 9984670, 3.3, 2.02, 5.96, 4.75, 29800, 97, 552.2, 10.78, 7.8);
-            yield return new Country("Cape Verde", "SUB-SAHARAN AFRICA", 420979, 4033, 104.4, 23.93, -12.07, 47.77, 1400, 76.6, 169.6, 24.87, 6.55);
-            yield return new Country("Cayman Islands", "LATIN AMER. & CARIB", 45436, 262, 173.4, 61.07, 18.75, 8.19, 35000, 98, 836.3, 12.74, 4.89);
-            yield return new Country("Central African Rep.", "SUB-SAHARAN AFRICA", 4303356, 622984, 6.9, 0, 0, 91, 1100, 51, 2.3, 33.91, 18.65);
-            yield return new Country("Chad", "SUB-SAHARAN AFRICA", 9944201, 1284000, 7.7, 0, -0.11, 93.82, 1200, 47.5, 1.3, 45.73, 16.38);
-            yield return new Country("Chile", "LATIN AMER. & CARIB", 16134219, 756950, 21.3, 0.85, 0, 8.8, 9900, 96.2, 213, 15.23, 5.81);
-            yield return new Country("China", "ASIA (EX. NEAR EAST)", 1313973713, 9596960, 136.9, 0.15, -0.4, 24.18, 5000, 90.9, 266.7, 13.25, 6.97);
-            yield return new Country("Colombia", "LATIN AMER. & CARIB", 43593035, 1138910, 38.3, 0.28, -0.31, 20.97, 6300, 92.5, 176.2, 20.48, 5.58);
-            yield return new Country("Comoros", "SUB-SAHARAN AFRICA", 690948, 2170, 318.4, 15.67, 0, 74.93, 700, 56.5, 24.5, 36.93, 8.2);
-            yield return new Country("Congo, Dem.Rep.", "SUB - SAHARAN AFRICA", 62660551, 2345410, 26.7, 0, 0, 94.69, 700, 65.5, 0.2, 43.69, 13.27);
-            yield return new Country("Congo, Repub.of the", "SUB - SAHARAN AFRICA", 3702314, 342000, 10.8, 0.05, -0.17, 93.86, 700, 83.8, 3.7, 42.57, 12.93);
-            yield return new Country("Cook Islands", "OCEANIA", 21388, 240, 89.1, 50,null,null, 5000, 95, 289.9, 21,null);
-            yield return new Country("Costa Rica", "LATIN AMER. & CARIB", 4075261, 51100, 79.8, 2.52, 0.51, 9.95, 9100, 96, 340.7, 18.32, 4.36);
-            yield return new Country("Cote d'Ivoire", "SUB-SAHARAN AFRICA", 17654843, 322460, 54.8, 0.16, -0.07, 90.83, 1400, 50.9, 14.6, 35.11, 14.84);
-            yield return new Country("Croatia", "EASTERN EUROPE", 4494749, 56542, 79.5, 10.32, 1.58, 6.84, 10600, 98.5, 420.4, 9.61, 11.48);
-            yield return new Country("Cuba", "LATIN AMER. & CARIB", 11382820, 110860, 102.7, 3.37, -1.58, 6.33, 2900, 97, 74.7, 11.89, 7.22);
-            yield return new Country("Cyprus", "NEAR EAST", 784301, 9250, 84.8, 7.01, 0.43, 7.18, 19200, 97.6,null, 12.56, 7.68);
-            yield return new Country("Czech Republic", "EASTERN EUROPE", 10235455, 78866, 129.8, 0, 0.97, 3.93, 15700, 99.9, 314.3, 9.02, 10.59);
-            yield return new Country("Denmark", "WESTERN EUROPE", 5450661, 43094, 126.5, 16.97, 2.48, 4.56, 31100, 100, 614.6, 11.13, 10.36);
-            yield return new Country("Djibouti", "SUB-SAHARAN AFRICA", 486530, 23000, 21.2, 1.37, 0, 104.13, 1300, 67.9, 22.8, 39.53, 19.31);
-            yield return new Country("Dominica", "LATIN AMER. & CARIB", 68910, 754, 91.4, 19.63, -13.87, 14.15, 5400, 94, 304.8, 15.27, 6.73);
-            yield return new Country("Dominican Republic", "LATIN AMER. & CARIB", 9183984, 48730, 188.5, 2.64, -3.22, 32.38, 6000, 84.7, 97.4, 23.22, 5.73);
-            yield return new Country("East Timor", "ASIA (EX. NEAR EAST)", 1062777, 15007, 70.8, 4.7, 0, 47.41, 500, 58.6,null, 26.99, 6.24);
-            yield return new Country("Ecuador", "LATIN AMER. & CARIB", 13547510, 283560, 47.8, 0.79, -8.58, 23.66, 3300, 92.5, 125.6, 22.29, 4.23);
-            yield return new Country("Egypt", "NORTHERN AFRICA", 78887007, 1001450, 78.8, 0.24, -0.22, 32.59, 4000, 57.7, 131.8, 22.94, 5.23);
-            yield return new Country("El Salvador", "LATIN AMER. & CARIB", 6822378, 21040, 324.3, 1.46, -3.74, 25.1, 4800, 80.2, 142.4, 26.61, 5.78);
-            yield return new Country("Equatorial Guinea", "SUB-SAHARAN AFRICA", 540109, 28051, 19.3, 1.06, 0, 85.13, 2700, 85.7, 18.5, 35.59, 15.06);
-            yield return new Country("Eritrea", "SUB-SAHARAN AFRICA", 4786994, 121320, 39.5, 1.84, 0, 74.87, 700, 58.6, 7.9, 34.33, 9.6);
-            yield return new Country("Estonia", "BALTICS", 1324333, 45226, 29.3, 8.39, -3.16, 7.87, 12300, 99.8, 333.8, 10.04, 13.25);
-            yield return new Country("Ethiopia", "SUB-SAHARAN AFRICA", 74777981, 1127127, 66.3, 0, 0, 95.32, 700, 42.7, 8.2, 37.98, 14.86);
-            yield return new Country("Faroe Islands", "WESTERN EUROPE", 47246, 1399, 33.8, 79.84, 1.41, 6.24, 22000,null, 503.8, 14.05, 8.7);
-            yield return new Country("Fiji", "OCEANIA", 905949, 18270, 49.6, 6.18, -3.14, 12.62, 5800, 93.7, 112.6, 22.55, 5.65);
-            yield return new Country("Finland", "WESTERN EUROPE", 5231372, 338145, 15.5, 0.37, 0.95, 3.57, 27400, 100, 405.3, 10.45, 9.86);
-            yield return new Country("France", "WESTERN EUROPE", 60876136, 547030, 111.3, 0.63, 0.66, 4.26, 27600, 99, 586.4, 11.99, 9.14);
-            yield return new Country("French Guiana", "LATIN AMER. & CARIB", 199509, 91000, 2.2, 0.42, 6.27, 12.07, 8300, 83, 255.6, 20.46, 4.88);
-            yield return new Country("French Polynesia", "OCEANIA", 274578, 4167, 65.9, 60.6, 2.94, 8.44, 17500, 98, 194.5, 16.68, 4.69);
-            yield return new Country("Gabon", "SUB-SAHARAN AFRICA", 1424906, 267667, 5.3, 0.33, 0, 53.64, 5500, 63.2, 27.4, 36.16, 12.25);
-            yield return new Country("Gambia, The", "SUB - SAHARAN AFRICA", 1641564, 11300, 145.3, 0.71, 1.57, 72.02, 1700, 40.1, 26.8, 39.37, 12.25);
-            yield return new Country("Gaza Strip", "NEAR EAST", 1428757, 360, 3968.8, 11.11, 1.6, 22.93, 600,null, 244.3, 39.45, 3.8);
-            yield return new Country("Georgia", "C.W. OF IND. STATES", 4661473, 69700, 66.9, 0.44, -4.7, 18.59, 2500, 99, 146.6, 10.41, 9.23);
-            yield return new Country("Germany", "WESTERN EUROPE", 82422299, 357021, 230.9, 0.67, 2.18, 4.16, 27600, 99, 667.9, 8.25, 10.62);
-            yield return new Country("Ghana", "SUB-SAHARAN AFRICA", 22409572, 239460, 93.6, 0.23, -0.64, 51.43, 2200, 74.8, 14.4, 30.52, 9.72);
-            yield return new Country("Gibraltar", "WESTERN EUROPE", 27928, 7, 3989.7, 171.43, 0, 5.13, 17500,null, 877.7, 10.74, 9.31);
-            yield return new Country("Greece", "WESTERN EUROPE", 10688058, 131940, 81, 10.37, 2.35, 5.53, 20000, 97.5, 589.7, 9.68, 10.24);
-            yield return new Country("Greenland", "NORTHERN AMERICA", 56361, 2166086, 0, 2.04, -8.37, 15.82, 20000,null, 448.9, 15.93, 7.84);
-            yield return new Country("Grenada", "LATIN AMER. & CARIB", 89703, 344, 260.8, 35.17, -13.92, 14.62, 5000, 98, 364.5, 22.08, 6.88);
-            yield return new Country("Guadeloupe", "LATIN AMER. & CARIB", 452776, 1780, 254.4, 17.19, -0.15, 8.6, 8000, 90, 463.8, 15.05, 6.09);
-            yield return new Country("Guam", "OCEANIA", 171019, 541, 316.1, 23.2, 0, 6.94, 21000, 99, 492, 18.79, 4.48);
-            yield return new Country("Guatemala", "LATIN AMER. & CARIB", 12293545, 108890, 112.9, 0.37, -1.67, 35.93, 4100, 70.6, 92.1, 29.88, 5.2);
-            yield return new Country("Guernsey", "WESTERN EUROPE", 65409, 78, 838.6, 64.1, 3.84, 4.71, 20000,null, 842.4, 8.81, 10.01);
-            yield return new Country("Guinea", "SUB-SAHARAN AFRICA", 9690222, 245857, 39.4, 0.13, -3.06, 90.37, 2100, 35.9, 2.7, 41.76, 15.48);
-            yield return new Country("Guinea-Bissau", "SUB-SAHARAN AFRICA", 1442029, 36120, 39.9, 0.97, -1.57, 107.17, 800, 42.4, 7.4, 37.22, 16.53);
-            yield return new Country("Guyana", "LATIN AMER. & CARIB", 767245, 214970, 3.6, 0.21, -2.07, 33.26, 4000, 98.8, 143.5, 18.28, 8.28);
-            yield return new Country("Haiti", "LATIN AMER. & CARIB", 8308504, 27750, 299.4, 6.38, -3.4, 73.45, 1600, 52.9, 16.9, 36.44, 12.17);
-            yield return new Country("Honduras", "LATIN AMER. & CARIB", 7326496, 112090, 65.4, 0.73, -1.99, 29.32, 2600, 76.2, 67.5, 28.24, 5.28);
-            yield return new Country("Hong Kong", "ASIA (EX. NEAR EAST)", 6940432, 1092, 6355.7, 67.12, 5.24, 2.97, 28800, 93.5, 546.7, 7.29, 6.29);
-            yield return new Country("Hungary", "EASTERN EUROPE", 9981334, 93030, 107.3, 0, 0.86, 8.57, 13900, 99.4, 336.2, 9.72, 13.11);
-            yield return new Country("Iceland", "WESTERN EUROPE", 299388, 103000, 2.9, 4.83, 2.38, 3.31, 30900, 99.9, 647.7, 13.64, 6.72);
-            yield return new Country("India", "ASIA (EX. NEAR EAST)", 1095351995, 3287590, 333.2, 0.21, -0.07, 56.29, 2900, 59.5, 45.4, 22.01, 8.18);
-            yield return new Country("Indonesia", "ASIA (EX. NEAR EAST)", 245452739, 1919440, 127.9, 2.85, 0, 35.6, 3200, 87.9, 52, 20.34, 6.25);
-            yield return new Country("Iran", "ASIA (EX. NEAR EAST)", 68688433, 1648000, 41.7, 0.15, -0.84, 41.58, 7000, 79.4, 276.4, 17, 5.55);
-            yield return new Country("Iraq", "NEAR EAST", 26783383, 437072, 61.3, 0.01, 0, 50.25, 1500, 40.4, 38.6, 31.98, 5.37);
-            yield return new Country("Ireland", "WESTERN EUROPE", 4062235, 70280, 57.8, 2.06, 4.99, 5.39, 29600, 98, 500.5, 14.45, 7.82);
-            yield return new Country("Isle of Man", "WESTERN EUROPE", 75441, 572, 131.9, 27.97, 5.36, 5.93, 21000,null, 676, 11.05, 11.19);
-            yield return new Country("Israel", "NEAR EAST", 6352117, 20770, 305.8, 1.31, 0.68, 7.03, 19800, 95.4, 462.3, 17.97, 6.18);
-            yield return new Country("Italy", "WESTERN EUROPE", 58133509, 301230, 193, 2.52, 2.07, 5.94, 26700, 98.6, 430.9, 8.72, 10.4);
-            yield return new Country("Jamaica", "LATIN AMER. & CARIB", 2758124, 10991, 250.9, 9.3, -4.92, 12.36, 3900, 87.9, 124, 20.82, 6.52);
-            yield return new Country("Japan", "ASIA (EX. NEAR EAST)", 127463611, 377835, 337.4, 7.87, 0, 3.26, 28200, 99, 461.2, 9.37, 9.16);
-            yield return new Country("Jersey", "WESTERN EUROPE", 91084, 116, 785.2, 60.34, 2.76, 5.24, 24800,null, 811.3, 9.3, 9.28);
-            yield return new Country("Jordan", "NEAR EAST", 5906760, 92300, 64, 0.03, 6.59, 17.35, 4300, 91.3, 104.5, 21.25, 2.65);
-            yield return new Country("Kazakhstan", "C.W. OF IND. STATES", 15233244, 2717300, 5.6, 0, -3.35, 29.21, 6300, 98.4, 164.1, 16, 9.42);
-            yield return new Country("Kenya", "SUB-SAHARAN AFRICA", 34707817, 582650, 59.6, 0.09, -0.1, 61.47, 1000, 85.1, 8.1, 39.72, 14.02);
-            yield return new Country("Kiribati", "OCEANIA", 105432, 811, 130, 140.94, 0, 48.52, 800,null, 42.7, 30.65, 8.26);
-            yield return new Country("North Korea", "ASIA(EX.NEAR EAST)", 23113019, 120540, 191.8, 2.07, 0, 24.04, 1300, 99, 42.4, 15.54, 7.13);
-            yield return new Country("South Korea", "ASIA(EX.NEAR EAST)", 48846823, 98480, 496, 2.45, 0, 7.05, 17800, 97.9, 486.1, 10, 5.85);
-            yield return new Country("Kuwait", "NEAR EAST", 2418393, 17820, 135.7, 2.8, 14.18, 9.95, 19000, 83.5, 211, 21.94, 2.41);
-            yield return new Country("Kyrgyzstan", "C.W. OF IND. STATES", 5213898, 198500, 26.3, 0, -2.45, 35.64, 1600, 97, 84, 22.8, 7.08);
-            yield return new Country("Laos", "ASIA (EX. NEAR EAST)", 6368481, 236800, 26.9, 0, 0, 85.22, 1700, 66.4, 14.1, 35.49, 11.55);
-            yield return new Country("Latvia", "BALTICS", 2274735, 64589, 35.2, 0.82, -2.23, 9.55, 10200, 99.8, 321.4, 9.24, 13.66);
-            yield return new Country("Lebanon", "NEAR EAST", 3874050, 10400, 372.5, 2.16, 0, 24.52, 4800, 87.4, 255.6, 18.52, 6.21);
-            yield return new Country("Lesotho", "SUB-SAHARAN AFRICA", 2022331, 30355, 66.6, 0, -0.74, 84.23, 3000, 84.8, 23.7, 24.75, 28.71);
-            yield return new Country("Liberia", "SUB-SAHARAN AFRICA", 3042004, 111370, 27.3, 0.52, 0, 128.87, 1000, 57.5, 2.3, 44.77, 23.1);
-            yield return new Country("Libya", "NORTHERN AFRICA", 5900754, 1759540, 3.4, 0.1, 0, 24.6, 6400, 82.6, 127.1, 26.49, 3.48);
-            yield return new Country("Liechtenstein", "WESTERN EUROPE", 33987, 160, 212.4, 0, 4.85, 4.7, 25000, 100, 585.5, 10.21, 7.18);
-            yield return new Country("Lithuania", "BALTICS", 3585906, 65200, 55, 0.14, -0.71, 6.89, 11400, 99.6, 223.4, 8.75, 10.98);
-            yield return new Country("Luxembourg", "WESTERN EUROPE", 474413, 2586, 183.5, 0, 8.97, 4.81, 55100, 100, 515.4, 11.94, 8.41);
-            yield return new Country("Macau", "ASIA (EX. NEAR EAST)", 453125, 28, 16183, 146.43, 4.86, 4.39, 19400, 94.5, 384.9, 8.48, 4.47);
-            yield return new Country("Macedonia", "EASTERN EUROPE", 2050554, 25333, 80.9, 0, -1.45, 10.09, 6700,null, 260, 12.02, 8.77);
-            yield return new Country("Madagascar", "SUB-SAHARAN AFRICA", 18595469, 587040, 31.7, 0.82, 0, 76.83, 800, 68.9, 3.6, 41.41, 11.11);
-            yield return new Country("Malawi", "SUB-SAHARAN AFRICA", 13013926, 118480, 109.8, 0, 0, 103.32, 600, 62.7, 7.9, 43.13, 19.33);
-            yield return new Country("Malaysia", "ASIA (EX. NEAR EAST)", 24385858, 329750, 74, 1.42, 0, 17.7, 9000, 88.7, 179, 22.86, 5.05);
-            yield return new Country("Maldives", "ASIA (EX. NEAR EAST)", 359008, 300, 1196.7, 214.67, 0, 56.52, 3900, 97.2, 90, 34.81, 7.06);
-            yield return new Country("Mali", "SUB-SAHARAN AFRICA", 11716829, 1240000, 9.5, 0, -0.33, 116.79, 900, 46.4, 6.4, 49.82, 16.89);
-            yield return new Country("Malta", "WESTERN EUROPE", 400214, 316, 1266.5, 62.28, 2.07, 3.89, 17700, 92.8, 505, 10.22, 8.1);
-            yield return new Country("Marshall Islands", "OCEANIA", 60422, 11854, 5.1, 3.12, -6.04, 29.45, 1600, 93.7, 91.2, 33.05, 4.78);
-            yield return new Country("Martinique", "LATIN AMER. & CARIB", 436131, 1100, 396.5, 31.82, -0.05, 7.09, 14400, 97.7, 394.4, 13.74, 6.48);
-            yield return new Country("Mauritania", "SUB-SAHARAN AFRICA", 3177388, 1030700, 3.1, 0.07, 0, 70.89, 1800, 41.7, 12.9, 40.99, 12.16);
-            yield return new Country("Mauritius", "SUB-SAHARAN AFRICA", 1240827, 2040, 608.3, 8.68, -0.9, 15.03, 11400, 85.6, 289.3, 15.43, 6.86);
-            yield return new Country("Mayotte", "SUB-SAHARAN AFRICA", 201234, 374, 538.1, 49.52, 6.78, 62.4, 2600,null, 49.7, 40.95, 7.7);
-            yield return new Country("Mexico", "LATIN AMER. & CARIB", 107449525, 1972550, 54.5, 0.47, -4.87, 20.91, 9000, 92.2, 181.6, 20.69, 4.74);
-            yield return new Country("Micronesia, Fed.St.", "OCEANIA", 108004, 702, 153.9, 870.66, -20.99, 30.21, 2000, 89, 114.8, 24.68, 4.75);
-            yield return new Country("Moldova", "C.W. OF IND. STATES", 4466706, 33843, 132, 0, -0.26, 40.42, 1800, 99.1, 208.1, 15.7, 12.64);
-            yield return new Country("Monaco", "WESTERN EUROPE", 32543, 2, 16271.5, 205, 7.75, 5.43, 27000, 99, 1035.6, 9.19, 12.91);
-            yield return new Country("Mongolia", "ASIA (EX. NEAR EAST)", 2832224, 1564116, 1.8, 0, 0, 53.79, 1800, 97.8, 55.1, 21.59, 6.95);
-            yield return new Country("Montserrat", "LATIN AMER. & CARIB", 9439, 102, 92.5, 39.22, 0, 7.35, 3400, 97,null, 17.59, 7.1);
-            yield return new Country("Morocco", "NORTHERN AFRICA", 33241259, 446550, 74.4, 0.41, -0.98, 41.62, 4000, 51.7, 40.4, 21.98, 5.58);
-            yield return new Country("Mozambique", "SUB-SAHARAN AFRICA", 19686505, 801590, 24.6, 0.31, 0, 130.79, 1200, 47.8, 3.5, 35.18, 21.35);
-            yield return new Country("Namibia", "SUB-SAHARAN AFRICA", 2044147, 825418, 2.5, 0.19, 0, 48.98, 7200, 84, 62.6, 24.32, 18.86);
-            yield return new Country("Nauru", "OCEANIA", 13287, 21, 632.7, 142.86, 0, 9.95, 5000,null, 143, 24.76, 6.7);
-            yield return new Country("Nepal", "ASIA (EX. NEAR EAST)", 28287147, 147181, 192.2, 0, 0, 66.98, 1400, 45.2, 15.9, 30.98, 9.31);
-            yield return new Country("Netherlands", "WESTERN EUROPE", 16491461, 41526, 397.1, 1.09, 2.91, 5.04, 28600, 99, 460.8, 10.9, 8.68);
-            yield return new Country("Netherlands Antilles", "LATIN AMER. & CARIB", 221736, 960, 231, 37.92, -0.41, 10.03, 11400, 96.7, 365.3, 14.78, 6.45);
-            yield return new Country("New Caledonia", "OCEANIA", 219246, 19060, 11.5, 11.83, 0, 7.72, 15000, 91, 252.2, 18.11, 5.69);
-            yield return new Country("New Zealand", "OCEANIA", 4076140, 268680, 15.2, 5.63, 4.05, 5.85, 21600, 99, 441.7, 13.76, 7.53);
-            yield return new Country("Nicaragua", "LATIN AMER. & CARIB", 5570129, 129494, 43, 0.7, -1.22, 29.11, 2300, 67.5, 39.7, 24.51, 4.45);
-            yield return new Country("Niger", "SUB-SAHARAN AFRICA", 12525094, 1267000, 9.9, 0, -0.67, 121.69, 800, 17.6, 1.9, 50.73, 20.91);
-            yield return new Country("Nigeria", "SUB-SAHARAN AFRICA", 131859731, 923768, 142.7, 0.09, 0.26, 98.8, 900, 68, 9.3, 40.43, 16.94);
-            yield return new Country("N. Mariana Islands", "OCEANIA", 82459, 477, 172.9, 310.69, 9.61, 7.11, 12500, 97, 254.7, 19.43, 2.29);
-            yield return new Country("Norway", "WESTERN EUROPE", 4610820, 323802, 14.2, 7.77, 1.74, 3.7, 37800, 100, 461.7, 11.46, 9.4);
-            yield return new Country("Oman", "NEAR EAST", 3102229, 212460, 14.6, 0.98, 0.28, 19.51, 13100, 75.8, 85.5, 36.24, 3.81);
-            yield return new Country("Pakistan", "ASIA (EX. NEAR EAST)", 165803560, 803940, 206.2, 0.13, -2.77, 72.44, 2100, 45.7, 31.8, 29.74, 8.23);
-            yield return new Country("Palau", "OCEANIA", 20579, 458, 44.9, 331.66, 2.85, 14.84, 9000, 92, 325.6, 18.03, 6.8);
-            yield return new Country("Panama", "LATIN AMER. & CARIB", 3191319, 78200, 40.8, 3.18, -0.91, 20.47, 6300, 92.6, 137.9, 21.74, 5.36);
-            yield return new Country("Papua New Guinea", "OCEANIA", 5670544, 462840, 12.3, 1.11, 0, 51.45, 2200, 64.6, 10.9, 29.36, 7.25);
-            yield return new Country("Paraguay", "LATIN AMER. & CARIB", 6506464, 406750, 16, 0, -0.08, 25.63, 4700, 94, 49.2, 29.1, 4.49);
-            yield return new Country("Peru", "LATIN AMER. & CARIB", 28302603, 1285220, 22, 0.19, -1.05, 31.94, 5100, 90.9, 79.5, 20.48, 6.23);
-            yield return new Country("Philippines", "ASIA (EX. NEAR EAST)", 89468677, 300000, 298.2, 12.1, -1.5, 23.51, 4600, 92.6, 38.4, 24.89, 5.41);
-            yield return new Country("Poland", "EASTERN EUROPE", 38536869, 312685, 123.3, 0.16, -0.49, 8.51, 11100, 99.8, 306.3, 9.85, 9.89);
-            yield return new Country("Portugal", "WESTERN EUROPE", 10605870, 92391, 114.8, 1.94, 3.57, 5.05, 18000, 93.3, 399.2, 10.72, 10.5);
-            yield return new Country("Puerto Rico", "LATIN AMER. & CARIB", 3927188, 13790, 284.8, 3.63, -1.46, 8.24, 16800, 94.1, 283.1, 12.77, 7.65);
-            yield return new Country("Qatar", "NEAR EAST", 885359, 11437, 77.4, 4.92, 16.29, 18.61, 21500, 82.5, 232, 15.56, 4.72);
-            yield return new Country("Reunion", "SUB-SAHARAN AFRICA", 787584, 2517, 312.9, 8.22, 0, 7.78, 5800, 88.9, 380.9, 18.9, 5.49);
-            yield return new Country("Romania", "EASTERN EUROPE", 22303552, 237500, 93.9, 0.09, -0.13, 26.43, 7000, 98.4, 196.9, 10.7, 11.77);
-            yield return new Country("Russia", "C.W. OF IND. STATES", 142893540, 17075200, 8.4, 0.22, 1.02, 15.39, 8900, 99.6, 280.6, 9.95, 14.65);
-            yield return new Country("Rwanda", "SUB-SAHARAN AFRICA", 8648248, 26338, 328.4, 0, 0, 91.23, 1300, 70.4, 2.7, 40.37, 16.09);
-            yield return new Country("Saint Helena", "SUB-SAHARAN AFRICA", 7502, 413, 18.2, 14.53, 0, 19, 2500, 97, 293.3, 12.13, 6.53);
-            yield return new Country("Saint Kitts & Nevis", "LATIN AMER. & CARIB", 39129, 261, 149.9, 51.72, -7.11, 14.49, 8800, 97, 638.9, 18.02, 8.33);
-            yield return new Country("Saint Lucia", "LATIN AMER. & CARIB", 168458, 616, 273.5, 25.65, -2.67, 13.53, 5400, 67, 303.3, 19.68, 5.08);
-            yield return new Country("St Pierre & Miquelon", "NORTHERN AMERICA", 7026, 242, 29, 49.59, -4.86, 7.54, 6900, 99, 683.2, 13.52, 6.83);
-            yield return new Country("Saint Vincent and the Grenadines", "LATIN AMER. & CARIB", 117848, 389, 303, 21.59, -7.64, 14.78, 2900, 96, 190.9, 16.18, 5.98);
-            yield return new Country("Samoa", "OCEANIA", 176908, 2944, 60.1, 13.69, -11.7, 27.71, 5600, 99.7, 75.2, 16.43, 6.62);
-            yield return new Country("San Marino", "WESTERN EUROPE", 29251, 61, 479.5, 0, 10.98, 5.73, 34600, 96, 704.3, 10.02, 8.17);
-            yield return new Country("Sao Tome & Principe", "SUB-SAHARAN AFRICA", 193413, 1001, 193.2, 20.88, -2.72, 43.11, 1200, 79.3, 36.2, 40.25, 6.47);
-            yield return new Country("Saudi Arabia", "NEAR EAST", 27019731, 1960582, 13.8, 0.13, -2.71, 13.24, 11800, 78.8, 140.6, 29.34, 2.58);
-            yield return new Country("Senegal", "SUB-SAHARAN AFRICA", 11987121, 196190, 61.1, 0.27, 0.2, 55.51, 1600, 40.2, 22.2, 32.78, 9.42);
-            yield return new Country("Serbia", "EASTERN EUROPE", 9396411, 88361, 106.3, 0, -1.33, 12.89, 2200, 93, 285.8,null,null);
-            yield return new Country("Seychelles", "SUB-SAHARAN AFRICA", 81541, 455, 179.2, 107.91, -5.69, 15.53, 7800, 58, 262.4, 16.03, 6.29);
-            yield return new Country("Sierra Leone", "SUB-SAHARAN AFRICA", 6005250, 71740, 83.7, 0.56, 0, 143.64, 500, 31.4, 4, 45.76, 23.03);
-            yield return new Country("Singapore", "ASIA (EX. NEAR EAST)", 4492150, 693, 6482.2, 27.85, 11.53, 2.29, 23700, 92.5, 411.4, 9.34, 4.28);
-            yield return new Country("Slovakia", "EASTERN EUROPE", 5439448, 48845, 111.4, 0, 0.3, 7.41, 13300,null, 220.1, 10.65, 9.45);
-            yield return new Country("Slovenia", "EASTERN EUROPE", 2010347, 20273, 99.2, 0.23, 1.12, 4.45, 19000, 99.7, 406.1, 8.98, 10.31);
-            yield return new Country("Solomon Islands", "OCEANIA", 552438, 28450, 19.4, 18.67, 0, 21.29, 1700,null, 13.4, 30.01, 3.92);
-            yield return new Country("Somalia", "SUB-SAHARAN AFRICA", 8863338, 637657, 13.9, 0.47, 5.37, 116.7, 500, 37.8, 11.3, 45.13, 16.63);
-            yield return new Country("South Africa", "SUB-SAHARAN AFRICA", 44187637, 1219912, 36.2, 0.23, -0.29, 61.81, 10700, 86.4, 107, 18.2, 22);
-            yield return new Country("Spain", "WESTERN EUROPE", 40397842, 504782, 80, 0.98, 0.99, 4.42, 22000, 97.9, 453.5, 10.06, 9.72);
-            yield return new Country("Sri Lanka", "ASIA (EX. NEAR EAST)", 20222240, 65610, 308.2, 2.04, -1.31, 14.35, 3700, 92.3, 61.5, 15.51, 6.52);
-            yield return new Country("Sudan", "SUB-SAHARAN AFRICA", 41236378, 2505810, 16.5, 0.03, -0.02, 62.5, 1900, 61.1, 16.3, 34.53, 8.97);
-            yield return new Country("Suriname", "LATIN AMER. & CARIB", 439117, 163270, 2.7, 0.24, -8.81, 23.57, 4000, 93, 184.7, 18.02, 7.27);
-            yield return new Country("Swaziland", "SUB-SAHARAN AFRICA", 1136334, 17363, 65.5, 0, 0, 69.27, 4900, 81.6, 30.8, 27.41, 29.74);
-            yield return new Country("Sweden", "WESTERN EUROPE", 9016596, 449964, 20, 0.72, 1.67, 2.77, 26800, 99, 715, 10.27, 10.31);
-            yield return new Country("Switzerland", "WESTERN EUROPE", 7523934, 41290, 182.2, 0, 4.05, 4.39, 32700, 99, 680.9, 9.71, 8.49);
-            yield return new Country("Syria", "NEAR EAST", 18881361, 185180, 102, 0.1, 0, 29.53, 3300, 76.9, 153.8, 27.76, 4.81);
-            yield return new Country("Taiwan", "ASIA (EX. NEAR EAST)", 23036087, 35980, 640.3, 4.35, 0, 6.4, 23400, 96.1, 591, 12.56, 6.48);
-            yield return new Country("Tajikistan", "C.W. OF IND. STATES", 7320815, 143100, 51.2, 0, -2.86, 110.76, 1000, 99.4, 33.5, 32.65, 8.25);
-            yield return new Country("Tanzania", "SUB-SAHARAN AFRICA", 37445392, 945087, 39.6, 0.15, -2.06, 98.54, 600, 78.2, 4, 37.71, 16.39);
-            yield return new Country("Thailand", "ASIA (EX. NEAR EAST)", 64631595, 514000, 125.7, 0.63, 0, 20.48, 7400, 92.6, 108.9, 13.87, 7.04);
-            yield return new Country("Togo", "SUB-SAHARAN AFRICA", 5548702, 56785, 97.7, 0.1, 0, 66.61, 1500, 60.9, 10.6, 37.01, 9.83);
-            yield return new Country("Tonga", "OCEANIA", 114689, 748, 153.3, 56.02, 0, 12.62, 2200, 98.5, 97.7, 25.37, 5.28);
-            yield return new Country("Trinidad & Tobago", "LATIN AMER. & CARIB", 1065842, 5128, 207.9, 7.06, -10.83, 24.31, 9500, 98.6, 303.5, 12.9, 10.57);
-            yield return new Country("Tunisia", "NORTHERN AFRICA", 10175014, 163610, 62.2, 0.7, -0.57, 24.77, 6900, 74.2, 123.6, 15.52, 5.13);
-            yield return new Country("Turkey", "NEAR EAST", 70413958, 780580, 90.2, 0.92, 0, 41.04, 6700, 86.5, 269.5, 16.62, 5.97);
-            yield return new Country("Turkmenistan", "C.W. OF IND. STATES", 5042920, 488100, 10.3, 0, -0.86, 73.08, 5800, 98, 74.6, 27.61, 8.6);
-            yield return new Country("Turks & Caicos Is", "LATIN AMER. & CARIB", 21152, 430, 49.2, 90.47, 11.68, 15.67, 9600, 98, 269.5, 21.84, 4.21);
-            yield return new Country("Tuvalu", "OCEANIA", 11810, 26, 454.2, 92.31, 0, 20.03, 1100,null, 59.3, 22.18, 7.11);
-            yield return new Country("Uganda", "SUB-SAHARAN AFRICA", 28195754, 236040, 119.5, 0, 0, 67.83, 1400, 69.9, 3.6, 47.35, 12.24);
-            yield return new Country("Ukraine", "C.W. OF IND. STATES", 46710816, 603700, 77.4, 0.46, -0.39, 20.34, 5400, 99.7, 259.9, 8.82, 14.39);
-            yield return new Country("United Arab Emirates", "NEAR EAST", 2602713, 82880, 31.4, 1.59, 1.03, 14.51, 23200, 77.9, 475.3, 18.96, 4.4);
-            yield return new Country("United Kingdom", "WESTERN EUROPE", 60609153, 244820, 247.6, 5.08, 2.19, 5.16, 27700, 99, 543.5, 10.71, 10.13);
-            yield return new Country("United States", "NORTHERN AMERICA", 298444215, 9631420, 31, 0.21, 3.41, 6.5, 37800, 97, 898, 14.14, 8.26);
-            yield return new Country("Uruguay", "LATIN AMER. & CARIB", 3431932, 176220, 19.5, 0.37, -0.32, 11.95, 12800, 98, 291.4, 13.91, 9.05);
-            yield return new Country("Uzbekistan", "C.W. OF IND. STATES", 27307134, 447400, 61, 0, -1.72, 71.1, 1700, 99.3, 62.9, 26.36, 7.84);
-            yield return new Country("Vanuatu", "OCEANIA", 208869, 12200, 17.1, 20.72, 0, 55.16, 2900, 53, 32.6, 22.72, 7.82);
-            yield return new Country("Venezuela", "LATIN AMER. & CARIB", 25730435, 912050, 28.2, 0.31, -0.04, 22.2, 4800, 93.4, 140.1, 18.71, 4.92);
-            yield return new Country("Vietnam", "ASIA (EX. NEAR EAST)", 84402966, 329560, 256.1, 1.05, -0.45, 25.95, 2500, 90.3, 187.7, 16.86, 6.22);
-            yield return new Country("Virgin Islands", "LATIN AMER. & CARIB", 108605, 1910, 56.9, 9.84, -8.94, 8.03, 17200,null, 652.8, 13.96, 6.43);
-            yield return new Country("Wallis and Futuna", "OCEANIA", 16025, 274, 58.5, 47.08,null,null, 3700, 50, 118.6,null,null);
-            yield return new Country("West Bank", "NEAR EAST", 2460492, 5860, 419.9, 0, 2.98, 19.62, 800,null, 145.2, 31.67, 3.92);
-            yield return new Country("Yemen", "NEAR EAST", 21456188, 527970, 40.6, 0.36, 0, 61.5, 800, 50.2, 37.2, 42.89, 8.3);
-            yield return new Country("Zambia", "SUB-SAHARAN AFRICA", 11502010, 752614, 15.3, 0, 0, 88.29, 800, 80.6, 8.2, 41, 19.93);
-            yield return new Country("Zimbabwe", "SUB-SAHARAN AFRICA", 12236805, 390580, 31.3, 0, 0, 67.69, 1900, 90.7, 26.8, 28.01, 21.84);
-        }
-
-        static IReadOnlyList<Country>? _all;
-
-        public static IReadOnlyList<Country> All
-        {
-            get
-            {
-                if(_all == null)
-                {
-                    _all = GetCountries().ToList().AsReadOnly();
-                }
-
-                return _all;
-            }
-
-        }
-    }
-}

+ 0 - 41
samples/ControlCatalog/Models/Country.cs

@@ -1,41 +0,0 @@
-namespace ControlCatalog.Models
-{
-    public class Country
-    {
-        public string Name { get; private set; }
-        public string Region { get; private set; }
-        public int Population { get; private set; }
-        //Square Miles
-        public int Area { get; private set; }
-        //Per Square Mile
-        public double PopulationDensity { get; private set; }
-        //Coast / Area
-        public double CoastLine { get; private set; }
-        public double? NetMigration { get; private set; }
-        //per 1000 births
-        public double? InfantMortality { get; private set; }
-        public int GDP { get; private set; }
-        public double? LiteracyPercent { get; private set; }
-        //per 1000
-        public double? Phones { get; private set; }
-        public double? BirthRate { get; private set; }
-        public double? DeathRate { get; private set; }
-
-        public Country(string name, string region, int population, int area, double density, double coast, double? migration, 
-                       double? infantMorality, int gdp, double? literacy, double? phones, double? birth, double? death)
-        {
-            Name = name;
-            Region = region;
-            Population = population;
-            Area = area;
-            PopulationDensity = density;
-            CoastLine = coast;
-            NetMigration = migration;
-            InfantMortality = infantMorality;
-            GDP = gdp;
-            LiteracyPercent = literacy;
-            BirthRate = birth;
-            DeathRate = death;
-        }
-    }
-}

+ 0 - 37
samples/ControlCatalog/Models/GDPValueConverter.cs

@@ -1,37 +0,0 @@
-using Avalonia;
-using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Globalization;
-using System.Linq;
-using Avalonia.Data.Converters;
-using Avalonia.Media;
-
-namespace ControlCatalog.Models
-{
-    public class GDPValueConverter : IValueConverter
-    {
-        public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
-        {
-            if (value is int gdp)
-            {
-                if (gdp <= 5000)
-                    return new SolidColorBrush(Colors.Orange, 0.6);
-                else if (gdp <= 10000)
-                    return new SolidColorBrush(Colors.Yellow, 0.6);
-                else
-                    return new SolidColorBrush(Colors.LightGreen, 0.6);
-            }
-
-            return value;
-        }
-
-        public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
-        {
-            throw new NotImplementedException();
-        }
-    }
-}

+ 0 - 31
samples/ControlCatalog/Models/GDPdLengthConverter.cs

@@ -1,31 +0,0 @@
-using System;
-using System.Globalization;
-using Avalonia.Data.Converters;
-
-namespace ControlCatalog.Models;
-
-internal class GDPdLengthConverter : IValueConverter
-{
-    public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
-    {
-        if (value is double d)
-        {
-            return new Avalonia.Controls.DataGridLength(d,Avalonia.Controls.DataGridLengthUnitType.Pixel,d,d);
-        }
-        else if (value is decimal d2)
-        {
-            var dv =System.Convert.ToDouble(d2);
-            return new Avalonia.Controls.DataGridLength(dv, Avalonia.Controls.DataGridLengthUnitType.Pixel, dv, dv);
-        }
-        return value;
-    }
-
-    public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
-    {
-        if (value is Avalonia.Controls.DataGridLength width)
-        {
-            return System.Convert.ToDecimal(width.DisplayValue);
-        }
-        return value;
-    }
-}

ファイルの差分が大きいため隠しています
+ 7 - 10
samples/ControlCatalog/Pages/DataGridPage.xaml


+ 10 - 94
samples/ControlCatalog/Pages/DataGridPage.xaml.cs

@@ -1,101 +1,17 @@
-using System.Collections;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.ComponentModel;
-using System.Linq;
+using System.Diagnostics;
 using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
-using ControlCatalog.Models;
-using Avalonia.Collections;
-using Avalonia.Controls.Primitives;
-using Avalonia.Data;
-using Avalonia.Threading;
+using Avalonia.Interactivity;
 
-namespace ControlCatalog.Pages
+namespace ControlCatalog.Pages;
+
+public class DataGridPage : UserControl
 {
-    public class DataGridPage : UserControl
+    private void OnLinkClicked(object? sender, RoutedEventArgs e)
     {
-        public DataGridPage()
-        {
-            this.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<DataGrid>("dataGrid1");
-            dg1.IsReadOnly = true;
-            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.ItemsSource = collectionView1;
-
-            var dg2 = this.Get<DataGrid>("dataGridGrouping");
-            dg2.IsReadOnly = true;
-
-            var collectionView2 = new DataGridCollectionView(Countries.All);
-            collectionView2.GroupDescriptions.Add(new DataGridPathGroupDescription("Region"));
-
-            dg2.ItemsSource = collectionView2;
-
-            var dg3 = this.Get<DataGrid>("dataGridEdit");
-            dg3.IsReadOnly = false;
-
-            var list = new ObservableCollection<Person>
-            {
-                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<Button>("btnAdd");
-            addButton.Click += (a, b) => list.Add(new Person());
-
-            DataContext = this;
-        }
-
-        public IEnumerable<Person> DataGrid3Source { get; }
-
-        private void InitializeComponent()
-        {
-            AvaloniaXamlLoader.Load(this);
-        }
-
-        private class ReversedStringComparer : IComparer<object>, IComparer
-        {
-            public int Compare(object? x, object? y)
-            {
-                if (x is string left && y is string right)
-                {
-                    var reversedLeft = new string(left.Reverse().ToArray());
-                    var reversedRight = new string(right.Reverse().ToArray());
-                    return reversedLeft.CompareTo(reversedRight);
-                }
-
-                return Comparer.Default.Compare(x, y);
-            }
-        }
-
-        private void NumericUpDown_OnTemplateApplied(object sender, TemplateAppliedEventArgs e)
+        Process.Start(new ProcessStartInfo
         {
-            // We want to focus the TextBox of the NumericUpDown. To do so we search for this control when the template
-            // is applied, but we postpone the action until the control is actually loaded. 
-            if (e.NameScope.Find<TextBox>("PART_TextBox") is {} textBox)
-            {
-                Dispatcher.UIThread.InvokeAsync(() =>
-                {
-                    textBox.Focus();
-                    textBox.SelectAll();
-                }, DispatcherPriority.Loaded);
-            }
-        }
+            FileName = "https://github.com/AvaloniaUI/Avalonia.Controls.DataGrid",
+            UseShellExecute = true
+        });
     }
 }

+ 0 - 1
samples/GpuInterop/GpuInterop.csproj

@@ -14,7 +14,6 @@
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
   </ItemGroup>

+ 0 - 1
samples/Sandbox/Sandbox.csproj

@@ -11,7 +11,6 @@
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />

+ 0 - 1
samples/SingleProjectSandbox/SingleProjectSandbox.csproj

@@ -19,7 +19,6 @@
   <ItemGroup>
     <ProjectReference Include="..\..\packages\Avalonia\Avalonia.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Fonts.Inter\Avalonia.Fonts.Inter.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj" />
     <ProjectReference Include="..\MiniMvvm\MiniMvvm.csproj" />

+ 1 - 0
src/Avalonia.Controls.DataGrid

@@ -0,0 +1 @@
+Subproject commit 85a0b32ef6d963c1d67619ca3e2f6da0bc43ac9a

+ 0 - 18
src/Avalonia.Controls.DataGrid/Automation/Peers/DataGridAutomationPeer.cs

@@ -1,18 +0,0 @@
-using Avalonia.Automation.Peers;
-
-namespace Avalonia.Controls.Automation.Peers;
-
-public class DataGridAutomationPeer : ControlAutomationPeer
-{
-    public DataGridAutomationPeer(DataGrid owner)
-        : base(owner)
-    {
-    }
-
-    public new DataGrid Owner => (DataGrid)base.Owner;
-
-    protected override AutomationControlType GetAutomationControlTypeCore()
-    {
-        return AutomationControlType.DataGrid;
-    }
-}

+ 0 - 22
src/Avalonia.Controls.DataGrid/Automation/Peers/DataGridCellAutomationPeer.cs

@@ -1,22 +0,0 @@
-using Avalonia.Automation.Peers;
-
-namespace Avalonia.Controls.Automation.Peers;
-
-public class DataGridCellAutomationPeer : ContentControlAutomationPeer
-{
-    public DataGridCellAutomationPeer(DataGridCell owner)
-        : base(owner)
-    {
-    }
-
-    public new DataGridCell Owner => (DataGridCell)base.Owner;
-
-    protected override AutomationControlType GetAutomationControlTypeCore()
-    {
-        return AutomationControlType.Custom;
-    }
-
-    protected override bool IsContentElementCore() => true;
-
-    protected override bool IsControlElementCore() => true;
-}

+ 0 - 22
src/Avalonia.Controls.DataGrid/Automation/Peers/DataGridColumnHeaderAutomationPeer.cs

@@ -1,22 +0,0 @@
-using Avalonia.Automation.Peers;
-
-namespace Avalonia.Controls.Automation.Peers;
-
-public class DataGridColumnHeaderAutomationPeer : ContentControlAutomationPeer
-{
-    public DataGridColumnHeaderAutomationPeer(DataGridColumnHeader owner)
-        : base(owner)
-    {
-    }
-
-    public new DataGridColumnHeader Owner => (DataGridColumnHeader)base.Owner;
-
-    protected override AutomationControlType GetAutomationControlTypeCore()
-    {
-        return AutomationControlType.HeaderItem;
-    }
-
-    protected override bool IsContentElementCore() => false;
-
-    protected override bool IsControlElementCore() => true;
-}

+ 0 - 23
src/Avalonia.Controls.DataGrid/Automation/Peers/DataGridColumnHeadersPresenterAutomationPeer.cs

@@ -1,23 +0,0 @@
-using Avalonia.Automation.Peers;
-using Avalonia.Controls.Primitives;
-
-namespace Avalonia.Controls.Automation.Peers;
-
-public class DataGridColumnHeadersPresenterAutomationPeer : ControlAutomationPeer
-{
-    public DataGridColumnHeadersPresenterAutomationPeer(DataGridColumnHeadersPresenter owner)
-        : base(owner)
-    {
-    }
-
-    public new DataGridColumnHeadersPresenter Owner => (DataGridColumnHeadersPresenter)base.Owner;
-
-    protected override AutomationControlType GetAutomationControlTypeCore()
-    {
-        return AutomationControlType.Header;
-    }
-
-    protected override bool IsContentElementCore() => false;
-
-    protected override bool IsControlElementCore() => true;
-}

+ 0 - 14
src/Avalonia.Controls.DataGrid/Automation/Peers/DataGridDetailsPresenterAutomationPeer.cs

@@ -1,14 +0,0 @@
-using Avalonia.Automation.Peers;
-using Avalonia.Controls.Primitives;
-
-namespace Avalonia.Controls.Automation.Peers;
-
-public class DataGridDetailsPresenterAutomationPeer : ControlAutomationPeer
-{
-    public DataGridDetailsPresenterAutomationPeer(DataGridDetailsPresenter owner)
-        : base(owner)
-    {
-    }
-
-    public new DataGridDetailsPresenter Owner => (DataGridDetailsPresenter)base.Owner;
-}

+ 0 - 20
src/Avalonia.Controls.DataGrid/Automation/Peers/DataGridRowAutomationPeer.cs

@@ -1,20 +0,0 @@
-using Avalonia.Controls;
-
-namespace Avalonia.Automation.Peers
-{
-    public class DataGridRowAutomationPeer : ControlAutomationPeer
-    {
-        public DataGridRowAutomationPeer(DataGridRow owner)
-            : base(owner)
-        {
-        }
-
-        protected override AutomationControlType GetAutomationControlTypeCore()
-        {
-            return AutomationControlType.DataItem;
-        }
-
-        protected override bool IsContentElementCore() => true;
-        protected override bool IsControlElementCore() => true;
-    }
-}

+ 0 - 22
src/Avalonia.Controls.DataGrid/Avalonia.Controls.DataGrid.csproj

@@ -1,22 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk">
-  <PropertyGroup>
-    <TargetFrameworks>$(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks);netstandard2.0</TargetFrameworks>
-  </PropertyGroup>
-  <ItemGroup>
-    <ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
-    <ProjectReference Include="..\Avalonia.Remote.Protocol\Avalonia.Remote.Protocol.csproj" />
-    <ProjectReference Include="..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
-    <ProjectReference Include="..\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />    
-    <ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
-    <!-- Compatibility with old apps -->
-    <EmbeddedResource Include="Themes\**\*.xaml" />
-  </ItemGroup>
-  <Import Project="..\..\build\EmbedXaml.props" />
-  <Import Project="..\..\build\BuildTargets.targets" />
-  <Import Project="..\..\build\DevAnalyzers.props" />
-
-  <ItemGroup Label="InternalsVisibleTo">
-    <InternalsVisibleTo Include="Avalonia.Controls.DataGrid.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
-    <InternalsVisibleTo Include="Avalonia.DesignerSupport, PublicKey=$(AvaloniaPublicKey)" />
-  </ItemGroup>
-</Project>

+ 0 - 4380
src/Avalonia.Controls.DataGrid/Collections/DataGridCollectionView.cs

@@ -1,4380 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Controls.Utils;
-using Avalonia.Utilities;
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Globalization;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-
-namespace Avalonia.Collections
-{
-    /// <summary>
-    /// Event argument used for page index change notifications. The requested page move
-    /// can be canceled by setting e.Cancel to True.
-    /// </summary>
-    public sealed class PageChangingEventArgs : CancelEventArgs
-    {
-        /// <summary>
-        /// Constructor that takes the target page index
-        /// </summary>
-        /// <param name="newPageIndex">Index of the requested page</param>
-        public PageChangingEventArgs(int newPageIndex)
-        {
-            NewPageIndex = newPageIndex;
-        }
-
-        /// <summary>
-        /// Gets the index of the requested page
-        /// </summary>
-        public int NewPageIndex
-        {
-            get;
-            private set;
-        }
-    }
-
-    /// <summary>Defines a method that enables a collection to provide a custom view for specialized sorting, filtering, grouping, and currency.</summary>
-    public interface IDataGridCollectionViewFactory
-    {
-        /// <summary>Returns a custom view for specialized sorting, filtering, grouping, and currency.</summary>
-        /// <returns>A custom view for specialized sorting, filtering, grouping, and currency.</returns>
-        IDataGridCollectionView CreateView();
-    }
-
-    /// <summary>
-    /// DataGrid-readable view over an IEnumerable.
-    /// </summary>
-    public sealed class DataGridCollectionView : IDataGridCollectionView, IDataGridEditableCollectionView, IList, INotifyPropertyChanged 
-    {
-        /// <summary>
-        /// Since there's nothing in the un-cancelable event args that is mutable,
-        /// just create one instance to be used universally.
-        /// </summary>
-        private static readonly DataGridCurrentChangingEventArgs uncancelableCurrentChangingEventArgs = new DataGridCurrentChangingEventArgs(false);
-
-        /// <summary>
-        /// Value that we cache for the PageIndex if we are in a DeferRefresh,
-        /// and the user has attempted to move to a different page.
-        /// </summary>
-        private int _cachedPageIndex = -1;
-
-        /// <summary>
-        /// Value that we cache for the PageSize if we are in a DeferRefresh,
-        /// and the user has attempted to change the PageSize.
-        /// </summary>
-        private int _cachedPageSize;
-
-        /// <summary>
-        /// CultureInfo used in this DataGridCollectionView
-        /// </summary>
-        private CultureInfo _culture;
-
-        /// <summary>
-        /// Private accessor for the Monitor we use to prevent recursion
-        /// </summary>
-        private SimpleMonitor _currentChangedMonitor = new SimpleMonitor();
-
-        /// <summary>
-        /// Private accessor for the CurrentItem
-        /// </summary>
-        private object _currentItem;
-
-        /// <summary>
-        /// Private accessor for the CurrentPosition
-        /// </summary>
-        private int _currentPosition;
-
-        /// <summary>
-        /// The number of requests to defer Refresh()
-        /// </summary>
-        private int _deferLevel;
-
-        /// <summary>
-        /// The item we are currently editing
-        /// </summary>
-        private object _editItem;
-
-        /// <summary>
-        /// Private accessor for the Filter
-        /// </summary>
-        private Func<object, bool> _filter;
-
-        /// <summary>
-        /// Private accessor for the CollectionViewFlags
-        /// </summary>
-        private CollectionViewFlags _flags = CollectionViewFlags.ShouldProcessCollectionChanged;
-
-        /// <summary>
-        /// Private accessor for the Grouping data
-        /// </summary>
-        private CollectionViewGroupRoot _group;
-
-        /// <summary>
-        /// Private accessor for the InternalList
-        /// </summary>
-        private IList _internalList;
-
-        /// <summary>
-        /// Keeps track of whether groups have been applied to the
-        /// collection already or not. Note that this can still be set
-        /// to false even though we specify a GroupDescription, as the 
-        /// collection may not have gone through the PrepareGroups function.
-        /// </summary>
-        private bool _isGrouping;
-
-        /// <summary>
-        /// Private accessor for indicating whether we want to point to the temporary grouping data for calculations
-        /// </summary>
-        private bool _isUsingTemporaryGroup;
-
-        /// <summary>
-        /// ConstructorInfo obtained from reflection for generating new items
-        /// </summary>
-        private ConstructorInfo _itemConstructor;
-
-        /// <summary>
-        /// Whether we have the correct ConstructorInfo information for the ItemConstructor
-        /// </summary>
-        private bool _itemConstructorIsValid;
-
-        /// <summary>
-        /// The new item we are getting ready to add to the collection
-        /// </summary>
-        private object _newItem;
-
-        /// <summary>
-        /// Private accessor for the PageIndex
-        /// </summary>
-        private int _pageIndex = -1;
-
-        /// <summary>
-        /// Private accessor for the PageSize
-        /// </summary>
-        private int _pageSize;
-
-        /// <summary>
-        /// Whether the source needs to poll for changes
-        /// (if it did not implement INotifyCollectionChanged)
-        /// </summary>
-        private bool _pollForChanges;
-
-        /// <summary>
-        /// Private accessor for the SortDescriptions
-        /// </summary>
-        private DataGridSortDescriptionCollection _sortDescriptions;
-
-        /// <summary>
-        /// Private accessor for the SourceCollection
-        /// </summary>
-        private IEnumerable _sourceCollection;
-
-        /// <summary>
-        /// Private accessor for the Grouping data on the entire collection
-        /// </summary>
-        private CollectionViewGroupRoot _temporaryGroup;
-
-        /// <summary>
-        /// Timestamp used to see if there was a collection change while 
-        /// processing enumerator changes
-        /// </summary>
-        private int _timestamp;
-
-        /// <summary>
-        /// Private accessor for the TrackingEnumerator
-        /// </summary>
-        private IEnumerator _trackingEnumerator;
-
-        /// <summary>
-        /// Helper constructor that sets default values for isDataSorted and isDataInGroupOrder.
-        /// </summary>
-        /// <param name="source">The source for the collection</param>
-        public DataGridCollectionView(IEnumerable source)
-            : this(source, false /*isDataSorted*/, false /*isDataInGroupOrder*/)
-        {
-        }
-
-        /// <summary>
-        /// Initializes a new instance of the DataGridCollectionView class.
-        /// </summary>
-        /// <param name="source">The source for the collection</param>
-        /// <param name="isDataSorted">Determines whether the source is already sorted</param>
-        /// <param name="isDataInGroupOrder">Whether the source is already in the correct order for grouping</param>
-        public DataGridCollectionView(IEnumerable source, bool isDataSorted, bool isDataInGroupOrder)
-        {
-            _sourceCollection = source ?? throw new ArgumentNullException(nameof(source));
-
-            SetFlag(CollectionViewFlags.IsDataSorted, isDataSorted);
-            SetFlag(CollectionViewFlags.IsDataInGroupOrder, isDataInGroupOrder);
-
-            _temporaryGroup = new CollectionViewGroupRoot(this, isDataInGroupOrder);
-            _group = new CollectionViewGroupRoot(this, false);
-            _group.GroupDescriptionChanged += OnGroupDescriptionChanged;
-            _group.GroupDescriptions.CollectionChanged += OnGroupByChanged;
-
-            CopySourceToInternalList();
-            _trackingEnumerator = source.GetEnumerator();
-
-            // set currency
-            if (_internalList.Count > 0)
-            {
-                SetCurrent(_internalList[0], 0, 1);
-            }
-            else
-            {
-                SetCurrent(null, -1, 0);
-            }
-
-            // Set flag for whether the collection is empty
-            SetFlag(CollectionViewFlags.CachedIsEmpty, Count == 0);
-
-            // If we implement INotifyCollectionChanged
-            if (source is INotifyCollectionChanged coll)
-            {
-                coll.CollectionChanged += (_, args) => ProcessCollectionChanged(args);
-            }
-            else
-            {
-                // If the source doesn't raise collection change events, try to
-                // detect changes by polling the enumerator
-                _pollForChanges = true;
-            }
-        }
-
-        /// <summary>
-        /// Raise this event when the (filtered) view changes
-        /// </summary>
-        public event NotifyCollectionChangedEventHandler CollectionChanged;
-
-        /// <summary>
-        /// CollectionChanged event (per INotifyCollectionChanged).
-        /// </summary>
-        event NotifyCollectionChangedEventHandler INotifyCollectionChanged.CollectionChanged
-        {
-            add { CollectionChanged += value; }
-            remove { CollectionChanged -= value; }
-        }
-
-        /// <summary>
-        /// Raised when the CurrentItem property changed
-        /// </summary>
-        public event EventHandler CurrentChanged;
-
-        /// <summary>
-        /// Raised when the CurrentItem property is changing
-        /// </summary>
-        public event EventHandler<DataGridCurrentChangingEventArgs> CurrentChanging;
-
-        /// <summary>
-        /// Raised when a page index change completed
-        /// </summary>
-        //TODO Paging
-        public event EventHandler<EventArgs> PageChanged;
-
-        /// <summary>
-        /// Raised when a page index change is requested
-        /// </summary>
-        //TODO Paging
-        public event EventHandler<PageChangingEventArgs> PageChanging;
-
-        /// <summary>
-        /// PropertyChanged event.
-        /// </summary>
-        public event PropertyChangedEventHandler PropertyChanged;
-
-        /// <summary>
-        /// PropertyChanged event (per INotifyPropertyChanged)
-        /// </summary>
-        event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
-        {
-            add { PropertyChanged += value; }
-            remove { PropertyChanged -= value; }
-        }
-
-        /// <summary>
-        /// Enum for CollectionViewFlags
-        /// </summary>
-        //TODO Paging
-        [Flags]
-        private enum CollectionViewFlags
-        {
-            /// <summary>
-            /// Whether the list of items (after applying the sort and filters, if any) 
-            /// is already in the correct order for grouping. 
-            /// </summary>
-            IsDataInGroupOrder = 0x01,
-
-            /// <summary>
-            /// Whether the source collection is already sorted according to the SortDescriptions collection
-            /// </summary>
-            IsDataSorted = 0x02,
-
-            /// <summary>
-            /// Whether we should process the collection changed event
-            /// </summary>
-            ShouldProcessCollectionChanged = 0x04,
-
-            /// <summary>
-            /// Whether the current item is before the first
-            /// </summary>
-            IsCurrentBeforeFirst = 0x08,
-
-            /// <summary>
-            /// Whether the current item is after the last
-            /// </summary>
-            IsCurrentAfterLast = 0x10,
-
-            /// <summary>
-            /// Whether we need to refresh
-            /// </summary>
-            NeedsRefresh = 0x20,
-
-            /// <summary>
-            /// Whether we cache the IsEmpty value
-            /// </summary>
-            CachedIsEmpty = 0x40,
-
-            /// <summary>
-            /// Indicates whether a page index change is in process or not
-            /// </summary>
-            IsPageChanging = 0x80,
-
-            /// <summary>
-            /// Whether we need to move to another page after EndDefer
-            /// </summary>
-            IsMoveToPageDeferred = 0x100,
-
-            /// <summary>
-            /// Whether we need to update the PageSize after EndDefer
-            /// </summary>
-            IsUpdatePageSizeDeferred = 0x200
-        }
-
-        private Type _itemType;
-        private Type ItemType
-        {
-            get
-            {
-                if (_itemType == null)
-                    _itemType = GetItemType(true);
-
-                return _itemType;
-            }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether the view supports AddNew.
-        /// </summary>
-        public bool CanAddNew
-        {
-            get
-            {
-                return !IsEditingItem &&
-                    (SourceList != null && !SourceList.IsFixedSize && CanConstructItem);
-            }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether the view supports the notion of "pending changes" 
-        /// on the current edit item.  This may vary, depending on the view and the particular
-        /// item.  For example, a view might return true if the current edit item
-        /// implements IEditableObject, or if the view has special knowledge about 
-        /// the item that it can use to support rollback of pending changes.
-        /// </summary>
-        public bool CanCancelEdit
-        {
-            get { return _editItem is IEditableObject; }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether the PageIndex value is allowed to change or not.
-        /// </summary>
-        //TODO Paging
-        public bool CanChangePage
-        {
-            get { return true; }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether we support filtering with this ICollectionView.
-        /// </summary>
-        public bool CanFilter
-        {
-            get { return true; }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether this view supports grouping.
-        /// When this returns false, the rest of the interface is ignored.
-        /// </summary>
-        public bool CanGroup
-        {
-            get { return true; }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether the view supports Remove and RemoveAt.
-        /// </summary>
-        public bool CanRemove
-        {
-            get
-            {
-                return !IsEditingItem && !IsAddingNew &&
-                    (SourceList != null && !SourceList.IsFixedSize);
-            }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether we support sorting with this ICollectionView.
-        /// </summary>
-        public bool CanSort
-        {
-            get { return true; }
-        }
-
-        /// <summary>
-        /// Gets the number of records in the view after 
-        /// filtering, sorting, and paging.
-        /// </summary>
-        //TODO Paging
-        public int Count
-        {
-            get
-            {
-                EnsureCollectionInSync();
-                VerifyRefreshNotDeferred();
-
-                // if we have paging
-                if (PageSize > 0 && PageIndex > -1)
-                {
-                    if (IsGrouping && !_isUsingTemporaryGroup)
-                    {
-                        return _group.ItemCount;
-                    }
-                    else
-                    {
-                        return Math.Max(0, Math.Min(PageSize, InternalCount - (_pageSize * PageIndex)));
-                    }
-                }
-                else
-                {
-                    if (IsGrouping)
-                    {
-                        if (_isUsingTemporaryGroup)
-                        {
-                            return _temporaryGroup.ItemCount;
-                        }
-                        else
-                        {
-                            return _group.ItemCount;
-                        }
-                    }
-                    else
-                    {
-                        return InternalCount;
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// Gets or sets Culture to use during sorting.
-        /// </summary>
-        public CultureInfo Culture
-        {
-            get
-            {
-                return _culture;
-            }
-
-            set
-            {
-                if (value == null)
-                {
-                    throw new ArgumentNullException(nameof(value));
-                }
-
-                if (_culture != value)
-                {
-                    _culture = value;
-                    OnPropertyChanged(nameof(Culture));
-                }
-            }
-        }
-
-        /// <summary>
-        /// Gets the new item when an AddNew transaction is in progress
-        /// Otherwise it returns null.
-        /// </summary>
-        public object CurrentAddItem
-        {
-            get
-            {
-                return _newItem;
-            }
-
-            private set
-            {
-                if (_newItem != value)
-                {
-                    Debug.Assert(value == null || _newItem == null, "Old and new _newItem values are unexpectedly non null");
-                    _newItem = value;
-                    OnPropertyChanged(nameof(IsAddingNew));
-                    OnPropertyChanged(nameof(CurrentAddItem));
-                }
-            }
-        }
-
-        /// <summary>
-        /// Gets the affected item when an EditItem transaction is in progress
-        /// Otherwise it returns null.
-        /// </summary>
-        public object CurrentEditItem
-        {
-            get
-            {
-                return _editItem;
-            }
-
-            private set
-            {
-                if (_editItem != value)
-                {
-                    Debug.Assert(value == null || _editItem == null, "Old and new _editItem values are unexpectedly non null");
-                    bool oldCanCancelEdit = CanCancelEdit;
-                    _editItem = value;
-                    OnPropertyChanged(nameof(IsEditingItem));
-                    OnPropertyChanged(nameof(CurrentEditItem));
-                    if (oldCanCancelEdit != CanCancelEdit)
-                    {
-                        OnPropertyChanged(nameof(CanCancelEdit));
-                    }
-                }
-            }
-        }
-
-        /// <summary> 
-        /// Gets the "current item" for this view 
-        /// </summary>
-        public object CurrentItem
-        {
-            get
-            {
-                VerifyRefreshNotDeferred();
-                return _currentItem;
-            }
-        }
-
-        /// <summary>
-        /// Gets the ordinal position of the CurrentItem within the 
-        /// (optionally sorted and filtered) view.
-        /// </summary>
-        public int CurrentPosition
-        {
-            get
-            {
-                VerifyRefreshNotDeferred();
-                return _currentPosition;
-            }
-        }
-
-        private string GetOperationNotAllowedDuringAddOrEditText(string action)
-        {
-            return $"'{action}' is not allowed during an AddNew or EditItem transaction.";
-        }
-        private string GetOperationNotAllowedText(string action, string transaction = null)
-        {
-            if (String.IsNullOrWhiteSpace(transaction))
-            {
-                return $"'{action}' is not allowed for this view.";
-            }
-            else
-            {
-                return $"'{action}' is not allowed during a transaction started by '{transaction}'.";
-            }
-        }
-
-        /// <summary>
-        /// Gets or sets the Filter, which is a callback set by the consumer of the ICollectionView
-        /// and used by the implementation of the ICollectionView to determine if an
-        /// item is suitable for inclusion in the view.
-        /// </summary>        
-        /// <exception cref="NotSupportedException">
-        /// Simpler implementations do not support filtering and will throw a NotSupportedException.
-        /// Use <seealso cref="CanFilter"/> property to test if filtering is supported before
-        /// assigning a non-null value.
-        /// </exception>
-        public Func<object, bool> Filter
-        {
-            get
-            {
-                return _filter;
-            }
-
-            set
-            {
-                if (IsAddingNew || IsEditingItem)
-                {
-                    throw new InvalidOperationException(GetOperationNotAllowedDuringAddOrEditText(nameof(Filter)));
-                }
-
-                if (!CanFilter)
-                {
-                    throw new NotSupportedException("The Filter property cannot be set when the CanFilter property returns false.");
-                }
-
-                if (_filter != value)
-                {
-                    _filter = value;
-                    RefreshOrDefer();
-                    OnPropertyChanged(nameof(Filter));
-                }
-            }
-        }
-
-        /// <summary>
-        /// Gets the description of grouping, indexed by level.
-        /// </summary>
-        public AvaloniaList<DataGridGroupDescription> GroupDescriptions
-        {
-            get
-            {
-                return _group?.GroupDescriptions;
-            }
-        }
-
-        int IDataGridCollectionView.GroupingDepth => GroupDescriptions?.Count ?? 0;
-        string IDataGridCollectionView.GetGroupingPropertyNameAtDepth(int level)
-        {
-            var groups = GroupDescriptions;
-            if(groups != null && level >= 0 && level < groups.Count)
-            {
-                return groups[level].PropertyName;
-            }
-            else
-            {
-                return String.Empty;
-            }
-        }
-
-        /// <summary>
-        /// Gets the top-level groups, constructed according to the descriptions
-        /// given in GroupDescriptions.
-        /// </summary>
-        public IAvaloniaReadOnlyList<object> Groups
-        {
-            get
-            {
-                if (!IsGrouping)
-                {
-                    return null;
-                }
-
-                return RootGroup?.Items;
-            }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether an "AddNew" transaction is in progress.
-        /// </summary>
-        public bool IsAddingNew
-        {
-            get { return _newItem != null; }
-        }
-
-        /// <summary> 
-        /// Gets a value indicating whether currency is beyond the end (End-Of-File). 
-        /// </summary>
-        /// <returns>Whether IsCurrentAfterLast</returns>
-        public bool IsCurrentAfterLast
-        {
-            get
-            {
-                VerifyRefreshNotDeferred();
-                return CheckFlag(CollectionViewFlags.IsCurrentAfterLast);
-            }
-        }
-
-        /// <summary> 
-        /// Gets a value indicating whether currency is before the beginning (Beginning-Of-File). 
-        /// </summary>
-        /// <returns>Whether IsCurrentBeforeFirst</returns>
-        public bool IsCurrentBeforeFirst
-        {
-            get
-            {
-                VerifyRefreshNotDeferred();
-                return CheckFlag(CollectionViewFlags.IsCurrentBeforeFirst);
-            }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether an EditItem transaction is in progress.
-        /// </summary>
-        public bool IsEditingItem
-        {
-            get { return _editItem != null; }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether the resulting (filtered) view is empty.
-        /// </summary>
-        public bool IsEmpty
-        {
-            get
-            {
-                EnsureCollectionInSync();
-                return InternalCount == 0;
-            }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether a page index change is in process or not.
-        /// </summary>
-        //TODO Paging
-        public bool IsPageChanging
-        {
-            get
-            {
-                return CheckFlag(CollectionViewFlags.IsPageChanging);
-            }
-
-            private set
-            {
-                if (CheckFlag(CollectionViewFlags.IsPageChanging) != value)
-                {
-                    SetFlag(CollectionViewFlags.IsPageChanging, value);
-                    OnPropertyChanged(nameof(IsPageChanging));
-                }
-            }
-        }
-
-        /// <summary>
-        /// Gets the minimum number of items known to be in the source collection
-        /// that verify the current filter if any
-        /// </summary>
-        public int ItemCount
-        {
-            get
-            {
-                return InternalList.Count;
-            }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether this view needs to be refreshed.
-        /// </summary>
-        public bool NeedsRefresh
-        {
-            get { return CheckFlag(CollectionViewFlags.NeedsRefresh); }
-        }
-
-        /// <summary>
-        /// Gets the current page we are on. (zero based)
-        /// </summary>
-        //TODO Paging
-        public int PageIndex
-        {
-            get
-            {
-                return _pageIndex;
-            }
-        }
-
-        /// <summary>
-        /// Gets or sets the number of items to display on a page. If the
-        /// PageSize = 0, then we are not paging, and will display all items
-        /// in the collection. Otherwise, we will have separate pages for 
-        /// the items to display.
-        /// </summary>
-        //TODO Paging
-        public int PageSize
-        {
-            get
-            {
-                return _pageSize;
-            }
-            set
-            {
-                if (value < 0)
-                {
-                    throw new ArgumentOutOfRangeException(nameof(value), "PageSize cannot have a negative value.");
-                }
-
-                // if the Refresh is currently deferred, cache the desired PageSize
-                // and set the flag so that once the defer is over, we can then
-                // update the PageSize.
-                if (IsRefreshDeferred)
-                {
-                    // set cached value and flag so that we update the PageSize on EndDefer
-                    _cachedPageSize = value;
-                    SetFlag(CollectionViewFlags.IsUpdatePageSizeDeferred, true);
-                    return;
-                }
-
-                // to see whether or not to fire an OnPropertyChanged
-                int oldCount = Count;
-
-                if (_pageSize != value)
-                {
-                    // Remember current currency values for upcoming OnPropertyChanged notifications
-                    object oldCurrentItem = CurrentItem;
-                    int oldCurrentPosition = CurrentPosition;
-                    bool oldIsCurrentAfterLast = IsCurrentAfterLast;
-                    bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
-
-                    // Check if there is a current edited or new item so changes can be committed first.
-                    if (CurrentAddItem != null || CurrentEditItem != null)
-                    {
-                        // Check with the ICollectionView.CurrentChanging listeners if it's OK to
-                        // change the currency. If not, then we can't fire the event to allow them to
-                        // commit their changes. So, we will not be able to change the PageSize.
-                        if (!OkToChangeCurrent())
-                        {
-                            throw new InvalidOperationException("Changing the PageSize is not allowed during an AddNew or EditItem transaction.");
-                        }
-
-                        // Currently CommitNew()/CommitEdit()/CancelNew()/CancelEdit() can't handle committing or 
-                        // cancelling an item that is no longer on the current page. That's acceptable and means that
-                        // the potential _newItem or _editItem needs to be committed before this PageSize change.
-                        // The reason why we temporarily reset currency here is to give a chance to the bound
-                        // controls to commit or cancel their potential edits/addition. The DataForm calls ForceEndEdit()
-                        // for example as a result of changing currency.
-                        SetCurrentToPosition(-1);
-                        RaiseCurrencyChanges(true /*fireChangedEvent*/, oldCurrentItem, oldCurrentPosition, oldIsCurrentBeforeFirst, oldIsCurrentAfterLast);
-
-                        // If the bound controls did not successfully end their potential item editing/addition, we 
-                        // need to throw an exception to show that the PageSize change failed. 
-                        if (CurrentAddItem != null || CurrentEditItem != null)
-                        {
-                            throw new InvalidOperationException("Changing the PageSize is not allowed during an AddNew or EditItem transaction.");
-                        }
-                    }
-
-                    _pageSize = value;
-                    OnPropertyChanged(nameof(PageSize));
-
-                    if (_pageSize == 0)
-                    {
-                        // update the groups for the current page
-                        //***************************************
-                        PrepareGroups();
-
-                        // if we are not paging
-                        MoveToPage(-1);
-                    }
-                    else if (_pageIndex != 0)
-                    {
-                        if (!CheckFlag(CollectionViewFlags.IsMoveToPageDeferred))
-                        {
-                            // if the temporaryGroup was not created yet and is out of sync
-                            // then create it so that we can use it as a reference while paging.
-                            if (IsGrouping && _temporaryGroup.ItemCount != InternalList.Count)
-                            {
-                                PrepareTemporaryGroups();
-                            }
-
-                            MoveToFirstPage();
-                        }
-                    }
-                    else if (IsGrouping)
-                    {
-                        // if the temporaryGroup was not created yet and is out of sync
-                        // then create it so that we can use it as a reference while paging.
-                        if (_temporaryGroup.ItemCount != InternalList.Count)
-                        {
-                            // update the groups that get created for the
-                            // entire collection as well as the current page
-                            PrepareTemporaryGroups();
-                        }
-
-                        // update the groups for the current page
-                        PrepareGroupsForCurrentPage();
-                    }
-
-                    // if the count has changed
-                    if (Count != oldCount)
-                    {
-                        OnPropertyChanged(nameof(Count));
-                    }
-
-                    // reset currency values
-                    ResetCurrencyValues(oldCurrentItem, oldIsCurrentBeforeFirst, oldIsCurrentAfterLast);
-
-                    // send a notification that our collection has been updated
-                    OnCollectionChanged(
-                        new NotifyCollectionChangedEventArgs(
-                            NotifyCollectionChangedAction.Reset));
-
-                    // now raise currency changes at the end
-                    RaiseCurrencyChanges(false, oldCurrentItem, oldCurrentPosition, oldIsCurrentBeforeFirst, oldIsCurrentAfterLast);
-                }
-            }
-        }
-
-        /// <summary>
-        /// Gets the Sort criteria to sort items in collection.
-        /// </summary>
-        /// <remarks>
-        /// <p>
-        /// Clear a sort criteria by assigning SortDescription.Empty to this property.
-        /// One or more sort criteria in form of <seealso cref="DataGridSortDescription"/>
-        /// can be used, each specifying a property and direction to sort by.
-        /// </p>
-        /// </remarks>
-        /// <exception cref="NotSupportedException">
-        /// Simpler implementations do not support sorting and will throw a NotSupportedException.
-        /// Use <seealso cref="CanSort"/> property to test if sorting is supported before adding
-        /// to SortDescriptions.
-        /// </exception>
-        public DataGridSortDescriptionCollection SortDescriptions
-        {
-            get
-            {
-                if (_sortDescriptions == null)
-                {
-                    SetSortDescriptions(new DataGridSortDescriptionCollection());
-                }
-
-                return _sortDescriptions;
-            }
-        }
-
-        /// <summary>
-        /// Gets the source of the IEnumerable collection we are using for our view.
-        /// </summary>
-        public IEnumerable SourceCollection
-        {
-            get { return _sourceCollection; }
-        }
-
-        /// <summary>
-        /// Gets the total number of items in the view before paging is applied.
-        /// </summary>
-        public int TotalItemCount
-        {
-            get
-            {
-                return InternalList.Count;
-            }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether we have a valid ItemConstructor of the correct type
-        /// </summary>
-        private bool CanConstructItem
-        {
-            get
-            {
-                if (!_itemConstructorIsValid)
-                {
-                    EnsureItemConstructor();
-                }
-
-                return _itemConstructor != null;
-            }
-        }
-
-        /// <summary>
-        /// Gets the private count without taking paging or
-        /// placeholders into account
-        /// </summary>
-        private int InternalCount
-        {
-            get { return InternalList.Count; }
-        }
-
-        /// <summary>
-        /// Gets the InternalList
-        /// </summary>
-        private IList InternalList
-        {
-            get { return _internalList; }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether CurrentItem and CurrentPosition are
-        /// up-to-date with the state and content of the collection.
-        /// </summary>
-        private bool IsCurrentInSync
-        {
-            get
-            {
-                if (IsCurrentInView)
-                {
-                    return GetItemAt(CurrentPosition).Equals(CurrentItem);
-                }
-                else
-                {
-                    return CurrentItem == null;
-                }
-            }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether the current item is in the view
-        /// </summary>
-        private bool IsCurrentInView
-        {
-            get
-            {
-                VerifyRefreshNotDeferred();
-
-                // Calling IndexOf will check whether the specified currentItem
-                // is within the (paged) view.
-                return IndexOf(CurrentItem) >= 0;
-            }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether or not we have grouping 
-        /// taking place in this collection.
-        /// </summary>
-        private bool IsGrouping
-        {
-            get { return _isGrouping; }
-        }
-
-        bool IDataGridCollectionView.IsGrouping => IsGrouping;
-
-        /// <summary>
-        /// Gets a value indicating whether there
-        /// is still an outstanding DeferRefresh in
-        /// use.  If at all possible, derived classes
-        /// should not call Refresh if IsRefreshDeferred
-        /// is true.
-        /// </summary>
-        private bool IsRefreshDeferred
-        {
-            get { return _deferLevel > 0; }
-        }
-
-        /// <summary>
-        /// Gets whether the current page is empty and we need
-        /// to move to a previous page.
-        /// </summary>
-        //TODO Paging
-        private bool NeedToMoveToPreviousPage
-        {
-            get { return (PageSize > 0 && Count == 0 && PageIndex != 0 && PageCount == PageIndex); }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether we are on the last local page
-        /// </summary>
-        //TODO Paging
-        private bool OnLastLocalPage
-        {
-            get
-            {
-                if (PageSize == 0)
-                {
-                    return false;
-                }
-
-                Debug.Assert(PageCount > 0, "Unexpected PageCount <= 0");
-
-                // if we have no items (PageCount==1) or there is just one page
-                if (PageCount == 1)
-                {
-                    return true;
-                }
-
-                return (PageIndex == PageCount - 1);
-            }
-        }
-
-        /// <summary>
-        /// Gets the number of pages we currently have
-        /// </summary>
-        //TODO Paging
-        private int PageCount
-        {
-            get { return (_pageSize > 0) ? Math.Max(1, (int)Math.Ceiling((double)ItemCount / _pageSize)) : 0; }
-        }
-
-        /// <summary>
-        /// Gets the root of the Group that we expose to the user
-        /// </summary>
-        private CollectionViewGroupRoot RootGroup
-        {
-            get
-            {
-                return _isUsingTemporaryGroup ? _temporaryGroup : _group;
-            }
-        }
-
-        /// <summary>
-        /// Gets the SourceCollection as an IList
-        /// </summary>
-        private IList SourceList
-        {
-            get { return SourceCollection as IList; }
-        }
-
-        /// <summary>
-        /// Gets Timestamp used by the NewItemAwareEnumerator to determine if a
-        /// collection change has occurred since the enumerator began.  (If so,
-        /// MoveNext should throw.)
-        /// </summary>
-        private int Timestamp
-        {
-            get { return _timestamp; }
-        }
-
-        /// <summary>
-        /// Gets a value indicating whether a private copy of the data 
-        /// is needed for sorting, filtering, and paging. We want any deriving 
-        /// classes to also be able to access this value to see whether or not 
-        /// to use the default source collection, or the internal list.
-        /// </summary>
-        //TODO Paging
-        private bool UsesLocalArray
-        {
-            get { return SortDescriptions.Count > 0 || Filter != null || _pageSize > 0 || GroupDescriptions.Count > 0; }
-        }
-
-        /// <summary>
-        /// Return the item at the specified index
-        /// </summary>
-        /// <param name="index">Index of the item we want to retrieve</param>
-        /// <returns>The item at the specified index</returns>
-        public object this[int index]
-        {
-            get { return GetItemAt(index); }
-        }
-
-        bool IList.IsFixedSize => SourceList?.IsFixedSize ?? true;
-        bool IList.IsReadOnly => SourceList?.IsReadOnly ?? true;
-        bool ICollection.IsSynchronized => false;
-        object ICollection.SyncRoot => this;
-
-        object IList.this[int index]
-        {
-            get => this[index];
-            set
-            {
-                SourceList[index] = value;
-                if (SourceList is not INotifyCollectionChanged)
-                {
-                    // TODO: implement Replace
-                    ProcessCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, value));
-                }
-            }
-        }
-
-        /// <summary>
-        /// Add a new item to the underlying collection.  Returns the new item.
-        /// After calling AddNew and changing the new item as desired, either
-        /// CommitNew or CancelNew" should be called to complete the transaction.
-        /// </summary>
-        /// <returns>The new item we are adding</returns>
-        //TODO Paging
-        public object AddNew()
-        {
-            EnsureCollectionInSync();
-            VerifyRefreshNotDeferred();
-
-            if (IsEditingItem)
-            {
-                // Implicitly close a previous EditItem
-                CommitEdit();
-            }
-
-            // Implicitly close a previous AddNew
-            CommitNew();
-
-            // Checking CanAddNew will validate that we have the correct itemConstructor
-            if (!CanAddNew)
-            {
-                throw new InvalidOperationException(GetOperationNotAllowedText(nameof(AddNew)));
-            }
-
-            object newItem = null;
-
-            if (_itemConstructor != null)
-            {
-                newItem = _itemConstructor.Invoke(null);
-            }
-
-            try
-            {
-                // temporarily disable the CollectionChanged event
-                // handler so filtering, sorting, or grouping
-                // doesn't get applied yet
-                SetFlag(CollectionViewFlags.ShouldProcessCollectionChanged, false);
-
-                if (SourceList != null)
-                {
-                    SourceList.Add(newItem);
-                }
-            }
-            finally
-            {
-                SetFlag(CollectionViewFlags.ShouldProcessCollectionChanged, true);
-            }
-
-            // Modify our _trackingEnumerator so that it shows that our collection is "up to date" 
-            // and will not refresh for now.
-            _trackingEnumerator = _sourceCollection.GetEnumerator();
-
-            int addIndex;
-            int removeIndex = -1;
-
-            // Adjust index based on where it should be displayed in view.
-            if (PageSize > 0)
-            {
-                // if the page is full (Count==PageSize), then replace last item (Count-1).
-                // otherwise, we just append at end (Count).
-                addIndex = Count - ((Count == PageSize) ? 1 : 0);
-
-                // if the page is full, remove the last item to make space for the new one.
-                removeIndex = (Count == PageSize) ? addIndex : -1;
-            }
-            else
-            {
-                // for non-paged lists, we want to insert the item 
-                // as the last item in the view
-                addIndex = Count;
-            }
-
-            // if we need to remove an item from the view due to paging
-            if (removeIndex > -1)
-            {
-                object removeItem = GetItemAt(removeIndex);
-                if (IsGrouping)
-                {
-                    _group.RemoveFromSubgroups(removeItem);
-                }
-
-                OnCollectionChanged(
-                    new NotifyCollectionChangedEventArgs(
-                        NotifyCollectionChangedAction.Remove,
-                        removeItem,
-                        removeIndex));
-            }
-
-            // add the new item to the internal list
-            _internalList.Insert(ConvertToInternalIndex(addIndex), newItem);
-            OnPropertyChanged(nameof(ItemCount));
-
-            object oldCurrentItem = CurrentItem;
-            int oldCurrentPosition = CurrentPosition;
-            bool oldIsCurrentAfterLast = IsCurrentAfterLast;
-            bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
-
-            AdjustCurrencyForAdd(null, addIndex);
-
-            if (IsGrouping)
-            {
-                _group.InsertSpecialItem(_group.Items.Count, newItem, false);
-                if (PageSize > 0)
-                {
-                    _temporaryGroup.InsertSpecialItem(_temporaryGroup.Items.Count, newItem, false);
-                }
-            }
-
-            // fire collection changed.
-            OnCollectionChanged(
-                new NotifyCollectionChangedEventArgs(
-                    NotifyCollectionChangedAction.Add,
-                    newItem,
-                    addIndex));
-
-            RaiseCurrencyChanges(false, oldCurrentItem, oldCurrentPosition, oldIsCurrentBeforeFirst, oldIsCurrentAfterLast);
-
-            // set the current new item
-            CurrentAddItem = newItem;
-
-            MoveCurrentTo(newItem);
-
-            // if the new item is editable, call BeginEdit on it
-            if (newItem is IEditableObject editableObject)
-            {
-                editableObject.BeginEdit();
-            }
-
-            return newItem;
-        }
-
-        /// <summary>
-        /// Complete the transaction started by <seealso cref="EditItem"/>.
-        /// The pending changes (if any) to the item are discarded.
-        /// </summary>
-        public void CancelEdit()
-        {
-            if (IsAddingNew)
-            {
-                throw new InvalidOperationException(GetOperationNotAllowedText(nameof(CancelEdit), nameof(AddNew)));
-            }
-            else if (!CanCancelEdit)
-            {
-                throw new InvalidOperationException("CancelEdit is not supported for the current edit item.");
-            }
-
-            VerifyRefreshNotDeferred();
-
-            if (CurrentEditItem == null)
-            {
-                return;
-            }
-
-            object editItem = CurrentEditItem;
-            CurrentEditItem = null;
-
-            if (editItem is IEditableObject ieo)
-            {
-                ieo.CancelEdit();
-            }
-            else
-            {
-                throw new InvalidOperationException("CancelEdit is not supported for the current edit item.");
-            }
-        }
-
-        /// <summary>
-        /// Complete the transaction started by AddNew. The new
-        /// item is removed from the collection.
-        /// </summary>
-        //TODO Paging
-        public void CancelNew()
-        {
-            if (IsEditingItem)
-            {
-                throw new InvalidOperationException(GetOperationNotAllowedText(nameof(CancelNew), nameof(EditItem)));
-            }
-
-            VerifyRefreshNotDeferred();
-
-            if (CurrentAddItem == null)
-            {
-                return;
-            }
-
-            // get index of item before it is removed
-            int index = IndexOf(CurrentAddItem);
-
-            // remove the new item from the underlying collection
-            try
-            {
-                // temporarily disable the CollectionChanged event
-                // handler so filtering, sorting, or grouping
-                // doesn't get applied yet
-                SetFlag(CollectionViewFlags.ShouldProcessCollectionChanged, false);
-
-                if (SourceList != null)
-                {
-                    SourceList.Remove(CurrentAddItem);
-                }
-            }
-            finally
-            {
-                SetFlag(CollectionViewFlags.ShouldProcessCollectionChanged, true);
-            }
-
-            // Modify our _trackingEnumerator so that it shows that our collection is "up to date" 
-            // and will not refresh for now.
-            _trackingEnumerator = _sourceCollection.GetEnumerator();
-
-            // fire the correct events
-            if (CurrentAddItem != null)
-            {
-                object newItem = EndAddNew(true);
-
-                int addIndex = -1;
-
-                // Adjust index based on where it should be displayed in view.
-                if (PageSize > 0 && !OnLastLocalPage)
-                {
-                    // if there is paging and we are not on the last page, we need
-                    // to bring in an item from the next page.
-                    addIndex = Count - 1;
-                }
-
-                // remove the new item from the internal list 
-                InternalList.Remove(newItem);
-
-                if (IsGrouping)
-                {
-                    _group.RemoveSpecialItem(_group.Items.Count - 1, newItem, false);
-                    if (PageSize > 0)
-                    {
-                        _temporaryGroup.RemoveSpecialItem(_temporaryGroup.Items.Count - 1, newItem, false);
-                    }
-                }
-
-                OnPropertyChanged(nameof(ItemCount));
-
-                object oldCurrentItem = CurrentItem;
-                int oldCurrentPosition = CurrentPosition;
-                bool oldIsCurrentAfterLast = IsCurrentAfterLast;
-                bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
-
-                AdjustCurrencyForRemove(index);
-
-                // fire collection changed.
-                OnCollectionChanged(
-                    new NotifyCollectionChangedEventArgs(
-                        NotifyCollectionChangedAction.Remove,
-                        newItem,
-                        index));
-
-                RaiseCurrencyChanges(false, oldCurrentItem, oldCurrentPosition, oldIsCurrentBeforeFirst, oldIsCurrentAfterLast);
-
-                // if we need to add an item into the view due to paging
-                if (addIndex > -1)
-                {
-                    int internalIndex = ConvertToInternalIndex(addIndex);
-                    object addItem = null;
-                    if (IsGrouping)
-                    {
-                        addItem = _temporaryGroup.LeafAt(internalIndex);
-                        _group.AddToSubgroups(addItem, loading: false);
-                    }
-                    else
-                    {
-                        addItem = InternalItemAt(internalIndex);
-                    }
-
-                    OnCollectionChanged(
-                        new NotifyCollectionChangedEventArgs(
-                            NotifyCollectionChangedAction.Add,
-                            addItem,
-                            IndexOf(addItem)));
-                }
-            }
-        }
-
-        /// <summary>
-        /// Complete the transaction started by <seealso cref="EditItem"/>.
-        /// The pending changes (if any) to the item are committed.
-        /// </summary>
-        //TODO Paging
-        public void CommitEdit()
-        {
-            if (IsAddingNew)
-            {
-                throw new InvalidOperationException(GetOperationNotAllowedText(nameof(CommitEdit), nameof(AddNew)));
-            }
-
-            VerifyRefreshNotDeferred();
-
-            if (CurrentEditItem == null)
-            {
-                return;
-            }
-
-            object editItem = CurrentEditItem;
-            CurrentEditItem = null;
-
-            if (editItem is IEditableObject ieo)
-            {
-                ieo.EndEdit();
-            }
-
-            if (UsesLocalArray)
-            {
-                // first remove the item from the array so that we can insert into the correct position
-                int removeIndex = IndexOf(editItem);
-                int internalRemoveIndex = InternalIndexOf(editItem);
-                _internalList.Remove(editItem);
-
-                // check whether to restore currency to the item being edited
-                object restoreCurrencyTo = (editItem == CurrentItem) ? editItem : null;
-
-                if (removeIndex >= 0 && IsGrouping)
-                {
-                    // we can't just call RemoveFromSubgroups, as the group name
-                    // for the item may have changed during the edit.
-                    _group.RemoveItemFromSubgroupsByExhaustiveSearch(editItem);
-                    if (PageSize > 0)
-                    {
-                        _temporaryGroup.RemoveItemFromSubgroupsByExhaustiveSearch(editItem);
-                    }
-                }
-
-                object oldCurrentItem = CurrentItem;
-                int oldCurrentPosition = CurrentPosition;
-                bool oldIsCurrentAfterLast = IsCurrentAfterLast;
-                bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
-
-                // only adjust currency and fire the event if we actually removed the item
-                if (removeIndex >= 0)
-                {
-                    AdjustCurrencyForRemove(removeIndex);
-
-                    // raise the remove event so we can next insert it into the correct place
-                    OnCollectionChanged(
-                        new NotifyCollectionChangedEventArgs(
-                            NotifyCollectionChangedAction.Remove,
-                            editItem,
-                            removeIndex));
-                }
-
-                // check to see that the item will be added back in
-                bool passedFilter = PassesFilter(editItem);
-
-                // if we removed all items from the current page,
-                // move to the previous page. we do not need to 
-                // fire additional notifications, as moving the page will
-                // trigger a reset.
-                if (NeedToMoveToPreviousPage && !passedFilter)
-                {
-                    MoveToPreviousPage();
-                    return;
-                }
-
-                // next process adding it into the correct location
-                ProcessInsertToCollection(editItem, internalRemoveIndex);
-
-                int pageStartIndex = PageIndex * PageSize;
-                int nextPageStartIndex = pageStartIndex + PageSize;
-
-                if (IsGrouping)
-                {
-                    int leafIndex = -1;
-                    if (passedFilter && PageSize > 0)
-                    {
-                        _temporaryGroup.AddToSubgroups(editItem, false /*loading*/);
-                        leafIndex = _temporaryGroup.LeafIndexOf(editItem);
-                    }
-
-                    // if we are not paging, we should just be able to add the item.
-                    // otherwise, we need to validate that it is within the current page.
-                    if (passedFilter && (PageSize == 0 ||
-                       (pageStartIndex <= leafIndex && nextPageStartIndex > leafIndex)))
-                    {
-                        _group.AddToSubgroups(editItem, false /*loading*/);
-                        int addIndex = IndexOf(editItem);
-                        AdjustCurrencyForEdit(restoreCurrencyTo, addIndex);
-                        OnCollectionChanged(
-                            new NotifyCollectionChangedEventArgs(
-                                NotifyCollectionChangedAction.Add,
-                                editItem,
-                                addIndex));
-                    }
-                    else if (PageSize > 0)
-                    {
-                        int addIndex = -1;
-                        if (passedFilter && leafIndex < pageStartIndex)
-                        {
-                            // if the item was added to an earlier page, then we need to bring
-                            // in the item that would have been pushed down to this page
-                            addIndex = pageStartIndex;
-                        }
-                        else if (!OnLastLocalPage && removeIndex >= 0)
-                        {
-                            // if the item was added to a later page, then we need to bring in the
-                            // first item from the next page
-                            addIndex = nextPageStartIndex - 1;
-                        }
-
-                        object addItem = _temporaryGroup.LeafAt(addIndex);
-                        if (addItem != null)
-                        {
-                            _group.AddToSubgroups(addItem, false /*loading*/);
-                            addIndex = IndexOf(addItem);
-                            AdjustCurrencyForEdit(restoreCurrencyTo, addIndex);
-                            OnCollectionChanged(
-                                new NotifyCollectionChangedEventArgs(
-                                    NotifyCollectionChangedAction.Add,
-                                    addItem,
-                                    addIndex));
-                        }
-                    }
-                }
-                else
-                {
-                    // if we are still within the view
-                    int addIndex = IndexOf(editItem);
-                    if (addIndex >= 0)
-                    {
-                        AdjustCurrencyForEdit(restoreCurrencyTo, addIndex);
-                        OnCollectionChanged(
-                            new NotifyCollectionChangedEventArgs(
-                                NotifyCollectionChangedAction.Add,
-                                editItem,
-                                addIndex));
-                    }
-                    else if (PageSize > 0)
-                    {
-                        // calculate whether the item was inserted into the previous page
-                        bool insertedToPreviousPage = PassesFilter(editItem) &&
-                            (InternalIndexOf(editItem) < ConvertToInternalIndex(0));
-                        addIndex = insertedToPreviousPage ? 0 : Count - 1;
-
-                        // don't fire the event if we are on the last page
-                        // and we don't have any items to bring in.
-                        if (insertedToPreviousPage || (!OnLastLocalPage && removeIndex >= 0))
-                        {
-                            AdjustCurrencyForEdit(restoreCurrencyTo, addIndex);
-                            OnCollectionChanged(
-                                new NotifyCollectionChangedEventArgs(
-                                    NotifyCollectionChangedAction.Add,
-                                    GetItemAt(addIndex),
-                                    addIndex));
-                        }
-                    }
-                }
-
-                // now raise currency changes at the end
-                RaiseCurrencyChanges(true, oldCurrentItem, oldCurrentPosition, oldIsCurrentBeforeFirst, oldIsCurrentAfterLast);
-            }
-            else if (!Contains(editItem))
-            {
-                // if the item did not belong to the collection, add it
-                InternalList.Add(editItem);
-            }
-        }
-
-        /// <summary>
-        /// Complete the transaction started by AddNew. We follow the WPF
-        /// convention in that the view's sort, filter, and paging
-        /// specifications (if any) are applied to the new item.
-        /// </summary>
-        //TODO Paging
-        public void CommitNew()
-        {
-            if (IsEditingItem)
-            {
-                throw new InvalidOperationException(GetOperationNotAllowedText(nameof(CommitNew), nameof(EditItem)));
-            }
-
-            VerifyRefreshNotDeferred();
-
-            if (CurrentAddItem == null)
-            {
-                return;
-            }
-
-            // End the AddNew transaction
-            object newItem = EndAddNew(false);
-
-            // keep track of the current item
-            object previousCurrentItem = CurrentItem;
-
-            // Modify our _trackingEnumerator so that it shows that our collection is "up to date" 
-            // and will not refresh for now.
-            _trackingEnumerator = _sourceCollection.GetEnumerator();
-
-            if (UsesLocalArray)
-            {
-                // first remove the item from the array so that we can insert into the correct position
-                int removeIndex = Count - 1;
-                int internalIndex = _internalList.IndexOf(newItem);
-                _internalList.Remove(newItem);
-
-                if (IsGrouping)
-                {
-                    _group.RemoveSpecialItem(_group.Items.Count - 1, newItem, false);
-                    if (PageSize > 0)
-                    {
-                        _temporaryGroup.RemoveSpecialItem(_temporaryGroup.Items.Count - 1, newItem, false);
-                    }
-                }
-
-                object oldCurrentItem = CurrentItem;
-                int oldCurrentPosition = CurrentPosition;
-                bool oldIsCurrentAfterLast = IsCurrentAfterLast;
-                bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
-
-                AdjustCurrencyForRemove(removeIndex);
-
-                // raise the remove event so we can next insert it into the correct place
-                OnCollectionChanged(
-                    new NotifyCollectionChangedEventArgs(
-                        NotifyCollectionChangedAction.Remove,
-                        newItem,
-                        removeIndex));
-
-                // check to see that the item will be added back in
-                bool passedFilter = PassesFilter(newItem);
-
-                // next process adding it into the correct location
-                ProcessInsertToCollection(newItem, internalIndex);
-
-                int pageStartIndex = PageIndex * PageSize;
-                int nextPageStartIndex = pageStartIndex + PageSize;
-
-                if (IsGrouping)
-                {
-                    int leafIndex = -1;
-                    if (passedFilter && PageSize > 0)
-                    {
-                        _temporaryGroup.AddToSubgroups(newItem, false /*loading*/);
-                        leafIndex = _temporaryGroup.LeafIndexOf(newItem);
-                    }
-
-                    // if we are not paging, we should just be able to add the item.
-                    // otherwise, we need to validate that it is within the current page.
-                    if (passedFilter && (PageSize == 0 ||
-                       (pageStartIndex <= leafIndex && nextPageStartIndex > leafIndex)))
-                    {
-                        _group.AddToSubgroups(newItem, false /*loading*/);
-                        int addIndex = IndexOf(newItem);
-
-                        // adjust currency to either the previous current item if possible
-                        // or to the item at the end of the list where the new item was.
-                        if (previousCurrentItem != null)
-                        {
-                            if (Contains(previousCurrentItem))
-                            {
-                                AdjustCurrencyForAdd(previousCurrentItem, addIndex);
-                            }
-                            else
-                            {
-                                AdjustCurrencyForAdd(GetItemAt(Count - 1), addIndex);
-                            }
-                        }
-
-                        OnCollectionChanged(
-                            new NotifyCollectionChangedEventArgs(
-                                NotifyCollectionChangedAction.Add,
-                                newItem,
-                                addIndex));
-                    }
-                    else
-                    {
-                        if (!passedFilter && (PageSize == 0 || OnLastLocalPage))
-                        {
-                            AdjustCurrencyForRemove(removeIndex);
-                        }
-                        else if (PageSize > 0)
-                        {
-                            int addIndex = -1;
-                            if (passedFilter && leafIndex < pageStartIndex)
-                            {
-                                // if the item was added to an earlier page, then we need to bring
-                                // in the item that would have been pushed down to this page
-                                addIndex = pageStartIndex;
-                            }
-                            else if (!OnLastLocalPage)
-                            {
-                                // if the item was added to a later page, then we need to bring in the
-                                // first item from the next page
-                                addIndex = nextPageStartIndex - 1;
-                            }
-
-                            object addItem = _temporaryGroup.LeafAt(addIndex);
-                            if (addItem != null)
-                            {
-                                _group.AddToSubgroups(addItem, false /*loading*/);
-                                addIndex = IndexOf(addItem);
-
-                                // adjust currency to either the previous current item if possible
-                                // or to the item at the end of the list where the new item was.
-                                if (previousCurrentItem != null)
-                                {
-                                    if (Contains(previousCurrentItem))
-                                    {
-                                        AdjustCurrencyForAdd(previousCurrentItem, addIndex);
-                                    }
-                                    else
-                                    {
-                                        AdjustCurrencyForAdd(GetItemAt(Count - 1), addIndex);
-                                    }
-                                }
-
-                                OnCollectionChanged(
-                                    new NotifyCollectionChangedEventArgs(
-                                        NotifyCollectionChangedAction.Add,
-                                        addItem,
-                                        addIndex));
-                            }
-                        }
-                    }
-                }
-                else
-                {
-                    // if we are still within the view
-                    int addIndex = IndexOf(newItem);
-                    if (addIndex >= 0)
-                    {
-                        AdjustCurrencyForAdd(newItem, addIndex);
-                        OnCollectionChanged(
-                            new NotifyCollectionChangedEventArgs(
-                                NotifyCollectionChangedAction.Add,
-                                newItem,
-                                addIndex));
-                    }
-                    else
-                    {
-                        if (!passedFilter && (PageSize == 0 || OnLastLocalPage))
-                        {
-                            AdjustCurrencyForRemove(removeIndex);
-                        }
-                        else if (PageSize > 0)
-                        {
-                            bool insertedToPreviousPage = InternalIndexOf(newItem) < ConvertToInternalIndex(0);
-                            addIndex = insertedToPreviousPage ? 0 : Count - 1;
-
-                            // don't fire the event if we are on the last page
-                            // and we don't have any items to bring in.
-                            if (insertedToPreviousPage || !OnLastLocalPage)
-                            {
-                                AdjustCurrencyForAdd(null, addIndex);
-                                OnCollectionChanged(
-                                    new NotifyCollectionChangedEventArgs(
-                                        NotifyCollectionChangedAction.Add,
-                                        GetItemAt(addIndex),
-                                        addIndex));
-                            }
-                        }
-                    }
-                }
-
-                // we want to fire the current changed event, even if we kept
-                // the same current item and position, since the item was
-                // removed/added back to the collection
-                RaiseCurrencyChanges(true, oldCurrentItem, oldCurrentPosition, oldIsCurrentBeforeFirst, oldIsCurrentAfterLast);
-            }
-        }
-
-        /// <summary>
-        /// Return true if the item belongs to this view.  No assumptions are
-        /// made about the item. This method will behave similarly to IList.Contains().
-        /// If the caller knows that the item belongs to the
-        /// underlying collection, it is more efficient to call PassesFilter.
-        /// </summary>
-        /// <param name="item">The item we are checking to see whether it is within the collection</param>
-        /// <returns>Boolean value of whether or not the collection contains the item</returns>
-        public bool Contains(object item)
-        {
-            EnsureCollectionInSync();
-            VerifyRefreshNotDeferred();
-            return IndexOf(item) >= 0;
-        }
-
-        /// <summary>
-        /// Enter a Defer Cycle.
-        /// Defer cycles are used to coalesce changes to the ICollectionView.
-        /// </summary>
-        /// <returns>IDisposable used to notify that we no longer need to defer, when we dispose</returns>
-        public IDisposable DeferRefresh()
-        {
-            if (IsAddingNew || IsEditingItem)
-            {
-                throw new InvalidOperationException(GetOperationNotAllowedDuringAddOrEditText(nameof(DeferRefresh)));
-            }
-
-            ++_deferLevel;
-            return new DeferHelper(this);
-        }
-
-        /// <summary>
-        /// Begins an editing transaction on the given item.  The transaction is
-        /// completed by calling either CommitEdit or CancelEdit.  Any changes made 
-        /// to the item during the transaction are considered "pending", provided 
-        /// that the view supports the notion of "pending changes" for the given item.
-        /// </summary>
-        /// <param name="item">Item we want to edit</param>
-        public void EditItem(object item)
-        {
-            VerifyRefreshNotDeferred();
-
-            if (IsAddingNew)
-            {
-                if (Object.Equals(item, CurrentAddItem))
-                {
-                    // EditItem(newItem) is a no-op
-                    return;
-                }
-
-                // implicitly close a previous AddNew
-                CommitNew();
-            }
-
-            // implicitly close a previous EditItem transaction
-            CommitEdit();
-
-            CurrentEditItem = item;
-
-            if (item is IEditableObject ieo)
-            {
-                ieo.BeginEdit();
-            }
-        }
-
-        /// <summary> 
-        /// Implementation of IEnumerable.GetEnumerator().
-        /// This provides a way to enumerate the members of the collection
-        /// without changing the currency.
-        /// </summary>
-        /// <returns>IEnumerator for the collection</returns>
-        //TODO Paging
-        public IEnumerator GetEnumerator()
-        {
-            EnsureCollectionInSync();
-            VerifyRefreshNotDeferred();
-
-            if (IsGrouping)
-            {
-                return RootGroup?.GetLeafEnumerator();
-            }
-
-            // if we are paging
-            if (PageSize > 0)
-            {
-                List<object> list = new List<object>();
-
-                // if we are in the middle of asynchronous load
-                if (PageIndex < 0)
-                {
-                    return list.GetEnumerator();
-                }
-
-                for (int index = _pageSize * PageIndex;
-                    index < (int)Math.Min(_pageSize * (PageIndex + 1), InternalList.Count);
-                    index++)
-                {
-                    list.Add(InternalList[index]);
-                }
-
-                return new NewItemAwareEnumerator(this, list.GetEnumerator(), CurrentAddItem);
-            }
-            else
-            {
-                return new NewItemAwareEnumerator(this, InternalList.GetEnumerator(), CurrentAddItem);
-            }
-        }
-
-        /// <summary>
-        /// Interface Implementation for GetEnumerator()
-        /// </summary>
-        /// <returns>IEnumerator that we get from our internal collection</returns>
-        IEnumerator IEnumerable.GetEnumerator()
-        {
-            return GetEnumerator();
-        }
-
-        /// <summary>
-        /// Retrieve item at the given zero-based index in this DataGridCollectionView, after the source collection
-        /// is filtered, sorted, and paged.
-        /// </summary>
-        /// <exception cref="ArgumentOutOfRangeException">
-        /// Thrown if index is out of range
-        /// </exception>
-        /// <param name="index">Index of the item we want to retrieve</param>
-        /// <returns>Item at specified index</returns>
-        public object GetItemAt(int index)
-        {
-            EnsureCollectionInSync();
-            VerifyRefreshNotDeferred();
-
-            // for indices larger than the count
-            if (index >= Count || index < 0)
-            {
-                throw new ArgumentOutOfRangeException(nameof(index));
-            }
-
-            if (IsGrouping)
-            {
-                return RootGroup?.LeafAt(_isUsingTemporaryGroup ? ConvertToInternalIndex(index) : index);
-            }
-
-            if (IsAddingNew && UsesLocalArray && index == Count - 1)
-            {
-                return CurrentAddItem;
-            }
-
-            return InternalItemAt(ConvertToInternalIndex(index));
-        }
-
-        /// <summary> 
-        /// Return the index where the given item appears, or -1 if doesn't appear.
-        /// </summary>
-        /// <param name="item">Item we are searching for</param>
-        /// <returns>Index of specified item</returns>
-        //TODO Paging
-        public int IndexOf(object item)
-        {
-            EnsureCollectionInSync();
-            VerifyRefreshNotDeferred();
-
-            if (IsGrouping)
-            {
-                return RootGroup?.LeafIndexOf(item) ?? -1;
-            }
-            if (IsAddingNew && Object.Equals(item, CurrentAddItem) && UsesLocalArray)
-            {
-                return Count - 1;
-            }
-
-            int internalIndex = InternalIndexOf(item);
-
-            if (PageSize > 0 && internalIndex != -1)
-            {
-                if ((internalIndex >= (PageIndex * _pageSize)) &&
-                    (internalIndex < ((PageIndex + 1) * _pageSize)))
-                {
-                    return internalIndex - (PageIndex * _pageSize);
-                }
-                else
-                {
-                    return -1;
-                }
-            }
-            else
-            {
-                return internalIndex;
-            }
-        }
-
-        /// <summary> 
-        /// Move to the given item. 
-        /// </summary>
-        /// <param name="item">Item we want to move the currency to</param>
-        /// <returns>Whether the operation was successful</returns>
-        public bool MoveCurrentTo(object item)
-        {
-            VerifyRefreshNotDeferred();
-
-            // if already on item, don't do anything
-            if (Object.Equals(CurrentItem, item))
-            {
-                // also check that we're not fooled by a false null currentItem
-                if (item != null || IsCurrentInView)
-                {
-                    return IsCurrentInView;
-                }
-            }
-
-            // if the item is not found IndexOf() will return -1, and
-            // the MoveCurrentToPosition() below will move current to BeforeFirst
-            // The IndexOf function takes into account paging, filtering, and sorting
-            return MoveCurrentToPosition(IndexOf(item));
-        }
-
-        /// <summary> 
-        /// Move to the first item. 
-        /// </summary>
-        /// <returns>Whether the operation was successful</returns>
-        public bool MoveCurrentToFirst()
-        {
-            VerifyRefreshNotDeferred();
-
-            return MoveCurrentToPosition(0);
-        }
-
-        /// <summary> 
-        /// Move to the last item. 
-        /// </summary>
-        /// <returns>Whether the operation was successful</returns>
-        public bool MoveCurrentToLast()
-        {
-            VerifyRefreshNotDeferred();
-
-            int index = Count - 1;
-
-            return MoveCurrentToPosition(index);
-        }
-
-        /// <summary> 
-        /// Move to the next item. 
-        /// </summary>
-        /// <returns>Whether the operation was successful</returns>
-        public bool MoveCurrentToNext()
-        {
-            VerifyRefreshNotDeferred();
-
-            int index = CurrentPosition + 1;
-
-            if (index <= Count)
-            {
-                return MoveCurrentToPosition(index);
-            }
-            else
-            {
-                return false;
-            }
-        }
-
-        /// <summary>
-        /// Move CurrentItem to this index
-        /// </summary>
-        /// <param name="position">Position we want to move the currency to</param>
-        /// <returns>True if the resulting CurrentItem is an item within the view; otherwise False</returns>
-        public bool MoveCurrentToPosition(int position)
-        {
-            VerifyRefreshNotDeferred();
-
-            // We want to allow the user to set the currency to just
-            // beyond the last item. EnumerableCollectionView in WPF
-            // also checks (position > Count) though the ListCollectionView
-            // looks for (position >= Count).
-            if (position < -1 || position > Count)
-            {
-                throw new ArgumentOutOfRangeException(nameof(position));
-            }
-
-            if ((position != CurrentPosition || !IsCurrentInSync)
-                && OkToChangeCurrent())
-            {
-                bool oldIsCurrentAfterLast = IsCurrentAfterLast;
-                bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
-
-                SetCurrentToPosition(position);
-                OnCurrentChanged();
-
-                if (IsCurrentAfterLast != oldIsCurrentAfterLast)
-                {
-                    OnPropertyChanged(nameof(IsCurrentAfterLast));
-                }
-
-                if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst)
-                {
-                    OnPropertyChanged(nameof(IsCurrentBeforeFirst));
-                }
-
-                OnPropertyChanged(nameof(CurrentPosition));
-                OnPropertyChanged(nameof(CurrentItem));
-            }
-
-            return IsCurrentInView;
-        }
-
-        /// <summary> 
-        /// Move to the previous item. 
-        /// </summary>
-        /// <returns>Whether the operation was successful</returns>
-        public bool MoveCurrentToPrevious()
-        {
-            VerifyRefreshNotDeferred();
-
-            int index = CurrentPosition - 1;
-
-            if (index >= -1)
-            {
-                return MoveCurrentToPosition(index);
-            }
-            else
-            {
-                return false;
-            }
-        }
-
-        /// <summary>
-        /// Moves to the first page.
-        /// </summary>
-        /// <returns>Whether or not the move was successful.</returns>
-        //TODO Paging
-        public bool MoveToFirstPage()
-        {
-            return MoveToPage(0);
-        }
-
-        /// <summary>
-        /// Moves to the last page.
-        /// The move is only attempted when TotalItemCount is known.
-        /// </summary>
-        /// <returns>Whether or not the move was successful.</returns>
-        //TODO Paging
-        public bool MoveToLastPage()
-        {
-            if (TotalItemCount != -1 && PageSize > 0)
-            {
-                return MoveToPage(PageCount - 1);
-            }
-            else
-            {
-                return false;
-            }
-        }
-
-        /// <summary>
-        /// Moves to the page after the current page we are on.
-        /// </summary>
-        /// <returns>Whether or not the move was successful.</returns>
-        //TODO Paging
-        public bool MoveToNextPage()
-        {
-            return MoveToPage(_pageIndex + 1);
-        }
-
-        /// <summary>
-        /// Requests a page move to page <paramref name="pageIndex"/>.
-        /// </summary>
-        /// <param name="pageIndex">Index of the target page</param>
-        /// <returns>Whether or not the move was successfully initiated.</returns>
-        //TODO Paging
-        public bool MoveToPage(int pageIndex)
-        {
-            // Boundary checks for negative pageIndex
-            if (pageIndex < -1)
-            {
-                return false;
-            }
-
-            // if the Refresh is deferred, cache the requested PageIndex so that we
-            // can move to the desired page when EndDefer is called.
-            if (IsRefreshDeferred)
-            {
-                // set cached value and flag so that we move to the page on EndDefer
-                _cachedPageIndex = pageIndex;
-                SetFlag(CollectionViewFlags.IsMoveToPageDeferred, true);
-                return false;
-            }
-
-            // check for invalid pageIndex
-            if (pageIndex == -1 && PageSize > 0)
-            {
-                return false;
-            }
-
-            // Check if the target page is out of bound, or equal to the current page
-            if (pageIndex >= PageCount || _pageIndex == pageIndex)
-            {
-                return false;
-            }
-
-            // Check with the ICollectionView.CurrentChanging listeners if it's OK to move
-            // on to another page
-            if (!OkToChangeCurrent())
-            {
-                return false;
-            }
-
-            if (RaisePageChanging(pageIndex) && pageIndex != -1)
-            {
-                // Page move was cancelled. Abort the move, but only if the target index isn't -1.
-                return false;
-            }
-
-            // Check if there is a current edited or new item so changes can be committed first.
-            if (CurrentAddItem != null || CurrentEditItem != null)
-            {
-                // Remember current currency values for upcoming OnPropertyChanged notifications
-                object oldCurrentItem = CurrentItem;
-                int oldCurrentPosition = CurrentPosition;
-                bool oldIsCurrentAfterLast = IsCurrentAfterLast;
-                bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
-
-                // Currently CommitNew()/CommitEdit()/CancelNew()/CancelEdit() can't handle committing or 
-                // cancelling an item that is no longer on the current page. That's acceptable and means that
-                // the potential _newItem or _editItem needs to be committed before this page move.
-                // The reason why we temporarily reset currency here is to give a chance to the bound
-                // controls to commit or cancel their potential edits/addition. The DataForm calls ForceEndEdit()
-                // for example as a result of changing currency.
-                SetCurrentToPosition(-1);
-                RaiseCurrencyChanges(true /*fireChangedEvent*/, oldCurrentItem, oldCurrentPosition, oldIsCurrentBeforeFirst, oldIsCurrentAfterLast);
-
-                // If the bound controls did not successfully end their potential item editing/addition, the 
-                // page move needs to be aborted. 
-                if (CurrentAddItem != null || CurrentEditItem != null)
-                {
-                    // Since PageChanging was raised and not cancelled, a PageChanged notification needs to be raised
-                    // even though the PageIndex actually did not change.
-                    RaisePageChanged();
-
-                    // Restore original currency
-                    Debug.Assert(CurrentItem == null, "Unexpected CurrentItem != null");
-                    Debug.Assert(CurrentPosition == -1, "Unexpected CurrentPosition != -1");
-                    Debug.Assert(IsCurrentBeforeFirst, "Unexpected IsCurrentBeforeFirst == false");
-                    Debug.Assert(!IsCurrentAfterLast, "Unexpected IsCurrentAfterLast == true");
-
-                    SetCurrentToPosition(oldCurrentPosition);
-                    RaiseCurrencyChanges(false /*fireChangedEvent*/, null /*oldCurrentItem*/, -1 /*oldCurrentPosition*/,
-                        true /*oldIsCurrentBeforeFirst*/, false /*oldIsCurrentAfterLast*/);
-
-                    return false;
-                }
-
-                // Finally raise a CurrentChanging notification for the upcoming currency change
-                // that will occur in CompletePageMove(pageIndex).
-                OnCurrentChanging();
-            }
-
-            IsPageChanging = true;
-            CompletePageMove(pageIndex);
-
-            return true;
-        }
-
-        /// <summary>
-        /// Moves to the page before the current page we are on.
-        /// </summary>
-        /// <returns>Whether or not the move was successful.</returns>
-        //TODO Paging
-        public bool MoveToPreviousPage()
-        {
-            return MoveToPage(_pageIndex - 1);
-        }
-
-        /// <summary>
-        /// Return true if the item belongs to this view.  The item is assumed to belong to the
-        /// underlying DataCollection;  this method merely takes filters into account.
-        /// It is commonly used during collection-changed notifications to determine if the added/removed
-        /// item requires processing.
-        /// Returns true if no filter is set on collection view.
-        /// </summary>
-        /// <param name="item">The item to compare against the Filter</param>
-        /// <returns>Whether the item passes the filter</returns>
-        public bool PassesFilter(object item)
-        {
-            if (Filter != null)
-            {
-                return Filter(item);
-            }
-
-            return true;
-        }
-
-        /// <summary>
-        /// Re-create the view, using any SortDescriptions and/or Filters.
-        /// </summary>
-        public void Refresh()
-        {
-            if (this is IDataGridEditableCollectionView ecv && (ecv.IsAddingNew || ecv.IsEditingItem))
-            {
-                throw new InvalidOperationException(GetOperationNotAllowedDuringAddOrEditText(nameof(Refresh)));
-            }
-
-            RefreshInternal();
-        }
-
-        /// <summary>
-        /// Remove the given item from the underlying collection. It
-        /// needs to be in the current filtered, sorted, and paged view
-        /// to call 
-        /// </summary>
-        /// <param name="item">Item we want to remove</param>
-        public void Remove(object item)
-        {
-            int index = IndexOf(item);
-            if (index >= 0)
-            {
-                RemoveAt(index);
-            }
-        }
-
-        /// <summary>
-        /// Remove the item at the given index from the underlying collection.
-        /// The index is interpreted with respect to the view (filtered, sorted,
-        /// and paged list).
-        /// </summary>
-        /// <param name="index">Index of the item we want to remove</param>
-        //TODO Paging
-        public void RemoveAt(int index)
-        {
-            if (index < 0 || index >= Count)
-            {
-                throw new ArgumentOutOfRangeException(nameof(index), "Index was out of range. Must be non-negative and less than the size of the collection.");
-            }
-
-            if (IsEditingItem || IsAddingNew)
-            {
-                throw new InvalidOperationException(GetOperationNotAllowedDuringAddOrEditText(nameof(RemoveAt)));
-            }
-            else if (!CanRemove)
-            {
-                throw new InvalidOperationException("Remove/RemoveAt is not supported.");
-            }
-
-            VerifyRefreshNotDeferred();
-
-            // convert the index from "view-relative" to "list-relative"
-            object item = GetItemAt(index);
-
-            // before we remove the item, see if we are not on the last page
-            // and will have to bring in a new item to replace it
-            bool replaceItem = PageSize > 0 && !OnLastLocalPage;
-
-            try
-            {
-                // temporarily disable the CollectionChanged event
-                // handler so filtering, sorting, or grouping
-                // doesn't get applied yet
-                SetFlag(CollectionViewFlags.ShouldProcessCollectionChanged, false);
-
-                if (SourceList != null)
-                {
-                    SourceList.Remove(item);
-                }
-            }
-            finally
-            {
-                SetFlag(CollectionViewFlags.ShouldProcessCollectionChanged, true);
-            }
-
-            // Modify our _trackingEnumerator so that it shows that our collection is "up to date" 
-            // and will not refresh for now.
-            _trackingEnumerator = _sourceCollection.GetEnumerator();
-
-            Debug.Assert(index == IndexOf(item), "IndexOf returned unexpected value");
-
-            // remove the item from the internal list
-            _internalList.Remove(item);
-
-            if (IsGrouping)
-            {
-                if (PageSize > 0)
-                {
-                    _temporaryGroup.RemoveFromSubgroups(item);
-                }
-                _group.RemoveFromSubgroups(item);
-            }
-
-            object oldCurrentItem = CurrentItem;
-            int oldCurrentPosition = CurrentPosition;
-            bool oldIsCurrentAfterLast = IsCurrentAfterLast;
-            bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
-
-            AdjustCurrencyForRemove(index);
-
-            // fire remove notification
-            OnCollectionChanged(
-                new NotifyCollectionChangedEventArgs(
-                    NotifyCollectionChangedAction.Remove,
-                    item,
-                    index));
-
-            RaiseCurrencyChanges(false, oldCurrentItem, oldCurrentPosition, oldIsCurrentBeforeFirst, oldIsCurrentAfterLast);
-
-            // if we removed all items from the current page,
-            // move to the previous page. we do not need to 
-            // fire additional notifications, as moving the page will
-            // trigger a reset.
-            if (NeedToMoveToPreviousPage)
-            {
-                MoveToPreviousPage();
-                return;
-            }
-
-            // if we are paging, we may have to fire another notification for the item
-            // that needs to replace the one we removed on this page.
-            if (replaceItem)
-            {
-                // we first need to add the item into the current group
-                if (IsGrouping)
-                {
-                    object newItem = _temporaryGroup.LeafAt((PageSize * (PageIndex + 1)) - 1);
-                    if (newItem != null)
-                    {
-                        _group.AddToSubgroups(newItem, loading: false);
-                    }
-                }
-
-                // fire the add notification
-                OnCollectionChanged(
-                    new NotifyCollectionChangedEventArgs(
-                        NotifyCollectionChangedAction.Add,
-                        GetItemAt(PageSize - 1),
-                        PageSize - 1));
-            }
-        }
-
-        /// <summary>
-        /// Helper for SortList to handle nested properties (e.g. Address.Street)
-        /// </summary>
-        /// <param name="item">parent object</param>
-        /// <param name="propertyPath">property names path</param>
-        /// <param name="propertyType">property type that we want to check for</param>
-        /// <returns>child object</returns>
-        private static object InvokePath(object item, string propertyPath, Type propertyType)
-        {
-            object propertyValue = TypeHelper.GetNestedPropertyValue(item, propertyPath, propertyType, out Exception exception);
-            if (exception != null)
-            {
-                throw exception;
-            }
-            return propertyValue;
-        }
-
-        /// <summary>
-        /// Fix up CurrentPosition and CurrentItem after a collection change
-        /// </summary>
-        /// <param name="newCurrentItem">Item that we want to set currency to</param>
-        /// <param name="index">Index of item involved in the collection change</param>
-        private void AdjustCurrencyForAdd(object newCurrentItem, int index)
-        {
-            if (newCurrentItem != null)
-            {
-                int newItemIndex = IndexOf(newCurrentItem);
-
-                // if we already have the correct currency set, we don't 
-                // want to unnecessarily fire events
-                if (newItemIndex >= 0 && (newItemIndex != CurrentPosition || !IsCurrentInSync))
-                {
-                    OnCurrentChanging();
-                    SetCurrent(newCurrentItem, newItemIndex);
-                }
-                return;
-            }
-
-            if (Count == 1)
-            {
-                if (CurrentItem != null || CurrentPosition != -1)
-                {
-                    // fire current changing notification
-                    OnCurrentChanging();
-                }
-
-                // added first item; set current at BeforeFirst
-                SetCurrent(null, -1);
-            }
-            else if (index <= CurrentPosition)
-            {
-                // fire current changing notification
-                OnCurrentChanging();
-
-                // adjust current index if insertion is earlier
-                int newPosition = CurrentPosition + 1;
-                if (newPosition >= Count)
-                {
-                    // if currency was on last item and it got shifted up,
-                    // keep currency on last item.
-                    newPosition = Count - 1;
-                }
-                SetCurrent(GetItemAt(newPosition), newPosition);
-            }
-        }
-
-        /// <summary>
-        /// Fix up CurrentPosition and CurrentItem after a collection change
-        /// </summary>
-        /// <param name="newCurrentItem">Item that we want to set currency to</param>
-        /// <param name="index">Index of item involved in the collection change</param>
-        private void AdjustCurrencyForEdit(object newCurrentItem, int index)
-        {
-            if (newCurrentItem != null && IndexOf(newCurrentItem) >= 0)
-            {
-                OnCurrentChanging();
-                SetCurrent(newCurrentItem, IndexOf(newCurrentItem));
-                return;
-            }
-
-            if (index <= CurrentPosition)
-            {
-                // fire current changing notification
-                OnCurrentChanging();
-
-                // adjust current index if insertion is earlier
-                int newPosition = CurrentPosition + 1;
-                if (newPosition < Count)
-                {
-                    // CurrentItem might be out of sync if underlying list is not INCC
-                    // or if this Add is the result of a Replace (Rem + Add)
-                    SetCurrent(GetItemAt(newPosition), newPosition);
-                }
-                else
-                {
-                    SetCurrent(null, Count);
-                }
-            }
-        }
-
-        /// <summary>
-        /// Fix up CurrentPosition and CurrentItem after a collection change
-        /// The index can be -1 if the item was removed from a previous page
-        /// </summary>
-        /// <param name="index">Index of item involved in the collection change</param>
-        private void AdjustCurrencyForRemove(int index)
-        {
-            // adjust current index if deletion is earlier
-            if (index < CurrentPosition)
-            {
-                // fire current changing notification
-                OnCurrentChanging();
-
-                SetCurrent(CurrentItem, CurrentPosition - 1);
-            }
-
-            // adjust current index if > Count
-            if (CurrentPosition >= Count)
-            {
-                // fire current changing notification
-                OnCurrentChanging();
-
-                SetCurrentToPosition(Count - 1);
-            }
-
-            // make sure that current position and item are in sync
-            if (!IsCurrentInSync)
-            {
-                // fire current changing notification
-                OnCurrentChanging();
-
-                SetCurrentToPosition(CurrentPosition);
-            }
-        }
-
-        /// <summary>
-        /// Returns true if specified flag in flags is set.
-        /// </summary>
-        /// <param name="flags">Flag we are checking for</param>
-        /// <returns>Whether the specified flag is set</returns>
-        private bool CheckFlag(CollectionViewFlags flags)
-        {
-            return _flags.HasAllFlags(flags);
-        }
-
-        /// <summary>
-        /// Called to complete the page move operation to set the
-        /// current page index.
-        /// </summary>
-        /// <param name="pageIndex">Final page index</param>
-        //TODO Paging
-        private void CompletePageMove(int pageIndex)
-        {
-            Debug.Assert(_pageIndex != pageIndex, "Unexpected _pageIndex == pageIndex");
-
-            // to see whether or not to fire an OnPropertyChanged
-            int oldCount = Count;
-            object oldCurrentItem = CurrentItem;
-            int oldCurrentPosition = CurrentPosition;
-            bool oldIsCurrentAfterLast = IsCurrentAfterLast;
-            bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
-
-            _pageIndex = pageIndex;
-
-            // update the groups
-            if (IsGrouping && PageSize > 0)
-            {
-                PrepareGroupsForCurrentPage();
-            }
-
-            // update currency
-            if (Count >= 1)
-            {
-                SetCurrent(GetItemAt(0), 0);
-            }
-            else
-            {
-                SetCurrent(null, -1);
-            }
-
-            IsPageChanging = false;
-            OnPropertyChanged(nameof(PageIndex));
-            RaisePageChanged();
-
-            // if the count has changed
-            if (Count != oldCount)
-            {
-                OnPropertyChanged(nameof(Count));
-            }
-
-            OnCollectionChanged(
-                new NotifyCollectionChangedEventArgs(
-                    NotifyCollectionChangedAction.Reset));
-
-            // Always raise CurrentChanged since the calling method MoveToPage(pageIndex) raised CurrentChanging.
-            RaiseCurrencyChanges(true /*fireChangedEvent*/, oldCurrentItem, oldCurrentPosition, oldIsCurrentBeforeFirst, oldIsCurrentAfterLast);
-        }
-
-        /// <summary>
-        /// Convert a value for the index passed in to the index it would be
-        /// relative to the InternalIndex property.
-        /// </summary>
-        /// <param name="index">Index to convert</param>
-        /// <returns>Value for the InternalIndex</returns>
-        //TODO Paging
-        private int ConvertToInternalIndex(int index)
-        {
-            Debug.Assert(index > -1, "Unexpected index == -1");
-            if (PageSize > 0)
-            {
-                return (_pageSize * PageIndex) + index;
-            }
-            else
-            {
-                return index;
-            }
-        }
-
-        /// <summary>
-        /// Copy all items from the source collection to the internal list for processing.
-        /// </summary>
-        private void CopySourceToInternalList()
-        {
-            _internalList = new List<object>();
-
-            IEnumerator enumerator = SourceCollection.GetEnumerator();
-
-            while (enumerator.MoveNext())
-            {
-                _internalList.Add(enumerator.Current);
-            }
-        }
-
-        /// <summary>
-        /// Common functionality used by CommitNew, CancelNew, and when the
-        /// new item is removed by Remove or Refresh.
-        /// </summary>
-        /// <param name="cancel">Whether we canceled the add</param>
-        /// <returns>The new item we ended adding</returns>
-        private object EndAddNew(bool cancel)
-        {
-            object newItem = CurrentAddItem;
-
-            CurrentAddItem = null;    // leave "adding-new" mode
-
-            if (newItem is IEditableObject ieo)
-            {
-                if (cancel)
-                {
-                    ieo.CancelEdit();
-                }
-                else
-                {
-                    ieo.EndEdit();
-                }
-            }
-
-            return newItem;
-        }
-
-        /// <summary>
-        /// Subtracts from the deferLevel counter and calls Refresh() if there are no other defers
-        /// </summary>
-        private void EndDefer()
-        {
-            --_deferLevel;
-
-            if (_deferLevel == 0)
-            {
-                if (CheckFlag(CollectionViewFlags.IsUpdatePageSizeDeferred))
-                {
-                    SetFlag(CollectionViewFlags.IsUpdatePageSizeDeferred, false);
-                    PageSize = _cachedPageSize;
-                }
-
-                if (CheckFlag(CollectionViewFlags.IsMoveToPageDeferred))
-                {
-                    SetFlag(CollectionViewFlags.IsMoveToPageDeferred, false);
-                    MoveToPage(_cachedPageIndex);
-                    _cachedPageIndex = -1;
-                }
-
-                if (CheckFlag(CollectionViewFlags.NeedsRefresh))
-                {
-                    Refresh();
-                }
-            }
-        }
-
-        /// <summary>
-        /// Makes sure that the ItemConstructor is set for the correct type
-        /// </summary>
-        private void EnsureItemConstructor()
-        {
-            if (!_itemConstructorIsValid)
-            {
-                Type itemType = ItemType;
-                if (itemType != null)
-                {
-                    _itemConstructor = itemType.GetConstructor(Type.EmptyTypes);
-                    _itemConstructorIsValid = true;
-                }
-            }
-        }
-
-        /// <summary>
-        ///  If the IEnumerable has changed, bring the collection up to date.
-        ///  (This isn't necessary if the IEnumerable is also INotifyCollectionChanged
-        ///  because we keep the collection in sync incrementally.)
-        /// </summary>
-        private void EnsureCollectionInSync()
-        {
-            // if the IEnumerable is not a INotifyCollectionChanged
-            if (_pollForChanges)
-            {
-                try
-                {
-                    _trackingEnumerator.MoveNext();
-                }
-                catch (InvalidOperationException)
-                {
-                    // When the collection has been modified, calling MoveNext()
-                    // on the enumerator throws an InvalidOperationException, stating
-                    // that the collection has been modified. Therefore, we know when
-                    // to update our internal collection.
-                    _trackingEnumerator = SourceCollection.GetEnumerator();
-                    RefreshOrDefer();
-                }
-            }
-        }
-
-        /// <summary>
-        /// Helper function used to determine the type of an item
-        /// </summary>
-        /// <param name="useRepresentativeItem">Whether we should use a representative item</param>
-        /// <returns>The type of the items in the collection</returns>
-        private Type GetItemType(bool useRepresentativeItem)
-        {
-            Type collectionType = SourceCollection.GetType();
-            Type[] interfaces = collectionType.GetInterfaces();
-
-            // Look for IEnumerable<T>.  All generic collections should implement
-            //   We loop through the interface list, rather than call
-            // GetInterface(IEnumerableT), so that we handle an ambiguous match
-            // (by using the first match) without an exception.
-            for (int i = 0; i < interfaces.Length; ++i)
-            {
-                Type interfaceType = interfaces[i];
-                if (interfaceType.Name == typeof(IEnumerable<>).Name)
-                {
-                    // found IEnumerable<>, extract T
-                    Type[] typeParameters = interfaceType.GetGenericArguments();
-                    if (typeParameters.Length == 1)
-                    {
-                        return typeParameters[0];
-                    }
-                }
-            }
-
-            // No generic information found.  Use a representative item instead.
-            if (useRepresentativeItem)
-            {
-                // get type of a representative item
-                object item = GetRepresentativeItem();
-                if (item != null)
-                {
-                    return item.GetType();
-                }
-            }
-
-            return null;
-        }
-
-        /// <summary>
-        /// Gets a representative item from the collection
-        /// </summary>
-        /// <returns>An item that can represent the collection</returns>
-        private object GetRepresentativeItem()
-        {
-            if (IsEmpty)
-            {
-                return null;
-            }
-
-            IEnumerator enumerator = GetEnumerator();
-            while (enumerator.MoveNext())
-            {
-                object item = enumerator.Current;
-                // Since this collection view does not support a NewItemPlaceholder, 
-                // simply return the first non-null item.
-                if (item != null)
-                {
-                    return item;
-                }
-            }
-
-            return null;
-        }
-
-        /// <summary>
-        /// Return index of item in the internal list.
-        /// </summary>
-        /// <param name="item">The item we are checking</param>
-        /// <returns>Integer value on where in the InternalList the object is located</returns>
-        private int InternalIndexOf(object item)
-        {
-            return InternalList.IndexOf(item);
-        }
-
-        /// <summary>
-        /// Return item at the given index in the internal list.
-        /// </summary>
-        /// <param name="index">The index we are checking</param>
-        /// <returns>The item at the specified index</returns>
-        private object InternalItemAt(int index)
-        {
-            if (index >= 0 && index < InternalList.Count)
-            {
-                return InternalList[index];
-            }
-            else
-            {
-                return null;
-            }
-        }
-
-        /// <summary>
-        /// Ask listeners (via ICollectionView.CurrentChanging event) if it's OK to change currency
-        /// </summary>
-        /// <returns>False if a listener cancels the change, True otherwise</returns>
-        private bool OkToChangeCurrent()
-        {
-            DataGridCurrentChangingEventArgs args = new DataGridCurrentChangingEventArgs();
-            OnCurrentChanging(args);
-            return !args.Cancel;
-        }
-
-        /// <summary>
-        ///     Notify listeners that this View has changed
-        /// </summary>
-        /// <remarks>
-        ///     CollectionViews (and sub-classes) should take their filter/sort/grouping/paging
-        ///     into account before calling this method to forward CollectionChanged events.
-        /// </remarks>
-        /// <param name="args">
-        ///     The NotifyCollectionChangedEventArgs to be passed to the EventHandler
-        /// </param>
-        //TODO Paging
-        private void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
-        {
-            if (args == null)
-            {
-                throw new ArgumentNullException(nameof(args));
-            }
-
-            unchecked
-            {
-                // invalidate enumerators because of a change
-                ++_timestamp;
-            }
-
-            if (CollectionChanged != null)
-            {
-                if (args.Action != NotifyCollectionChangedAction.Add || PageSize == 0 || args.NewStartingIndex < Count)
-                {
-                    CollectionChanged(this, args);
-                }
-            }
-
-            // Collection changes change the count unless an item is being
-            // replaced within the collection.
-            if (args.Action != NotifyCollectionChangedAction.Replace)
-            {
-                OnPropertyChanged(nameof(Count));
-            }
-
-            bool listIsEmpty = IsEmpty;
-            if (listIsEmpty != CheckFlag(CollectionViewFlags.CachedIsEmpty))
-            {
-                SetFlag(CollectionViewFlags.CachedIsEmpty, listIsEmpty);
-                OnPropertyChanged(nameof(IsEmpty));
-            }
-        }
-
-        /// <summary>
-        /// Raises the CurrentChanged event
-        /// </summary>
-        private void OnCurrentChanged()
-        {
-            if (CurrentChanged != null && _currentChangedMonitor.Enter())
-            {
-                using (_currentChangedMonitor)
-                {
-                    CurrentChanged(this, EventArgs.Empty);
-                }
-            }
-        }
-
-        /// <summary>
-        /// Raise a CurrentChanging event that is not cancelable.
-        /// This is called by CollectionChanges (Add, Remove, and Refresh) that 
-        /// affect the CurrentItem.
-        /// </summary>
-        /// <exception cref="InvalidOperationException">
-        /// This CurrentChanging event cannot be canceled.
-        /// </exception>
-        private void OnCurrentChanging()
-        {
-            OnCurrentChanging(uncancelableCurrentChangingEventArgs);
-        }
-
-        /// <summary>
-        /// Raises the CurrentChanging event
-        /// </summary>
-        /// <param name="args">
-        ///     CancelEventArgs used by the consumer of the event.  args.Cancel will
-        ///     be true after this call if the CurrentItem should not be changed for
-        ///     any reason.
-        /// </param>
-        /// <exception cref="InvalidOperationException">
-        ///     This CurrentChanging event cannot be canceled.
-        /// </exception>
-        private void OnCurrentChanging(DataGridCurrentChangingEventArgs args)
-        {
-            if (args == null)
-            {
-                throw new ArgumentNullException(nameof(args));
-            }
-
-            if (_currentChangedMonitor.Busy)
-            {
-                if (args.IsCancelable)
-                {
-                    args.Cancel = true;
-                }
-
-                return;
-            }
-
-            CurrentChanging?.Invoke(this, args);
-        }
-
-        /// <summary>
-        /// GroupBy changed handler
-        /// </summary>
-        /// <param name="sender">CollectionViewGroup whose GroupBy has changed</param>
-        /// <param name="e">Arguments for the NotifyCollectionChanged event</param>
-        private void OnGroupByChanged(object sender, NotifyCollectionChangedEventArgs e)
-        {
-            if (IsAddingNew || IsEditingItem)
-            {
-                throw new InvalidOperationException(GetOperationNotAllowedDuringAddOrEditText("Grouping"));
-            }
-
-            RefreshOrDefer();
-        }
-
-        /// <summary>
-        /// GroupDescription changed handler
-        /// </summary>
-        /// <param name="sender">CollectionViewGroup whose GroupDescription has changed</param>
-        /// <param name="e">Arguments for the GroupDescriptionChanged event</param>
-        //TODO Paging
-        private void OnGroupDescriptionChanged(object sender, EventArgs e)
-        {
-            if (IsAddingNew || IsEditingItem)
-            {
-                throw new InvalidOperationException(GetOperationNotAllowedDuringAddOrEditText("Grouping"));
-            }
-
-            // we want to make sure that the data is refreshed before we try to move to a page
-            // since the refresh would take care of the filtering, sorting, and grouping.
-            RefreshOrDefer();
-
-            if (PageSize > 0)
-            {
-                if (IsRefreshDeferred)
-                {
-                    // set cached value and flag so that we move to first page on EndDefer
-                    _cachedPageIndex = 0;
-                    SetFlag(CollectionViewFlags.IsMoveToPageDeferred, true);
-                }
-                else
-                {
-                    MoveToFirstPage();
-                }
-            }
-        }
-
-        /// <summary>
-        /// Raises a PropertyChanged event.
-        /// </summary>
-        /// <param name="e">PropertyChangedEventArgs for this change</param>
-        private void OnPropertyChanged(PropertyChangedEventArgs e)
-        {
-            PropertyChanged?.Invoke(this, e);
-        }
-
-        /// <summary>
-        /// Helper to raise a PropertyChanged event.
-        /// </summary>
-        /// <param name="propertyName">Property name for the property that changed</param>
-        private void OnPropertyChanged(string propertyName)
-        {
-            OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
-        }
-
-        /// <summary>
-        /// Sets up the ActiveComparer for the CollectionViewGroupRoot specified
-        /// </summary>
-        /// <param name="groupRoot">The CollectionViewGroupRoot</param>
-        private void PrepareGroupingComparer(CollectionViewGroupRoot groupRoot)
-        {
-            if (groupRoot == _temporaryGroup || PageSize == 0)
-            {
-                if (groupRoot.ActiveComparer is DataGridCollectionViewGroupInternal.ListComparer listComparer)
-                {
-                    listComparer.ResetList(InternalList);
-                }
-                else
-                {
-                    groupRoot.ActiveComparer = new DataGridCollectionViewGroupInternal.ListComparer(InternalList);
-                }
-            }
-            else if (groupRoot == _group)
-            {
-                // create the new comparer based on the current _temporaryGroup
-                groupRoot.ActiveComparer = new DataGridCollectionViewGroupInternal.CollectionViewGroupComparer(_temporaryGroup);
-            }
-        }
-
-        /// <summary>
-        /// Use the GroupDescriptions to place items into their respective groups.
-        /// This assumes that there is no paging, so we just group the entire collection
-        /// of items that the CollectionView holds.
-        /// </summary>
-        private void PrepareGroups()
-        {
-            // we should only use this method if we aren't paging
-            Debug.Assert(PageSize == 0, "Unexpected PageSize != 0");
-
-            _group.Clear();
-            _group.Initialize();
-
-            _group.IsDataInGroupOrder = CheckFlag(CollectionViewFlags.IsDataInGroupOrder);
-
-            // set to false so that we access internal collection items
-            // instead of the group items, as they have been cleared
-            _isGrouping = false;
-
-            if (_group.GroupDescriptions.Count > 0)
-            {
-                for (int num = 0, count = _internalList.Count; num < count; ++num)
-                {
-                    object item = _internalList[num];
-                    if (item != null && (!IsAddingNew || !object.Equals(CurrentAddItem, item)))
-                    {
-                        _group.AddToSubgroups(item, loading: true);
-                    }
-                }
-                if (IsAddingNew)
-                {
-                    _group.InsertSpecialItem(_group.Items.Count, CurrentAddItem, true);
-                }
-            }
-
-            _isGrouping = _group.GroupBy != null;
-
-            // now we set the value to false, so that subsequent adds will insert
-            // into the correct groups.
-            _group.IsDataInGroupOrder = false;
-
-            // reset the grouping comparer
-            PrepareGroupingComparer(_group);
-        }
-
-        /// <summary>
-        /// Use the GroupDescriptions to place items into their respective groups.
-        /// Because of the fact that we have paging, it is possible that we are only
-        /// going to need a subset of the items to be displayed. However, before we 
-        /// actually group the entire collection, we can't display the items in the
-        /// correct order. We therefore want to just create a temporary group with
-        /// the entire collection, and then using this data we can create the group
-        /// that is exposed with just the items we need.
-        /// </summary>
-        private void PrepareTemporaryGroups()
-        {
-            _temporaryGroup = new CollectionViewGroupRoot(this, CheckFlag(CollectionViewFlags.IsDataInGroupOrder));
-
-            foreach (var gd in _group.GroupDescriptions)
-            {
-                _temporaryGroup.GroupDescriptions.Add(gd);
-            }
-
-            _temporaryGroup.Initialize();
-
-            // set to false so that we access internal collection items
-            // instead of the group items, as they have been cleared
-            _isGrouping = false;
-
-            if (_temporaryGroup.GroupDescriptions.Count > 0)
-            {
-                for (int num = 0, count = _internalList.Count; num < count; ++num)
-                {
-                    object item = _internalList[num];
-                    if (item != null && (!IsAddingNew || !object.Equals(CurrentAddItem, item)))
-                    {
-                        _temporaryGroup.AddToSubgroups(item, loading: true);
-                    }
-                }
-                if (IsAddingNew)
-                {
-                    _temporaryGroup.InsertSpecialItem(_temporaryGroup.Items.Count, CurrentAddItem, true);
-                }
-            }
-
-            _isGrouping = _temporaryGroup.GroupBy != null;
-
-            // reset the grouping comparer
-            PrepareGroupingComparer(_temporaryGroup);
-        }
-
-        /// <summary>
-        /// Update our Groups private accessor to point to the subset of data
-        /// covered by the current page, or to display the entire group if paging is not
-        /// being used.
-        /// </summary>
-        //TODO Paging
-        private void PrepareGroupsForCurrentPage()
-        {
-            _group.Clear();
-            _group.Initialize();
-
-            // set to indicate that we will be pulling data from the temporary group data
-            _isUsingTemporaryGroup = true;
-
-            // since we are getting our data from the temporary group, it should
-            // already be in group order
-            _group.IsDataInGroupOrder = true;
-            _group.ActiveComparer = null;
-
-            if (GroupDescriptions.Count > 0)
-            {
-                for (int num = 0, count = Count; num < count; ++num)
-                {
-                    object item = GetItemAt(num);
-                    if (item != null && (!IsAddingNew || !object.Equals(CurrentAddItem, item)))
-                    {
-                        _group.AddToSubgroups(item, loading: true);
-                    }
-                }
-                if (IsAddingNew)
-                {
-                    _group.InsertSpecialItem(_group.Items.Count, CurrentAddItem, true);
-                }
-            }
-
-            // set flag to indicate that we do not need to access the temporary data any longer
-            _isUsingTemporaryGroup = false;
-
-            // now we set the value to false, so that subsequent adds will insert
-            // into the correct groups.
-            _group.IsDataInGroupOrder = false;
-
-            // reset the grouping comparer
-            PrepareGroupingComparer(_group);
-
-            _isGrouping = _group.GroupBy != null;
-        }
-
-        /// <summary>
-        /// Create, filter and sort the local index array.
-        /// called from Refresh(), override in derived classes as needed.
-        /// </summary>
-        /// <param name="enumerable">new IEnumerable to associate this view with</param>
-        /// <returns>new local array to use for this view</returns>
-        private IList PrepareLocalArray(IEnumerable enumerable)
-        {
-            Debug.Assert(enumerable != null, "Input list to filter/sort should not be null");
-
-            // filter the collection's array into the local array
-            List<object> localList = new List<object>();
-
-            foreach (object item in enumerable)
-            {
-                if (Filter == null || PassesFilter(item))
-                {
-                    localList.Add(item);
-                }
-            }
-
-            // sort the local array
-            if (!CheckFlag(CollectionViewFlags.IsDataSorted) && SortDescriptions.Count > 0)
-            {
-                localList = SortList(localList);
-            }
-
-            return localList;
-        }
-
-        /// <summary>
-        /// Process an Add operation from an INotifyCollectionChanged event handler.
-        /// </summary>
-        /// <param name="addedItem">Item added to the source collection</param>
-        /// <param name="addIndex">Index item was added into</param>
-        //TODO Paging
-        private void ProcessAddEvent(object addedItem, int addIndex)
-        {
-            // item to fire remove notification for if necessary
-            object removeNotificationItem = null;
-            if (PageSize > 0 && !IsGrouping)
-            {
-                removeNotificationItem = (Count == PageSize) ?
-                    GetItemAt(PageSize - 1) : null;
-            }
-
-            // process the add by filtering and sorting the item
-            ProcessInsertToCollection(
-                addedItem,
-                addIndex);
-
-            // next check if we need to add an item into the current group
-            bool needsGrouping = false;
-            if (Count == 1 && GroupDescriptions.Count > 0)
-            {
-                // if this is the first item being added
-                // we want to setup the groups with the
-                // correct element type comparer
-                if (PageSize > 0)
-                {
-                    PrepareGroupingComparer(_temporaryGroup);
-                }
-                PrepareGroupingComparer(_group);
-            }
-
-            if (IsGrouping)
-            {
-                int leafIndex = -1;
-
-                if (PageSize > 0)
-                {
-                    _temporaryGroup.AddToSubgroups(addedItem, false /*loading*/);
-                    leafIndex = _temporaryGroup.LeafIndexOf(addedItem);
-                }
-
-                // if we are not paging, we should just be able to add the item.
-                // otherwise, we need to validate that it is within the current page.
-                if (PageSize == 0 || (PageIndex + 1) * PageSize > leafIndex)
-                {
-                    needsGrouping = true;
-
-                    int pageStartIndex = PageIndex * PageSize;
-
-                    // if the item was inserted on a previous page
-                    if (pageStartIndex > leafIndex && PageSize > 0)
-                    {
-                        addedItem = _temporaryGroup.LeafAt(pageStartIndex);
-                    }
-
-                    // if we're grouping and have more items than the 
-                    // PageSize will allow, remove the last item
-                    if (PageSize > 0 && _group.ItemCount == PageSize)
-                    {
-                        removeNotificationItem = _group.LeafAt(PageSize - 1);
-                        _group.RemoveFromSubgroups(removeNotificationItem);
-                    }
-                }
-            }
-
-            // if we are paging, we may have to fire another notification for the item
-            // that needs to be removed for the one we added on this page.
-            if (PageSize > 0 && !OnLastLocalPage &&
-               (((IsGrouping && removeNotificationItem != null) ||
-               (!IsGrouping && (PageIndex + 1) * PageSize > InternalIndexOf(addedItem)))))
-            {
-                if (removeNotificationItem != null && removeNotificationItem != addedItem)
-                {
-                    AdjustCurrencyForRemove(PageSize - 1);
-
-                    OnCollectionChanged(
-                        new NotifyCollectionChangedEventArgs(
-                            NotifyCollectionChangedAction.Remove,
-                            removeNotificationItem,
-                            PageSize - 1));
-                }
-            }
-
-            // if we need to add the item into the current group
-            // that will be displayed
-            if (needsGrouping)
-            {
-                this._group.AddToSubgroups(addedItem, false /*loading*/);
-            }
-
-            int addedIndex = IndexOf(addedItem);
-
-            // if the item is within the current page
-            if (addedIndex >= 0)
-            {
-                object oldCurrentItem = CurrentItem;
-                int oldCurrentPosition = CurrentPosition;
-                bool oldIsCurrentAfterLast = IsCurrentAfterLast;
-                bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
-
-                AdjustCurrencyForAdd(null, addedIndex);
-
-                // fire add notification
-                OnCollectionChanged(
-                    new NotifyCollectionChangedEventArgs(
-                        NotifyCollectionChangedAction.Add,
-                        addedItem,
-                        addedIndex));
-
-                RaiseCurrencyChanges(false, oldCurrentItem, oldCurrentPosition, oldIsCurrentBeforeFirst, oldIsCurrentAfterLast);
-            }
-            else if (PageSize > 0)
-            {
-                // otherwise if the item was added into a previous page
-                int internalIndex = InternalIndexOf(addedItem);
-
-                if (internalIndex < ConvertToInternalIndex(0))
-                {
-                    // fire add notification for item pushed in
-                    OnCollectionChanged(
-                        new NotifyCollectionChangedEventArgs(
-                            NotifyCollectionChangedAction.Add,
-                            GetItemAt(0),
-                            0));
-                }
-            }
-        }
-
-        /// <summary>
-        /// Process CollectionChanged event on source collection 
-        /// that implements INotifyCollectionChanged.
-        /// </summary>
-        /// <param name="args">
-        /// The NotifyCollectionChangedEventArgs to be processed.
-        /// </param>
-        private void ProcessCollectionChanged(NotifyCollectionChangedEventArgs args)
-        {
-            // if we do not want to handle the CollectionChanged event, return
-            if (!CheckFlag(CollectionViewFlags.ShouldProcessCollectionChanged))
-            {
-                return;
-            }
-
-            if (args.Action == NotifyCollectionChangedAction.Reset)
-            {
-                // if we have no items now, clear our own internal list
-                if (!SourceCollection.GetEnumerator().MoveNext())
-                {
-                    _internalList.Clear();
-                }
-
-                // calling Refresh, will fire the collectionchanged event
-                RefreshOrDefer();
-                return;
-            }
-            
-            // fire notifications for removes
-            if (args.OldItems != null && 
-                (args.Action == NotifyCollectionChangedAction.Remove ||
-                args.Action == NotifyCollectionChangedAction.Replace))
-            {
-                foreach (var removedItem in args.OldItems)
-                {
-                    ProcessRemoveEvent(removedItem, args.Action == NotifyCollectionChangedAction.Replace);
-                }
-            }
-
-            // fire notifications for adds
-            if (args.NewItems != null && 
-                (args.Action == NotifyCollectionChangedAction.Add ||
-                 args.Action == NotifyCollectionChangedAction.Replace))
-            {
-                for (var i = 0; i < args.NewItems.Count; i++)
-                {
-                    if (Filter == null || PassesFilter(args.NewItems[i]))
-                    {
-                        ProcessAddEvent(args.NewItems[i], args.NewStartingIndex + i);
-                    }
-                }
-            }
-            if (args.Action != NotifyCollectionChangedAction.Replace)
-            {
-                OnPropertyChanged(nameof(ItemCount));
-            }
-        }
-
-        /// <summary>
-        /// Process a Remove operation from an INotifyCollectionChanged event handler.
-        /// </summary>
-        /// <param name="removedItem">Item removed from the source collection</param>
-        /// <param name="isReplace">Whether this was part of a Replace operation</param>
-        //TODO Paging
-        private void ProcessRemoveEvent(object removedItem, bool isReplace)
-        {
-            int internalRemoveIndex = -1;
-
-            if (IsGrouping)
-            {
-                internalRemoveIndex = PageSize > 0 ? _temporaryGroup.LeafIndexOf(removedItem) :
-                    _group.LeafIndexOf(removedItem);
-            }
-            else
-            {
-                internalRemoveIndex = InternalIndexOf(removedItem);
-            }
-
-            int removeIndex = IndexOf(removedItem);
-
-            // remove the item from the collection
-            _internalList.Remove(removedItem);
-
-            // only fire the remove if it was removed from either the current page, or a previous page
-            bool needToRemove = (PageSize == 0 && removeIndex >= 0) || (internalRemoveIndex < (PageIndex + 1) * PageSize);
-
-            if (IsGrouping)
-            {
-                if (PageSize > 0)
-                {
-                    _temporaryGroup.RemoveFromSubgroups(removedItem);
-                }
-
-                if (needToRemove)
-                {
-                    _group.RemoveFromSubgroups(removeIndex >= 0 ? removedItem : _group.LeafAt(0));
-                }
-            }
-
-            if (needToRemove)
-            {
-                object oldCurrentItem = CurrentItem;
-                int oldCurrentPosition = CurrentPosition;
-                bool oldIsCurrentAfterLast = IsCurrentAfterLast;
-                bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
-
-                AdjustCurrencyForRemove(removeIndex);
-
-                // fire remove notification 
-                // if we removed from current page, remove from removeIndex,
-                // if we removed from previous page, remove first item (index=0)
-                OnCollectionChanged(
-                    new NotifyCollectionChangedEventArgs(
-                        NotifyCollectionChangedAction.Remove,
-                        removedItem,
-                        Math.Max(0, removeIndex)));
-
-                RaiseCurrencyChanges(false, oldCurrentItem, oldCurrentPosition, oldIsCurrentBeforeFirst, oldIsCurrentAfterLast);
-
-                // if we removed all items from the current page,
-                // move to the previous page. we do not need to 
-                // fire additional notifications, as moving the page will
-                // trigger a reset.
-                if (NeedToMoveToPreviousPage && !isReplace)
-                {
-                    MoveToPreviousPage();
-                    return;
-                }
-
-                // if we are paging, we may have to fire another notification for the item
-                // that needs to replace the one we removed on this page.
-                if (PageSize > 0 && Count == PageSize)
-                {
-                    // we first need to add the item into the current group
-                    if (IsGrouping)
-                    {
-                        object newItem = _temporaryGroup.LeafAt((PageSize * (PageIndex + 1)) - 1);
-                        if (newItem != null)
-                        {
-                            _group.AddToSubgroups(newItem, false /*loading*/);
-                        }
-                    }
-
-                    // fire the add notification
-                    OnCollectionChanged(
-                        new NotifyCollectionChangedEventArgs(
-                            NotifyCollectionChangedAction.Add,
-                            GetItemAt(PageSize - 1),
-                            PageSize - 1));
-                }
-            }
-        }
-
-        /// <summary>
-        /// Handles adding an item into the collection, and applying sorting, filtering, grouping, paging.
-        /// </summary>
-        /// <param name="item">Item to insert in the collection</param>
-        /// <param name="index">Index to insert item into</param>
-        private void ProcessInsertToCollection(object item, int index)
-        {
-            // first check to see if it passes the filter
-            if (Filter == null || PassesFilter(item))
-            {
-                if (SortDescriptions.Count > 0)
-                {
-                    var itemType = ItemType;
-                    foreach (var sort in SortDescriptions)
-                        sort.Initialize(itemType);
-
-                    // create the SortFieldComparer to use
-                    var sortFieldComparer = new MergedComparer(this);
-
-                    // check if the item would be in sorted order if inserted into the specified index
-                    // otherwise, calculate the correct sorted index
-                    if (index < 0 || /* if item was not originally part of list */
-                        (index > 0 && (sortFieldComparer.Compare(item, InternalItemAt(index - 1)) < 0)) || /* item has moved up in the list */
-                        ((index < InternalList.Count - 1) && (sortFieldComparer.Compare(item, InternalItemAt(index)) > 0))) /* item has moved down in the list */
-                    {
-                        index = sortFieldComparer.FindInsertIndex(item, _internalList);
-                    }
-                }
-
-                // make sure that the specified insert index is within the valid range
-                // otherwise, just add it to the end. the index can be set to an invalid
-                // value if the item was originally not in the collection, on a different
-                // page, or if it had been previously filtered out.
-                if (index < 0 || index > _internalList.Count)
-                {
-                    index = _internalList.Count;
-                }
-
-                _internalList.Insert(index, item);
-            }
-        }
-
-        /// <summary>
-        /// Raises Currency Change events
-        /// </summary>
-        /// <param name="fireChangedEvent">Whether to fire the CurrentChanged event even if the parameters have not changed</param>
-        /// <param name="oldCurrentItem">CurrentItem before processing changes</param>
-        /// <param name="oldCurrentPosition">CurrentPosition before processing changes</param>
-        /// <param name="oldIsCurrentBeforeFirst">IsCurrentBeforeFirst before processing changes</param>
-        /// <param name="oldIsCurrentAfterLast">IsCurrentAfterLast before processing changes</param>
-        private void RaiseCurrencyChanges(bool fireChangedEvent, object oldCurrentItem, int oldCurrentPosition, bool oldIsCurrentBeforeFirst, bool oldIsCurrentAfterLast)
-        {
-            // fire events for currency changes
-            if (fireChangedEvent || CurrentItem != oldCurrentItem || CurrentPosition != oldCurrentPosition)
-            {
-                OnCurrentChanged();
-            }
-            if (CurrentItem != oldCurrentItem)
-            {
-                OnPropertyChanged(nameof(CurrentItem));
-            }
-            if (CurrentPosition != oldCurrentPosition)
-            {
-                OnPropertyChanged(nameof(CurrentPosition));
-            }
-            if (IsCurrentAfterLast != oldIsCurrentAfterLast)
-            {
-                OnPropertyChanged(nameof(IsCurrentAfterLast));
-            }
-            if (IsCurrentBeforeFirst != oldIsCurrentBeforeFirst)
-            {
-                OnPropertyChanged(nameof(IsCurrentBeforeFirst));
-            }
-        }
-
-        /// <summary>
-        /// Raises the PageChanged event
-        /// </summary>
-        private void RaisePageChanged()
-        {
-            PageChanged?.Invoke(this, EventArgs.Empty);
-        }
-
-        /// <summary>
-        /// Raises the PageChanging event
-        /// </summary>
-        /// <param name="newPageIndex">Index of the requested page</param>
-        /// <returns>True if the event is cancelled (e.Cancel was set to True), False otherwise</returns>
-        private bool RaisePageChanging(int newPageIndex)
-        {
-            EventHandler<PageChangingEventArgs> handler = PageChanging;
-            if (handler != null)
-            {
-                PageChangingEventArgs pageChangingEventArgs = new PageChangingEventArgs(newPageIndex);
-                handler(this, pageChangingEventArgs);
-                return pageChangingEventArgs.Cancel;
-            }
-
-            return false;
-        }
-
-        /// <summary>
-        /// Will call RefreshOverride and clear the NeedsRefresh flag
-        /// </summary>
-        private void RefreshInternal()
-        {
-            RefreshOverride();
-            SetFlag(CollectionViewFlags.NeedsRefresh, false);
-        }
-
-        /// <summary>
-        /// Refresh, or mark that refresh is needed when defer cycle completes.
-        /// </summary>
-        private void RefreshOrDefer()
-        {
-            if (IsRefreshDeferred)
-            {
-                SetFlag(CollectionViewFlags.NeedsRefresh, true);
-            }
-            else
-            {
-                RefreshInternal();
-            }
-        }
-
-        /// <summary>
-        /// Re-create the view, using any SortDescriptions. 
-        /// Also updates currency information.
-        /// </summary>
-        //TODO Paging
-        private void RefreshOverride()
-        {
-            object oldCurrentItem = CurrentItem;
-            int oldCurrentPosition = CurrentPosition;
-            bool oldIsCurrentAfterLast = IsCurrentAfterLast;
-            bool oldIsCurrentBeforeFirst = IsCurrentBeforeFirst;
-
-            // set IsGrouping to false
-            _isGrouping = false;
-
-            // force currency off the collection (gives user a chance to save dirty information)
-            OnCurrentChanging();
-
-            // if there's no sort/filter/paging/grouping, just use the collection's array
-            if (UsesLocalArray)
-            {
-                try
-                {
-                    // apply filtering/sorting through the PrepareLocalArray method
-                    _internalList = PrepareLocalArray(_sourceCollection);
-
-                    // apply grouping
-                    if (PageSize == 0)
-                    {
-                        PrepareGroups();
-                    }
-                    else
-                    {
-                        PrepareTemporaryGroups();
-                        PrepareGroupsForCurrentPage();
-                    }
-                }
-                catch (TargetInvocationException e)
-                {
-                    // If there's an exception while invoking PrepareLocalArray,
-                    // we want to unwrap it and throw its inner exception
-                    if (e.InnerException != null)
-                    {
-                        throw e.InnerException;
-                    }
-                    else
-                    {
-                        throw;
-                    }
-                }
-            }
-            else
-            {
-                CopySourceToInternalList();
-            }
-
-            // check if PageIndex is still valid after filter/sort
-            if (PageSize > 0 &&
-                PageIndex > 0 &&
-                PageIndex >= PageCount)
-            {
-                MoveToPage(PageCount - 1);
-            }
-
-            // reset currency values
-            ResetCurrencyValues(oldCurrentItem, oldIsCurrentBeforeFirst, oldIsCurrentAfterLast);
-
-            OnCollectionChanged(
-                new NotifyCollectionChangedEventArgs(
-                    NotifyCollectionChangedAction.Reset));
-
-            // now raise currency changes at the end
-            RaiseCurrencyChanges(false, oldCurrentItem, oldCurrentPosition, oldIsCurrentBeforeFirst, oldIsCurrentAfterLast);
-        }
-
-        /// <summary>
-        /// Set currency back to the previous value it had if possible. If the item is no longer in view
-        /// then either use the first item in the view, or if the list is empty, use null.
-        /// </summary>
-        /// <param name="oldCurrentItem">CurrentItem before processing changes</param>
-        /// <param name="oldIsCurrentBeforeFirst">IsCurrentBeforeFirst before processing changes</param>
-        /// <param name="oldIsCurrentAfterLast">IsCurrentAfterLast before processing changes</param>
-        private void ResetCurrencyValues(object oldCurrentItem, bool oldIsCurrentBeforeFirst, bool oldIsCurrentAfterLast)
-        {
-            if (oldIsCurrentBeforeFirst || IsEmpty)
-            {
-                SetCurrent(null, -1);
-            }
-            else if (oldIsCurrentAfterLast)
-            {
-                SetCurrent(null, Count);
-            }
-            else
-            {
-                // try to set currency back to old current item
-                // if there are duplicates, use the position of the first matching item
-                int newPosition = IndexOf(oldCurrentItem);
-
-                // if the old current item is no longer in view
-                if (newPosition < 0)
-                {
-                    // if we are adding a new item, set it as the current item, otherwise, set it to null
-                    newPosition = 0;
-
-                    if (newPosition < Count)
-                    {
-                        SetCurrent(GetItemAt(newPosition), newPosition);
-                    }
-                    else if (!IsEmpty)
-                    {
-                        SetCurrent(GetItemAt(0), 0);
-                    }
-                    else
-                    {
-                        SetCurrent(null, -1);
-                    }
-                }
-                else
-                {
-                    SetCurrent(oldCurrentItem, newPosition);
-                }
-            }
-        }
-
-        /// <summary>
-        /// Set CurrentItem and CurrentPosition, no questions asked!
-        /// </summary>
-        /// <remarks>
-        /// CollectionViews (and sub-classes) should use this method to update
-        /// the Current values.
-        /// </remarks>
-        /// <param name="newItem">New CurrentItem</param>
-        /// <param name="newPosition">New CurrentPosition</param>
-        private void SetCurrent(object newItem, int newPosition)
-        {
-            int count = (newItem != null) ? 0 : (IsEmpty ? 0 : Count);
-            SetCurrent(newItem, newPosition, count);
-        }
-
-        /// <summary>
-        /// Set CurrentItem and CurrentPosition, no questions asked!
-        /// </summary>
-        /// <remarks>
-        /// This method can be called from a constructor - it does not call
-        /// any virtuals. The 'count' parameter is substitute for the real Count,
-        /// used only when newItem is null.
-        /// In that case, this method sets IsCurrentAfterLast to true if and only
-        /// if newPosition >= count.  This distinguishes between a null belonging
-        /// to the view and the dummy null when CurrentPosition is past the end.
-        /// </remarks>
-        /// <param name="newItem">New CurrentItem</param>
-        /// <param name="newPosition">New CurrentPosition</param>
-        /// <param name="count">Numbers of items in the collection</param>
-        private void SetCurrent(object newItem, int newPosition, int count)
-        {
-            if (newItem != null)
-            {
-                // non-null item implies position is within range.
-                // We ignore count - it's just a placeholder
-                SetFlag(CollectionViewFlags.IsCurrentBeforeFirst, false);
-                SetFlag(CollectionViewFlags.IsCurrentAfterLast, false);
-            }
-            else if (count == 0)
-            {
-                // empty collection - by convention both flags are true and position is -1
-                SetFlag(CollectionViewFlags.IsCurrentBeforeFirst, true);
-                SetFlag(CollectionViewFlags.IsCurrentAfterLast, true);
-                newPosition = -1;
-            }
-            else
-            {
-                // null item, possibly within range.
-                SetFlag(CollectionViewFlags.IsCurrentBeforeFirst, newPosition < 0);
-                SetFlag(CollectionViewFlags.IsCurrentAfterLast, newPosition >= count);
-            }
-
-            _currentItem = newItem;
-            _currentPosition = newPosition;
-        }
-
-        /// <summary>
-        /// Just move it. No argument check, no events, just move current to position.
-        /// </summary>
-        /// <param name="position">Position to move the current item to</param>
-        private void SetCurrentToPosition(int position)
-        {
-            if (position < 0)
-            {
-                SetFlag(CollectionViewFlags.IsCurrentBeforeFirst, true);
-                SetCurrent(null, -1);
-            }
-            else if (position >= Count)
-            {
-                SetFlag(CollectionViewFlags.IsCurrentAfterLast, true);
-                SetCurrent(null, Count);
-            }
-            else
-            {
-                SetFlag(CollectionViewFlags.IsCurrentBeforeFirst | CollectionViewFlags.IsCurrentAfterLast, false);
-                SetCurrent(GetItemAt(position), position);
-            }
-        }
-
-        /// <summary>
-        /// Sets the specified Flag(s)
-        /// </summary>
-        /// <param name="flags">Flags we want to set</param>
-        /// <param name="value">Value we want to set these flags to</param>
-        private void SetFlag(CollectionViewFlags flags, bool value)
-        {
-            if (value)
-            {
-                _flags = _flags | flags;
-            }
-            else
-            {
-                _flags = _flags & ~flags;
-            }
-        }
-
-        /// <summary>
-        /// Set new SortDescription collection; re-hook collection change notification handler
-        /// </summary>
-        /// <param name="descriptions">SortDescriptionCollection to set the property value to</param>
-        private void SetSortDescriptions(DataGridSortDescriptionCollection descriptions)
-        {
-            if (_sortDescriptions != null)
-            {
-                _sortDescriptions.CollectionChanged -= SortDescriptionsChanged;
-            }
-
-            _sortDescriptions = descriptions;
-
-            if (_sortDescriptions != null)
-            {
-                Debug.Assert(_sortDescriptions.Count == 0, "must be empty SortDescription collection");
-                _sortDescriptions.CollectionChanged += SortDescriptionsChanged;
-            }
-        }
-
-        /// <summary>
-        /// SortDescription was added/removed, refresh DataGridCollectionView
-        /// </summary>
-        /// <param name="sender">Sender that triggered this handler</param>
-        /// <param name="e">NotifyCollectionChangedEventArgs for this change</param>
-        private void SortDescriptionsChanged(object sender, NotifyCollectionChangedEventArgs e)
-        {
-            if (IsAddingNew || IsEditingItem)
-            {
-                throw new InvalidOperationException(GetOperationNotAllowedDuringAddOrEditText("Sorting"));
-            }
-
-            // we want to make sure that the data is refreshed before we try to move to a page
-            // since the refresh would take care of the filtering, sorting, and grouping.
-            RefreshOrDefer();
-
-            if (PageSize > 0)
-            {
-                if (IsRefreshDeferred)
-                {
-                    // set cached value and flag so that we move to first page on EndDefer
-                    _cachedPageIndex = 0;
-                    SetFlag(CollectionViewFlags.IsMoveToPageDeferred, true);
-                }
-                else
-                {
-                    MoveToFirstPage();
-                }
-            }
-
-            OnPropertyChanged("SortDescriptions");
-        }
-
-        /// <summary>
-        /// Sort the List based on the SortDescriptions property.
-        /// </summary>
-        /// <param name="list">List of objects to sort</param>
-        /// <returns>The sorted list</returns>
-        private List<object> SortList(List<object> list)
-        {
-            Debug.Assert(list != null, "Input list to sort should not be null");
-
-            IEnumerable<object> seq = (IEnumerable<object>)list;
-            IComparer<object> comparer = new CultureSensitiveComparer(Culture);
-            var itemType = ItemType;
-
-            foreach (DataGridSortDescription sort in SortDescriptions)
-            {
-                sort.Initialize(itemType); 
-
-                if (seq is IOrderedEnumerable<object> orderedEnum)
-                {
-                    seq = sort.ThenBy(orderedEnum);
-                }
-                else
-                {
-                    seq = sort.OrderBy(seq);
-                }
-            }
-
-            return seq.ToList();
-        }
-
-        /// <summary>
-        /// Helper to validate that we are not in the middle of a DeferRefresh
-        /// and throw if that is the case.
-        /// </summary>
-        private void VerifyRefreshNotDeferred()
-        {
-            // If the Refresh is being deferred to change filtering or sorting of the
-            // data by this DataGridCollectionView, then DataGridCollectionView will not reflect the correct
-            // state of the underlying data.
-            if (IsRefreshDeferred)
-            {
-                throw new InvalidOperationException("Cannot change or check the contents or current position of the CollectionView while Refresh is being deferred.");
-            }
-        }
-
-        int IList.Add(object value)
-        {
-            var index = SourceList.Add(value);
-            if (SourceList is not INotifyCollectionChanged)
-            {
-                ProcessCollectionChanged(
-                    new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, value));
-            }
-            return index;
-        }
-
-        void IList.Clear()
-        {
-            SourceList.Clear();
-            if (SourceList is not INotifyCollectionChanged)
-            {
-                ProcessCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
-            }
-        }
-
-        void IList.Insert(int index, object value) 
-        {
-            SourceList.Insert(index, value);
-            if (SourceList is not INotifyCollectionChanged)
-            {
-                // TODO: implement Insert
-                ProcessCollectionChanged(
-                    new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset, value));
-            }
-        }
-        void ICollection.CopyTo(Array array, int index) => InternalList.CopyTo(array, index);
-
-        /// <summary>
-        /// Creates a comparer class that takes in a CultureInfo as a parameter,
-        /// which it will use when comparing strings.
-        /// </summary>
-        private class CultureSensitiveComparer : IComparer<object>
-        {
-            /// <summary>
-            /// Private accessor for the CultureInfo of our comparer
-            /// </summary>
-            private CultureInfo _culture;
-
-            /// <summary>
-            /// Creates a comparer which will respect the CultureInfo
-            /// that is passed in when comparing strings.
-            /// </summary>
-            /// <param name="culture">The CultureInfo to use in string comparisons</param>
-            public CultureSensitiveComparer(CultureInfo culture)
-                : base()
-            {
-                _culture = culture ?? CultureInfo.InvariantCulture;
-            }
-
-            /// <summary>
-            /// Compares two objects and returns a value indicating whether one is less than, equal to or greater than the other.
-            /// </summary>
-            /// <param name="x">first item to compare</param>
-            /// <param name="y">second item to compare</param>
-            /// <returns>Negative number if x is less than y, zero if equal, and a positive number if x is greater than y</returns>
-            /// <remarks>
-            /// Compares the 2 items using the specified CultureInfo for string and using the default object comparer for all other objects.
-            /// </remarks>
-            public int Compare(object x, object y)
-            {
-                if (x == null)
-                {
-                    if (y != null)
-                    {
-                        return -1;
-                    }
-                    return 0;
-                }
-                if (y == null)
-                {
-                    return 1;
-                }
-
-                // at this point x and y are not null
-                if (x.GetType() == typeof(string) && y.GetType() == typeof(string))
-                {
-                    return _culture.CompareInfo.Compare((string)x, (string)y);
-                }
-                else
-                {
-                    return Comparer<object>.Default.Compare(x, y);
-                }
-            }
-        }
-
-        /// <summary>
-        /// Used to keep track of Defer calls on the DataGridCollectionView, which
-        /// will prevent the user from calling Refresh() on the view. In order
-        /// to allow refreshes again, the user will have to call IDisposable.Dispose,
-        /// to end the Defer operation.
-        /// </summary>
-        private class DeferHelper : IDisposable
-        {
-            /// <summary>
-            /// Private reference to the CollectionView that created this DeferHelper
-            /// </summary>
-            private DataGridCollectionView collectionView;
-
-            /// <summary>
-            /// Initializes a new instance of the DeferHelper class
-            /// </summary>
-            /// <param name="collectionView">CollectionView that created this DeferHelper</param>
-            public DeferHelper(DataGridCollectionView collectionView)
-            {
-                this.collectionView = collectionView;
-            }
-
-            /// <summary>
-            /// Cleanup method called when done using this class
-            /// </summary>
-            public void Dispose()
-            {
-                if (collectionView != null)
-                {
-                    collectionView.EndDefer();
-                    collectionView = null;
-                }
-                GC.SuppressFinalize(this);
-            }
-        }
-
-        /// <summary>
-        /// A simple monitor class to help prevent re-entrant calls
-        /// </summary>
-        private class SimpleMonitor : IDisposable
-        {
-            /// <summary>
-            /// Whether the monitor is entered
-            /// </summary>
-            private bool entered;
-
-            /// <summary>
-            /// Gets a value indicating whether we have been entered or not
-            /// </summary>
-            public bool Busy
-            {
-                get { return entered; }
-            }
-
-            /// <summary>
-            /// Sets a value indicating that we have been entered
-            /// </summary>
-            /// <returns>Boolean value indicating whether we were already entered</returns>
-            public bool Enter()
-            {
-                if (entered)
-                {
-                    return false;
-                }
-
-                entered = true;
-                return true;
-            }
-
-            /// <summary>
-            /// Cleanup method called when done using this class
-            /// </summary>
-            public void Dispose()
-            {
-                entered = false;
-                GC.SuppressFinalize(this);
-            }
-        }
-
-        /// <summary>
-        /// IEnumerator generated using the new item taken into account
-        /// </summary>
-        private class NewItemAwareEnumerator : IEnumerator
-        {
-            private enum Position
-            {
-                /// <summary>
-                /// Whether the position is before the new item
-                /// </summary>
-                BeforeNewItem,
-
-                /// <summary>
-                /// Whether the position is on the new item that is being created
-                /// </summary>
-                OnNewItem,
-
-                /// <summary>
-                /// Whether the position is after the new item
-                /// </summary>
-                AfterNewItem
-            }
-
-            /// <summary>
-            /// Initializes a new instance of the NewItemAwareEnumerator class.
-            /// </summary>
-            /// <param name="collectionView">The DataGridCollectionView we are creating the enumerator for</param>
-            /// <param name="baseEnumerator">The baseEnumerator that we pass in</param>
-            /// <param name="newItem">The new item we are adding to the collection</param>
-            public NewItemAwareEnumerator(DataGridCollectionView collectionView, IEnumerator baseEnumerator, object newItem)
-            {
-                _collectionView = collectionView;
-                _timestamp = collectionView.Timestamp;
-                _baseEnumerator = baseEnumerator;
-                _newItem = newItem;
-            }
-
-            /// <summary>
-            /// Implements the MoveNext function for IEnumerable
-            /// </summary>
-            /// <returns>Whether we can move to the next item</returns>
-            public bool MoveNext()
-            {
-                if (_timestamp != _collectionView.Timestamp)
-                {
-                    throw new InvalidOperationException("Collection was modified; enumeration operation cannot execute.");
-                }
-
-                switch (_position)
-                {
-                    case Position.BeforeNewItem:
-                        if (_baseEnumerator.MoveNext() &&
-                                    (_newItem == null || _baseEnumerator.Current != _newItem
-                                            || _baseEnumerator.MoveNext()))
-                        {
-                            // advance base, skipping the new item
-                        }
-                        else if (_newItem != null)
-                        {
-                            // if base has reached the end, move to new item
-                            _position = Position.OnNewItem;
-                        }
-                        else
-                        {
-                            return false;
-                        }
-                        return true;
-                }
-
-                // in all other cases, simply advance base, skipping the new item
-                _position = Position.AfterNewItem;
-                return _baseEnumerator.MoveNext() &&
-                    (_newItem == null
-                        || _baseEnumerator.Current != _newItem
-                        || _baseEnumerator.MoveNext());
-            }
-
-            /// <summary>
-            /// Gets the Current value for IEnumerable
-            /// </summary>
-            public object Current
-            {
-                get
-                {
-                    return (_position == Position.OnNewItem) ? _newItem : _baseEnumerator.Current;
-                }
-            }
-
-            /// <summary>
-            /// Implements the Reset function for IEnumerable
-            /// </summary>
-            public void Reset()
-            {
-                _position = Position.BeforeNewItem;
-                _baseEnumerator.Reset();
-            }
-
-            /// <summary>
-            /// CollectionView that we are creating the enumerator for
-            /// </summary>
-            private DataGridCollectionView _collectionView;
-
-            /// <summary>
-            /// The Base Enumerator that we are passing in
-            /// </summary>
-            private IEnumerator _baseEnumerator;
-
-            /// <summary>
-            /// The position we are appending items to the enumerator
-            /// </summary>
-            private Position _position;
-
-            /// <summary>
-            /// Reference to any new item that we want to add to the collection
-            /// </summary>
-            private object _newItem;
-
-            /// <summary>
-            /// Timestamp to let us know whether there have been updates to the collection
-            /// </summary>
-            private int _timestamp;
-        }
-
-        internal class MergedComparer
-        {
-            private readonly IComparer<object>[] _comparers;
-
-            public MergedComparer(DataGridSortDescriptionCollection coll)
-            {
-                _comparers = MakeComparerArray(coll);
-            }
-            public MergedComparer(DataGridCollectionView collectionView)
-                : this(collectionView.SortDescriptions)
-            { }
-
-            private static IComparer<object>[] MakeComparerArray(DataGridSortDescriptionCollection coll)
-            {
-                return 
-                    coll.Select(c => c.Comparer)
-                        .ToArray();
-            }
-
-            /// <summary>
-            /// Compares two objects and returns a value indicating whether one is less than, equal to or greater than the other.
-            /// </summary>
-            /// <param name="x">first item to compare</param>
-            /// <param name="y">second item to compare</param>
-            /// <returns>Negative number if x is less than y, zero if equal, and a positive number if x is greater than y</returns>
-            /// <remarks>
-            /// Compares the 2 items using the list of property names and directions.
-            /// </remarks>
-            public int Compare(object x, object y)
-            {
-                int result = 0;
-
-                // compare both objects by each of the properties until property values don't match
-                for (int k = 0; k < _comparers.Length; ++k)
-                {
-                    var comparer = _comparers[k];
-                    result = comparer.Compare(x, y);
-
-                    if (result != 0)
-                    {
-                        break;
-                    }
-                }
-
-                return result;
-            }
-
-            /// <summary>
-            /// Steps through the given list using the comparer to find where
-            /// to insert the specified item to maintain sorted order
-            /// </summary>
-            /// <param name="x">Item to insert into the list</param>
-            /// <param name="list">List where we want to insert the item</param>
-            /// <returns>Index where we should insert into</returns>
-            public int FindInsertIndex(object x, IList list)
-            {
-                int min = 0;
-                int max = list.Count - 1;
-                int index;
-
-                // run a binary search to find the right index
-                // to insert into.
-                while (min <= max)
-                {
-                    index = (min + max) / 2;
-
-                    int result = Compare(x, list[index]);
-                    if (result == 0)
-                    {
-                        return index;
-                    }
-                    else if (result > 0)
-                    {
-                        min = index + 1;
-                    }
-                    else
-                    {
-                        max = index - 1;
-                    }
-                }
-
-                return min;
-            }
-        }       
-    }
-}

+ 0 - 1369
src/Avalonia.Controls.DataGrid/Collections/DataGridGroupDescription.cs

@@ -1,1369 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Globalization;
-using System.Text;
-using Avalonia.Controls;
-using Avalonia.Controls.Utils;
-using Avalonia.Data;
-using Avalonia.Data.Converters;
-using Avalonia.Utilities;
-
-namespace Avalonia.Collections
-{
-    public abstract class DataGridGroupDescription : INotifyPropertyChanged
-    {
-        public AvaloniaList<object> GroupKeys { get; }
-
-        public DataGridGroupDescription()
-        {
-            GroupKeys = new AvaloniaList<object>();
-            GroupKeys.CollectionChanged += (sender, e) => OnPropertyChanged(new PropertyChangedEventArgs(nameof(GroupKeys)));
-        }
-
-        protected virtual event PropertyChangedEventHandler PropertyChanged;
-        event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
-        {
-            add
-            {
-                PropertyChanged += value;
-            }
-
-            remove
-            {
-                PropertyChanged -= value;
-            }
-        }
-        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
-        {
-            PropertyChanged?.Invoke(this, e);
-        }
-
-        public virtual string PropertyName => String.Empty;
-        public abstract object GroupKeyFromItem(object item, int level, CultureInfo culture);
-        public virtual bool KeysMatch(object groupKey, object itemKey)
-        {
-            return object.Equals(groupKey, itemKey);
-        }
-    }
-    public class DataGridPathGroupDescription : DataGridGroupDescription
-    {
-        private string _propertyPath;
-        private Type _propertyType;
-        private IValueConverter _valueConverter;
-        private StringComparison _stringComparison = StringComparison.Ordinal;
-
-        public DataGridPathGroupDescription(string propertyPath)
-        {
-            _propertyPath = propertyPath;
-        }
-
-        public override object GroupKeyFromItem(object item, int level, CultureInfo culture)
-        {
-            object GetKey(object o)
-            {
-                if(o == null)
-                    return null;
-
-                if (_propertyType == null)
-                    _propertyType = GetPropertyType(o);
-
-                return InvokePath(o, _propertyPath, _propertyType);
-            }
-
-            var key = GetKey(item);
-            if (key == null)
-                key = item;
-
-            var valueConverter = ValueConverter;
-            if (valueConverter != null)
-                key = valueConverter.Convert(key, typeof(object), level, culture);
-
-            return key;
-        }
-        public override bool KeysMatch(object groupKey, object itemKey)
-        {
-            if(groupKey is string k1 && itemKey is string k2)
-            {
-                return String.Equals(k1, k2, _stringComparison);
-            }
-            else
-                return base.KeysMatch(groupKey, itemKey);
-        }
-        public override string PropertyName => _propertyPath;
-
-        public IValueConverter ValueConverter { get => _valueConverter; set => _valueConverter = value; }
-
-        private Type GetPropertyType(object o)
-        {
-            return o.GetType().GetNestedPropertyType(_propertyPath);
-        }
-        private static object InvokePath(object item, string propertyPath, Type propertyType)
-        {
-            object propertyValue = TypeHelper.GetNestedPropertyValue(item, propertyPath, propertyType, out Exception exception);
-            if (exception != null)
-            {
-                throw exception;
-            }
-            return propertyValue;
-        }
-    }
-
-    public abstract class DataGridCollectionViewGroup : INotifyPropertyChanged
-    {
-        private int _itemCount;
-
-        public object Key { get; }
-        public int ItemCount => _itemCount;
-        public IAvaloniaReadOnlyList<object> Items => ProtectedItems;
-
-        protected AvaloniaList<object> ProtectedItems { get; }
-        protected int ProtectedItemCount
-        {
-            get { return _itemCount; }
-            set
-            {
-                _itemCount = value;
-                OnPropertyChanged(new PropertyChangedEventArgs(nameof(ItemCount)));
-            }
-        }
-
-        protected DataGridCollectionViewGroup(object key)
-        {
-            Key = key;
-            ProtectedItems = new AvaloniaList<object>();
-        }
-
-        public abstract bool IsBottomLevel { get; }
-
-        protected virtual event PropertyChangedEventHandler PropertyChanged;
-        event PropertyChangedEventHandler INotifyPropertyChanged.PropertyChanged
-        {
-            add
-            {
-                PropertyChanged += value;
-            }
-
-            remove
-            {
-                PropertyChanged -= value;
-            }
-        }
-        protected virtual void OnPropertyChanged(PropertyChangedEventArgs e)
-        {
-            PropertyChanged?.Invoke(this, e);
-        }
-    }
-    internal class DataGridCollectionViewGroupInternal : DataGridCollectionViewGroup
-    {
-        /// <summary>
-        /// GroupDescription used to define how to group the items
-        /// </summary>
-        private DataGridGroupDescription _groupBy;
-
-        /// <summary>
-        /// Parent group of this CollectionViewGroupInternal
-        /// </summary>
-        private readonly DataGridCollectionViewGroupInternal _parentGroup;
-
-        /// <summary>
-        /// Used for detecting stale enumerators
-        /// </summary>
-        private int _version;
-
-        public DataGridCollectionViewGroupInternal(object key, DataGridCollectionViewGroupInternal parent)
-            : base(key)
-        {
-            _parentGroup = parent;
-        }
-
-        public override bool IsBottomLevel => _groupBy == null;
-
-        internal int FullCount { get; set; }
-
-        internal DataGridGroupDescription GroupBy
-        {
-            get { return _groupBy; }
-            set
-            {
-                bool oldIsBottomLevel = IsBottomLevel;
-
-                if (_groupBy != null)
-                {
-                    ((INotifyPropertyChanged)_groupBy).PropertyChanged -= OnGroupByChanged;
-                }
-
-                _groupBy = value;
-
-                if (_groupBy != null)
-                {
-                    ((INotifyPropertyChanged)_groupBy).PropertyChanged += OnGroupByChanged;
-                }
-
-                if (oldIsBottomLevel != IsBottomLevel)
-                {
-                    OnPropertyChanged(new PropertyChangedEventArgs(nameof(IsBottomLevel)));
-                }
-            }
-        }
-
-        private void OnGroupByChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
-        {
-            OnGroupByChanged();
-        }
-        protected virtual void OnGroupByChanged()
-        {
-            _parentGroup?.OnGroupByChanged();
-        }
-
-        /// <summary>
-        /// Gets or sets the most recent index where activity took place
-        /// </summary>
-        internal int LastIndex { get; set; }
-
-        /// <summary>
-        /// Gets the first item (leaf) added to this group.  If this can't be determined,
-        /// DependencyProperty.UnsetValue.
-        /// </summary>
-        internal object SeedItem
-        {
-            get
-            {
-                if (ItemCount > 0 && (GroupBy == null || GroupBy.GroupKeys.Count == 0))
-                {
-                    // look for first item, child by child
-                    for (int k = 0, n = Items.Count; k < n; ++k)
-                    {
-                        if (!(Items[k] is DataGridCollectionViewGroupInternal subgroup))
-                        {
-                            // child is an item - return it
-                            return Items[k];
-                        }
-                        else if (subgroup.ItemCount > 0)
-                        {
-                            // child is a nonempty subgroup - ask it
-                            return subgroup.SeedItem;
-                        }
-                        //// otherwise child is an empty subgroup - go to next child
-                    }
-
-                    // we shouldn't get here, but just in case...
-
-                    return AvaloniaProperty.UnsetValue;
-                }
-                else
-                {
-                    // the group is empty, or it has explicit subgroups.
-                    // In either case, we cannot determine the first item -
-                    // it could have gone into any of the subgroups.
-                    return AvaloniaProperty.UnsetValue;
-                }
-            }
-        }
-
-        private DataGridCollectionViewGroupInternal Parent => _parentGroup;
-
-        /// <summary>
-        /// Adds the specified item to the collection
-        /// </summary>
-        /// <param name="item">Item to add</param>
-        internal void Add(object item)
-        {
-            ChangeCounts(item, +1);
-            ProtectedItems.Add(item);
-        }
-
-        /// <summary>
-        /// Clears the collection of items
-        /// </summary>
-        internal void Clear()
-        {
-            ProtectedItems.Clear();
-            FullCount = 1;
-            ProtectedItemCount = 0;
-        }
-
-        /// <summary>
-        /// Finds the index of the specified item
-        /// </summary>
-        /// <param name="item">Item we are looking for</param>
-        /// <param name="seed">Seed of the item we are looking for</param>
-        /// <param name="comparer">Comparer used to find the item</param>
-        /// <param name="low">Low range of item index</param>
-        /// <param name="high">High range of item index</param>
-        /// <returns>Index of the specified item</returns>
-        protected virtual int FindIndex(object item, object seed, IComparer comparer, int low, int high)
-        {
-            int index;
-
-            if (comparer != null)
-            {
-                if (comparer is ListComparer listComparer)
-                {
-                    // reset the IListComparer before each search. This cannot be done
-                    // any less frequently (e.g. in Root.AddToSubgroups), due to the
-                    // possibility that the item may appear in more than one subgroup.
-                    listComparer.Reset();
-                }
-
-                if (comparer is CollectionViewGroupComparer groupComparer)
-                {
-                    // reset the CollectionViewGroupComparer before each search. This cannot be done
-                    // any less frequently (e.g. in Root.AddToSubgroups), due to the
-                    // possibility that the item may appear in more than one subgroup.
-                    groupComparer.Reset();
-                }
-
-                for (index = low; index < high; ++index)
-                {
-                    object seed1 = (ProtectedItems[index] is DataGridCollectionViewGroupInternal subgroup) ? subgroup.SeedItem : ProtectedItems[index];
-                    if (seed1 == AvaloniaProperty.UnsetValue)
-                    {
-                        continue;
-                    }
-                    if (comparer.Compare(seed, seed1) < 0)
-                    {
-                        break;
-                    }
-                }
-            }
-            else
-            {
-                index = high;
-            }
-
-            return index;
-        }
-
-        /// <summary>
-        /// Returns an enumerator over the leaves governed by this group
-        /// </summary>
-        /// <returns>Enumerator of leaves</returns>
-        internal IEnumerator GetLeafEnumerator()
-        {
-            return new LeafEnumerator(this);
-        }
-
-        /// <summary>
-        /// Insert a new item or subgroup and return its index.  Seed is a
-        /// representative from the subgroup (or the item itself) that
-        /// is used to position the new item/subgroup w.r.t. the order given
-        /// by the comparer. (If comparer is null, just add at the end).
-        /// </summary>
-        /// <param name="item">Item we are looking for</param>
-        /// <param name="seed">Seed of the item we are looking for</param>
-        /// <param name="comparer">Comparer used to find the item</param>
-        /// <returns>The index where the item was inserted</returns>
-        internal int Insert(object item, object seed, IComparer comparer)
-        {
-            // never insert the new item/group before the explicit subgroups
-            int low = (GroupBy == null) ? 0 : GroupBy.GroupKeys.Count;
-            int index = FindIndex(item, seed, comparer, low, ProtectedItems.Count);
-
-            // now insert the item
-            ChangeCounts(item, +1);
-            ProtectedItems.Insert(index, item);
-
-            return index;
-        }
-
-        /// <summary>
-        /// Return the item at the given index within the list of leaves governed
-        /// by this group
-        /// </summary>
-        /// <param name="index">Index of the leaf</param>
-        /// <returns>Item at given index</returns>
-        internal object LeafAt(int index)
-        {
-            for (int k = 0, n = Items.Count; k < n; ++k)
-            {
-                if (Items[k] is DataGridCollectionViewGroupInternal subgroup)
-                {
-                    // current item is a group - either drill in, or skip over
-                    if (index < subgroup.ItemCount)
-                    {
-                        return subgroup.LeafAt(index);
-                    }
-                    else
-                    {
-                        index -= subgroup.ItemCount;
-                    }
-                }
-                else
-                {
-                    // current item is a leaf - see if we're done
-                    if (index == 0)
-                    {
-                        return Items[k];
-                    }
-                    else
-                    {
-                        index -= 1;
-                    }
-                }
-            }
-
-            return null;
-        }
-
-        /// <summary>
-        /// Returns the index of the given item within the list of leaves governed
-        /// by the full group structure.  The item must be a (direct) child of this
-        /// group.  The caller provides the index of the item within this group,
-        /// if known, or -1 if not.
-        /// </summary>
-        /// <param name="item">Item we are looking for</param>
-        /// <param name="index">Index of the leaf</param>
-        /// <returns>Number of items under that leaf</returns>
-        internal int LeafIndexFromItem(object item, int index)
-        {
-            int result = 0;
-
-            // accumulate the number of predecessors at each level
-            for (DataGridCollectionViewGroupInternal group = this;
-                    group != null;
-                    item = group, group = group.Parent, index = -1)
-            {
-                // accumulate the number of predecessors at the level of item
-                for (int k = 0, n = group.Items.Count; k < n; ++k)
-                {
-                    // if we've reached the item, move up to the next level
-                    if ((index < 0 && Object.Equals(item, group.Items[k])) ||
-                        index == k)
-                    {
-                        break;
-                    }
-
-                    // accumulate leaf count
-                    DataGridCollectionViewGroupInternal subgroup = group.Items[k] as DataGridCollectionViewGroupInternal;
-                    result += subgroup?.ItemCount ?? 1;
-                }
-            }
-
-            return result;
-        }
-
-        /// <summary>
-        /// Returns the index of the given item within the list of leaves governed
-        /// by this group
-        /// </summary>
-        /// <param name="item">Item we are looking for</param>
-        /// <returns>Number of items under that leaf</returns>
-        internal int LeafIndexOf(object item)
-        {
-            int leaves = 0;         // number of leaves we've passed over so far
-            for (int k = 0, n = Items.Count; k < n; ++k)
-            {
-                if (Items[k] is DataGridCollectionViewGroupInternal subgroup)
-                {
-                    int subgroupIndex = subgroup.LeafIndexOf(item);
-                    if (subgroupIndex < 0)
-                    {
-                        leaves += subgroup.ItemCount;       // item not in this subgroup
-                    }
-                    else
-                    {
-                        return leaves + subgroupIndex;    // item is in this subgroup
-                    }
-                }
-                else
-                {
-                    // current item is a leaf - compare it directly
-                    if (Object.Equals(item, Items[k]))
-                    {
-                        return leaves;
-                    }
-                    else
-                    {
-                        leaves += 1;
-                    }
-                }
-            }
-
-            // item not found
-            return -1;
-        }
-
-        /// <summary>
-        /// Removes the specified item from the collection
-        /// </summary>
-        /// <param name="item">Item to remove</param>
-        /// <param name="returnLeafIndex">Whether we want to return the leaf index</param>
-        /// <returns>Leaf index where item was removed, if value was specified. Otherwise '-1'</returns>
-        internal int Remove(object item, bool returnLeafIndex)
-        {
-            int index = -1;
-            int localIndex = ProtectedItems.IndexOf(item);
-
-            if (localIndex >= 0)
-            {
-                if (returnLeafIndex)
-                {
-                    index = LeafIndexFromItem(null, localIndex);
-                }
-
-                ChangeCounts(item, -1);
-                ProtectedItems.RemoveAt(localIndex);
-            }
-
-            return index;
-        }
-
-        /// <summary>
-        /// Removes an empty group from the PagedCollectionView grouping
-        /// </summary>
-        /// <param name="group">Empty subgroup to remove</param>
-        private static void RemoveEmptyGroup(DataGridCollectionViewGroupInternal group)
-        {
-            DataGridCollectionViewGroupInternal parent = group.Parent;
-
-            if (parent != null)
-            {
-                DataGridGroupDescription groupBy = parent.GroupBy;
-                int index = parent.ProtectedItems.IndexOf(group);
-
-                // remove the subgroup unless it is one of the explicit groups
-                if (index >= groupBy.GroupKeys.Count)
-                {
-                    parent.Remove(group, false);
-                }
-            }
-        }
-
-        /// <summary>
-        /// Update the item count of the CollectionViewGroup
-        /// </summary>
-        /// <param name="item">CollectionViewGroup to update</param>
-        /// <param name="delta">Delta to change count by</param>
-        protected void ChangeCounts(object item, int delta)
-        {
-            bool changeLeafCount = !(item is DataGridCollectionViewGroup);
-
-            for (DataGridCollectionViewGroupInternal group = this;
-                    group != null;
-                    group = group._parentGroup)
-            {
-                group.FullCount += delta;
-                if (changeLeafCount)
-                {
-                    group.ProtectedItemCount += delta;
-
-                    if (group.ProtectedItemCount == 0)
-                    {
-                        RemoveEmptyGroup(group);
-                    }
-                }
-            }
-
-            unchecked
-            {
-                // this invalidates enumerators
-                ++_version;
-            }
-        }
-
-        /// <summary>
-        /// Enumerator for the leaves in the CollectionViewGroupInternal class.
-        /// </summary>
-        private class LeafEnumerator : IEnumerator
-        {
-            private object _current;   // current item
-            private DataGridCollectionViewGroupInternal _group; // parent group
-            private int _index;     // current index into Items
-            private IEnumerator _subEnum;   // enumerator over current subgroup
-            private int _version;   // parent group's version at ctor
-
-            /// <summary>
-            /// Initializes a new instance of the LeafEnumerator class.
-            /// </summary>
-            /// <param name="group">CollectionViewGroupInternal that uses the enumerator</param>
-            public LeafEnumerator(DataGridCollectionViewGroupInternal group)
-            {
-                _group = group;
-                DoReset();  // don't call virtual Reset in ctor
-            }
-
-            /// <summary>
-            /// Private helper to reset the enumerator
-            /// </summary>
-            private void DoReset()
-            {
-                Debug.Assert(_group != null, "_group should have been initialized in constructor");
-                _version = _group._version;
-                _index = -1;
-                _subEnum = null;
-            }
-
-            /// <summary>
-            /// Reset implementation for IEnumerator
-            /// </summary>
-            void IEnumerator.Reset()
-            {
-                DoReset();
-            }
-
-            /// <summary>
-            /// MoveNext implementation for IEnumerator
-            /// </summary>
-            /// <returns>Returns whether the MoveNext operation was successful</returns>
-            bool IEnumerator.MoveNext()
-            {
-                Debug.Assert(_group != null, "_group should have been initialized in constructor");
-
-                // check for invalidated enumerator
-                if (_group._version != _version)
-                {
-                    throw new InvalidOperationException();
-                }
-
-                // move forward to the next leaf
-                while (_subEnum == null || !_subEnum.MoveNext())
-                {
-                    // done with the current top-level item.  Move to the next one.
-                    ++_index;
-                    if (_index >= _group.Items.Count)
-                    {
-                        return false;
-                    }
-
-                    DataGridCollectionViewGroupInternal subgroup = _group.Items[_index] as DataGridCollectionViewGroupInternal;
-                    if (subgroup == null)
-                    {
-                        // current item is a leaf - it's the new Current
-                        _current = _group.Items[_index];
-                        _subEnum = null;
-                        return true;
-                    }
-                    else
-                    {
-                        // current item is a subgroup - get its enumerator
-                        _subEnum = subgroup.GetLeafEnumerator();
-                    }
-                }
-
-                // the loop terminates only when we have a subgroup enumerator
-                // positioned at the new Current item
-                _current = _subEnum.Current;
-                return true;
-            }
-
-            /// <summary>
-            /// Gets the current implementation for IEnumerator
-            /// </summary>
-            object IEnumerator.Current
-            {
-                get
-                {
-                    Debug.Assert(_group != null, "_group should have been initialized in constructor");
-
-                    if (_index < 0 || _index >= _group.Items.Count)
-                    {
-                        throw new InvalidOperationException();
-                    }
-
-                    return _current;
-                }
-            }
-
-        }
-
-        // / <summary>
-        // / This comparer is used to insert an item into a group in a position consistent
-        // / with a given IList.  It only works when used in the pattern that FindIndex
-        // / uses, namely first call Reset(), then call Compare(item, itemSequence) any number of
-        // / times with the same item (the new item) as the first argument, and a sequence
-        // / of items as the second argument that appear in the IList in the same sequence.
-        // / This makes the total search time linear in the size of the IList.  (To give
-        // / the correct answer regardless of the sequence of arguments would involve
-        // / calling IndexOf and leads to O(N^2) total search time.) 
-        // / </summary>
-        internal class ListComparer : IComparer
-        {
-            /// <summary>
-            /// Constructor for the ListComparer that takes
-            /// in an IList.
-            /// </summary>
-            /// <param name="list">IList used to compare on</param>
-            internal ListComparer(IList list)
-            {
-                ResetList(list);
-            }
-
-            /// <summary>
-            /// Sets the index that we start comparing
-            /// from to 0.
-            /// </summary>
-            internal void Reset()
-            {
-                _index = 0;
-            }
-
-            /// <summary>
-            /// Sets our IList to a new instance
-            /// of a list being passed in and resets
-            /// the index.
-            /// </summary>
-            /// <param name="list">IList used to compare on</param>
-            internal void ResetList(IList list)
-            {
-                _list = list;
-                _index = 0;
-            }
-
-            /// <summary>
-            /// Compares objects x and y to see which one
-            /// should appear first.
-            /// </summary>
-            /// <param name="x">The first object</param>
-            /// <param name="y">The second object</param>
-            /// <returns>-1 if x is less than y, +1 otherwise</returns>
-            public int Compare(object x, object y)
-            {
-                if (Object.Equals(x, y))
-                {
-                    return 0;
-                }
-
-                // advance the index until seeing one x or y
-                int n = (_list != null) ? _list.Count : 0;
-                for (; _index < n; ++_index)
-                {
-                    object z = _list[_index];
-                    if (Object.Equals(x, z))
-                    {
-                        return -1;  // x occurs first, so x < y
-                    }
-                    else if (Object.Equals(y, z))
-                    {
-                        return +1;  // y occurs first, so x > y
-                    }
-                }
-
-                // if we don't see either x or y, declare x > y.
-                // This has the effect of putting x at the end of the list.
-                return +1;
-            }
-
-            private int _index;
-            private IList _list;
-        }
-
-        // / <summary>
-        // / This comparer is used to insert an item into a group in a position consistent
-        // / with a given CollectionViewGroupRoot. We will only use this when dealing with
-        // / a temporary CollectionViewGroupRoot that points to the correct grouping of the
-        // / entire collection, and we have paging that requires us to keep the paged group
-        // / consistent with the order of items in the temporary group.
-        // / </summary>
-        internal class CollectionViewGroupComparer : IComparer
-        {
-            /// <summary>
-            /// Constructor for the CollectionViewGroupComparer that takes
-            /// in an CollectionViewGroupRoot.
-            /// </summary>
-            /// <param name="group">CollectionViewGroupRoot used to compare on</param>
-            internal CollectionViewGroupComparer(CollectionViewGroupRoot group)
-            {
-                ResetGroup(group);
-            }
-
-            /// <summary>
-            /// Sets the index that we start comparing
-            /// from to 0.
-            /// </summary>
-            internal void Reset()
-            {
-                _index = 0;
-            }
-
-            /// <summary>
-            /// Sets our group to a new instance of a
-            /// CollectionViewGroupRoot being passed in
-            /// and resets the index.
-            /// </summary>
-            /// <param name="group">CollectionViewGroupRoot used to compare on</param>
-            internal void ResetGroup(CollectionViewGroupRoot group)
-            {
-                _group = group;
-                _index = 0;
-            }
-
-            /// <summary>
-            /// Compares objects x and y to see which one
-            /// should appear first.
-            /// </summary>
-            /// <param name="x">The first object</param>
-            /// <param name="y">The second object</param>
-            /// <returns>-1 if x is less than y, +1 otherwise</returns>
-            public int Compare(object x, object y)
-            {
-                if (Object.Equals(x, y))
-                {
-                    return 0;
-                }
-
-                // advance the index until seeing one x or y
-                int n = (_group != null) ? _group.ItemCount : 0;
-                for (; _index < n; ++_index)
-                {
-                    object z = _group.LeafAt(_index);
-                    if (Object.Equals(x, z))
-                    {
-                        return -1;  // x occurs first, so x < y
-                    }
-                    else if (Object.Equals(y, z))
-                    {
-                        return +1;  // y occurs first, so x > y
-                    }
-                }
-
-                // if we don't see either x or y, declare x > y.
-                // This has the effect of putting x at the end of the list.
-                return +1;
-            }
-
-            private int _index;
-            private CollectionViewGroupRoot _group;
-        }
-
-    }
-
-    internal class CollectionViewGroupRoot : DataGridCollectionViewGroupInternal, INotifyCollectionChanged
-    {
-        /// <summary>
-        /// String constant used for the Root Name
-        /// </summary>
-        private const string RootName = "Root";
-
-        /// <summary>
-        /// Private accessor for empty object instance
-        /// </summary>
-        private static readonly object UseAsItemDirectly = new object();
-
-        /// <summary>
-        /// Private accessor for the top level GroupDescription
-        /// </summary>
-        private static DataGridGroupDescription topLevelGroupDescription;
-
-        /// <summary>
-        /// Private accessor for an ObservableCollection containing group descriptions
-        /// </summary>
-        private readonly AvaloniaList<DataGridGroupDescription> _groupBy = new AvaloniaList<DataGridGroupDescription>();
-
-        /// <summary>
-        /// Indicates whether the list of items (after applying the sort and filters, if any) 
-        /// is already in the correct order for grouping.
-        /// </summary>
-        private bool _isDataInGroupOrder;
-
-        /// <summary>
-        /// Private accessor for the owning ICollectionView
-        /// </summary>
-        private readonly IDataGridCollectionView _view;
-
-        /// <summary>
-        /// Raise this event when the (grouped) view changes
-        /// </summary>
-        public event NotifyCollectionChangedEventHandler CollectionChanged;
-
-        /// <summary>
-        /// Raise this event when the GroupDescriptions change
-        /// </summary>
-        internal event EventHandler GroupDescriptionChanged;
-
-        /// <summary>
-        /// Initializes a new instance of the CollectionViewGroupRoot class.
-        /// </summary>
-        /// <param name="view">CollectionView that contains this grouping</param>
-        /// <param name="isDataInGroupOrder">True if items are already in correct order for grouping</param>
-        internal CollectionViewGroupRoot(IDataGridCollectionView view, bool isDataInGroupOrder)
-            : base(RootName, null)
-        {
-            _view = view;
-            _isDataInGroupOrder = isDataInGroupOrder;
-        }
-
-        /// <summary>
-        /// Gets the description of grouping, indexed by level.
-        /// </summary>
-        public virtual AvaloniaList<DataGridGroupDescription> GroupDescriptions => _groupBy;
-
-        /// <summary>
-        /// Gets or sets the current IComparer being used
-        /// </summary>
-        internal IComparer ActiveComparer { get; set; }
-
-        /// <summary>
-        /// Gets the culture to use during sorting.
-        /// </summary>
-        internal CultureInfo Culture
-        {
-            get
-            {
-                Debug.Assert(_view != null, "this._view should have been set from the constructor");
-                return _view.Culture;
-            }
-        }
-
-        /// <summary>
-        /// Gets or sets a value indicating whether the data is in group order
-        /// </summary>
-        internal bool IsDataInGroupOrder
-        {
-            get { return _isDataInGroupOrder; }
-            set { _isDataInGroupOrder = value; }
-        }
-
-        /// <summary>
-        /// Finds the index of the specified item
-        /// </summary>
-        /// <param name="item">Item we are looking for</param>
-        /// <param name="seed">Seed of the item we are looking for</param>
-        /// <param name="comparer">Comparer used to find the item</param>
-        /// <param name="low">Low range of item index</param>
-        /// <param name="high">High range of item index</param>
-        /// <returns>Index of the specified item</returns>
-        protected override int FindIndex(object item, object seed, IComparer comparer, int low, int high)
-        {
-            // root group needs to adjust the bounds of the search to exclude the new item (if any)
-            if (_view is IDataGridEditableCollectionView iecv && iecv.IsAddingNew)
-            {
-                --high;
-            }
-
-            return base.FindIndex(item, seed, comparer, low, high);
-        }
-
-        /// <summary>
-        /// Initializes the group descriptions
-        /// </summary>
-        internal void Initialize()
-        {
-            if (topLevelGroupDescription == null)
-            {
-                topLevelGroupDescription = new TopLevelGroupDescription();
-            }
-
-            InitializeGroup(this, 0, null);
-        }
-
-        /// <summary>
-        /// Inserts specified item into the collection
-        /// </summary>
-        /// <param name="index">Index to insert into</param>
-        /// <param name="item">Item to insert</param>
-        /// <param name="loading">Whether we are currently loading</param>
-        internal void InsertSpecialItem(int index, object item, bool loading)
-        {
-            ChangeCounts(item, +1);
-            ProtectedItems.Insert(index, item);
-
-            if (!loading)
-            {
-                int globalIndex = LeafIndexFromItem(item, index);
-                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, globalIndex));
-            }
-        }
-
-        /// <summary>
-        /// Notify listeners that this View has changed
-        /// </summary>
-        /// <remarks>
-        /// CollectionViews (and sub-classes) should take their filter/sort/grouping
-        /// into account before calling this method to forward CollectionChanged events.
-        /// </remarks>
-        /// <param name="args">The NotifyCollectionChangedEventArgs to be passed to the EventHandler</param>
-        public void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
-        {
-            Debug.Assert(args != null, "Arguments passed in should not be null");
-            CollectionChanged?.Invoke(this, args);
-        }
-
-        /// <summary>
-        /// Notify host that a group description has changed somewhere in the tree
-        /// </summary>
-        protected override void OnGroupByChanged()
-        {
-            GroupDescriptionChanged?.Invoke(this, EventArgs.Empty);
-        }
-
-        /// <summary>
-        /// Remove specified item from subgroups
-        /// </summary>
-        /// <param name="item">Item to remove</param>
-        /// <returns>Whether the operation was successful</returns>
-        internal bool RemoveFromSubgroups(object item)
-        {
-            return RemoveFromSubgroups(item, this, 0);
-        }
-
-        /// <summary>
-        /// Remove specified item from subgroups using an exhaustive search
-        /// </summary>
-        /// <param name="item">Item to remove</param>
-        internal void RemoveItemFromSubgroupsByExhaustiveSearch(object item)
-        {
-            RemoveItemFromSubgroupsByExhaustiveSearch(this, item);
-        }
-
-        /// <summary>
-        /// Removes specified item into the collection
-        /// </summary>
-        /// <param name="index">Index to remove from</param>
-        /// <param name="item">Item to remove</param>
-        /// <param name="loading">Whether we are currently loading</param>
-        internal void RemoveSpecialItem(int index, object item, bool loading)
-        {
-            Debug.Assert(Object.Equals(item, ProtectedItems[index]), "RemoveSpecialItem finds inconsistent data");
-            int globalIndex = -1;
-
-            if (!loading)
-            {
-                globalIndex = LeafIndexFromItem(item, index);
-            }
-
-            ChangeCounts(item, -1);
-            ProtectedItems.RemoveAt(index);
-
-            if (!loading)
-            {
-                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, globalIndex));
-            }
-        }
-
-        /// <summary>
-        /// Adds specified item to subgroups
-        /// </summary>
-        /// <param name="item">Item to add</param>
-        /// <param name="loading">Whether we are currently loading</param>
-        internal void AddToSubgroups(object item, bool loading)
-        {
-            AddToSubgroups(item, this, 0, loading);
-        }
-
-        /// <summary>
-        /// Add an item to the subgroup with the given name
-        /// </summary>
-        /// <param name="item">Item to add</param>
-        /// <param name="group">Group to add item to</param>
-        /// <param name="level">The level of grouping.</param>
-        /// <param name="key">Name of subgroup to add to</param>
-        /// <param name="loading">Whether we are currently loading</param>
-        private void AddToSubgroup(object item, DataGridCollectionViewGroupInternal group, int level, object key, bool loading)
-        {
-            DataGridCollectionViewGroupInternal subgroup;
-            int index = (_isDataInGroupOrder) ? group.LastIndex : 0;
-
-            // find the desired subgroup
-            for (int n = group.Items.Count; index < n; ++index)
-            {
-                subgroup = group.Items[index] as DataGridCollectionViewGroupInternal;
-                if (subgroup == null)
-                {
-                    continue;           // skip children that are not groups
-                }
-
-                if (group.GroupBy.KeysMatch(subgroup.Key, key))
-                {
-                    group.LastIndex = index;
-                    AddToSubgroups(item, subgroup, level + 1, loading);
-                    return;
-                }
-            }
-
-            // the item didn't match any subgroups.  Create a new subgroup and add the item.
-            subgroup = new DataGridCollectionViewGroupInternal(key, group);
-            InitializeGroup(subgroup, level + 1, item);
-
-            if (loading)
-            {
-                group.Add(subgroup);
-                group.LastIndex = index;
-            }
-            else
-            {
-                // using insert will find the correct sort index to
-                // place the subgroup, and will default to the last
-                // position if no ActiveComparer is specified
-                group.Insert(subgroup, item, ActiveComparer);
-            }
-
-            AddToSubgroups(item, subgroup, level + 1, loading);
-        }
-
-        /// <summary>
-        /// Add an item to the desired subgroup(s) of the given group
-        /// </summary>
-        /// <param name="item">Item to add</param>
-        /// <param name="group">Group to add item to</param>
-        /// <param name="level">The level of grouping</param>
-        /// <param name="loading">Whether we are currently loading</param>
-        private void AddToSubgroups(object item, DataGridCollectionViewGroupInternal group, int level, bool loading)
-        {
-            object key = GetGroupKey(item, group.GroupBy, level);
-
-            if (key == UseAsItemDirectly)
-            {
-                // the item belongs to the group itself (not to any subgroups)
-                if (loading)
-                {
-                    group.Add(item);
-                }
-                else
-                {
-                    int localIndex = group.Insert(item, item, ActiveComparer);
-                    int index = group.LeafIndexFromItem(item, localIndex);
-                    OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, item, index));
-                }
-            }
-            else if(key is ICollection keyList)
-            {
-                // the item belongs to multiple subgroups
-                foreach (object o in keyList)
-                {
-                    AddToSubgroup(item, group, level, o, loading);
-                }
-            }
-            else
-            {
-                // the item belongs to one subgroup
-                AddToSubgroup(item, group, level, key, loading);
-            }
-        }
-
-        public virtual Func<DataGridCollectionViewGroup, int, DataGridGroupDescription> GroupBySelector { get; set; }
-
-        /// <summary>
-        /// Returns the description of how to divide the given group into subgroups
-        /// </summary>
-        /// <param name="group">CollectionViewGroup to get group description from</param>
-        /// <param name="level">The level of grouping</param>
-        /// <returns>GroupDescription of how to divide the given group</returns>
-        private DataGridGroupDescription GetGroupDescription(DataGridCollectionViewGroup group, int level)
-        {
-            DataGridGroupDescription result = null;
-            if (group == this)
-            {
-                group = null;
-            }
-
-            if (result == null && GroupBySelector != null)
-            {
-                result = GroupBySelector?.Invoke(group, level);
-            }
-
-            if (result == null && level < GroupDescriptions.Count)
-            {
-                result = GroupDescriptions[level];
-            }
-
-            return result;
-        }
-
-        /// <summary>
-        /// Get the group name(s) for the given item
-        /// </summary>
-        /// <param name="item">Item to get group name for</param>
-        /// <param name="groupDescription">GroupDescription for the group</param>
-        /// <param name="level">The level of grouping</param>
-        /// <returns>Group names for the specified item</returns>
-        private object GetGroupKey(object item, DataGridGroupDescription groupDescription, int level)
-        {
-            if (groupDescription != null)
-            {
-                return groupDescription.GroupKeyFromItem(item, level, Culture);
-            }
-            else
-            {
-                return UseAsItemDirectly;
-            }
-        }
-
-        /// <summary>
-        /// Initialize the given group
-        /// </summary>
-        /// <param name="group">Group to initialize</param>
-        /// <param name="level">The level of grouping</param>
-        /// <param name="seedItem">The seed item to compare with to see where to insert</param>
-        private void InitializeGroup(DataGridCollectionViewGroupInternal group, int level, object seedItem)
-        {
-            // set the group description for dividing the group into subgroups
-            DataGridGroupDescription groupDescription = GetGroupDescription(group, level);
-            group.GroupBy = groupDescription;
-
-            // create subgroups for each of the explicit names
-            var keys = groupDescription?.GroupKeys;
-            if (keys != null)
-            {
-                for (int k = 0, n = keys.Count; k < n; ++k)
-                {
-                    DataGridCollectionViewGroupInternal subgroup = new DataGridCollectionViewGroupInternal(keys[k], group);
-                    InitializeGroup(subgroup, level + 1, seedItem);
-                    group.Add(subgroup);
-                }
-            }
-
-            group.LastIndex = 0;
-        }
-
-        /// <summary>
-        /// Remove an item from the direct children of a group.
-        /// </summary>
-        /// <param name="group">Group to remove item from</param>
-        /// <param name="item">Item to remove</param>
-        /// <returns>True if item could not be removed</returns>
-        private bool RemoveFromGroupDirectly(DataGridCollectionViewGroupInternal group, object item)
-        {
-            int leafIndex = group.Remove(item, true);
-            if (leafIndex >= 0)
-            {
-                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, item, leafIndex));
-                return false;
-            }
-            else
-            {
-                return true;
-            }
-        }
-
-        /// <summary>
-        /// Remove an item from the subgroup with the given name.
-        /// </summary>
-        /// <param name="item">Item to remove</param>
-        /// <param name="group">Group to remove item from</param>
-        /// <param name="level">The level of grouping</param>
-        /// <param name="key">Name of item to remove</param>
-        /// <returns>Return true if the item was not in one of the subgroups it was supposed to be.</returns>
-        private bool RemoveFromSubgroup(object item, DataGridCollectionViewGroupInternal group, int level, object key)
-        {
-            bool itemIsMissing = false;
-            DataGridCollectionViewGroupInternal subgroup;
-
-            // find the desired subgroup
-            for (int index = 0, n = group.Items.Count; index < n; ++index)
-            {
-                subgroup = group.Items[index] as DataGridCollectionViewGroupInternal;
-                if (subgroup == null)
-                {
-                    continue;           // skip children that are not groups
-                }
-
-                if (group.GroupBy.KeysMatch(subgroup.Key, key))
-                {
-                    if (RemoveFromSubgroups(item, subgroup, level + 1))
-                    {
-                        itemIsMissing = true;
-                    }
-
-                    return itemIsMissing;
-                }
-            }
-
-            // the item didn't match any subgroups.  It should have.
-            return true;
-        }
-
-        /// <summary>
-        /// Remove an item from the desired subgroup(s) of the given group.
-        /// </summary>
-        /// <param name="item">Item to remove</param>
-        /// <param name="group">Group to remove item from</param>
-        /// <param name="level">The level of grouping</param>
-        /// <returns>Return true if the item was not in one of the subgroups it was supposed to be.</returns>
-        private bool RemoveFromSubgroups(object item, DataGridCollectionViewGroupInternal group, int level)
-        {
-            bool itemIsMissing = false;
-            object key = GetGroupKey(item, group.GroupBy, level);
-
-            if (key == UseAsItemDirectly)
-            {
-                // the item belongs to the group itself (not to any subgroups)
-                itemIsMissing = RemoveFromGroupDirectly(group, item);
-            }
-            else if (key is ICollection keyList)
-            {
-                // the item belongs to multiple subgroups
-                foreach (object o in keyList)
-                {
-                    if (RemoveFromSubgroup(item, group, level, o))
-                    {
-                        itemIsMissing = true;
-                    }
-                }
-            }
-            else
-            {
-                // the item belongs to one subgroup
-                if (RemoveFromSubgroup(item, group, level, key))
-                {
-                    itemIsMissing = true;
-                }
-            }
-
-            return itemIsMissing;
-        }
-
-        /// <summary>
-        /// The item did not appear in one or more of the subgroups it
-        /// was supposed to.  This can happen if the item's properties
-        /// change so that the group names we used to insert it are
-        /// different from the names used to remove it. If this happens,
-        /// remove the item the hard way.
-        /// </summary>
-        /// <param name="group">Group to remove item from</param>
-        /// <param name="item">Item to remove</param>
-        private void RemoveItemFromSubgroupsByExhaustiveSearch(DataGridCollectionViewGroupInternal group, object item)
-        {
-            // try to remove the item from the direct children 
-            // this function only returns true if it failed to remove from group directly
-            // in which case we will step through and search exhaustively
-            if (RemoveFromGroupDirectly(group, item))
-            {
-                // if that didn't work, recurse into each subgroup
-                // (loop runs backwards in case an entire group is deleted)
-                for (int k = group.Items.Count - 1; k >= 0; --k)
-                {
-                    if (group.Items[k] is DataGridCollectionViewGroupInternal subgroup)
-                    {
-                        RemoveItemFromSubgroupsByExhaustiveSearch(subgroup, item);
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// TopLevelGroupDescription class
-        /// </summary>
-        private class TopLevelGroupDescription : DataGridGroupDescription
-        {
-            /// <summary>
-            /// Initializes a new instance of the TopLevelGroupDescription class.
-            /// </summary>
-            public TopLevelGroupDescription()
-            {
-            }
-
-            /// <summary>
-            /// We have to implement this abstract method, but it should never be called
-            /// </summary>
-            /// <param name="item">Item to get group name from</param>
-            /// <param name="level">The level of grouping</param>
-            /// <param name="culture">Culture used for sorting</param>
-            /// <returns>We do not return a value here</returns>
-            public override object GroupKeyFromItem(object item, int level, CultureInfo culture)
-            {
-                Debug.Assert(true, "We have to implement this abstract method, but it should never be called");
-                return null;
-            }
-        }
-    }
-
-}

+ 0 - 324
src/Avalonia.Controls.DataGrid/Collections/DataGridSortDescription.cs

@@ -1,324 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Globalization;
-using System.Linq;
-using Avalonia.Controls.Utils;
-
-namespace Avalonia.Collections
-{
-    public abstract class DataGridSortDescription
-    {
-        public virtual string PropertyPath => null;
-
-        public virtual ListSortDirection Direction => ListSortDirection.Ascending;
-        public bool HasPropertyPath => !String.IsNullOrEmpty(PropertyPath);
-        public abstract IComparer<object> Comparer { get; }
-
-        public virtual IOrderedEnumerable<object> OrderBy(IEnumerable<object> seq)
-        {
-            return seq.OrderBy(o => o, Comparer);
-        }
-        public virtual IOrderedEnumerable<object> ThenBy(IOrderedEnumerable<object> seq)
-        {
-            return seq.ThenBy(o => o, Comparer);
-        }
-
-        public virtual DataGridSortDescription SwitchSortDirection()
-        {
-            return this;
-        }
-
-        internal virtual void Initialize(Type itemType)
-        { }
-
-        private static object InvokePath(object item, string propertyPath, Type propertyType)
-        {
-            object propertyValue = TypeHelper.GetNestedPropertyValue(item, propertyPath, propertyType, out Exception exception);
-            if (exception != null)
-            {
-                throw exception;
-            }
-            return propertyValue;
-        }
-
-        /// <summary>
-        /// Creates a comparer class that takes in a CultureInfo as a parameter,
-        /// which it will use when comparing strings.
-        /// </summary>
-        private class CultureSensitiveComparer : Comparer<object>
-        {
-            /// <summary>
-            /// Private accessor for the CultureInfo of our comparer
-            /// </summary>
-            private CultureInfo _culture;
-
-            /// <summary>
-            /// Creates a comparer which will respect the CultureInfo
-            /// that is passed in when comparing strings.
-            /// </summary>
-            /// <param name="culture">The CultureInfo to use in string comparisons</param>
-            public CultureSensitiveComparer(CultureInfo culture)
-                : base()
-            {
-                _culture = culture ?? CultureInfo.InvariantCulture;
-            }
-
-            /// <summary>
-            /// Compares two objects and returns a value indicating whether one is less than, equal to or greater than the other.
-            /// </summary>
-            /// <param name="x">first item to compare</param>
-            /// <param name="y">second item to compare</param>
-            /// <returns>Negative number if x is less than y, zero if equal, and a positive number if x is greater than y</returns>
-            /// <remarks>
-            /// Compares the 2 items using the specified CultureInfo for string and using the default object comparer for all other objects.
-            /// </remarks>
-            public override int Compare(object x, object y)
-            {
-                if (x == null)
-                {
-                    if (y != null)
-                    {
-                        return -1;
-                    }
-                    return 0;
-                }
-                if (y == null)
-                {
-                    return 1;
-                }
-
-                // at this point x and y are not null
-                if (x.GetType() == typeof(string) && y.GetType() == typeof(string))
-                {
-                    return _culture.CompareInfo.Compare((string)x, (string)y);
-                }
-                else
-                {
-                    return Comparer<object>.Default.Compare(x, y);
-                }
-            }
-
-        }
-
-        private class DataGridPathSortDescription : DataGridSortDescription
-        {
-            private readonly ListSortDirection _direction;
-            private readonly string _propertyPath;
-            private readonly Lazy<CultureSensitiveComparer> _cultureSensitiveComparer;
-            private readonly Lazy<IComparer<object>> _comparer;
-            private Type _propertyType;
-            private IComparer _internalComparer;
-            private IComparer<object> _internalComparerTyped;
-            private IComparer<object> InternalComparer
-            {
-                get
-                {
-                    if (_internalComparerTyped == null && _internalComparer != null)
-                    {
-                        if (_internalComparer is IComparer<object> c)
-                            _internalComparerTyped = c;
-                        else
-                            _internalComparerTyped = Comparer<object>.Create((x, y) => _internalComparer.Compare(x, y));
-                    }
-
-                    return _internalComparerTyped;
-                }
-            }
-
-            public override string PropertyPath => _propertyPath;
-            public override IComparer<object> Comparer => _comparer.Value;
-            public override ListSortDirection Direction => _direction;
-
-            public DataGridPathSortDescription(string propertyPath, ListSortDirection direction, IComparer internalComparer, CultureInfo culture)
-            {
-                _propertyPath = propertyPath;
-                _direction = direction;
-                _cultureSensitiveComparer = new Lazy<CultureSensitiveComparer>(() => new CultureSensitiveComparer(culture ?? CultureInfo.CurrentCulture));
-                _internalComparer = internalComparer;
-                _comparer = new Lazy<IComparer<object>>(() => Comparer<object>.Create((x, y) => Compare(x, y)));
-            }
-            private DataGridPathSortDescription(DataGridPathSortDescription inner, ListSortDirection direction)
-            {
-                _propertyPath = inner._propertyPath;
-                _direction = direction;
-                _propertyType = inner._propertyType;
-                _cultureSensitiveComparer = inner._cultureSensitiveComparer;
-                _internalComparer = inner._internalComparer;
-                _internalComparerTyped = inner._internalComparerTyped;
-
-                _comparer = new Lazy<IComparer<object>>(() => Comparer<object>.Create((x, y) => Compare(x, y)));
-            }
-
-            private object GetValue(object o)
-            {
-                if (o == null)
-                    return null;
-
-                if (HasPropertyPath)
-                    return InvokePath(o, _propertyPath, _propertyType);
-
-                if (_propertyType == o.GetType())
-                    return o;
-                else
-                    return null;
-            }
-
-            private IComparer GetComparerForType(Type type)
-            {
-                if (type == typeof(string))
-                    return _cultureSensitiveComparer.Value;
-                else
-                    return GetComparerForNotStringType(type);
-            }
-
-            internal static IComparer GetComparerForNotStringType(Type type)
-            {
-#if NET6_0_OR_GREATER
-                if(System.Runtime.CompilerServices.RuntimeFeature.IsDynamicCodeSupported == false)
-                {
-                    if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>) && type.GetGenericArguments()[0].IsAssignableTo(typeof(IComparable)))
-                        return Comparer<object>.Create((x, y) => 
-                        {
-                            if (x == null)
-                                return y == null ? 0 : -1;
-                            else
-                                return (x as IComparable)!.CompareTo(y);
-                        });
-                    else if (type.IsAssignableTo(typeof(IComparable))) //enum should be here
-                        return Comparer<object>.Create((x, y) => (x as IComparable)!.CompareTo(y));
-                    else
-                        return Comparer<object>.Create((x, y) => 0); //avoid using reflection to avoid crash on AOT
-                }
-                else
-#endif
-                return (typeof(Comparer<>).MakeGenericType(type).GetProperty("Default")).GetValue(null, null) as IComparer;
-
-            }
-
-            private Type GetPropertyType(object o)
-            {
-                return o.GetType().GetNestedPropertyType(_propertyPath);
-            }
-
-            private int Compare(object x, object y)
-            {
-                int result = 0;
-
-                if(_propertyType == null)
-                {
-                    if(x != null)
-                    {
-                        _propertyType = GetPropertyType(x);
-                    }
-                    if(_propertyType == null && y != null)
-                    {
-                        _propertyType = GetPropertyType(y);
-                    }
-                }
-
-                object v1 = GetValue(x);
-                object v2 = GetValue(y);
-
-                if (_propertyType != null && _internalComparer == null)
-                    _internalComparer = GetComparerForType(_propertyType);
-
-                result = _internalComparer?.Compare(v1, v2) ?? 0;
-
-                if (Direction == ListSortDirection.Descending)
-                    return -result;
-                else
-                    return result;
-            }
-
-            internal override void Initialize(Type itemType)
-            {
-                base.Initialize(itemType);
-
-                if(_propertyType == null)
-                    _propertyType = itemType.GetNestedPropertyType(_propertyPath);
-                if (_internalComparer == null && _propertyType != null)
-                    _internalComparer = GetComparerForType(_propertyType);
-            }
-            public override IOrderedEnumerable<object> OrderBy(IEnumerable<object> seq)
-            {
-                if (Direction == ListSortDirection.Descending)
-                {
-                    return seq.OrderByDescending(o => GetValue(o), InternalComparer);
-                }
-                else
-                {
-                    return seq.OrderBy(o => GetValue(o), InternalComparer);
-                }
-            }
-            public override IOrderedEnumerable<object> ThenBy(IOrderedEnumerable<object> seq)
-            {
-                if (Direction == ListSortDirection.Descending)
-                {
-                    return seq.ThenByDescending(o => GetValue(o), InternalComparer);
-                }
-                else
-                {
-                    return seq.ThenBy(o => GetValue(o), InternalComparer);
-                }
-            }
-
-            public override DataGridSortDescription SwitchSortDirection()
-            {
-                var newDirection = _direction == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending;
-                return new DataGridPathSortDescription(this, newDirection);
-            }
-        }
-
-        public static DataGridSortDescription FromPath(string propertyPath, ListSortDirection direction = ListSortDirection.Ascending, CultureInfo culture = null)
-        {
-            return new DataGridPathSortDescription(propertyPath, direction, null, culture);
-        }
-
-        public static DataGridSortDescription FromPath(string propertyPath, ListSortDirection direction, IComparer comparer)
-        {
-            return new DataGridPathSortDescription(propertyPath, direction, comparer, null);
-        }
-
-        public static DataGridSortDescription FromComparer(IComparer comparer, ListSortDirection direction = ListSortDirection.Ascending)
-        {
-            return new DataGridComparerSortDescription(comparer, direction);
-        }
-    }
-
-    public class DataGridComparerSortDescription : DataGridSortDescription
-    {
-        private readonly IComparer _innerComparer;
-        private readonly ListSortDirection _direction;
-        private readonly IComparer<object> _comparer;
-
-        public IComparer SourceComparer => _innerComparer;
-        public override IComparer<object> Comparer => _comparer;
-        public override ListSortDirection Direction => _direction;
-        public DataGridComparerSortDescription(IComparer comparer, ListSortDirection direction)
-        {
-            _innerComparer = comparer;
-            _direction = direction;
-            _comparer = Comparer<object>.Create((x, y) => Compare(x, y));
-        }
-
-        private int Compare(object x, object y)
-        {
-            int result = _innerComparer.Compare(x, y);
-
-            if (Direction == ListSortDirection.Descending)
-                return -result;
-            else
-                return result;
-        }
-        public override DataGridSortDescription SwitchSortDirection()
-        {
-            var newDirection = _direction == ListSortDirection.Ascending ? ListSortDirection.Descending : ListSortDirection.Ascending;
-            return new DataGridComparerSortDescription(_innerComparer, newDirection);
-        }
-    }
-
-    public class DataGridSortDescriptionCollection : AvaloniaList<DataGridSortDescription>
-    { }
-}

+ 0 - 233
src/Avalonia.Controls.DataGrid/Collections/IDataGridCollectionView.cs

@@ -1,233 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.Globalization;
-using System.Text;
-
-namespace Avalonia.Collections
-{
-    /// <summary>Provides data for the <see cref="E:Avalonia.Collections.ICollectionView.CurrentChanging" /> event.</summary>
-    public class DataGridCurrentChangingEventArgs : EventArgs
-    {
-        private bool _cancel;
-        private bool _isCancelable;
-
-        /// <summary>Initializes a new instance of the <see cref="T:System.ComponentModel.CurrentChangingEventArgs" /> class and sets the <see cref="P:System.ComponentModel.CurrentChangingEventArgs.IsCancelable" /> property to true.</summary>
-        public DataGridCurrentChangingEventArgs()
-        {
-            Initialize(true);
-        }
-
-        /// <summary>Initializes a new instance of the <see cref="T:System.ComponentModel.CurrentChangingEventArgs" /> class and sets the <see cref="P:System.ComponentModel.CurrentChangingEventArgs.IsCancelable" /> property to the specified value.</summary>
-        /// <param name="isCancelable">true to disable the ability to cancel a <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> change; false to enable cancellation.</param>
-        public DataGridCurrentChangingEventArgs(bool isCancelable)
-        {
-            Initialize(isCancelable);
-        }
-
-        private void Initialize(bool isCancelable)
-        {
-            _isCancelable = isCancelable;
-        }
-
-        /// <summary>Gets a value that indicates whether the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> change can be canceled. </summary>
-        /// <returns>true if the event can be canceled; false if the event cannot be canceled.</returns>
-        public bool IsCancelable
-        {
-            get
-            {
-                return _isCancelable;
-            }
-        }
-
-        /// <summary>Gets or sets a value that indicates whether the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> change should be canceled. </summary>
-        /// <returns>true if the event should be canceled; otherwise, false. The default is false.</returns>
-        /// <exception cref="T:System.InvalidOperationException">The <see cref="P:System.ComponentModel.CurrentChangingEventArgs.IsCancelable" /> property value is false.</exception>
-        public bool Cancel
-        {
-            get
-            {
-                return _cancel;
-            }
-            set
-            {
-                if (IsCancelable)
-                    _cancel = value;
-                else if (value)
-                    throw new InvalidOperationException("CurrentChanging Cannot Be Canceled");
-            }
-        }
-    }
-
-    /// <summary>Enables collections to have the functionalities of current record management, custom sorting, filtering, and grouping.</summary>
-    public interface IDataGridCollectionView : IEnumerable, INotifyCollectionChanged
-    {
-        /// <summary>Gets or sets the cultural information for any operations of the view that may differ by culture, such as sorting.</summary>
-        /// <returns>The culture information to use during culture-sensitive operations. </returns>
-        CultureInfo Culture { get; set; }
-
-        /// <summary>Indicates whether the specified item belongs to this collection view. </summary>
-        /// <returns>true if the item belongs to this collection view; otherwise, false.</returns>
-        /// <param name="item">The object to check. </param>
-        bool Contains(object item);
-
-        /// <summary>Gets the underlying collection.</summary>
-        /// <returns>The underlying collection.</returns>
-        IEnumerable SourceCollection { get; }
-
-        /// <summary>Gets or sets a callback that is used to determine whether an item is appropriate for inclusion in the view. </summary>
-        /// <returns>A method that is used to determine whether an item is appropriate for inclusion in the view.</returns>
-        Func<object, bool> Filter { get; set; }
-
-        /// <summary>Gets a value that indicates whether this view supports filtering by way of the <see cref="P:System.ComponentModel.ICollectionView.Filter" /> property.</summary>
-        /// <returns>true if this view supports filtering; otherwise, false.</returns>
-        bool CanFilter { get; }
-
-        /// <summary>Gets a collection of <see cref="T:System.ComponentModel.SortDescription" /> instances that describe how the items in the collection are sorted in the view.</summary>
-        /// <returns>A collection of values that describe how the items in the collection are sorted in the view.</returns>
-        DataGridSortDescriptionCollection SortDescriptions { get; }
-
-        /// <summary>Gets a value that indicates whether this view supports sorting by way of the <see cref="P:System.ComponentModel.ICollectionView.SortDescriptions" /> property.</summary>
-        /// <returns>true if this view supports sorting; otherwise, false.</returns>
-        bool CanSort { get; }
-
-        /// <summary>Gets a value that indicates whether this view supports grouping by way of the <see cref="P:System.ComponentModel.ICollectionView.GroupDescriptions" /> property.</summary>
-        /// <returns>true if this view supports grouping; otherwise, false.</returns>
-        bool CanGroup { get; }
-
-        /// <summary>Gets a collection of <see cref="T:System.ComponentModel.GroupDescription" /> objects that describe how the items in the collection are grouped in the view. </summary>
-        /// <returns>A collection of objects that describe how the items in the collection are grouped in the view. </returns>
-        //ObservableCollection<GroupDescription> GroupDescriptions { get; }
-
-        bool IsGrouping { get; }
-        int GroupingDepth { get; }
-        string GetGroupingPropertyNameAtDepth(int level);
-
-        /// <summary>Gets the top-level groups.</summary>
-        /// <returns>A read-only collection of the top-level groups or null if there are no groups.</returns>
-        IAvaloniaReadOnlyList<object> Groups { get; }
-
-        /// <summary>Gets a value that indicates whether the view is empty.</summary>
-        /// <returns>true if the view is empty; otherwise, false.</returns>
-        bool IsEmpty { get; }
-
-        /// <summary>Recreates the view.</summary>
-        void Refresh();
-
-        /// <summary>Enters a defer cycle that you can use to merge changes to the view and delay automatic refresh. </summary>
-        /// <returns>The typical usage is to create a using scope with an implementation of this method and then include multiple view-changing calls within the scope. The implementation should delay automatic refresh until after the using scope exits. </returns>
-        IDisposable DeferRefresh();
-
-        /// <summary>Gets the current item in the view.</summary>
-        /// <returns>The current item in the view or null if there is no current item.</returns>
-        object CurrentItem { get; }
-
-        /// <summary>Gets the ordinal position of the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> in the view.</summary>
-        /// <returns>The ordinal position of the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> in the view.</returns>
-        int CurrentPosition { get; }
-
-        /// <summary>Gets a value that indicates whether the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> of the view is beyond the end of the collection.</summary>
-        /// <returns>true if the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> of the view is beyond the end of the collection; otherwise, false.</returns>
-        bool IsCurrentAfterLast { get; }
-
-        /// <summary>Gets a value that indicates whether the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> of the view is beyond the start of the collection.</summary>
-        /// <returns>true if the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> of the view is beyond the start of the collection; otherwise, false.</returns>
-        bool IsCurrentBeforeFirst { get; }
-
-        /// <summary>Sets the first item in the view as the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" />.</summary>
-        /// <returns>true if the resulting <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> is an item in the view; otherwise, false.</returns>
-        bool MoveCurrentToFirst();
-
-        /// <summary>Sets the last item in the view as the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" />.</summary>
-        /// <returns>true if the resulting <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> is an item in the view; otherwise, false.</returns>
-        bool MoveCurrentToLast();
-
-        /// <summary>Sets the item after the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> in the view as the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" />.</summary>
-        /// <returns>true if the resulting <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> is an item in the view; otherwise, false.</returns>
-        bool MoveCurrentToNext();
-
-        /// <summary>Sets the item before the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> in the view to the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" />.</summary>
-        /// <returns>true if the resulting <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> is an item in the view; otherwise, false.</returns>
-        bool MoveCurrentToPrevious();
-
-        /// <summary>Sets the specified item in the view as the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" />.</summary>
-        /// <returns>true if the resulting <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> is an item in the view; otherwise, false.</returns>
-        /// <param name="item">The item to set as the current item.</param>
-        bool MoveCurrentTo(object item);
-
-        /// <summary>Sets the item at the specified index to be the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> in the view.</summary>
-        /// <returns>true if the resulting <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> is an item in the view; otherwise, false.</returns>
-        /// <param name="position">The index to set the <see cref="P:System.ComponentModel.ICollectionView.CurrentItem" /> to.</param>
-        bool MoveCurrentToPosition(int position);
-
-        /// <summary>Occurs before the current item changes.</summary>
-        event EventHandler<DataGridCurrentChangingEventArgs> CurrentChanging;
-
-        /// <summary>Occurs after the current item has been changed.</summary>
-        event EventHandler CurrentChanged;
-    }
-    internal interface IDataGridEditableCollectionView
-    {
-        /// <summary>Gets a value that indicates whether a new item can be added to the collection.</summary>
-        /// <returns>true if a new item can be added to the collection; otherwise, false.</returns>
-        bool CanAddNew { get; }
-
-        /// <summary>Adds a new item to the underlying collection.</summary>
-        /// <returns>The new item that is added to the collection.</returns>
-        object AddNew();
-
-        /// <summary>Ends the add transaction and saves the pending new item.</summary>
-        void CommitNew();
-
-        /// <summary>Ends the add transaction and discards the pending new item.</summary>
-        void CancelNew();
-
-        /// <summary>Gets a value that indicates whether an add transaction is in progress.</summary>
-        /// <returns>true if an add transaction is in progress; otherwise, false.</returns>
-        bool IsAddingNew { get; }
-
-        /// <summary>Gets the item that is being added during the current add transaction.</summary>
-        /// <returns>The item that is being added if <see cref="P:System.ComponentModel.IEditableCollectionView.IsAddingNew" /> is true; otherwise, null.</returns>
-        object CurrentAddItem { get; }
-
-        /// <summary>Gets a value that indicates whether an item can be removed from the collection.</summary>
-        /// <returns>true if an item can be removed from the collection; otherwise, false.</returns>
-        bool CanRemove { get; }
-
-        /// <summary>Removes the item at the specified position from the collection.</summary>
-        /// <param name="index">Index of item to remove.</param>
-        void RemoveAt(int index);
-
-        /// <summary>Removes the specified item from the collection.</summary>
-        /// <param name="item">The item to remove.</param>
-        void Remove(object item);
-
-        /// <summary>Begins an edit transaction on the specified item.</summary>
-        /// <param name="item">The item to edit.</param>
-        void EditItem(object item);
-
-        /// <summary>Ends the edit transaction and saves the pending changes.</summary>
-        void CommitEdit();
-
-        /// <summary>Ends the edit transaction and, if possible, restores the original value of the item.</summary>
-        void CancelEdit();
-
-        /// <summary>Gets a value that indicates whether the collection view can discard pending changes and restore the original values of an edited object.</summary>
-        /// <returns>true if the collection view can discard pending changes and restore the original values of an edited object; otherwise, false.</returns>
-        bool CanCancelEdit { get; }
-
-        /// <summary>Gets a value that indicates whether an edit transaction is in progress.</summary>
-        /// <returns>true if an edit transaction is in progress; otherwise, false.</returns>
-        bool IsEditingItem { get; }
-
-        /// <summary>Gets the item in the collection that is being edited.</summary>
-        /// <returns>The item that is being edited if <see cref="P:System.ComponentModel.IEditableCollectionView.IsEditingItem" /> is true; otherwise, null.</returns>
-        object CurrentEditItem { get; }
-    }
-}

+ 0 - 6236
src/Avalonia.Controls.DataGrid/DataGrid.cs

@@ -1,6236 +0,0 @@
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Collections;
-using Avalonia.Controls.Primitives;
-using Avalonia.Controls.Templates;
-using Avalonia.Data;
-using Avalonia.Input;
-using Avalonia.Interactivity;
-using Avalonia.Media;
-using Avalonia.VisualTree;
-using Avalonia.Utilities;
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Collections.Specialized;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Text;
-using System.Linq;
-using Avalonia.Input.Platform;
-using System.ComponentModel.DataAnnotations;
-using Avalonia.Automation.Peers;
-using Avalonia.Controls.Automation.Peers;
-using Avalonia.Controls.Utils;
-using Avalonia.Layout;
-using Avalonia.Controls.Metadata;
-using Avalonia.Input.GestureRecognizers;
-using Avalonia.Styling;
-using Avalonia.Reactive;
-
-namespace Avalonia.Controls
-{
-    /// <summary>
-    /// Displays data in a customizable grid.
-    /// </summary>
-    [TemplatePart(DATAGRID_elementBottomRightCornerHeaderName,     typeof(Visual))]
-    [TemplatePart(DATAGRID_elementColumnHeadersPresenterName,      typeof(DataGridColumnHeadersPresenter))]
-    [TemplatePart(DATAGRID_elementFrozenColumnScrollBarSpacerName, typeof(Control))]
-    [TemplatePart(DATAGRID_elementHorizontalScrollbarName,         typeof(ScrollBar))]
-    [TemplatePart(DATAGRID_elementRowsPresenterName,               typeof(DataGridRowsPresenter))]
-    [TemplatePart(DATAGRID_elementTopLeftCornerHeaderName,         typeof(ContentControl))]
-    [TemplatePart(DATAGRID_elementTopRightCornerHeaderName,        typeof(ContentControl))]
-    [TemplatePart(DATAGRID_elementVerticalScrollbarName,           typeof(ScrollBar))]
-    [PseudoClasses(":invalid", ":empty-rows", ":empty-columns")]
-    public partial class DataGrid : TemplatedControl
-    {
-        private const string DATAGRID_elementRowsPresenterName = "PART_RowsPresenter";
-        private const string DATAGRID_elementColumnHeadersPresenterName = "PART_ColumnHeadersPresenter";
-        private const string DATAGRID_elementFrozenColumnScrollBarSpacerName = "PART_FrozenColumnScrollBarSpacer";
-        private const string DATAGRID_elementHorizontalScrollbarName = "PART_HorizontalScrollbar";
-        private const string DATAGRID_elementTopLeftCornerHeaderName = "PART_TopLeftCornerHeader";
-        private const string DATAGRID_elementTopRightCornerHeaderName = "PART_TopRightCornerHeader";
-        private const string DATAGRID_elementBottomRightCornerHeaderName = "PART_BottomRightCorner";
-        private const string DATAGRID_elementVerticalScrollbarName = "PART_VerticalScrollbar";
-        internal const bool DATAGRID_defaultCanUserReorderColumns = true;
-        internal const bool DATAGRID_defaultCanUserResizeColumns = true;
-        internal const bool DATAGRID_defaultCanUserSortColumns = true;
-
-        /// <summary>
-        /// The default order to use for columns when there is no <see cref="DisplayAttribute.Order"/>
-        /// value available for the property.
-        /// </summary>
-        /// <remarks>
-        /// The value of 10,000 comes from the DataAnnotations spec, allowing
-        /// some properties to be ordered at the beginning and some at the end.
-        /// </remarks>
-        private const int DATAGRID_defaultColumnDisplayOrder = 10000;
-
-        private const double DATAGRID_horizontalGridLinesThickness = 1;
-        private const double DATAGRID_minimumRowHeaderWidth = 4;
-        private const double DATAGRID_minimumColumnHeaderHeight = 4;
-        internal const double DATAGRID_maximumStarColumnWidth = 10000;
-        internal const double DATAGRID_minimumStarColumnWidth = 0.001;
-        private const double DATAGRID_mouseWheelDelta = 50.0;
-        private const double DATAGRID_maxHeadersThickness = 32768;
-
-        private const double DATAGRID_defaultRowHeight = 22;
-        internal const double DATAGRID_defaultRowGroupSublevelIndent = 20;
-        private const double DATAGRID_defaultMinColumnWidth = 20;
-        private const double DATAGRID_defaultMaxColumnWidth = double.PositiveInfinity;
-
-        private List<Exception> _bindingValidationErrors;
-        private IDisposable _validationSubscription;
-
-        private INotifyCollectionChanged _topLevelGroup;
-        private ContentControl _clipboardContentControl;
-
-        private Visual _bottomRightCorner;
-        private DataGridColumnHeadersPresenter _columnHeadersPresenter;
-        private DataGridRowsPresenter _rowsPresenter;
-        private ScrollBar _vScrollBar;
-        private ScrollBar _hScrollBar;
-
-        private ContentControl _topLeftCornerHeader;
-        private ContentControl _topRightCornerHeader;
-        private Control _frozenColumnScrollBarSpacer;
-
-        // the sum of the widths in pixels of the scrolling columns preceding
-        // the first displayed scrolling column
-        private double _horizontalOffset;
-
-        // the number of pixels of the firstDisplayedScrollingCol which are not displayed
-        private double _negHorizontalOffset;
-        private byte _autoGeneratingColumnOperationCount;
-        private bool _areHandlersSuspended;
-        private bool _autoSizingColumns;
-        private IndexToValueTable<bool> _collapsedSlotsTable;
-        private Control _clickedElement;
-
-        // used to store the current column during a Reset
-        private int _desiredCurrentColumnIndex;
-        private int _editingColumnIndex;
-
-        // this is a workaround only for the scenarios where we need it, it is not all encompassing nor always updated
-        private RoutedEventArgs _editingEventArgs;
-        private bool _executingLostFocusActions;
-        private bool _flushCurrentCellChanged;
-        private bool _focusEditingControl;
-        private Visual _focusedObject;
-        private byte _horizontalScrollChangesIgnored;
-        private DataGridRow _focusedRow;
-        private bool _ignoreNextScrollBarsLayout;
-
-        // Nth row of rows 0..N that make up the RowHeightEstimate
-        private int _lastEstimatedRow;
-        private List<DataGridRow> _loadedRows;
-
-        // prevents reentry into the VerticalScroll event handler
-        private Queue<Action> _lostFocusActions;
-        private int _noSelectionChangeCount;
-        private int _noCurrentCellChangeCount;
-        private bool _makeFirstDisplayedCellCurrentCellPending;
-        private bool _measured;
-        private int? _mouseOverRowIndex;    // -1 is used for the 'new row'
-        private DataGridColumn _previousCurrentColumn;
-        private object _previousCurrentItem;
-        private double[] _rowGroupHeightsByLevel;
-        private double _rowHeaderDesiredWidth;
-        private Size? _rowsPresenterAvailableSize;
-        private bool _scrollingByHeight;
-        private IndexToValueTable<bool> _showDetailsTable;
-        private bool _successfullyUpdatedSelection;
-        private DataGridSelectedItemsCollection _selectedItems;
-        private bool _temporarilyResetCurrentCell;
-        private object _uneditedValue; // Represents the original current cell value at the time it enters editing mode.
-
-        // An approximation of the sum of the heights in pixels of the scrolling rows preceding
-        // the first displayed scrolling row.  Since the scrolled off rows are discarded, the grid
-        // does not know their actual height. The heights used for the approximation are the ones
-        // set as the rows were scrolled off.
-        private double _verticalOffset;
-        private byte _verticalScrollChangesIgnored;
-
-        public event EventHandler<ScrollEventArgs> HorizontalScroll;
-        public event EventHandler<ScrollEventArgs> VerticalScroll;
-
-        /// <summary>
-        /// Identifies the CanUserReorderColumns dependency property.
-        /// </summary>
-        public static readonly StyledProperty<bool> CanUserReorderColumnsProperty =
-            AvaloniaProperty.Register<DataGrid, bool>(nameof(CanUserReorderColumns));
-
-        /// <summary>
-        /// Gets or sets a value that indicates whether the user can change
-        /// the column display order by dragging column headers with the mouse.
-        /// </summary>
-        public bool CanUserReorderColumns
-        {
-            get { return GetValue(CanUserReorderColumnsProperty); }
-            set { SetValue(CanUserReorderColumnsProperty, value); }
-        }
-
-        /// <summary>
-        /// Identifies the CanUserResizeColumns dependency property.
-        /// </summary>
-        public static readonly StyledProperty<bool> CanUserResizeColumnsProperty =
-            AvaloniaProperty.Register<DataGrid, bool>(nameof(CanUserResizeColumns));
-
-        /// <summary>
-        /// Gets or sets a value that indicates whether the user can adjust column widths using the mouse.
-        /// </summary>
-        public bool CanUserResizeColumns
-        {
-            get { return GetValue(CanUserResizeColumnsProperty); }
-            set { SetValue(CanUserResizeColumnsProperty, value); }
-        }
-
-        /// <summary>
-        /// Identifies the CanUserSortColumns dependency property.
-        /// </summary>
-        public static readonly StyledProperty<bool> CanUserSortColumnsProperty =
-            AvaloniaProperty.Register<DataGrid, bool>(nameof(CanUserSortColumns), true);
-
-        /// <summary>
-        /// Gets or sets a value that indicates whether the user can sort columns by clicking the column header.
-        /// </summary>
-        public bool CanUserSortColumns
-        {
-            get { return GetValue(CanUserSortColumnsProperty); }
-            set { SetValue(CanUserSortColumnsProperty, value); }
-        }
-
-        /// <summary>
-        /// Identifies the ColumnHeaderHeight dependency property.
-        /// </summary>
-        public static readonly StyledProperty<double> ColumnHeaderHeightProperty =
-            AvaloniaProperty.Register<DataGrid, double>(
-                nameof(ColumnHeaderHeight),
-                defaultValue: double.NaN,
-                validate: IsValidColumnHeaderHeight);
-
-        private static bool IsValidColumnHeaderHeight(double value)
-        {
-            return double.IsNaN(value) ||
-                (value >= DATAGRID_minimumColumnHeaderHeight && value <= DATAGRID_maxHeadersThickness);
-        }
-
-        /// <summary>
-        /// Gets or sets the height of the column headers row.
-        /// </summary>
-        public double ColumnHeaderHeight
-        {
-            get { return GetValue(ColumnHeaderHeightProperty); }
-            set { SetValue(ColumnHeaderHeightProperty, value); }
-        }
-
-        /// <summary>
-        /// Identifies the ColumnWidth dependency property.
-        /// </summary>
-        public static readonly StyledProperty<DataGridLength> ColumnWidthProperty =
-            AvaloniaProperty.Register<DataGrid, DataGridLength>(nameof(ColumnWidth), defaultValue: DataGridLength.Auto);
-
-        /// <summary>
-        /// Identifies the <see cref="RowTheme"/> dependency property.
-        /// </summary>
-        public static readonly StyledProperty<ControlTheme> RowThemeProperty =
-            AvaloniaProperty.Register<DataGrid, ControlTheme>(nameof(RowTheme));
-
-        /// <summary>
-        /// Gets or sets the theme applied to all rows.
-        /// </summary>
-        public ControlTheme RowTheme
-        {
-            get { return GetValue(RowThemeProperty); }
-            set { SetValue(RowThemeProperty, value); }
-        }
-
-        /// <summary>
-        /// Identifies the <see cref="CellTheme"/> dependency property.
-        /// </summary>
-        public static readonly StyledProperty<ControlTheme> CellThemeProperty =
-            AvaloniaProperty.Register<DataGrid, ControlTheme>(nameof(CellTheme));
-
-        /// <summary>
-        /// Gets or sets the theme applied to all cells.
-        /// </summary>
-        public ControlTheme CellTheme
-        {
-            get { return GetValue(CellThemeProperty); }
-            set { SetValue(CellThemeProperty, value); }
-        }
-
-        /// <summary>
-        /// Identifies the <see cref="ColumnHeaderTheme"/> dependency property.
-        /// </summary>
-        public static readonly StyledProperty<ControlTheme> ColumnHeaderThemeProperty =
-            AvaloniaProperty.Register<DataGrid, ControlTheme>(nameof(ColumnHeaderTheme));
-
-        /// <summary>
-        /// Gets or sets the theme applied to all column headers.
-        /// </summary>
-        public ControlTheme ColumnHeaderTheme
-        {
-            get { return GetValue(ColumnHeaderThemeProperty); }
-            set { SetValue(ColumnHeaderThemeProperty, value); }
-        }
-
-        /// <summary>
-        /// Identifies the <see cref="RowGroupTheme"/> dependency property.
-        /// </summary>
-        public static readonly StyledProperty<ControlTheme> RowGroupThemeProperty =
-            AvaloniaProperty.Register<DataGrid, ControlTheme>(nameof(RowGroupTheme));
-
-        /// <summary>
-        /// Gets or sets the theme applied to all row groups.
-        /// </summary>
-        public ControlTheme RowGroupTheme
-        {
-            get { return GetValue(RowGroupThemeProperty); }
-            set { SetValue(RowGroupThemeProperty, value); }
-        }
-
-        /// <summary>
-        /// Gets or sets the standard width or automatic sizing mode of columns in the control.
-        /// </summary>
-        public DataGridLength ColumnWidth
-        {
-            get { return GetValue(ColumnWidthProperty); }
-            set { SetValue(ColumnWidthProperty, value); }
-        }
-
-        public static readonly StyledProperty<int> FrozenColumnCountProperty =
-            AvaloniaProperty.Register<DataGrid, int>(
-                nameof(FrozenColumnCount),
-                validate: ValidateFrozenColumnCount);
-
-        /// <summary>
-        /// Gets or sets the number of columns that the user cannot scroll horizontally.
-        /// </summary>
-        public int FrozenColumnCount
-        {
-            get { return GetValue(FrozenColumnCountProperty); }
-            set { SetValue(FrozenColumnCountProperty, value); }
-        }
-
-        private static bool ValidateFrozenColumnCount(int value) => value >= 0;
-
-        public static readonly StyledProperty<DataGridGridLinesVisibility> GridLinesVisibilityProperty =
-            AvaloniaProperty.Register<DataGrid, DataGridGridLinesVisibility>(nameof(GridLinesVisibility));
-
-        /// <summary>
-        /// Gets or sets a value that indicates which grid lines separating inner cells are shown.
-        /// </summary>
-        public DataGridGridLinesVisibility GridLinesVisibility
-        {
-            get { return GetValue(GridLinesVisibilityProperty); }
-            set { SetValue(GridLinesVisibilityProperty, value); }
-        }
-
-        public static readonly StyledProperty<DataGridHeadersVisibility> HeadersVisibilityProperty =
-            AvaloniaProperty.Register<DataGrid, DataGridHeadersVisibility>(nameof(HeadersVisibility));
-
-        /// <summary>
-        /// Gets or sets a value that indicates the visibility of row and column headers.
-        /// </summary>
-        public DataGridHeadersVisibility HeadersVisibility
-        {
-            get { return GetValue(HeadersVisibilityProperty); }
-            set { SetValue(HeadersVisibilityProperty, value); }
-        }
-
-        public static readonly StyledProperty<IBrush> HorizontalGridLinesBrushProperty =
-            AvaloniaProperty.Register<DataGrid, IBrush>(nameof(HorizontalGridLinesBrush));
-
-        /// <summary>
-        /// Gets or sets the <see cref="T:System.Windows.Media.Brush" /> that is used to paint grid lines separating rows.
-        /// </summary>
-        public IBrush HorizontalGridLinesBrush
-        {
-            get { return GetValue(HorizontalGridLinesBrushProperty); }
-            set { SetValue(HorizontalGridLinesBrushProperty, value); }
-        }
-
-        public static readonly StyledProperty<ScrollBarVisibility> HorizontalScrollBarVisibilityProperty =
-            AvaloniaProperty.Register<DataGrid, ScrollBarVisibility>(nameof(HorizontalScrollBarVisibility));
-
-        /// <summary>
-        /// Gets or sets a value that indicates how the horizontal scroll bar is displayed.
-        /// </summary>
-        public ScrollBarVisibility HorizontalScrollBarVisibility
-        {
-            get { return GetValue(HorizontalScrollBarVisibilityProperty); }
-            set { SetValue(HorizontalScrollBarVisibilityProperty, value); }
-        }
-
-        public static readonly StyledProperty<bool> IsReadOnlyProperty =
-            AvaloniaProperty.Register<DataGrid, bool>(nameof(IsReadOnly));
-
-        /// <summary>
-        /// Gets or sets a value that indicates whether the user can edit the values in the control.
-        /// </summary>
-        public bool IsReadOnly
-        {
-            get { return GetValue(IsReadOnlyProperty); }
-            set { SetValue(IsReadOnlyProperty, value); }
-        }
-
-        public static readonly StyledProperty<bool> AreRowGroupHeadersFrozenProperty =
-            AvaloniaProperty.Register<DataGrid, bool>(
-                nameof(AreRowGroupHeadersFrozen),
-                defaultValue: true);
-
-        /// <summary>
-        /// Gets or sets a value that indicates whether the row group header sections
-        /// remain fixed at the width of the display area or can scroll horizontally.
-        /// </summary>
-        public bool AreRowGroupHeadersFrozen
-        {
-            get { return GetValue(AreRowGroupHeadersFrozenProperty); }
-            set { SetValue(AreRowGroupHeadersFrozenProperty, value); }
-        }
-
-        private void OnAreRowGroupHeadersFrozenChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            var value = (bool)e.NewValue;
-            ProcessFrozenColumnCount();
-
-            // Update elements in the RowGroupHeader that were previously frozen
-            if (value)
-            {
-                if (_rowsPresenter != null)
-                {
-                    foreach (Control element in _rowsPresenter.Children)
-                    {
-                        if (element is DataGridRowGroupHeader groupHeader)
-                        {
-                            groupHeader.ClearFrozenStates();
-                        }
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// Defines the <see cref="IsScrollInertiaEnabled"/> property.
-        /// </summary>
-        public static readonly AttachedProperty<bool> IsScrollInertiaEnabledProperty =
-            ScrollViewer.IsScrollInertiaEnabledProperty.AddOwner<DataGrid>();
-
-        /// <summary>
-        /// Gets or sets whether scroll gestures should include inertia in their behavior and value.
-        /// </summary>
-        public bool IsScrollInertiaEnabled
-        {
-            get => GetValue(IsScrollInertiaEnabledProperty);
-            set => SetValue(IsScrollInertiaEnabledProperty, value);
-        }
-
-        private bool _isValid = true;
-
-        public static readonly DirectProperty<DataGrid, bool> IsValidProperty =
-            AvaloniaProperty.RegisterDirect<DataGrid, bool>(
-                nameof(IsValid),
-                o => o.IsValid);
-
-        public bool IsValid
-        {
-            get { return _isValid; }
-            internal set
-            {
-                SetAndRaise(IsValidProperty, ref _isValid, value);
-                PseudoClasses.Set(":invalid", !value);
-            }
-        }
-
-        public static readonly StyledProperty<double> MaxColumnWidthProperty =
-            AvaloniaProperty.Register<DataGrid, double>(
-                nameof(MaxColumnWidth),
-                defaultValue: DATAGRID_defaultMaxColumnWidth,
-                validate: IsValidColumnWidth);
-
-        private static bool IsValidColumnWidth(double value)
-        {
-            return !double.IsNaN(value) && value > 0;
-        }
-
-        /// <summary>
-        /// Gets or sets the maximum width of columns in the <see cref="T:Avalonia.Controls.DataGrid" /> .
-        /// </summary>
-        public double MaxColumnWidth
-        {
-            get { return GetValue(MaxColumnWidthProperty); }
-            set { SetValue(MaxColumnWidthProperty, value); }
-        }
-
-        public static readonly StyledProperty<double> MinColumnWidthProperty =
-            AvaloniaProperty.Register<DataGrid, double>(
-                nameof(MinColumnWidth),
-                defaultValue: DATAGRID_defaultMinColumnWidth,
-                validate: IsValidMinColumnWidth);
-
-        private static bool IsValidMinColumnWidth(double value)
-        {
-            return !double.IsNaN(value) && !double.IsPositiveInfinity(value) && value >= 0;
-        }
-
-        /// <summary>
-        /// Gets or sets the minimum width of columns in the <see cref="T:Avalonia.Controls.DataGrid" />.
-        /// </summary>
-        public double MinColumnWidth
-        {
-            get { return GetValue(MinColumnWidthProperty); }
-            set { SetValue(MinColumnWidthProperty, value); }
-        }
-
-        public static readonly StyledProperty<IBrush> RowBackgroundProperty =
-            AvaloniaProperty.Register<DataGrid, IBrush>(nameof(RowBackground));
-
-        /// <summary>
-        /// Gets or sets the <see cref="T:System.Windows.Media.Brush" /> that is used to paint row backgrounds.
-        /// </summary>
-        public IBrush RowBackground
-        {
-            get { return GetValue(RowBackgroundProperty); }
-            set { SetValue(RowBackgroundProperty, value); }
-        }
-
-        public static readonly StyledProperty<double> RowHeightProperty =
-            AvaloniaProperty.Register<DataGrid, double>(
-                nameof(RowHeight),
-                defaultValue: double.NaN,
-                validate: IsValidRowHeight);
-        private static bool IsValidRowHeight(double value)
-        {
-            return double.IsNaN(value) ||
-                (value >= DataGridRow.DATAGRIDROW_minimumHeight &&
-                 value <= DataGridRow.DATAGRIDROW_maximumHeight);
-        }
-
-        /// <summary>
-        /// Gets or sets the standard height of rows in the control.
-        /// </summary>
-        public double RowHeight
-        {
-            get { return GetValue(RowHeightProperty); }
-            set { SetValue(RowHeightProperty, value); }
-        }
-
-        public static readonly StyledProperty<double> RowHeaderWidthProperty =
-            AvaloniaProperty.Register<DataGrid, double>(
-                nameof(RowHeaderWidth),
-                defaultValue: double.NaN,
-                validate: IsValidRowHeaderWidth);
-        private static bool IsValidRowHeaderWidth(double value)
-        {
-            return double.IsNaN(value) ||
-                (value >= DATAGRID_minimumRowHeaderWidth &&
-                 value <= DATAGRID_maxHeadersThickness);
-        }
-
-        /// <summary>
-        /// Gets or sets the width of the row header column.
-        /// </summary>
-        public double RowHeaderWidth
-        {
-            get { return GetValue(RowHeaderWidthProperty); }
-            set { SetValue(RowHeaderWidthProperty, value); }
-        }
-
-        public static readonly StyledProperty<DataGridSelectionMode> SelectionModeProperty =
-            AvaloniaProperty.Register<DataGrid, DataGridSelectionMode>(nameof(SelectionMode));
-
-        /// <summary>
-        /// Gets or sets the selection behavior of the data grid.
-        /// </summary>
-        public DataGridSelectionMode SelectionMode
-        {
-            get { return GetValue(SelectionModeProperty); }
-            set { SetValue(SelectionModeProperty, value); }
-        }
-
-        public static readonly StyledProperty<IBrush> VerticalGridLinesBrushProperty =
-            AvaloniaProperty.Register<DataGrid, IBrush>(nameof(VerticalGridLinesBrush));
-
-        /// <summary>
-        /// Gets or sets the <see cref="T:System.Windows.Media.Brush" /> that is used to paint grid lines separating columns.
-        /// </summary>
-        public IBrush VerticalGridLinesBrush
-        {
-            get { return GetValue(VerticalGridLinesBrushProperty); }
-            set { SetValue(VerticalGridLinesBrushProperty, value); }
-        }
-
-        public static readonly StyledProperty<ScrollBarVisibility> VerticalScrollBarVisibilityProperty =
-            AvaloniaProperty.Register<DataGrid, ScrollBarVisibility>(nameof(VerticalScrollBarVisibility));
-
-        /// <summary>
-        /// Gets or sets a value that indicates how the vertical scroll bar is displayed.
-        /// </summary>
-        public ScrollBarVisibility VerticalScrollBarVisibility
-        {
-            get { return GetValue(VerticalScrollBarVisibilityProperty); }
-            set { SetValue(VerticalScrollBarVisibilityProperty, value); }
-        }
-
-        public static readonly StyledProperty<ITemplate<Control>> DropLocationIndicatorTemplateProperty =
-            AvaloniaProperty.Register<DataGrid, ITemplate<Control>>(nameof(DropLocationIndicatorTemplate));
-
-        /// <summary>
-        /// Gets or sets the template that is used when rendering the column headers.
-        /// </summary>
-        public ITemplate<Control> DropLocationIndicatorTemplate
-        {
-            get { return GetValue(DropLocationIndicatorTemplateProperty); }
-            set { SetValue(DropLocationIndicatorTemplateProperty, value); }
-        }
-
-        private int _selectedIndex = -1;
-        private object _selectedItem;
-
-        public static readonly DirectProperty<DataGrid, int> SelectedIndexProperty =
-            AvaloniaProperty.RegisterDirect<DataGrid, int>(
-                nameof(SelectedIndex),
-                o => o.SelectedIndex,
-                (o, v) => o.SelectedIndex = v,
-                defaultBindingMode: BindingMode.TwoWay);
-
-        /// <summary>
-        /// Gets or sets the index of the current selection.
-        /// </summary>
-        /// <returns>
-        /// The index of the current selection, or -1 if the selection is empty.
-        /// </returns>
-        public int SelectedIndex
-        {
-            get { return _selectedIndex; }
-            set { SetAndRaise(SelectedIndexProperty, ref _selectedIndex, value); }
-        }
-
-        public static readonly DirectProperty<DataGrid, object> SelectedItemProperty =
-            AvaloniaProperty.RegisterDirect<DataGrid, object>(
-                nameof(SelectedItem),
-                o => o.SelectedItem,
-                (o, v) => o.SelectedItem = v,
-                defaultBindingMode: BindingMode.TwoWay);
-
-        /// <summary>
-        /// Gets or sets the data item corresponding to the selected row.
-        /// </summary>
-        public object SelectedItem
-        {
-            get { return _selectedItem; }
-            set { SetAndRaise(SelectedItemProperty, ref _selectedItem, value); }
-        }
-
-        public static readonly StyledProperty<DataGridClipboardCopyMode> ClipboardCopyModeProperty =
-            AvaloniaProperty.Register<DataGrid, DataGridClipboardCopyMode>(
-                nameof(ClipboardCopyMode),
-                defaultValue: DataGridClipboardCopyMode.ExcludeHeader);
-
-        /// <summary>
-        /// The property which determines how DataGrid content is copied to the Clipboard.
-        /// </summary>
-        public DataGridClipboardCopyMode ClipboardCopyMode
-        {
-            get { return GetValue(ClipboardCopyModeProperty); }
-            set { SetValue(ClipboardCopyModeProperty, value); }
-        }
-
-        public static readonly StyledProperty<bool> AutoGenerateColumnsProperty =
-            AvaloniaProperty.Register<DataGrid, bool>(nameof(AutoGenerateColumns));
-
-        /// <summary>
-        /// Gets or sets a value that indicates whether columns are created
-        /// automatically when the <see cref="P:Avalonia.Controls.DataGrid.ItemsSource" /> property is set.
-        /// </summary>
-        public bool AutoGenerateColumns
-        {
-            get { return GetValue(AutoGenerateColumnsProperty); }
-            set { SetValue(AutoGenerateColumnsProperty, value); }
-        }
-
-        private void OnAutoGenerateColumnsChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            var value = (bool)e.NewValue;
-            if (value)
-            {
-                InitializeElements(recycleRows: false);
-            }
-            else
-            {
-                RemoveAutoGeneratedColumns();
-            }
-        }
-
-        /// <summary>
-        /// Identifies the ItemsSource property.
-        /// </summary>
-        public static readonly StyledProperty<IEnumerable> ItemsSourceProperty =
-            AvaloniaProperty.Register<DataGrid, IEnumerable>(nameof(ItemsSource));
-
-        /// <summary>
-        /// Gets or sets a collection that is used to generate the content of the control.
-        /// </summary>
-        public IEnumerable ItemsSource
-        {
-            get => GetValue(ItemsSourceProperty);
-            set => SetValue(ItemsSourceProperty, value);
-        }
-
-        public static readonly StyledProperty<bool> AreRowDetailsFrozenProperty =
-            AvaloniaProperty.Register<DataGrid, bool>(nameof(AreRowDetailsFrozen));
-
-        /// <summary>
-        /// Gets or sets a value that indicates whether the row details sections remain
-        /// fixed at the width of the display area or can scroll horizontally.
-        /// </summary>
-        public bool AreRowDetailsFrozen
-        {
-            get { return GetValue(AreRowDetailsFrozenProperty); }
-            set { SetValue(AreRowDetailsFrozenProperty, value); }
-        }
-
-        public static readonly StyledProperty<IDataTemplate> RowDetailsTemplateProperty =
-            AvaloniaProperty.Register<DataGrid, IDataTemplate>(nameof(RowDetailsTemplate));
-
-        /// <summary>
-        /// Gets or sets the template that is used to display the content of the details section of rows.
-        /// </summary>
-        public IDataTemplate RowDetailsTemplate
-        {
-            get { return GetValue(RowDetailsTemplateProperty); }
-            set { SetValue(RowDetailsTemplateProperty, value); }
-        }
-
-        public static readonly StyledProperty<DataGridRowDetailsVisibilityMode> RowDetailsVisibilityModeProperty =
-            AvaloniaProperty.Register<DataGrid, DataGridRowDetailsVisibilityMode>(nameof(RowDetailsVisibilityMode));
-
-        /// <summary>
-        /// Gets or sets a value that indicates when the details sections of rows are displayed.
-        /// </summary>
-        public DataGridRowDetailsVisibilityMode RowDetailsVisibilityMode
-        {
-            get { return GetValue(RowDetailsVisibilityModeProperty); }
-            set { SetValue(RowDetailsVisibilityModeProperty, value); }
-        }
-
-
-        public static readonly DirectProperty<DataGrid, IDataGridCollectionView> CollectionViewProperty =
-            AvaloniaProperty.RegisterDirect<DataGrid, IDataGridCollectionView>(nameof(CollectionView),
-                o => o.CollectionView);
-
-        /// <summary>
-        /// Gets current <see cref="IDataGridCollectionView"/>.
-        /// </summary>
-        public IDataGridCollectionView CollectionView =>
-            DataConnection.CollectionView;
-
-        static DataGrid()
-        {
-            AffectsMeasure<DataGrid>(
-                ColumnHeaderHeightProperty,
-                HorizontalScrollBarVisibilityProperty,
-                VerticalScrollBarVisibilityProperty);
-
-            ItemsSourceProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnItemsSourcePropertyChanged(e));
-            CanUserResizeColumnsProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnCanUserResizeColumnsChanged(e));
-            ColumnWidthProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnColumnWidthChanged(e));
-            FrozenColumnCountProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnFrozenColumnCountChanged(e));
-            GridLinesVisibilityProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnGridLinesVisibilityChanged(e));
-            HeadersVisibilityProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnHeadersVisibilityChanged(e));
-            HorizontalGridLinesBrushProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnHorizontalGridLinesBrushChanged(e));
-            IsReadOnlyProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnIsReadOnlyChanged(e));
-            MaxColumnWidthProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnMaxColumnWidthChanged(e));
-            MinColumnWidthProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnMinColumnWidthChanged(e));
-            RowHeightProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowHeightChanged(e));
-            RowHeaderWidthProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowHeaderWidthChanged(e));
-            SelectionModeProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnSelectionModeChanged(e));
-            VerticalGridLinesBrushProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnVerticalGridLinesBrushChanged(e));
-            SelectedIndexProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnSelectedIndexChanged(e));
-            SelectedItemProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnSelectedItemChanged(e));
-            IsEnabledProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.DataGrid_IsEnabledChanged(e));
-            AreRowGroupHeadersFrozenProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnAreRowGroupHeadersFrozenChanged(e));
-            RowDetailsTemplateProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowDetailsTemplateChanged(e));
-            RowDetailsVisibilityModeProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnRowDetailsVisibilityModeChanged(e));
-            AutoGenerateColumnsProperty.Changed.AddClassHandler<DataGrid>((x, e) => x.OnAutoGenerateColumnsChanged(e));
-
-            FocusableProperty.OverrideDefaultValue<DataGrid>(true);
-        }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGrid" /> class.
-        /// </summary>
-        public DataGrid()
-        {
-            KeyDown += DataGrid_KeyDown;
-            KeyUp += DataGrid_KeyUp;
-
-            //TODO: Check if override works
-            GotFocus += DataGrid_GotFocus;
-            LostFocus += DataGrid_LostFocus;
-
-            _loadedRows = new List<DataGridRow>();
-            _lostFocusActions = new Queue<Action>();
-            _selectedItems = new DataGridSelectedItemsCollection(this);
-            RowGroupHeadersTable = new IndexToValueTable<DataGridRowGroupInfo>();
-            _bindingValidationErrors = new List<Exception>();
-
-            DisplayData = new DataGridDisplayData(this);
-            ColumnsInternal = CreateColumnsInstance();
-            ColumnsInternal.CollectionChanged += ColumnsInternal_CollectionChanged;
-
-            RowHeightEstimate = DATAGRID_defaultRowHeight;
-            RowDetailsHeightEstimate = 0;
-            _rowHeaderDesiredWidth = 0;
-
-            DataConnection = new DataGridDataConnection(this);
-            _showDetailsTable = new IndexToValueTable<bool>();
-            _collapsedSlotsTable = new IndexToValueTable<bool>();
-
-            AnchorSlot = -1;
-            _lastEstimatedRow = -1;
-            _editingColumnIndex = -1;
-            _mouseOverRowIndex = null;
-            CurrentCellCoordinates = new DataGridCellCoordinates(-1, -1);
-
-            RowGroupHeaderHeightEstimate = DATAGRID_defaultRowHeight;
-
-            UpdatePseudoClasses();
-        }
-
-        protected override AutomationPeer OnCreateAutomationPeer()
-        {
-            return new DataGridAutomationPeer(this);
-        }
-
-        private void SetValueNoCallback<T>(AvaloniaProperty<T> property, T value, BindingPriority priority = BindingPriority.LocalValue)
-        {
-            _areHandlersSuspended = true;
-            try
-            {
-                SetValue(property, value, priority);
-            }
-            finally
-            {
-                _areHandlersSuspended = false;
-            }
-        }
-
-        private void OnRowDetailsVisibilityModeChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            UpdateRowDetailsVisibilityMode((DataGridRowDetailsVisibilityMode)e.NewValue);
-        }
-
-        private void OnRowDetailsTemplateChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-
-            // Update the RowDetails templates if necessary
-            if (_rowsPresenter != null)
-            {
-                foreach (DataGridRow row in GetAllRows())
-                {
-                    if (GetRowDetailsVisibility(row.Index))
-                    {
-                        // DetailsPreferredHeight is initialized when the DetailsElement's size changes.
-                        row.ApplyDetailsTemplate(initializeDetailsPreferredHeight: false);
-                    }
-                }
-            }
-
-            UpdateRowDetailsHeightEstimate();
-            InvalidateMeasure();
-        }
-
-        /// <summary>
-        /// ItemsSourceProperty property changed handler.
-        /// </summary>
-        /// <param name="e">The event arguments.</param>
-        private void OnItemsSourcePropertyChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            if (!_areHandlersSuspended)
-            {
-                Debug.Assert(DataConnection != null);
-
-                var oldCollectionView = DataConnection.CollectionView;
-
-                var oldValue = (IEnumerable)e.OldValue;
-                var newItemsSource = (IEnumerable)e.NewValue;
-
-                if (LoadingOrUnloadingRow)
-                {
-                    SetValueNoCallback(ItemsSourceProperty, oldValue);
-                    throw DataGridError.DataGrid.CannotChangeItemsWhenLoadingRows();
-                }
-
-                // Try to commit edit on the old DataSource, but force a cancel if it fails
-                if (!CommitEdit())
-                {
-                    CancelEdit(DataGridEditingUnit.Row, false);
-                }
-
-                DataConnection.UnWireEvents(DataConnection.DataSource);
-                DataConnection.ClearDataProperties();
-                ClearRowGroupHeadersTable();
-
-                // The old selected indexes are no longer relevant. There's a perf benefit from
-                // updating the selected indexes with a null DataSource, because we know that all
-                // of the previously selected indexes have been removed from selection
-                DataConnection.DataSource = null;
-                _selectedItems.UpdateIndexes();
-                CoerceSelectedItem();
-
-                // Wrap an IEnumerable in an ICollectionView if it's not already one
-                bool setDefaultSelection = false;
-                if (newItemsSource is IDataGridCollectionView newCollectionView)
-                {
-                    setDefaultSelection = true;
-                }
-                else
-                {
-                    newCollectionView =  newItemsSource is not null
-                        ? DataGridDataConnection.CreateView(newItemsSource)
-                        : default;
-                }
-
-                DataConnection.DataSource = newCollectionView;
-
-                if (oldCollectionView != DataConnection.CollectionView)
-                {
-                    RaisePropertyChanged(CollectionViewProperty, 
-                        oldCollectionView, 
-                        newCollectionView);
-                }
-
-                if (DataConnection.DataSource != null)
-                {
-                    // Setup the column headers
-                    if (DataConnection.DataType != null)
-                    {
-                        foreach (var column in ColumnsInternal.GetDisplayedColumns())
-                        {
-                            if (column is DataGridBoundColumn boundColumn)
-                            {
-                                boundColumn.SetHeaderFromBinding();
-                            }
-                        }
-                    }
-                    DataConnection.WireEvents(DataConnection.DataSource);
-                }
-
-                // Wait for the current cell to be set before we raise any SelectionChanged events
-                _makeFirstDisplayedCellCurrentCellPending = true;
-
-                // Clear out the old rows and remove the generated columns
-                ClearRows(false); //recycle
-                RemoveAutoGeneratedColumns();
-
-                // Set the SlotCount (from the data count and number of row group headers) before we make the default selection
-                PopulateRowGroupHeadersTable();
-                SelectedItem = null;
-                if (DataConnection.CollectionView != null && setDefaultSelection)
-                {
-                    SelectedItem = DataConnection.CollectionView.CurrentItem;
-                }
-
-                // Treat this like the DataGrid has never been measured because all calculations at
-                // this point are invalid until the next layout cycle.  For instance, the ItemsSource
-                // can be set when the DataGrid is not part of the visual tree
-                _measured = false;
-                InvalidateMeasure();
-
-                UpdatePseudoClasses();
-            }
-        }
-
-        private void ColumnsInternal_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
-        {
-            if (e.Action == NotifyCollectionChangedAction.Add
-                || e.Action == NotifyCollectionChangedAction.Remove
-                || e.Action == NotifyCollectionChangedAction.Reset)
-            {
-                UpdatePseudoClasses();
-            }
-        }
-
-        internal void UpdatePseudoClasses()
-        {
-            PseudoClasses.Set(":empty-columns", !ColumnsInternal.GetVisibleColumns().Any());
-            PseudoClasses.Set(":empty-rows", !DataConnection.Any());
-        }
-
-        private void OnSelectedIndexChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            if (!_areHandlersSuspended)
-            {
-                int index = (int)e.NewValue;
-
-                // GetDataItem returns null if index is >= Count, we do not check newValue
-                // against Count here to avoid enumerating through an Enumerable twice
-                // Setting SelectedItem coerces the finally value of the SelectedIndex
-                object newSelectedItem = (index < 0) ? null : DataConnection.GetDataItem(index);
-                SelectedItem = newSelectedItem;
-                if (SelectedItem != newSelectedItem)
-                {
-                    SetValueNoCallback(SelectedIndexProperty, (int)e.OldValue);
-                }
-            }
-        }
-
-        private void OnSelectedItemChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            if (!_areHandlersSuspended)
-            {
-                int rowIndex = (e.NewValue == null) ? -1 : DataConnection.IndexOf(e.NewValue);
-                if (rowIndex == -1)
-                {
-                    // If the Item is null or it's not found, clear the Selection
-                    if (!CommitEdit(DataGridEditingUnit.Row, exitEditingMode: true))
-                    {
-                        // Edited value couldn't be committed or aborted
-                        SetValueNoCallback(SelectedItemProperty, e.OldValue);
-                        return;
-                    }
-
-                    // Clear all row selections
-                    ClearRowSelection(resetAnchorSlot: true);
-
-                    if (DataConnection.CollectionView != null)
-                    {
-                        DataConnection.CollectionView.MoveCurrentTo(null);
-                    }
-                }
-                else
-                {
-                    int slot = SlotFromRowIndex(rowIndex);
-                    if (slot != CurrentSlot)
-                    {
-                        if (!CommitEdit(DataGridEditingUnit.Row, exitEditingMode: true))
-                        {
-                            // Edited value couldn't be committed or aborted
-                            SetValueNoCallback(SelectedItemProperty, e.OldValue);
-                            return;
-                        }
-                        if (slot >= SlotCount || slot < -1)
-                        {
-                            if (DataConnection.CollectionView != null)
-                            {
-                                DataConnection.CollectionView.MoveCurrentToPosition(rowIndex);
-                            }
-                        }
-                    }
-
-                    int oldSelectedIndex = SelectedIndex;
-                    SetValueNoCallback(SelectedIndexProperty, rowIndex);
-                    try
-                    {
-                        _noSelectionChangeCount++;
-                        int columnIndex = CurrentColumnIndex;
-
-                        if (columnIndex == -1)
-                        {
-                            columnIndex = FirstDisplayedNonFillerColumnIndex;
-                        }
-                        if (IsSlotOutOfSelectionBounds(slot))
-                        {
-                            ClearRowSelection(slotException: slot, setAnchorSlot: true);
-                            return;
-                        }
-
-                        UpdateSelectionAndCurrency(columnIndex, slot, DataGridSelectionAction.SelectCurrent, scrollIntoView: false);
-                    }
-                    finally
-                    {
-                        NoSelectionChangeCount--;
-                    }
-
-                    if (!_successfullyUpdatedSelection)
-                    {
-                        SetValueNoCallback(SelectedIndexProperty, oldSelectedIndex);
-                        SetValueNoCallback(SelectedItemProperty, e.OldValue);
-                    }
-                }
-            }
-        }
-
-        private void OnVerticalGridLinesBrushChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            if (_rowsPresenter != null)
-            {
-                foreach (DataGridRow row in GetAllRows())
-                {
-                    row.EnsureGridLines();
-                }
-            }
-        }
-
-        private void OnSelectionModeChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            if (!_areHandlersSuspended)
-            {
-                ClearRowSelection(resetAnchorSlot: true);
-            }
-        }
-
-        private void OnRowHeaderWidthChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            if (!_areHandlersSuspended)
-            {
-                EnsureRowHeaderWidth();
-            }
-        }
-
-        private void OnRowHeightChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            if (!_areHandlersSuspended)
-            {
-                InvalidateRowHeightEstimate();
-                // Re-measure all the rows due to the Height change
-                InvalidateRowsMeasure(invalidateIndividualElements: true);
-                // DataGrid needs to update the layout information and the ScrollBars
-                InvalidateMeasure();
-            }
-        }
-
-        private void OnMinColumnWidthChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            if (!_areHandlersSuspended)
-            {
-                double oldValue = (double)e.OldValue;
-                foreach (DataGridColumn column in ColumnsInternal.GetDisplayedColumns())
-                {
-                    OnColumnMinWidthChanged(column, Math.Max(column.MinWidth, oldValue));
-                }
-            }
-        }
-
-        private void OnMaxColumnWidthChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            if (!_areHandlersSuspended)
-            {
-                var oldValue = (double)e.OldValue;
-                foreach (DataGridColumn column in ColumnsInternal.GetDisplayedColumns())
-                {
-                    OnColumnMaxWidthChanged(column, Math.Min(column.MaxWidth, oldValue));
-                }
-            }
-        }
-
-        private void OnIsReadOnlyChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            if (!_areHandlersSuspended)
-            {
-                var value = (bool)e.NewValue;
-                if (value && !CommitEdit(DataGridEditingUnit.Row, exitEditingMode: true))
-                {
-                    CancelEdit(DataGridEditingUnit.Row, raiseEvents: false);
-                }
-            }
-        }
-
-        private void OnHorizontalGridLinesBrushChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            if (!_areHandlersSuspended && _rowsPresenter != null)
-            {
-                foreach (DataGridRow row in GetAllRows())
-                {
-                    row.EnsureGridLines();
-                }
-            }
-        }
-
-        private void OnHeadersVisibilityChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            var oldValue = (DataGridHeadersVisibility)e.OldValue;
-            var newValue = (DataGridHeadersVisibility)e.NewValue;
-            bool hasFlags(DataGridHeadersVisibility value, DataGridHeadersVisibility flags) => ((value & flags) == flags);
-
-            bool newValueCols = hasFlags(newValue, DataGridHeadersVisibility.Column);
-            bool newValueRows = hasFlags(newValue, DataGridHeadersVisibility.Row);
-            bool oldValueCols = hasFlags(oldValue, DataGridHeadersVisibility.Column);
-            bool oldValueRows = hasFlags(oldValue, DataGridHeadersVisibility.Row);
-
-            // Columns
-            if (newValueCols != oldValueCols)
-            {
-                if (_columnHeadersPresenter != null)
-                {
-                    EnsureColumnHeadersVisibility();
-                    if (!newValueCols)
-                    {
-                        _columnHeadersPresenter.Measure(default);
-                    }
-                    else
-                    {
-                        EnsureVerticalGridLines();
-                    }
-                    InvalidateMeasure();
-                }
-            }
-
-            // Rows
-            if (newValueRows != oldValueRows)
-            {
-                if (_rowsPresenter != null)
-                {
-                    foreach (Control element in _rowsPresenter.Children)
-                    {
-                        if (element is DataGridRow row)
-                        {
-                            row.EnsureHeaderStyleAndVisibility(null);
-                            if (newValueRows)
-                            {
-                                row.ApplyState();
-                                row.EnsureHeaderVisibility();
-                            }
-                        }
-                        else if (element is DataGridRowGroupHeader rowGroupHeader)
-                        {
-                            rowGroupHeader.EnsureHeaderVisibility();
-                        }
-                    }
-                    InvalidateRowHeightEstimate();
-                    InvalidateRowsMeasure(invalidateIndividualElements: true);
-                }
-            }
-
-            if (_topLeftCornerHeader != null)
-            {
-                _topLeftCornerHeader.IsVisible = newValueRows && newValueCols;
-                if (_topLeftCornerHeader.IsVisible)
-                {
-                    _topLeftCornerHeader.Measure(default);
-                }
-            }
-
-        }
-
-        private void OnGridLinesVisibilityChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            foreach (DataGridRow row in GetAllRows())
-            {
-                row.EnsureGridLines();
-                row.InvalidateHorizontalArrange();
-            }
-        }
-
-        private void OnFrozenColumnCountChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            ProcessFrozenColumnCount();
-        }
-
-        private void ProcessFrozenColumnCount()
-        {
-            CorrectColumnFrozenStates();
-            ComputeScrollBarsLayout();
-
-            InvalidateColumnHeadersArrange();
-            InvalidateCellsArrange();
-        }
-
-        private void OnColumnWidthChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            var value = (DataGridLength)e.NewValue;
-
-            foreach (DataGridColumn column in ColumnsInternal.GetDisplayedColumns())
-            {
-                if (column.InheritsWidth)
-                {
-                    column.SetWidthInternalNoCallback(value);
-                }
-            }
-
-            EnsureHorizontalLayout();
-        }
-
-        private void OnCanUserResizeColumnsChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            EnsureHorizontalLayout();
-        }
-
-        /// <summary>
-        /// Occurs one time for each public, non-static property in the bound data type when the
-        /// <see cref="P:Avalonia.Controls.DataGrid.ItemsSource" /> property is changed and the
-        /// <see cref="P:Avalonia.Controls.DataGrid.AutoGenerateColumns" /> property is true.
-        /// </summary>
-        public event EventHandler<DataGridAutoGeneratingColumnEventArgs> AutoGeneratingColumn;
-
-        /// <summary>
-        /// Occurs before a cell or row enters editing mode.
-        /// </summary>
-        public event EventHandler<DataGridBeginningEditEventArgs> BeginningEdit;
-
-        /// <summary>
-        /// Occurs after cell editing has ended.
-        /// </summary>
-        public event EventHandler<DataGridCellEditEndedEventArgs> CellEditEnded;
-
-        /// <summary>
-        /// Occurs immediately before cell editing has ended.
-        /// </summary>
-        public event EventHandler<DataGridCellEditEndingEventArgs> CellEditEnding;
-
-        /// <summary>
-        /// Occurs when cell is mouse-pressed.
-        /// </summary>
-        public event EventHandler<DataGridCellPointerPressedEventArgs> CellPointerPressed;
-
-        /// <summary>
-        /// Occurs when the <see cref="P:Avalonia.Controls.DataGridColumn.DisplayIndex" />
-        /// property of a column changes.
-        /// </summary>
-        public event EventHandler<DataGridColumnEventArgs> ColumnDisplayIndexChanged;
-
-        /// <summary>
-        /// Raised when column reordering ends, to allow subscribers to clean up.
-        /// </summary>
-        public event EventHandler<DataGridColumnEventArgs> ColumnReordered;
-
-        /// <summary>
-        /// Raised when starting a column reordering action.  Subscribers to this event can
-        /// set tooltip and caret UIElements, constrain tooltip position, indicate that
-        /// a preview should be shown, or cancel reordering.
-        /// </summary>
-        public event EventHandler<DataGridColumnReorderingEventArgs> ColumnReordering;
-
-        /// <summary>
-        /// Occurs when a different cell becomes the current cell.
-        /// </summary>
-        public event EventHandler<EventArgs> CurrentCellChanged;
-
-        /// <summary>
-        /// Occurs after a <see cref="T:Avalonia.Controls.DataGridRow" />
-        /// is instantiated, so that you can customize it before it is used.
-        /// </summary>
-        public event EventHandler<DataGridRowEventArgs> LoadingRow;
-
-        /// <summary>
-        /// Occurs when a cell in a <see cref="T:Avalonia.Controls.DataGridTemplateColumn" /> enters editing mode.
-        ///
-        /// </summary>
-        public event EventHandler<DataGridPreparingCellForEditEventArgs> PreparingCellForEdit;
-
-        /// <summary>
-        /// Occurs when the row has been successfully committed or cancelled.
-        /// </summary>
-        public event EventHandler<DataGridRowEditEndedEventArgs> RowEditEnded;
-
-        /// <summary>
-        /// Occurs immediately before the row has been successfully committed or cancelled.
-        /// </summary>
-        public event EventHandler<DataGridRowEditEndingEventArgs> RowEditEnding;
-
-        public static readonly RoutedEvent<SelectionChangedEventArgs> SelectionChangedEvent =
-            RoutedEvent.Register<DataGrid, SelectionChangedEventArgs>(nameof(SelectionChanged), RoutingStrategies.Bubble);
-
-        /// <summary>
-        /// Occurs when the <see cref="P:Avalonia.Controls.DataGrid.SelectedItem" /> or
-        /// <see cref="P:Avalonia.Controls.DataGrid.SelectedItems" /> property value changes.
-        /// </summary>
-        public event EventHandler<SelectionChangedEventArgs> SelectionChanged
-        {
-            add { AddHandler(SelectionChangedEvent, value); }
-            remove { RemoveHandler(SelectionChangedEvent, value); }
-        }
-
-        /// <summary>
-        /// Occurs when the <see cref="DataGridColumn"/> sorting request is triggered.
-        /// </summary>
-        public event EventHandler<DataGridColumnEventArgs> Sorting;
-
-        /// <summary>
-        /// Occurs when a <see cref="T:Avalonia.Controls.DataGridRow" />
-        /// object becomes available for reuse.
-        /// </summary>
-        public event EventHandler<DataGridRowEventArgs> UnloadingRow;
-
-        /// <summary>
-        /// Occurs when a new row details template is applied to a row, so that you can customize
-        /// the details section before it is used.
-        /// </summary>
-        public event EventHandler<DataGridRowDetailsEventArgs> LoadingRowDetails;
-
-        /// <summary>
-        /// Occurs when the <see cref="P:Avalonia.Controls.DataGrid.RowDetailsVisibilityMode" />
-        /// property value changes.
-        /// </summary>
-        public event EventHandler<DataGridRowDetailsEventArgs> RowDetailsVisibilityChanged;
-
-        /// <summary>
-        /// Occurs when a row details element becomes available for reuse.
-        /// </summary>
-        public event EventHandler<DataGridRowDetailsEventArgs> UnloadingRowDetails;
-
-        /// <summary>
-        /// Gets a collection that contains all the columns in the control.
-        /// </summary>
-        public ObservableCollection<DataGridColumn> Columns
-        {
-            get
-            {
-                // we use a backing field here because the field's type
-                // is a subclass of the property's
-                return ColumnsInternal;
-            }
-        }
-
-        /// <summary>
-        /// Gets or sets the column that contains the current cell.
-        /// </summary>
-        public DataGridColumn CurrentColumn
-        {
-            get
-            {
-                if (CurrentColumnIndex == -1)
-                {
-                    return null;
-                }
-                Debug.Assert(CurrentColumnIndex < ColumnsItemsInternal.Count);
-                return ColumnsItemsInternal[CurrentColumnIndex];
-            }
-            set
-            {
-                DataGridColumn dataGridColumn = value;
-                if (dataGridColumn == null)
-                {
-                    throw DataGridError.DataGrid.ValueCannotBeSetToNull("value", "CurrentColumn");
-                }
-                if (CurrentColumn != dataGridColumn)
-                {
-                    if (dataGridColumn.OwningGrid != this)
-                    {
-                        // Provided column does not belong to this DataGrid
-                        throw DataGridError.DataGrid.ColumnNotInThisDataGrid();
-                    }
-                    if (!dataGridColumn.IsVisible)
-                    {
-                        // CurrentColumn cannot be set to an invisible column
-                        throw DataGridError.DataGrid.ColumnCannotBeCollapsed();
-                    }
-                    if (CurrentSlot == -1)
-                    {
-                        // There is no current row so the current column cannot be set
-                        throw DataGridError.DataGrid.NoCurrentRow();
-                    }
-                    bool beginEdit = _editingColumnIndex != -1;
-
-                    //exitEditingMode, keepFocus, raiseEvents
-                    if (!EndCellEdit(DataGridEditAction.Commit, true, ContainsFocus, true))
-                    {
-                        // Edited value couldn't be committed or aborted
-                        return;
-                    }
-
-                    UpdateSelectionAndCurrency(dataGridColumn.Index, CurrentSlot, DataGridSelectionAction.None, false); //scrollIntoView
-                    Debug.Assert(_successfullyUpdatedSelection);
-
-                    if (beginEdit &&
-                        _editingColumnIndex == -1 &&
-                        CurrentSlot != -1 &&
-                        CurrentColumnIndex != -1 &&
-                        CurrentColumnIndex == dataGridColumn.Index &&
-                        dataGridColumn.OwningGrid == this &&
-                        !GetColumnEffectiveReadOnlyState(dataGridColumn))
-                    {
-                        // Returning to editing mode since the grid was in that mode prior to the EndCellEdit call above.
-                        BeginCellEdit(new RoutedEventArgs());
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// Gets a list that contains the data items corresponding to the selected rows.
-        /// </summary>
-        public IList SelectedItems
-        {
-            get { return _selectedItems as IList; }
-        }
-
-        internal DataGridColumnCollection ColumnsInternal
-        {
-            get;
-        }
-
-        internal int AnchorSlot
-        {
-            get;
-            private set;
-        }
-
-        internal double ActualRowHeaderWidth
-        {
-            get
-            {
-                if (!AreRowHeadersVisible)
-                {
-                    return 0;
-                }
-                else
-                {
-                    return !double.IsNaN(RowHeaderWidth) ? RowHeaderWidth : RowHeadersDesiredWidth;
-                }
-            }
-        }
-
-        internal double ActualRowsPresenterHeight
-        {
-            get
-            {
-                if (_rowsPresenter != null)
-                {
-                    return _rowsPresenter.Bounds.Height;
-                }
-                return 0;
-            }
-        }
-
-        internal bool AreColumnHeadersVisible
-        {
-            get
-            {
-                return (HeadersVisibility & DataGridHeadersVisibility.Column) == DataGridHeadersVisibility.Column;
-            }
-        }
-
-        internal bool AreRowHeadersVisible
-        {
-            get
-            {
-                return (HeadersVisibility & DataGridHeadersVisibility.Row) == DataGridHeadersVisibility.Row;
-            }
-        }
-
-        /// <summary>
-        /// Indicates whether or not at least one auto-sizing column is waiting for all the rows
-        /// to be measured before its final width is determined.
-        /// </summary>
-        internal bool AutoSizingColumns
-        {
-            get
-            {
-                return _autoSizingColumns;
-            }
-            set
-            {
-                if (_autoSizingColumns && !value && ColumnsInternal != null)
-                {
-                    double adjustment = CellsWidth - ColumnsInternal.VisibleEdgedColumnsWidth;
-                    AdjustColumnWidths(0, adjustment, false);
-                    foreach (DataGridColumn column in ColumnsInternal.GetVisibleColumns())
-                    {
-                        column.IsInitialDesiredWidthDetermined = true;
-                    }
-                    ColumnsInternal.EnsureVisibleEdgedColumnsWidth();
-                    ComputeScrollBarsLayout();
-                    InvalidateColumnHeadersMeasure();
-                    InvalidateRowsMeasure(true);
-                }
-                _autoSizingColumns = value;
-            }
-        }
-
-        internal double AvailableSlotElementRoom
-        {
-            get;
-            set;
-        }
-
-        internal double CellsEstimatedHeight
-        {
-            get
-            {
-                return RowsPresenterAvailableSize?.Height ?? 0;
-            }
-        }
-
-        // Width currently available for cells this value is smaller.  This width is reduced by the existence of RowHeaders
-        // or a vertical scrollbar.  Layout is asynchronous so changes to the RowHeaders or the vertical scrollbar are
-        // not reflected immediately
-        internal double CellsWidth
-        {
-            get
-            {
-                double rowsWidth = double.PositiveInfinity;
-                if (RowsPresenterAvailableSize.HasValue)
-                {
-                    rowsWidth = Math.Max(0, RowsPresenterAvailableSize.Value.Width - ActualRowHeaderWidth);
-                }
-                return double.IsPositiveInfinity(rowsWidth) ? ColumnsInternal.VisibleEdgedColumnsWidth : rowsWidth;
-            }
-        }
-
-        internal DataGridColumnHeadersPresenter ColumnHeaders => _columnHeadersPresenter;
-
-        internal List<DataGridColumn> ColumnsItemsInternal => ColumnsInternal.ItemsInternal;
-
-        internal bool ContainsFocus
-        {
-            get;
-            private set;
-        }
-
-        internal int CurrentColumnIndex
-        {
-            get
-            {
-                return CurrentCellCoordinates.ColumnIndex;
-            }
-
-            private set
-            {
-                CurrentCellCoordinates.ColumnIndex = value;
-            }
-        }
-
-        internal int CurrentSlot
-        {
-            get
-            {
-                return CurrentCellCoordinates.Slot;
-            }
-
-            private set
-            {
-                CurrentCellCoordinates.Slot = value;
-            }
-        }
-
-        internal DataGridDataConnection DataConnection
-        {
-            get;
-            private set;
-        }
-
-        internal DataGridDisplayData DisplayData
-        {
-            get;
-            private set;
-        }
-
-        internal int EditingColumnIndex
-        {
-            get;
-            private set;
-        }
-
-        internal DataGridRow EditingRow
-        {
-            get;
-            private set;
-        }
-
-        internal double FirstDisplayedScrollingColumnHiddenWidth => _negHorizontalOffset;
-
-        // When the RowsPresenter's width increases, the HorizontalOffset will be incorrect until
-        // the scrollbar's layout is recalculated, which doesn't occur until after the cells are measured.
-        // This property exists to account for this scenario, and avoid collapsing the incorrect cells.
-        internal double HorizontalAdjustment
-        {
-            get;
-            private set;
-        }
-
-        internal static double HorizontalGridLinesThickness => DATAGRID_horizontalGridLinesThickness;
-
-        // the sum of the widths in pixels of the scrolling columns preceding
-        // the first displayed scrolling column
-        internal double HorizontalOffset
-        {
-            get
-            {
-                return _horizontalOffset;
-            }
-            set
-            {
-                if (value < 0)
-                {
-                    value = 0;
-                }
-                double widthNotVisible = Math.Max(0, ColumnsInternal.VisibleEdgedColumnsWidth - CellsWidth);
-                if (value > widthNotVisible)
-                {
-                    value = widthNotVisible;
-                }
-                if (value == _horizontalOffset)
-                {
-                    return;
-                }
-
-                if (_hScrollBar != null && value != _hScrollBar.Value)
-                {
-                    _hScrollBar.Value = value;
-                }
-                _horizontalOffset = value;
-
-                DisplayData.FirstDisplayedScrollingCol = ComputeFirstVisibleScrollingColumn();
-                // update the lastTotallyDisplayedScrollingCol
-                ComputeDisplayedColumns();
-            }
-        }
-
-        internal ScrollBar HorizontalScrollBar => _hScrollBar;
-
-        internal IndexToValueTable<DataGridRowGroupInfo> RowGroupHeadersTable
-        {
-            get;
-            private set;
-        }
-
-        internal bool LoadingOrUnloadingRow
-        {
-            get;
-            private set;
-        }
-
-        internal bool InDisplayIndexAdjustments
-        {
-            get;
-            set;
-        }
-
-        internal int? MouseOverRowIndex
-        {
-            get
-            {
-                return _mouseOverRowIndex;
-            }
-            set
-            {
-                if (_mouseOverRowIndex != value)
-                {
-                    DataGridRow oldMouseOverRow = null;
-                    if (_mouseOverRowIndex.HasValue)
-                    {
-                        int oldSlot = SlotFromRowIndex(_mouseOverRowIndex.Value);
-                        if (IsSlotVisible(oldSlot))
-                        {
-                            oldMouseOverRow = DisplayData.GetDisplayedElement(oldSlot) as DataGridRow;
-                        }
-                    }
-
-                    _mouseOverRowIndex = value;
-
-                    // State for the old row needs to be applied after setting the new value
-                    if (oldMouseOverRow != null)
-                    {
-                        oldMouseOverRow.ApplyState();
-                    }
-
-                    if (_mouseOverRowIndex.HasValue)
-                    {
-                        int newSlot = SlotFromRowIndex(_mouseOverRowIndex.Value);
-                        if (IsSlotVisible(newSlot))
-                        {
-                            DataGridRow newMouseOverRow = DisplayData.GetDisplayedElement(newSlot) as DataGridRow;
-                            Debug.Assert(newMouseOverRow != null);
-                            if (newMouseOverRow != null)
-                            {
-                                newMouseOverRow.ApplyState();
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        internal double NegVerticalOffset
-        {
-            get;
-            private set;
-        }
-
-        internal int NoCurrentCellChangeCount
-        {
-            get
-            {
-                return _noCurrentCellChangeCount;
-            }
-            set
-            {
-                _noCurrentCellChangeCount = value;
-                if (value == 0)
-                {
-                    FlushCurrentCellChanged();
-                }
-            }
-        }
-
-        internal double RowDetailsHeightEstimate
-        {
-            get;
-            private set;
-        }
-
-        internal double RowHeadersDesiredWidth
-        {
-            get
-            {
-                return _rowHeaderDesiredWidth;
-            }
-            set
-            {
-                // We only auto grow
-                if (_rowHeaderDesiredWidth < value)
-                {
-                    double oldActualRowHeaderWidth = ActualRowHeaderWidth;
-                    _rowHeaderDesiredWidth = value;
-                    if (oldActualRowHeaderWidth != ActualRowHeaderWidth)
-                    {
-                        EnsureRowHeaderWidth();
-                    }
-                }
-            }
-        }
-
-        internal double RowGroupHeaderHeightEstimate
-        {
-            get;
-            private set;
-        }
-
-        internal double RowHeightEstimate
-        {
-            get;
-            private set;
-        }
-
-        internal Size? RowsPresenterAvailableSize
-        {
-            get
-            {
-                return _rowsPresenterAvailableSize;
-            }
-            set
-            {
-                if (_rowsPresenterAvailableSize.HasValue && value.HasValue && value.Value.Width > RowsPresenterAvailableSize.Value.Width)
-                {
-                    // When the available cells width increases, the horizontal offset can be incorrect.
-                    // Store away an adjustment to use during the CellsPresenter's measure, so that the
-                    // ShouldDisplayCell method correctly determines if a cell will be in view.
-                    //
-                    //     |   h. offset   |       new available cells width          |
-                    //     |-------------->|----------------------------------------->|
-                    //      __________________________________________________        |
-                    //     |           |           |             |            |       |
-                    //     |  column0  |  column1  |   column2   |  column3   |<----->|
-                    //     |           |           |             |            |  adj. |
-                    //
-                    double adjustment = (_horizontalOffset + value.Value.Width) - ColumnsInternal.VisibleEdgedColumnsWidth;
-                    HorizontalAdjustment = Math.Min(HorizontalOffset, Math.Max(0, adjustment));
-                }
-                else
-                {
-                    HorizontalAdjustment = 0;
-                }
-                _rowsPresenterAvailableSize = value;
-            }
-        }
-
-        internal double[] RowGroupSublevelIndents
-        {
-            get;
-            private set;
-        }
-
-        // This flag indicates whether selection has actually changed during a selection operation,
-        // and exists to ensure that FlushSelectionChanged doesn't unnecessarily raise SelectionChanged.
-        internal bool SelectionHasChanged
-        {
-            get;
-            set;
-        }
-
-        internal int SlotCount
-        {
-            get;
-            private set;
-        }
-
-        /// <summary>
-        /// Indicates whether or not to use star-sizing logic.  If the DataGrid has infinite available space,
-        /// then star sizing doesn't make sense.  In this case, all star columns grow to a predefined size of
-        /// 10,000 pixels in order to show the developer that star columns shouldn't be used.
-        /// </summary>
-        internal bool UsesStarSizing
-        {
-            get
-            {
-                if (ColumnsInternal != null)
-                {
-                    return ColumnsInternal.VisibleStarColumnCount > 0 &&
-                        (!RowsPresenterAvailableSize.HasValue || !double.IsPositiveInfinity(RowsPresenterAvailableSize.Value.Width));
-                }
-                return false;
-            }
-        }
-
-        internal ScrollBar VerticalScrollBar => _vScrollBar;
-
-        internal int VisibleSlotCount
-        {
-            get;
-            set;
-        }
-
-        /// <summary>
-        /// Gets the data item bound to the row that contains the current cell.
-        /// </summary>
-        protected object CurrentItem
-        {
-            get
-            {
-                if (CurrentSlot == -1 || ItemsSource == null || RowGroupHeadersTable.Contains(CurrentSlot))
-                {
-                    return null;
-                }
-                return DataConnection.GetDataItem(RowIndexFromSlot(CurrentSlot));
-            }
-        }
-
-        private DataGridCellCoordinates CurrentCellCoordinates
-        {
-            get;
-            set;
-        }
-
-        private int FirstDisplayedNonFillerColumnIndex
-        {
-            get
-            {
-                DataGridColumn column = ColumnsInternal.FirstVisibleNonFillerColumn;
-                if (column != null)
-                {
-                    if (column.IsFrozen)
-                    {
-                        return column.Index;
-                    }
-                    else
-                    {
-                        if (DisplayData.FirstDisplayedScrollingCol >= column.Index)
-                        {
-                            return DisplayData.FirstDisplayedScrollingCol;
-                        }
-                        else
-                        {
-                            return column.Index;
-                        }
-                    }
-                }
-                return -1;
-            }
-        }
-
-        private bool IsHorizontalScrollBarOverCells
-        {
-            get
-            {
-                return _columnHeadersPresenter != null && Grid.GetColumnSpan(_columnHeadersPresenter) == 2;
-            }
-        }
-
-        private bool IsVerticalScrollBarOverCells
-        {
-            get
-            {
-                return _rowsPresenter != null && Grid.GetRowSpan(_rowsPresenter) == 2;
-            }
-        }
-
-        private int NoSelectionChangeCount
-        {
-            get
-            {
-                return _noSelectionChangeCount;
-            }
-            set
-            {
-                _noSelectionChangeCount = value;
-                if (value == 0)
-                {
-                    FlushSelectionChanged();
-                }
-            }
-        }
-
-        /// <summary>
-        /// Enters editing mode for the current cell and current row (if they're not already in editing mode).
-        /// </summary>
-        /// <returns>True if operation was successful. False otherwise.</returns>
-        public bool BeginEdit()
-        {
-            return BeginEdit(null);
-        }
-
-        /// <summary>
-        /// Enters editing mode for the current cell and current row (if they're not already in editing mode).
-        /// </summary>
-        /// <param name="editingEventArgs">Provides information about the user gesture that caused the call to BeginEdit. Can be null.</param>
-        /// <returns>True if operation was successful. False otherwise.</returns>
-        public bool BeginEdit(RoutedEventArgs editingEventArgs)
-        {
-            if (CurrentColumnIndex == -1 || !GetRowSelection(CurrentSlot))
-            {
-                return false;
-            }
-
-            Debug.Assert(CurrentColumnIndex >= 0);
-            Debug.Assert(CurrentColumnIndex < ColumnsItemsInternal.Count);
-            Debug.Assert(CurrentSlot >= -1);
-            Debug.Assert(CurrentSlot < SlotCount);
-            Debug.Assert(EditingRow == null || EditingRow.Slot == CurrentSlot);
-
-            if (GetColumnEffectiveReadOnlyState(CurrentColumn))
-            {
-                // Current column is read-only
-                return false;
-            }
-            return BeginCellEdit(editingEventArgs);
-        }
-
-        /// <summary>
-        /// Cancels editing mode and restores the original value.
-        /// </summary>
-        /// <returns>True if operation was successful. False otherwise.</returns>
-        public bool CancelEdit()
-        {
-            return CancelEdit(DataGridEditingUnit.Row);
-        }
-
-        /// <summary>
-        /// Cancels editing mode for the specified DataGridEditingUnit and restores its original value.
-        /// </summary>
-        /// <param name="editingUnit">Specifies whether to cancel edit for a Cell or Row.</param>
-        /// <returns>True if operation was successful. False otherwise.</returns>
-        public bool CancelEdit(DataGridEditingUnit editingUnit)
-        {
-            return CancelEdit(editingUnit, raiseEvents: true);
-        }
-
-        /// <summary>
-        /// Commits editing mode and pushes changes to the backend.
-        /// </summary>
-        /// <returns>True if operation was successful. False otherwise.</returns>
-        public bool CommitEdit()
-        {
-            return CommitEdit(DataGridEditingUnit.Row, true);
-        }
-
-        /// <summary>
-        /// Commits editing mode for the specified DataGridEditingUnit and pushes changes to the backend.
-        /// </summary>
-        /// <param name="editingUnit">Specifies whether to commit edit for a Cell or Row.</param>
-        /// <param name="exitEditingMode">Editing mode is left if True.</param>
-        /// <returns>True if operation was successful. False otherwise.</returns>
-        public bool CommitEdit(DataGridEditingUnit editingUnit, bool exitEditingMode)
-        {
-            if (!EndCellEdit(
-                    editAction: DataGridEditAction.Commit,
-                    exitEditingMode: editingUnit == DataGridEditingUnit.Cell ? exitEditingMode : true,
-                    keepFocus: ContainsFocus,
-                    raiseEvents: true))
-            {
-                return false;
-            }
-            if (editingUnit == DataGridEditingUnit.Row)
-            {
-                return EndRowEdit(DataGridEditAction.Commit, exitEditingMode, raiseEvents: true);
-            }
-            return true;
-        }
-
-        /// <summary>
-        /// Scrolls the specified item or RowGroupHeader and/or column into view.
-        /// If item is not null: scrolls the row representing the item into view;
-        /// If column is not null: scrolls the column into view;
-        /// If both item and column are null, the method returns without scrolling.
-        /// </summary>
-        /// <param name="item">an item from the DataGrid's items source or a CollectionViewGroup from the collection view</param>
-        /// <param name="column">a column from the DataGrid's columns collection</param>
-        public void ScrollIntoView(object item, DataGridColumn column)
-        {
-            if ((column == null && (item == null || FirstDisplayedNonFillerColumnIndex == -1))
-                || (column != null && column.OwningGrid != this))
-            {
-                // no-op
-                return;
-            }
-            if (item == null)
-            {
-                // scroll column into view
-                ScrollSlotIntoView(
-                    column.Index,
-                    DisplayData.FirstScrollingSlot,
-                    forCurrentCellChange: false,
-                    forceHorizontalScroll: true);
-            }
-            else
-            {
-                int slot = -1;
-                DataGridRowGroupInfo rowGroupInfo = null;
-                if (item is DataGridCollectionViewGroup collectionViewGroup)
-                {
-                    rowGroupInfo = RowGroupInfoFromCollectionViewGroup(collectionViewGroup);
-                    if (rowGroupInfo == null)
-                    {
-                        Debug.Assert(false);
-                        return;
-                    }
-                    slot = rowGroupInfo.Slot;
-                }
-                else
-                {
-                    // the row index will be set to -1 if the item is null or not in the list
-                    int rowIndex = DataConnection.IndexOf(item);
-                    if (rowIndex == -1)
-                    {
-                        return;
-                    }
-                    slot = SlotFromRowIndex(rowIndex);
-                }
-
-                int columnIndex = (column == null) ? FirstDisplayedNonFillerColumnIndex : column.Index;
-
-                if (_collapsedSlotsTable.Contains(slot))
-                {
-                    // We need to expand all parent RowGroups so that the slot is visible
-                    if (rowGroupInfo != null)
-                    {
-                        ExpandRowGroupParentChain(rowGroupInfo.Level - 1, rowGroupInfo.Slot);
-                    }
-                    else
-                    {
-                        rowGroupInfo = RowGroupHeadersTable.GetValueAt(RowGroupHeadersTable.GetPreviousIndex(slot));
-                        Debug.Assert(rowGroupInfo != null);
-                        if (rowGroupInfo != null)
-                        {
-                            ExpandRowGroupParentChain(rowGroupInfo.Level, rowGroupInfo.Slot);
-                        }
-                    }
-
-                    // Update Scrollbar and display information
-                    NegVerticalOffset = 0;
-                    SetVerticalOffset(0);
-                    ResetDisplayedRows();
-                    DisplayData.FirstScrollingSlot = 0;
-                    ComputeScrollBarsLayout();
-                }
-
-                ScrollSlotIntoView(
-                    columnIndex, slot,
-                    forCurrentCellChange: true,
-                    forceHorizontalScroll: true);
-            }
-        }
-
-        protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
-        {
-            base.OnAttachedToVisualTree(e);
-            if (DataConnection.DataSource != null && !DataConnection.EventsWired)
-            {
-                DataConnection.WireEvents(DataConnection.DataSource);
-                InitializeElements(true /*recycleRows*/);
-            }
-        }
-
-        protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
-        {
-            base.OnDetachedFromVisualTree(e);
-            // When wired to INotifyCollectionChanged, the DataGrid will be cleaned up by GC
-            if (DataConnection.DataSource != null && DataConnection.EventsWired)
-            {
-                DataConnection.UnWireEvents(DataConnection.DataSource);
-            }
-        }
-
-        /// <summary>
-        /// Arranges the content of the <see cref="T:Avalonia.Controls.DataGridRow" />.
-        /// </summary>
-        /// <param name="finalSize">
-        /// The final area within the parent that this element should use to arrange itself and its children.
-        /// </param>
-        /// <returns>
-        /// The actual size used by the <see cref="T:Avalonia.Controls.DataGridRow" />.
-        /// </returns>
-        protected override Size ArrangeOverride(Size finalSize)
-        {
-            if (_makeFirstDisplayedCellCurrentCellPending)
-            {
-                MakeFirstDisplayedCellCurrentCell();
-            }
-
-            if (Bounds.Width != finalSize.Width)
-            {
-                // If our final width has changed, we might need to update the filler
-                InvalidateColumnHeadersArrange();
-                InvalidateCellsArrange();
-            }
-
-            return base.ArrangeOverride(finalSize);
-        }
-
-        /// <summary>
-        /// Measures the children of a <see cref="T:Avalonia.Controls.DataGridRow" /> to prepare for
-        /// arranging them during the
-        /// <see cref="M:Avalonia.Controls.DataGridRow.ArrangeOverride(System.Windows.Size)" /> pass.
-        /// </summary>
-        /// <returns>
-        /// The size that the <see cref="T:Avalonia.Controls.DataGridRow" /> determines it needs during layout, based on its calculations of child object allocated sizes.
-        /// </returns>
-        /// <param name="availableSize">
-        /// The available size that this element can give to child elements. Indicates an upper limit that
-        /// child elements should not exceed.
-        /// </param>
-        protected override Size MeasureOverride(Size availableSize)
-        {
-            // Delay layout until after the initial measure to avoid invalid calculations when the
-            // DataGrid is not part of the visual tree
-            if (!_measured)
-            {
-                _measured = true;
-
-                // We don't need to clear the rows because it was already done when the ItemsSource changed
-                RefreshRowsAndColumns(clearRows: false);
-
-                //// Update our estimates now that the DataGrid has all of the information necessary
-                UpdateRowDetailsHeightEstimate();
-
-                // Update frozen columns to account for columns added prior to loading or autogenerated columns
-                if (FrozenColumnCountWithFiller > 0)
-                {
-                    ProcessFrozenColumnCount();
-                }
-            }
-
-            Size desiredSize;
-            // This is a shortcut to skip layout if we don't have any columns
-            if (ColumnsInternal.VisibleEdgedColumnsWidth == 0)
-            {
-                if (_hScrollBar != null && _hScrollBar.IsVisible)
-                {
-                    _hScrollBar.IsVisible = false;
-                }
-                if (_vScrollBar != null && _vScrollBar.IsVisible)
-                {
-                    _vScrollBar.IsVisible = false;
-                }
-                desiredSize = base.MeasureOverride(availableSize);
-            }
-            else
-            {
-                if (_rowsPresenter != null)
-                {
-                    _rowsPresenter.InvalidateMeasure();
-                }
-
-                InvalidateColumnHeadersMeasure();
-
-                desiredSize = base.MeasureOverride(availableSize);
-
-                ComputeScrollBarsLayout();
-            }
-
-            return desiredSize;
-        }
-
-        /// <inheritdoc/>
-        protected override void OnDataContextBeginUpdate()
-        {
-            base.OnDataContextBeginUpdate();
-
-            NotifyDataContextPropertyForAllRowCells(GetAllRows(), true);
-        }
-
-        /// <inheritdoc/>
-        protected override void OnDataContextEndUpdate()
-        {
-            base.OnDataContextEndUpdate();
-
-            NotifyDataContextPropertyForAllRowCells(GetAllRows(), false);
-        }
-
-        /// <summary>
-        /// Raises the BeginningEdit event.
-        /// </summary>
-        protected virtual void OnBeginningEdit(DataGridBeginningEditEventArgs e)
-        {
-            BeginningEdit?.Invoke(this, e);
-        }
-
-        /// <summary>
-        /// Raises the CellEditEnded event.
-        /// </summary>
-        protected virtual void OnCellEditEnded(DataGridCellEditEndedEventArgs e)
-        {
-            CellEditEnded?.Invoke(this, e);
-        }
-
-        /// <summary>
-        /// Raises the CellEditEnding event.
-        /// </summary>
-        protected virtual void OnCellEditEnding(DataGridCellEditEndingEventArgs e)
-        {
-            CellEditEnding?.Invoke(this, e);
-        }
-
-        /// <summary>
-        /// Raises the CellPointerPressed event.
-        /// </summary>
-        internal virtual void OnCellPointerPressed(DataGridCellPointerPressedEventArgs e)
-        {
-            CellPointerPressed?.Invoke(this, e);
-        }
-
-        /// <summary>
-        /// Raises the CurrentCellChanged event.
-        /// </summary>
-        protected virtual void OnCurrentCellChanged(EventArgs e)
-        {
-            CurrentCellChanged?.Invoke(this, e);
-        }
-
-        /// <summary>
-        /// Raises the LoadingRow event for row preparation.
-        /// </summary>
-        protected virtual void OnLoadingRow(DataGridRowEventArgs e)
-        {
-            EventHandler<DataGridRowEventArgs> handler = LoadingRow;
-            if (handler != null)
-            {
-                Debug.Assert(!_loadedRows.Contains(e.Row));
-                _loadedRows.Add(e.Row);
-                LoadingOrUnloadingRow = true;
-                handler(this, e);
-                LoadingOrUnloadingRow = false;
-                Debug.Assert(_loadedRows.Contains(e.Row));
-                _loadedRows.Remove(e.Row);
-            }
-        }
-
-        /// <summary>
-        /// Scrolls the DataGrid according to the direction of the delta.
-        /// </summary>
-        /// <param name="e">PointerWheelEventArgs</param>
-        protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
-        {
-            var delta = e.Delta;
-            
-            // KeyModifiers.Shift should scroll in horizontal direction. This does not work on every platform. 
-            // If Shift-Key is pressed and X is close to 0 we swap the Vector.
-            if (e.KeyModifiers == KeyModifiers.Shift && MathUtilities.IsZero(delta.X))
-            {
-                delta = new Vector(delta.Y, delta.X);
-            }
-            
-            if(UpdateScroll(delta * DATAGRID_mouseWheelDelta))
-            {
-                e.Handled = true;
-            }
-            else
-            {
-                e.Handled = e.Handled || !ScrollViewer.GetIsScrollChainingEnabled(this);
-            }
-        }
-
-        internal bool UpdateScroll(Vector delta)
-        {
-            if (IsEnabled && DisplayData.NumDisplayedScrollingElements > 0)
-            {
-                var handled = false;
-                var ignoreInvalidate = false;
-                var scrollHeight = 0d;
-
-                // Vertical scroll handling
-                if (delta.Y > 0)
-                {
-                    scrollHeight = Math.Max(-_verticalOffset, -delta.Y);
-                }
-                else if (delta.Y < 0)
-                {
-                    if (_vScrollBar != null && VerticalScrollBarVisibility == ScrollBarVisibility.Visible)
-                    {
-                        scrollHeight = Math.Min(Math.Max(0, _vScrollBar.Maximum - _verticalOffset), -delta.Y);
-                    }
-                    else
-                    {
-                        double maximum = EdgedRowsHeightCalculated - CellsEstimatedHeight;
-                        scrollHeight = Math.Min(Math.Max(0, maximum - _verticalOffset), -delta.Y);
-                    }
-                }
-
-                if (scrollHeight != 0)
-                {
-                    DisplayData.PendingVerticalScrollHeight = scrollHeight;
-                    handled = true;
-                }
-
-                // Horizontal scroll handling
-                if (delta.X != 0)
-                {
-                    var horizontalOffset = HorizontalOffset - delta.X;
-                    var widthNotVisible = Math.Max(0, ColumnsInternal.VisibleEdgedColumnsWidth - CellsWidth);
-
-                    if (horizontalOffset < 0)
-                    {
-                        horizontalOffset = 0;
-                    }
-                    if (horizontalOffset > widthNotVisible)
-                    {
-                        horizontalOffset = widthNotVisible;
-                    }
-
-                    if (UpdateHorizontalOffset(horizontalOffset))
-                    {
-                        // We don't need to invalidate once again after UpdateHorizontalOffset.
-                        ignoreInvalidate = true;
-                        handled = true;
-                    }
-                }
-
-                if (handled)
-                {
-                    if (!ignoreInvalidate)
-                    {
-                        InvalidateRowsMeasure(invalidateIndividualElements: false);
-                    }
-                    return true;
-                }
-            }
-
-            return false;
-        }
-
-        /// <summary>
-        /// Raises the PreparingCellForEdit event.
-        /// </summary>
-        protected virtual void OnPreparingCellForEdit(DataGridPreparingCellForEditEventArgs e)
-        {
-            PreparingCellForEdit?.Invoke(this, e);
-        }
-
-        /// <summary>
-        /// Raises the RowEditEnded event.
-        /// </summary>
-        protected virtual void OnRowEditEnded(DataGridRowEditEndedEventArgs e)
-        {
-            RowEditEnded?.Invoke(this, e);
-        }
-
-        /// <summary>
-        /// Raises the RowEditEnding event.
-        /// </summary>
-        protected virtual void OnRowEditEnding(DataGridRowEditEndingEventArgs e)
-        {
-            RowEditEnding?.Invoke(this, e);
-        }
-
-        /// <summary>
-        /// Raises the SelectionChanged event and clears the _selectionChanged.
-        /// This event won't get raised again until after _selectionChanged is set back to true.
-        /// </summary>
-        protected virtual void OnSelectionChanged(SelectionChangedEventArgs e)
-        {
-            RaiseEvent(e);
-        }
-
-        /// <summary>
-        /// Raises the UnloadingRow event for row recycling.
-        /// </summary>
-        protected virtual void OnUnloadingRow(DataGridRowEventArgs e)
-        {
-            EventHandler<DataGridRowEventArgs> handler = UnloadingRow;
-            if (handler != null)
-            {
-                LoadingOrUnloadingRow = true;
-                handler(this, e);
-                LoadingOrUnloadingRow = false;
-            }
-        }
-
-        /// <summary>
-        /// Comparator class so we can sort list by the display index
-        /// </summary>
-        public class DisplayIndexComparer : IComparer<DataGridColumn>
-        {
-            int IComparer<DataGridColumn>.Compare(DataGridColumn x, DataGridColumn y)
-            {
-                return (x.DisplayIndexWithFiller < y.DisplayIndexWithFiller) ? -1 : 1;
-            }
-        }
-
-        /// <summary>
-        /// Builds the visual tree for the column header when a new template is applied.
-        /// </summary>
-        //TODO Validation UI
-        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
-        {
-            // The template has changed, so we need to refresh the visuals
-            _measured = false;
-
-            if (_columnHeadersPresenter != null)
-            {
-                // If we're applying a new template, we want to remove the old column headers first
-                _columnHeadersPresenter.Children.Clear();
-            }
-
-            _columnHeadersPresenter = e.NameScope.Find<DataGridColumnHeadersPresenter>(DATAGRID_elementColumnHeadersPresenterName);
-
-            if (_columnHeadersPresenter != null)
-            {
-                if (ColumnsInternal.FillerColumn != null)
-                {
-                    ColumnsInternal.FillerColumn.IsRepresented = false;
-                }
-                _columnHeadersPresenter.OwningGrid = this;
-
-                // Columns were added before our Template was applied, add the ColumnHeaders now
-                List<DataGridColumn> sortedInternal = new List<DataGridColumn>(ColumnsItemsInternal);
-                sortedInternal.Sort(new DisplayIndexComparer());
-                foreach (DataGridColumn column in sortedInternal)
-                {
-                    InsertDisplayedColumnHeader(column);
-                }
-            }
-
-            if (_rowsPresenter != null)
-            {
-                // If we're applying a new template, we want to remove the old rows first
-                UnloadElements(recycle: false);
-            }
-
-            _rowsPresenter = e.NameScope.Find<DataGridRowsPresenter>(DATAGRID_elementRowsPresenterName);
-
-            if (_rowsPresenter != null)
-            {
-                _rowsPresenter.OwningGrid = this;
-                InvalidateRowHeightEstimate();
-                UpdateRowDetailsHeightEstimate();
-            }
-
-            _frozenColumnScrollBarSpacer = e.NameScope.Find<Control>(DATAGRID_elementFrozenColumnScrollBarSpacerName);
-
-            if (_hScrollBar != null)
-            {
-                _hScrollBar.Scroll -= HorizontalScrollBar_Scroll;
-            }
-
-            _hScrollBar = e.NameScope.Find<ScrollBar>(DATAGRID_elementHorizontalScrollbarName);
-
-            if (_hScrollBar != null)
-            {
-                _hScrollBar.IsTabStop = false;
-                _hScrollBar.Maximum = 0.0;
-                _hScrollBar.Orientation = Orientation.Horizontal;
-                _hScrollBar.IsVisible = false;
-                _hScrollBar.Scroll += HorizontalScrollBar_Scroll;
-            }
-
-            if (_vScrollBar != null)
-            {
-                _vScrollBar.Scroll -= VerticalScrollBar_Scroll;
-            }
-
-            _vScrollBar = e.NameScope.Find<ScrollBar>(DATAGRID_elementVerticalScrollbarName);
-
-            if (_vScrollBar != null)
-            {
-                _vScrollBar.IsTabStop = false;
-                _vScrollBar.Maximum = 0.0;
-                _vScrollBar.Orientation = Orientation.Vertical;
-                _vScrollBar.IsVisible = false;
-                _vScrollBar.Scroll += VerticalScrollBar_Scroll;
-            }
-
-            _topLeftCornerHeader = e.NameScope.Find<ContentControl>(DATAGRID_elementTopLeftCornerHeaderName);
-            EnsureTopLeftCornerHeader(); // EnsureTopLeftCornerHeader checks for a null _topLeftCornerHeader;
-            _topRightCornerHeader = e.NameScope.Find<ContentControl>(DATAGRID_elementTopRightCornerHeaderName);
-            _bottomRightCorner = e.NameScope.Find<Visual>(DATAGRID_elementBottomRightCornerHeaderName);
-        }
-
-        /// <summary>
-        /// Cancels editing mode for the specified DataGridEditingUnit and restores its original value.
-        /// </summary>
-        /// <param name="editingUnit">Specifies whether to cancel edit for a Cell or Row.</param>
-        /// <param name="raiseEvents">Specifies whether or not to raise editing events</param>
-        /// <returns>True if operation was successful. False otherwise.</returns>
-        internal bool CancelEdit(DataGridEditingUnit editingUnit, bool raiseEvents)
-        {
-            if (!EndCellEdit(
-                    DataGridEditAction.Cancel,
-                    exitEditingMode: true,
-                    keepFocus: ContainsFocus,
-                    raiseEvents: raiseEvents))
-            {
-                return false;
-            }
-
-            if (editingUnit == DataGridEditingUnit.Row)
-            {
-                return EndRowEdit(DataGridEditAction.Cancel, true, raiseEvents);
-            }
-
-            return true;
-        }
-
-        /// <summary>
-        /// call when: selection changes or SelectedItems object changes
-        /// </summary>
-        internal void CoerceSelectedItem()
-        {
-            object selectedItem = null;
-
-            if (SelectionMode == DataGridSelectionMode.Extended &&
-                CurrentSlot != -1 &&
-                _selectedItems.ContainsSlot(CurrentSlot))
-            {
-                selectedItem = CurrentItem;
-            }
-            else if (_selectedItems.Count > 0)
-            {
-                selectedItem = _selectedItems[0];
-            }
-
-            SetValueNoCallback(SelectedItemProperty, selectedItem);
-
-            // Update the SelectedIndex
-            int newIndex = -1;
-
-            if (selectedItem != null)
-            {
-                newIndex = DataConnection.IndexOf(selectedItem);
-            }
-
-            SetValueNoCallback(SelectedIndexProperty, newIndex);
-        }
-
-        internal static DataGridCell GetOwningCell(Control element)
-        {
-            Debug.Assert(element != null);
-            DataGridCell cell = element as DataGridCell;
-            while (element != null && cell == null)
-            {
-                element = element.Parent as Control;
-                cell = element as DataGridCell;
-            }
-            return cell;
-        }
-
-        internal IEnumerable<object> GetSelectionInclusive(int startRowIndex, int endRowIndex)
-        {
-            int endSlot = SlotFromRowIndex(endRowIndex);
-            foreach (int slot in _selectedItems.GetSlots(SlotFromRowIndex(startRowIndex)))
-            {
-                if (slot > endSlot)
-                {
-                    break;
-                }
-                yield return DataConnection.GetDataItem(RowIndexFromSlot(slot));
-            }
-        }
-
-        internal void InitializeElements(bool recycleRows)
-        {
-            try
-            {
-                _noCurrentCellChangeCount++;
-
-                // The underlying collection has changed and our editing row (if there is one)
-                // is no longer relevant, so we should force a cancel edit.
-                CancelEdit(DataGridEditingUnit.Row, raiseEvents: false);
-
-                // We want to persist selection throughout a reset, so store away the selected items
-                List<object> selectedItemsCache = new List<object>(_selectedItems.SelectedItemsCache);
-
-                if (recycleRows)
-                {
-                    RefreshRows(recycleRows, clearRows: true);
-                }
-                else
-                {
-                    RefreshRowsAndColumns(clearRows: true);
-                }
-
-                // Re-select the old items
-                _selectedItems.SelectedItemsCache = selectedItemsCache;
-                CoerceSelectedItem();
-                if (RowDetailsVisibilityMode != DataGridRowDetailsVisibilityMode.Collapsed)
-                {
-                    UpdateRowDetailsVisibilityMode(RowDetailsVisibilityMode);
-                }
-
-                // The currently displayed rows may have incorrect visual states because of the selection change
-                ApplyDisplayedRowsState(DisplayData.FirstScrollingSlot, DisplayData.LastScrollingSlot);
-            }
-            finally
-            {
-                NoCurrentCellChangeCount--;
-            }
-        }
-
-        internal bool IsDoubleClickRecordsClickOnCall(Control element)
-        {
-            if (_clickedElement == element)
-            {
-                _clickedElement = null;
-                return true;
-            }
-            else
-            {
-                _clickedElement = element;
-                return false;
-            }
-        }
-
-        // Returns the item or the CollectionViewGroup that is used as the DataContext for a given slot.
-        // If the DataContext is an item, rowIndex is set to the index of the item within the collection
-        internal object ItemFromSlot(int slot, ref int rowIndex)
-        {
-            if (RowGroupHeadersTable.Contains(slot))
-            {
-                return RowGroupHeadersTable.GetValueAt(slot)?.CollectionViewGroup;
-            }
-            else
-            {
-                rowIndex = RowIndexFromSlot(slot);
-                return DataConnection.GetDataItem(rowIndex);
-            }
-        }
-
-        internal bool ProcessDownKey(KeyEventArgs e)
-        {
-            KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift);
-            return ProcessDownKeyInternal(shift, ctrl);
-        }
-
-        internal bool ProcessEndKey(KeyEventArgs e)
-        {
-            KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift);
-            return ProcessEndKey(shift, ctrl);
-        }
-
-        internal bool ProcessEnterKey(KeyEventArgs e)
-        {
-            KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift);
-            return ProcessEnterKey(shift, ctrl);
-        }
-
-        internal bool ProcessHomeKey(KeyEventArgs e)
-        {
-            KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift);
-            return ProcessHomeKey(shift, ctrl);
-        }
-
-        internal void ProcessHorizontalScroll(ScrollEventType scrollEventType)
-        {
-            if (_horizontalScrollChangesIgnored > 0)
-            {
-                return;
-            }
-
-            // If the user scrolls with the buttons, we need to update the new value of the scroll bar since we delay
-            // this calculation.  If they scroll in another other way, the scroll bar's correct value has already been set
-            double scrollBarValueDifference = 0;
-            if (scrollEventType == ScrollEventType.SmallIncrement)
-            {
-                scrollBarValueDifference = GetHorizontalSmallScrollIncrease();
-            }
-            else if (scrollEventType == ScrollEventType.SmallDecrement)
-            {
-                scrollBarValueDifference = -GetHorizontalSmallScrollDecrease();
-            }
-            _horizontalScrollChangesIgnored++;
-            try
-            {
-                if (scrollBarValueDifference != 0)
-                {
-                    Debug.Assert(_horizontalOffset + scrollBarValueDifference >= 0);
-                    _hScrollBar.Value = _horizontalOffset + scrollBarValueDifference;
-                }
-                UpdateHorizontalOffset(_hScrollBar.Value);
-            }
-            finally
-            {
-                _horizontalScrollChangesIgnored--;
-            }
-        }
-
-        internal bool ProcessLeftKey(KeyEventArgs e)
-        {
-            KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift);
-            return ProcessLeftKey(shift, ctrl);
-        }
-
-        internal bool ProcessNextKey(KeyEventArgs e)
-        {
-            KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift);
-            return ProcessNextKey(shift, ctrl);
-        }
-
-        internal bool ProcessPriorKey(KeyEventArgs e)
-        {
-            KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift);
-            return ProcessPriorKey(shift, ctrl);
-        }
-
-        internal bool ProcessRightKey(KeyEventArgs e)
-        {
-            KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift);
-            return ProcessRightKey(shift, ctrl);
-        }
-
-        /// <summary>
-        /// Selects items and updates currency based on parameters
-        /// </summary>
-        /// <param name="columnIndex">column index to make current</param>
-        /// <param name="item">data item or CollectionViewGroup to make current</param>
-        /// <param name="backupSlot">slot to use in case the item is no longer valid</param>
-        /// <param name="action">selection action to perform</param>
-        /// <param name="scrollIntoView">whether or not the new current item should be scrolled into view</param>
-        internal void ProcessSelectionAndCurrency(int columnIndex, object item, int backupSlot, DataGridSelectionAction action, bool scrollIntoView)
-        {
-            _noSelectionChangeCount++;
-            _noCurrentCellChangeCount++;
-            try
-            {
-                int slot = -1;
-                if (item is DataGridCollectionViewGroup group)
-                {
-                    DataGridRowGroupInfo groupInfo = RowGroupInfoFromCollectionViewGroup(group);
-                    if (groupInfo != null)
-                    {
-                        slot = groupInfo.Slot;
-                    }
-                }
-                else
-                {
-                    slot = SlotFromRowIndex(DataConnection.IndexOf(item));
-                }
-                if (slot == -1)
-                {
-                    slot = backupSlot;
-                }
-                if (slot < 0 || slot > SlotCount)
-                {
-                    return;
-                }
-
-                switch (action)
-                {
-                    case DataGridSelectionAction.AddCurrentToSelection:
-                        SetRowSelection(slot, isSelected: true, setAnchorSlot: true);
-                        break;
-                    case DataGridSelectionAction.RemoveCurrentFromSelection:
-                        SetRowSelection(slot, isSelected: false, setAnchorSlot: false);
-                        break;
-                    case DataGridSelectionAction.SelectFromAnchorToCurrent:
-                        if (SelectionMode == DataGridSelectionMode.Extended && AnchorSlot != -1)
-                        {
-                            int anchorSlot = AnchorSlot;
-                            if (slot <= anchorSlot)
-                            {
-                                SetRowsSelection(slot, anchorSlot);
-                            }
-                            else
-                            {
-                                SetRowsSelection(anchorSlot, slot);
-                            }
-                        }
-                        else
-                        {
-                            goto case DataGridSelectionAction.SelectCurrent;
-                        }
-                        break;
-                    case DataGridSelectionAction.SelectCurrent:
-                        ClearRowSelection(slot, setAnchorSlot: true);
-                        break;
-                    case DataGridSelectionAction.None:
-                        break;
-                }
-
-                if (CurrentSlot != slot || (CurrentColumnIndex != columnIndex && columnIndex != -1))
-                {
-                    if (columnIndex == -1)
-                    {
-                        if (CurrentColumnIndex != -1)
-                        {
-                            columnIndex = CurrentColumnIndex;
-                        }
-                        else
-                        {
-                            DataGridColumn firstVisibleColumn = ColumnsInternal.FirstVisibleNonFillerColumn;
-                            if (firstVisibleColumn != null)
-                            {
-                                columnIndex = firstVisibleColumn.Index;
-                            }
-                        }
-                    }
-                    if (columnIndex != -1)
-                    {
-                        if (!SetCurrentCellCore(
-                                columnIndex, slot,
-                                commitEdit: true,
-                                endRowEdit: SlotFromRowIndex(SelectedIndex) != slot)
-                            || (scrollIntoView &&
-                                !ScrollSlotIntoView(
-                                    columnIndex, slot,
-                                    forCurrentCellChange: true,
-                                    forceHorizontalScroll: false)))
-                        {
-                            return;
-                        }
-                    }
-                }
-                _successfullyUpdatedSelection = true;
-            }
-            finally
-            {
-                NoCurrentCellChangeCount--;
-                NoSelectionChangeCount--;
-            }
-        }
-
-        internal bool ProcessUpKey(KeyEventArgs e)
-        {
-            KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift);
-            return ProcessUpKey(shift, ctrl);
-        }
-
-        //internal void ProcessVerticalScroll(double oldValue, double newValue)
-        internal void ProcessVerticalScroll(ScrollEventType scrollEventType)
-        {
-            if (_verticalScrollChangesIgnored > 0)
-            {
-                return;
-            }
-            Debug.Assert(MathUtilities.LessThanOrClose(_vScrollBar.Value, _vScrollBar.Maximum));
-
-            _verticalScrollChangesIgnored++;
-            try
-            {
-                Debug.Assert(_vScrollBar != null);
-                if (scrollEventType == ScrollEventType.SmallIncrement)
-                {
-                    DisplayData.PendingVerticalScrollHeight = GetVerticalSmallScrollIncrease();
-                    double newVerticalOffset = _verticalOffset + DisplayData.PendingVerticalScrollHeight;
-                    if (newVerticalOffset > _vScrollBar.Maximum)
-                    {
-                        DisplayData.PendingVerticalScrollHeight -= newVerticalOffset - _vScrollBar.Maximum;
-                    }
-                }
-                else if (scrollEventType == ScrollEventType.SmallDecrement)
-                {
-                    if (MathUtilities.GreaterThan(NegVerticalOffset, 0))
-                    {
-                        DisplayData.PendingVerticalScrollHeight -= NegVerticalOffset;
-                    }
-                    else
-                    {
-                        int previousScrollingSlot = GetPreviousVisibleSlot(DisplayData.FirstScrollingSlot);
-                        if (previousScrollingSlot >= 0)
-                        {
-                            ScrollSlotIntoView(previousScrollingSlot, scrolledHorizontally: false);
-                        }
-                        return;
-                    }
-                }
-                else
-                {
-                    DisplayData.PendingVerticalScrollHeight = _vScrollBar.Value - _verticalOffset;
-                }
-
-                if (!MathUtilities.IsZero(DisplayData.PendingVerticalScrollHeight))
-                {
-                    // Invalidate so the scroll happens on idle
-                    InvalidateRowsMeasure(invalidateIndividualElements: false);
-                }
-            }
-            finally
-            {
-                _verticalScrollChangesIgnored--;
-            }
-        }
-
-        internal void RefreshRowsAndColumns(bool clearRows)
-        {
-            if (_measured)
-            {
-                try
-                {
-                    _noCurrentCellChangeCount++;
-
-                    if (clearRows)
-                    {
-                        ClearRows(false);
-                        ClearRowGroupHeadersTable();
-                        PopulateRowGroupHeadersTable();
-                    }
-                    if (AutoGenerateColumns)
-                    {
-                        //Column auto-generation refreshes the rows too
-                        AutoGenerateColumnsPrivate();
-                    }
-                    foreach (DataGridColumn column in ColumnsItemsInternal)
-                    {
-                        //We don't need to refresh the state of AutoGenerated column headers because they're up-to-date
-                        if (!column.IsAutoGenerated && column.HasHeaderCell)
-                        {
-                            column.HeaderCell.UpdatePseudoClasses();
-                        }
-                    }
-
-                    RefreshRows(recycleRows: false, clearRows: false);
-
-                    if (Columns.Count > 0 && CurrentColumnIndex == -1)
-                    {
-                        MakeFirstDisplayedCellCurrentCell();
-                    }
-                    else
-                    {
-                        _makeFirstDisplayedCellCurrentCellPending = false;
-                        _desiredCurrentColumnIndex = -1;
-                        FlushCurrentCellChanged();
-                    }
-                }
-                finally
-                {
-                    NoCurrentCellChangeCount--;
-                }
-            }
-            else
-            {
-                if (clearRows)
-                {
-                    ClearRows(recycle: false);
-                }
-                ClearRowGroupHeadersTable();
-                PopulateRowGroupHeadersTable();
-            }
-        }
-
-        internal bool ScrollSlotIntoView(int columnIndex, int slot, bool forCurrentCellChange, bool forceHorizontalScroll)
-        {
-            Debug.Assert(columnIndex >= 0 && columnIndex < ColumnsItemsInternal.Count);
-            Debug.Assert(DisplayData.FirstDisplayedScrollingCol >= -1 && DisplayData.FirstDisplayedScrollingCol < ColumnsItemsInternal.Count);
-            Debug.Assert(DisplayData.LastTotallyDisplayedScrollingCol >= -1 && DisplayData.LastTotallyDisplayedScrollingCol < ColumnsItemsInternal.Count);
-            Debug.Assert(!IsSlotOutOfBounds(slot));
-            Debug.Assert(DisplayData.FirstScrollingSlot >= -1 && DisplayData.FirstScrollingSlot < SlotCount);
-            Debug.Assert(ColumnsItemsInternal[columnIndex].IsVisible);
-
-            if (CurrentColumnIndex >= 0 &&
-                (CurrentColumnIndex != columnIndex || CurrentSlot != slot))
-            {
-                if (!CommitEditForOperation(columnIndex, slot, forCurrentCellChange) || IsInnerCellOutOfBounds(columnIndex, slot))
-                {
-                    return false;
-                }
-            }
-
-            double oldHorizontalOffset = HorizontalOffset;
-
-            //scroll horizontally unless we're on a RowGroupHeader and we're not forcing horizontal scrolling
-            if ((forceHorizontalScroll || (slot != -1))
-                && !ScrollColumnIntoView(columnIndex))
-            {
-                return false;
-            }
-
-            //scroll vertically
-            if (!ScrollSlotIntoView(slot, scrolledHorizontally: oldHorizontalOffset != HorizontalOffset))
-            {
-                return false;
-            }
-
-            return true;
-        }
-
-        // Convenient overload that commits the current edit.
-        internal bool SetCurrentCellCore(int columnIndex, int slot)
-        {
-            return SetCurrentCellCore(columnIndex, slot, commitEdit: true, endRowEdit: true);
-        }
-
-        internal bool UpdateHorizontalOffset(double newValue)
-        {
-            if (HorizontalOffset != newValue)
-            {
-                HorizontalOffset = newValue;
-
-                InvalidateColumnHeadersMeasure();
-                InvalidateRowsMeasure(true);
-                return true;
-            }
-            return false;
-        }
-
-        internal bool UpdateSelectionAndCurrency(int columnIndex, int slot, DataGridSelectionAction action, bool scrollIntoView)
-        {
-            _successfullyUpdatedSelection = false;
-
-            _noSelectionChangeCount++;
-            _noCurrentCellChangeCount++;
-            try
-            {
-                if (ColumnsInternal.RowGroupSpacerColumn.IsRepresented &&
-                    columnIndex == ColumnsInternal.RowGroupSpacerColumn.Index)
-                {
-                    columnIndex = -1;
-                }
-                if (IsSlotOutOfSelectionBounds(slot) || (columnIndex != -1 && IsColumnOutOfBounds(columnIndex)))
-                {
-                    return false;
-                }
-
-                int newCurrentPosition = -1;
-                object item = ItemFromSlot(slot, ref newCurrentPosition);
-
-                if (EditingRow != null && slot != EditingRow.Slot && !CommitEdit(DataGridEditingUnit.Row, true))
-                {
-                    return false;
-                }
-
-                if (DataConnection.CollectionView != null &&
-                    DataConnection.CollectionView.CurrentPosition != newCurrentPosition)
-                {
-                    DataConnection.MoveCurrentTo(item, slot, columnIndex, action, scrollIntoView);
-                }
-                else
-                {
-                    ProcessSelectionAndCurrency(columnIndex, item, slot, action, scrollIntoView);
-                }
-            }
-            finally
-            {
-                NoCurrentCellChangeCount--;
-                NoSelectionChangeCount--;
-            }
-
-            return _successfullyUpdatedSelection;
-        }
-
-        internal void UpdateStateOnCurrentChanged(object currentItem, int currentPosition)
-        {
-            if (currentItem == CurrentItem && currentItem == SelectedItem && currentPosition == SelectedIndex)
-            {
-                // The DataGrid's CurrentItem is already up-to-date, so we don't need to do anything
-                return;
-            }
-
-            int columnIndex = CurrentColumnIndex;
-            if (columnIndex == -1)
-            {
-                if (IsColumnOutOfBounds(_desiredCurrentColumnIndex) ||
-                    (ColumnsInternal.RowGroupSpacerColumn.IsRepresented && _desiredCurrentColumnIndex == ColumnsInternal.RowGroupSpacerColumn.Index))
-                {
-                    columnIndex = FirstDisplayedNonFillerColumnIndex;
-                }
-                else
-                {
-                    columnIndex = _desiredCurrentColumnIndex;
-                }
-            }
-            _desiredCurrentColumnIndex = -1;
-
-            try
-            {
-                _noSelectionChangeCount++;
-                _noCurrentCellChangeCount++;
-
-                if (!CommitEdit())
-                {
-                    CancelEdit(DataGridEditingUnit.Row, false);
-                }
-
-                ClearRowSelection(true);
-                if (currentItem == null)
-                {
-                    SetCurrentCellCore(-1, -1);
-                }
-                else
-                {
-                    int slot = SlotFromRowIndex(currentPosition);
-                    ProcessSelectionAndCurrency(columnIndex, currentItem, slot, DataGridSelectionAction.SelectCurrent, false);
-                }
-            }
-            finally
-            {
-                NoCurrentCellChangeCount--;
-                NoSelectionChangeCount--;
-            }
-        }
-
-        //TODO: Ensure right button is checked for
-        internal bool UpdateStateOnMouseRightButtonDown(PointerPressedEventArgs pointerPressedEventArgs, int columnIndex, int slot, bool allowEdit)
-        {
-            KeyboardHelper.GetMetaKeyState(this, pointerPressedEventArgs.KeyModifiers, out bool ctrl, out bool shift);
-            return UpdateStateOnMouseRightButtonDown(pointerPressedEventArgs, columnIndex, slot, allowEdit, shift, ctrl);
-        }
-        //TODO: Ensure left button is checked for
-        internal bool UpdateStateOnMouseLeftButtonDown(PointerPressedEventArgs pointerPressedEventArgs, int columnIndex, int slot, bool allowEdit)
-        {
-            KeyboardHelper.GetMetaKeyState(this, pointerPressedEventArgs.KeyModifiers, out bool ctrl, out bool shift);
-            return UpdateStateOnMouseLeftButtonDown(pointerPressedEventArgs, columnIndex, slot, allowEdit, shift, ctrl);
-        }
-
-        internal void UpdateVerticalScrollBar()
-        {
-            if (_vScrollBar != null && _vScrollBar.IsVisible)
-            {
-                double cellsHeight = CellsEstimatedHeight;
-                double edgedRowsHeightCalculated = EdgedRowsHeightCalculated;
-                UpdateVerticalScrollBar(
-                    needVertScrollbar: edgedRowsHeightCalculated > cellsHeight,
-                    forceVertScrollbar: VerticalScrollBarVisibility == ScrollBarVisibility.Visible,
-                    totalVisibleHeight: edgedRowsHeightCalculated,
-                    cellsHeight: cellsHeight);
-            }
-        }
-
-        /// <summary>
-        /// If the editing element has focus, this method will set focus to the DataGrid itself
-        /// in order to force the element to lose focus.  It will then wait for the editing element's
-        /// LostFocus event, at which point it will perform the specified action.
-        ///
-        /// NOTE: It is important to understand that the specified action will be performed when the editing
-        /// element loses focus only if this method returns true.  If it returns false, then the action
-        /// will not be performed later on, and should instead be performed by the caller, if necessary.
-        /// </summary>
-        /// <param name="action">Action to perform after the editing element loses focus</param>
-        /// <returns>True if the editing element had focus and the action was cached away; false otherwise</returns>
-        //TODO TabStop
-        internal bool WaitForLostFocus(Action action)
-        {
-            if (EditingRow != null && EditingColumnIndex != -1 && !_executingLostFocusActions)
-            {
-                DataGridColumn editingColumn = ColumnsItemsInternal[EditingColumnIndex];
-                Control editingElement = editingColumn.GetCellContent(EditingRow);
-                if (editingElement != null && editingElement.ContainsChild(_focusedObject))
-                {
-                    Debug.Assert(_lostFocusActions != null);
-                    _lostFocusActions.Enqueue(action);
-                    editingElement.LostFocus += EditingElement_LostFocus;
-                    //IsTabStop = true;
-                    Focus();
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        /// <summary>
-        /// Raises the LoadingRowDetails for row details preparation
-        /// </summary>
-        protected virtual void OnLoadingRowDetails(DataGridRowDetailsEventArgs e)
-        {
-            EventHandler<DataGridRowDetailsEventArgs> handler = LoadingRowDetails;
-            if (handler != null)
-            {
-                LoadingOrUnloadingRow = true;
-                handler(this, e);
-                LoadingOrUnloadingRow = false;
-            }
-        }
-
-        /// <summary>
-        /// Raises the UnloadingRowDetails event
-        /// </summary>
-        protected virtual void OnUnloadingRowDetails(DataGridRowDetailsEventArgs e)
-        {
-            EventHandler<DataGridRowDetailsEventArgs> handler = UnloadingRowDetails;
-            if (handler != null)
-            {
-                LoadingOrUnloadingRow = true;
-                handler(this, e);
-                LoadingOrUnloadingRow = false;
-            }
-        }
-
-        internal void OnRowDetailsChanged()
-        {
-            if (!_scrollingByHeight)
-            {
-                // Update layout when RowDetails are expanded or collapsed, just updating the vertical scroll bar is not enough
-                // since rows could be added or removed
-                InvalidateMeasure();
-            }
-        }
-
-        private static void NotifyDataContextPropertyForAllRowCells(IEnumerable<DataGridRow> rowSource, bool arg2)
-        {
-            foreach (DataGridRow row in rowSource)
-            {
-                foreach (DataGridCell cell in row.Cells)
-                {
-                    if (cell.Content is StyledElement cellContent)
-                    {
-                        DataContextProperty.Notifying?.Invoke(cellContent, arg2);
-                    }
-                }
-            }
-        }
-
-        private void UpdateRowDetailsVisibilityMode(DataGridRowDetailsVisibilityMode newDetailsMode)
-        {
-            int itemCount = DataConnection.Count;
-            if (_rowsPresenter != null && itemCount > 0)
-            {
-                bool newDetailsVisibility = false;
-                switch (newDetailsMode)
-                {
-                    case DataGridRowDetailsVisibilityMode.Visible:
-                        newDetailsVisibility = true;
-                        _showDetailsTable.AddValues(0, itemCount, true);
-                        break;
-                    case DataGridRowDetailsVisibilityMode.Collapsed:
-                        newDetailsVisibility = false;
-                        _showDetailsTable.AddValues(0, itemCount, false);
-                        break;
-                    case DataGridRowDetailsVisibilityMode.VisibleWhenSelected:
-                        _showDetailsTable.Clear();
-                        break;
-                }
-
-                bool updated = false;
-                foreach (DataGridRow row in GetAllRows())
-                {
-                    if (row.IsVisible)
-                    {
-                        if (newDetailsMode == DataGridRowDetailsVisibilityMode.VisibleWhenSelected)
-                        {
-                            // For VisibleWhenSelected, we need to calculate the value for each individual row
-                            newDetailsVisibility = _selectedItems.ContainsSlot(row.Slot);
-                        }
-                        if (row.AreDetailsVisible != newDetailsVisibility)
-                        {
-                            updated = true;
-
-                            row.SetDetailsVisibilityInternal(newDetailsVisibility, raiseNotification: true, animate: false);
-                        }
-                    }
-                }
-                if (updated)
-                {
-                    UpdateDisplayedRows(DisplayData.FirstScrollingSlot, CellsEstimatedHeight);
-                    InvalidateRowsMeasure(invalidateIndividualElements: false);
-                }
-            }
-        }
-
-        private void AddNewCellPrivate(DataGridRow row, DataGridColumn column)
-        {
-            DataGridCell newCell = new DataGridCell();
-            PopulateCellContent(
-                isCellEdited: false,
-                dataGridColumn: column,
-                dataGridRow: row,
-                dataGridCell: newCell);
-            if (row.OwningGrid != null)
-            {
-                newCell.OwningColumn = column;
-                newCell.IsVisible = column.IsVisible;
-                if (row.OwningGrid.CellTheme is {} cellTheme)
-                {
-                    newCell.SetValue(ThemeProperty, cellTheme, BindingPriority.Template);
-                }
-            }
-            row.Cells.Insert(column.Index, newCell);
-        }
-
-        private bool BeginCellEdit(RoutedEventArgs editingEventArgs)
-        {
-            if (CurrentColumnIndex == -1 || !GetRowSelection(CurrentSlot))
-            {
-                return false;
-            }
-
-            Debug.Assert(CurrentColumnIndex >= 0);
-            Debug.Assert(CurrentColumnIndex < ColumnsItemsInternal.Count);
-            Debug.Assert(CurrentSlot >= -1);
-            Debug.Assert(CurrentSlot < SlotCount);
-            Debug.Assert(EditingRow == null || EditingRow.Slot == CurrentSlot);
-            Debug.Assert(!GetColumnEffectiveReadOnlyState(CurrentColumn));
-            Debug.Assert(CurrentColumn.IsVisible);
-
-            if (_editingColumnIndex != -1)
-            {
-                // Current cell is already in edit mode
-                Debug.Assert(_editingColumnIndex == CurrentColumnIndex);
-                return true;
-            }
-
-            // Get or generate the editing row if it doesn't exist
-            DataGridRow dataGridRow = EditingRow;
-            if (dataGridRow == null)
-            {
-                if (IsSlotVisible(CurrentSlot))
-                {
-                    dataGridRow = DisplayData.GetDisplayedElement(CurrentSlot) as DataGridRow;
-                    Debug.Assert(dataGridRow != null);
-                }
-                else
-                {
-                    dataGridRow = GenerateRow(RowIndexFromSlot(CurrentSlot), CurrentSlot);
-                }
-            }
-            Debug.Assert(dataGridRow != null);
-
-            // Cache these to see if they change later
-            int currentRowIndex = CurrentSlot;
-            int currentColumnIndex = CurrentColumnIndex;
-
-            // Raise the BeginningEdit event
-            DataGridCell dataGridCell = dataGridRow.Cells[CurrentColumnIndex];
-            DataGridBeginningEditEventArgs e = new DataGridBeginningEditEventArgs(CurrentColumn, dataGridRow, editingEventArgs);
-            OnBeginningEdit(e);
-            if (e.Cancel
-                || currentRowIndex != CurrentSlot
-                || currentColumnIndex != CurrentColumnIndex
-                || !GetRowSelection(CurrentSlot)
-                || (EditingRow == null && !BeginRowEdit(dataGridRow)))
-            {
-                // If either BeginningEdit was canceled, currency/selection was changed in the event handler,
-                // or we failed opening the row for edit, then we can no longer continue BeginCellEdit
-                return false;
-            }
-            Debug.Assert(EditingRow != null);
-            Debug.Assert(EditingRow.Slot == CurrentSlot);
-
-            // Finally, we can prepare the cell for editing
-            _editingColumnIndex = CurrentColumnIndex;
-            _editingEventArgs = editingEventArgs;
-            EditingRow.Cells[CurrentColumnIndex].UpdatePseudoClasses();
-            PopulateCellContent(
-                isCellEdited: true,
-                dataGridColumn: CurrentColumn,
-                dataGridRow: dataGridRow,
-                dataGridCell: dataGridCell);
-            return true;
-        }
-
-        //TODO Validation
-        private bool BeginRowEdit(DataGridRow dataGridRow)
-        {
-            Debug.Assert(EditingRow == null);
-            Debug.Assert(dataGridRow != null);
-
-            Debug.Assert(CurrentSlot >= -1);
-            Debug.Assert(CurrentSlot < SlotCount);
-
-            if (DataConnection.BeginEdit(dataGridRow.DataContext))
-            {
-                EditingRow = dataGridRow;
-                GenerateEditingElements();
-                return true;
-            }
-            return false;
-        }
-
-        private bool CancelRowEdit(bool exitEditingMode)
-        {
-            if (EditingRow == null)
-            {
-                return true;
-            }
-            Debug.Assert(EditingRow != null && EditingRow.Index >= -1);
-            Debug.Assert(EditingRow.Slot < SlotCount);
-            Debug.Assert(CurrentColumn != null);
-
-            object dataItem = EditingRow.DataContext;
-            if (!DataConnection.CancelEdit(dataItem))
-            {
-                return false;
-            }
-            foreach (DataGridColumn column in Columns)
-            {
-                if (!exitEditingMode && column.Index == _editingColumnIndex && column is DataGridBoundColumn)
-                {
-                    continue;
-                }
-                PopulateCellContent(
-                    isCellEdited: !exitEditingMode && column.Index == _editingColumnIndex,
-                    dataGridColumn: column,
-                    dataGridRow: EditingRow,
-                    dataGridCell: EditingRow.Cells[column.Index]);
-            }
-            return true;
-        }
-
-        private bool CommitEditForOperation(int columnIndex, int slot, bool forCurrentCellChange)
-        {
-            if (forCurrentCellChange)
-            {
-                if (!EndCellEdit(DataGridEditAction.Commit, exitEditingMode: true, keepFocus: true, raiseEvents: true))
-                {
-                    return false;
-                }
-                if (CurrentSlot != slot &&
-                    !EndRowEdit(DataGridEditAction.Commit, exitEditingMode: true, raiseEvents: true))
-                {
-                    return false;
-                }
-            }
-
-            if (IsColumnOutOfBounds(columnIndex))
-            {
-                return false;
-            }
-            if (slot >= SlotCount)
-            {
-                // Current cell was reset because the commit deleted row(s).
-                // Since the user wants to change the current cell, we don't
-                // want to end up with no current cell. We pick the last row
-                // in the grid which may be the 'new row'.
-                int lastSlot = LastVisibleSlot;
-                if (forCurrentCellChange &&
-                    CurrentColumnIndex == -1 &&
-                    lastSlot != -1)
-                {
-                    SetAndSelectCurrentCell(columnIndex, lastSlot, forceCurrentCellSelection: false);
-                }
-                // Interrupt operation because it has become invalid.
-                return false;
-            }
-            return true;
-        }
-
-        //TODO Validation
-        private bool CommitRowEdit(bool exitEditingMode)
-        {
-            if (EditingRow == null)
-            {
-                return true;
-            }
-            Debug.Assert(EditingRow != null && EditingRow.Index >= -1);
-            Debug.Assert(EditingRow.Slot < SlotCount);
-
-            //if (!ValidateEditingRow(scrollIntoView: true, wireEvents: false))
-            if (!EditingRow.IsValid)
-            {
-                return false;
-            }
-
-            DataConnection.EndEdit(EditingRow.DataContext);
-
-            if (!exitEditingMode)
-            {
-                DataConnection.BeginEdit(EditingRow.DataContext);
-            }
-            return true;
-        }
-
-        private void CompleteCellsCollection(DataGridRow dataGridRow)
-        {
-            Debug.Assert(dataGridRow != null);
-            int cellsInCollection = dataGridRow.Cells.Count;
-            if (ColumnsItemsInternal.Count > cellsInCollection)
-            {
-                for (int columnIndex = cellsInCollection; columnIndex < ColumnsItemsInternal.Count; columnIndex++)
-                {
-                    AddNewCellPrivate(dataGridRow, ColumnsItemsInternal[columnIndex]);
-                }
-            }
-        }
-
-        private void ComputeScrollBarsLayout()
-        {
-            if (_ignoreNextScrollBarsLayout)
-            {
-                _ignoreNextScrollBarsLayout = false;
-                //
-
-            }
-
-            bool isHorizontalScrollBarOverCells = IsHorizontalScrollBarOverCells;
-            bool isVerticalScrollBarOverCells = IsVerticalScrollBarOverCells;
-
-            double cellsWidth = CellsWidth;
-            double cellsHeight = CellsEstimatedHeight;
-
-            bool allowHorizScrollbar = false;
-            bool forceHorizScrollbar = false;
-            double horizScrollBarHeight = 0;
-            if (_hScrollBar != null)
-            {
-                forceHorizScrollbar = HorizontalScrollBarVisibility == ScrollBarVisibility.Visible;
-                allowHorizScrollbar = forceHorizScrollbar || (ColumnsInternal.VisibleColumnCount > 0 &&
-                    HorizontalScrollBarVisibility != ScrollBarVisibility.Disabled &&
-                    HorizontalScrollBarVisibility != ScrollBarVisibility.Hidden);
-                // Compensate if the horizontal scrollbar is already taking up space
-                if (!forceHorizScrollbar && _hScrollBar.IsVisible)
-                {
-                    if (!isHorizontalScrollBarOverCells)
-                    {
-                        cellsHeight += _hScrollBar.DesiredSize.Height;
-                    }
-                }
-                if (!isHorizontalScrollBarOverCells)
-                {
-                    horizScrollBarHeight = _hScrollBar.Height + _hScrollBar.Margin.Top + _hScrollBar.Margin.Bottom;
-                }
-            }
-
-            bool allowVertScrollbar = false;
-            bool forceVertScrollbar = false;
-            double vertScrollBarWidth = 0;
-            if (_vScrollBar != null)
-            {
-                forceVertScrollbar = VerticalScrollBarVisibility == ScrollBarVisibility.Visible;
-                allowVertScrollbar = forceVertScrollbar || (ColumnsItemsInternal.Count > 0 &&
-                    VerticalScrollBarVisibility != ScrollBarVisibility.Disabled &&
-                    VerticalScrollBarVisibility != ScrollBarVisibility.Hidden);
-                // Compensate if the vertical scrollbar is already taking up space
-                if (!forceVertScrollbar && _vScrollBar.IsVisible)
-                {
-                    if (!isVerticalScrollBarOverCells)
-                    {
-                        cellsWidth += _vScrollBar.DesiredSize.Width;
-                    }
-                }
-                if (!isVerticalScrollBarOverCells)
-                {
-                    vertScrollBarWidth = _vScrollBar.Width + _vScrollBar.Margin.Left + _vScrollBar.Margin.Right;
-                }
-            }
-
-            // Now cellsWidth is the width potentially available for displaying data cells.
-            // Now cellsHeight is the height potentially available for displaying data cells.
-
-            bool needHorizScrollbar = false;
-            bool needVertScrollbar = false;
-
-            double totalVisibleWidth = ColumnsInternal.VisibleEdgedColumnsWidth;
-            double totalVisibleFrozenWidth = ColumnsInternal.GetVisibleFrozenEdgedColumnsWidth();
-
-            UpdateDisplayedRows(DisplayData.FirstScrollingSlot, CellsEstimatedHeight);
-            double totalVisibleHeight = EdgedRowsHeightCalculated;
-
-            if (!forceHorizScrollbar && !forceVertScrollbar)
-            {
-                bool needHorizScrollbarWithoutVertScrollbar = false;
-
-                if (allowHorizScrollbar &&
-                    MathUtilities.GreaterThan(totalVisibleWidth, cellsWidth) &&
-                    MathUtilities.LessThan(totalVisibleFrozenWidth, cellsWidth) &&
-                    MathUtilities.LessThanOrClose(horizScrollBarHeight, cellsHeight))
-                {
-                    double oldDataHeight = cellsHeight;
-                    cellsHeight -= horizScrollBarHeight;
-                    Debug.Assert(cellsHeight >= 0);
-                    needHorizScrollbarWithoutVertScrollbar = needHorizScrollbar = true;
-
-                    if (vertScrollBarWidth > 0 &&
-                        allowVertScrollbar && (MathUtilities.LessThanOrClose(totalVisibleWidth - cellsWidth, vertScrollBarWidth) ||
-                        MathUtilities.LessThanOrClose(cellsWidth - totalVisibleFrozenWidth, vertScrollBarWidth)))
-                    {
-                        // Would we still need a horizontal scrollbar without the vertical one?
-                        UpdateDisplayedRows(DisplayData.FirstScrollingSlot, cellsHeight);
-                        if (DisplayData.NumTotallyDisplayedScrollingElements != VisibleSlotCount)
-                        {
-                            needHorizScrollbar = MathUtilities.LessThan(totalVisibleFrozenWidth, cellsWidth - vertScrollBarWidth);
-                        }
-                    }
-
-                    if (!needHorizScrollbar)
-                    {
-                        // Restore old data height because turns out a horizontal scroll bar wouldn't make sense
-                        cellsHeight = oldDataHeight;
-                    }
-                }
-
-                // Store the current FirstScrollingSlot because removing the horizontal scrollbar could scroll
-                // the DataGrid up; however, if we realize later that we need to keep the horizontal scrollbar
-                // then we should use the first slot stored here which is not scrolled.
-                int firstScrollingSlot = DisplayData.FirstScrollingSlot;
-
-                UpdateDisplayedRows(firstScrollingSlot, cellsHeight);
-                if (allowVertScrollbar &&
-                    MathUtilities.GreaterThan(cellsHeight, 0) &&
-                    MathUtilities.LessThanOrClose(vertScrollBarWidth, cellsWidth) &&
-                    DisplayData.NumTotallyDisplayedScrollingElements != VisibleSlotCount)
-                {
-                    cellsWidth -= vertScrollBarWidth;
-                    Debug.Assert(cellsWidth >= 0);
-                    needVertScrollbar = true;
-                }
-
-                DisplayData.FirstDisplayedScrollingCol = ComputeFirstVisibleScrollingColumn();
-
-                // we compute the number of visible columns only after we set up the vertical scroll bar.
-                ComputeDisplayedColumns();
-
-                if ((vertScrollBarWidth > 0 || horizScrollBarHeight > 0) &&
-                    allowHorizScrollbar &&
-                    needVertScrollbar && !needHorizScrollbar &&
-                    MathUtilities.GreaterThan(totalVisibleWidth, cellsWidth) &&
-                    MathUtilities.LessThan(totalVisibleFrozenWidth, cellsWidth) &&
-                    MathUtilities.LessThanOrClose(horizScrollBarHeight, cellsHeight))
-                {
-                    cellsWidth += vertScrollBarWidth;
-                    cellsHeight -= horizScrollBarHeight;
-                    Debug.Assert(cellsHeight >= 0);
-                    needVertScrollbar = false;
-
-                    UpdateDisplayedRows(firstScrollingSlot, cellsHeight);
-                    if (cellsHeight > 0 &&
-                        vertScrollBarWidth <= cellsWidth &&
-                        DisplayData.NumTotallyDisplayedScrollingElements != VisibleSlotCount)
-                    {
-                        cellsWidth -= vertScrollBarWidth;
-                        Debug.Assert(cellsWidth >= 0);
-                        needVertScrollbar = true;
-                    }
-                    if (needVertScrollbar)
-                    {
-                        needHorizScrollbar = true;
-                    }
-                    else
-                    {
-                        needHorizScrollbar = needHorizScrollbarWithoutVertScrollbar;
-                    }
-                }
-            }
-            else if (forceHorizScrollbar && !forceVertScrollbar)
-            {
-                if (allowVertScrollbar)
-                {
-                    if (cellsHeight > 0 &&
-                        MathUtilities.LessThanOrClose(vertScrollBarWidth, cellsWidth) &&
-                        DisplayData.NumTotallyDisplayedScrollingElements != VisibleSlotCount)
-                    {
-                        cellsWidth -= vertScrollBarWidth;
-                        Debug.Assert(cellsWidth >= 0);
-                        needVertScrollbar = true;
-                    }
-                    DisplayData.FirstDisplayedScrollingCol = ComputeFirstVisibleScrollingColumn();
-                    ComputeDisplayedColumns();
-                }
-                needHorizScrollbar = totalVisibleWidth > cellsWidth && totalVisibleFrozenWidth < cellsWidth;
-            }
-            else if (!forceHorizScrollbar && forceVertScrollbar)
-            {
-                if (allowHorizScrollbar)
-                {
-                    if (cellsWidth > 0 &&
-                        MathUtilities.LessThanOrClose(horizScrollBarHeight, cellsHeight) &&
-                        MathUtilities.GreaterThan(totalVisibleWidth, cellsWidth) &&
-                        MathUtilities.LessThan(totalVisibleFrozenWidth, cellsWidth))
-                    {
-                        cellsHeight -= horizScrollBarHeight;
-                        Debug.Assert(cellsHeight >= 0);
-                        needHorizScrollbar = true;
-                        UpdateDisplayedRows(DisplayData.FirstScrollingSlot, cellsHeight);
-                    }
-                    DisplayData.FirstDisplayedScrollingCol = ComputeFirstVisibleScrollingColumn();
-                    ComputeDisplayedColumns();
-                }
-                needVertScrollbar = DisplayData.NumTotallyDisplayedScrollingElements != VisibleSlotCount;
-            }
-            else
-            {
-                Debug.Assert(forceHorizScrollbar && forceVertScrollbar);
-                Debug.Assert(allowHorizScrollbar && allowVertScrollbar);
-                DisplayData.FirstDisplayedScrollingCol = ComputeFirstVisibleScrollingColumn();
-                ComputeDisplayedColumns();
-                needVertScrollbar = DisplayData.NumTotallyDisplayedScrollingElements != VisibleSlotCount;
-                needHorizScrollbar = totalVisibleWidth > cellsWidth && totalVisibleFrozenWidth < cellsWidth;
-            }
-
-            UpdateHorizontalScrollBar(needHorizScrollbar, forceHorizScrollbar, totalVisibleWidth, totalVisibleFrozenWidth, cellsWidth);
-            UpdateVerticalScrollBar(needVertScrollbar, forceVertScrollbar, totalVisibleHeight, cellsHeight);
-
-            if (_topRightCornerHeader != null)
-            {
-                // Show the TopRightHeaderCell based on vertical ScrollBar visibility
-                if (AreColumnHeadersVisible &&
-                    _vScrollBar != null && _vScrollBar.IsVisible)
-                {
-                    _topRightCornerHeader.IsVisible = true;
-                }
-                else
-                {
-                    _topRightCornerHeader.IsVisible = false;
-                }
-            }
-
-            if (_bottomRightCorner != null)
-            {
-                // Show the BottomRightCorner when both scrollbars are visible.
-                _bottomRightCorner.IsVisible =
-                    _hScrollBar != null && _hScrollBar.IsVisible &&
-                    _vScrollBar != null && _vScrollBar.IsVisible;
-            }
-
-            DisplayData.FullyRecycleElements();
-        }
-
-        /// <summary>
-        /// Handles the current editing element's LostFocus event by performing any actions that
-        /// were cached by the WaitForLostFocus method.
-        /// </summary>
-        /// <param name="sender">Editing element</param>
-        /// <param name="e">RoutedEventArgs</param>
-        private void EditingElement_LostFocus(object sender, RoutedEventArgs e)
-        {
-            if (sender is Control editingElement)
-            {
-                editingElement.LostFocus -= EditingElement_LostFocus;
-                if (EditingRow != null && _editingColumnIndex != -1)
-                {
-                    FocusEditingCell(true);
-                }
-                Debug.Assert(_lostFocusActions != null);
-                try
-                {
-                    _executingLostFocusActions = true;
-                    while (_lostFocusActions.Count > 0)
-                    {
-                        _lostFocusActions.Dequeue()();
-                    }
-                }
-                finally
-                {
-                    _executingLostFocusActions = false;
-                }
-            }
-        }
-
-        // Makes sure horizontal layout is updated to reflect any changes that affect it
-        private void EnsureHorizontalLayout()
-        {
-            ColumnsInternal.EnsureVisibleEdgedColumnsWidth();
-            InvalidateColumnHeadersMeasure();
-            InvalidateRowsMeasure(true);
-            InvalidateMeasure();
-        }
-
-        private void EnsureRowHeaderWidth()
-        {
-            if (AreRowHeadersVisible)
-            {
-                if (AreColumnHeadersVisible)
-                {
-                    EnsureTopLeftCornerHeader();
-                }
-
-                if (_rowsPresenter != null)
-                {
-
-                    bool updated = false;
-
-                    foreach (Control element in _rowsPresenter.Children)
-                    {
-                        if (element is DataGridRow row)
-                        {
-                            // If the RowHeader resulted in a different width the last time it was measured, we need
-                            // to re-measure it
-                            if (row.HeaderCell != null && row.HeaderCell.DesiredSize.Width != ActualRowHeaderWidth)
-                            {
-                                row.HeaderCell.InvalidateMeasure();
-                                updated = true;
-                            }
-                        }
-                        else if (element is DataGridRowGroupHeader groupHeader && groupHeader.HeaderCell != null && groupHeader.HeaderCell.DesiredSize.Width != ActualRowHeaderWidth)
-                        {
-                            groupHeader.HeaderCell.InvalidateMeasure();
-                            updated = true;
-                        }
-                    }
-
-                    if (updated)
-                    {
-                        // We need to update the width of the horizontal scrollbar if the rowHeaders' width actually changed
-                        InvalidateMeasure();
-                    }
-                }
-            }
-        }
-
-        private void EnsureRowsPresenterVisibility()
-        {
-            if (_rowsPresenter != null)
-            {
-                // RowCount doesn't need to be considered, doing so might cause extra Visibility changes
-                _rowsPresenter.IsVisible = (ColumnsInternal.FirstVisibleNonFillerColumn != null);
-            }
-        }
-
-        private void EnsureTopLeftCornerHeader()
-        {
-            if (_topLeftCornerHeader != null)
-            {
-                _topLeftCornerHeader.IsVisible = (HeadersVisibility == DataGridHeadersVisibility.All);
-
-                if (_topLeftCornerHeader.IsVisible)
-                {
-                    if (!double.IsNaN(RowHeaderWidth))
-                    {
-                        // RowHeaderWidth is set explicitly so we should use that
-                        _topLeftCornerHeader.Width = RowHeaderWidth;
-                    }
-                    else if (VisibleSlotCount > 0)
-                    {
-                        // RowHeaders AutoSize and we have at least 1 row so take the desired width
-                        _topLeftCornerHeader.Width = RowHeadersDesiredWidth;
-                    }
-                }
-            }
-        }
-
-        private void InvalidateCellsArrange()
-        {
-            foreach (DataGridRow row in GetAllRows())
-            {
-                row.InvalidateHorizontalArrange();
-            }
-        }
-
-        private void InvalidateColumnHeadersArrange()
-        {
-            if (_columnHeadersPresenter != null)
-            {
-                _columnHeadersPresenter.InvalidateArrange();
-            }
-        }
-
-        private void InvalidateColumnHeadersMeasure()
-        {
-            if (_columnHeadersPresenter != null)
-            {
-                EnsureColumnHeadersVisibility();
-                _columnHeadersPresenter.InvalidateMeasure();
-            }
-        }
-
-        private void InvalidateRowsArrange()
-        {
-            if (_rowsPresenter != null)
-            {
-                _rowsPresenter.InvalidateArrange();
-            }
-        }
-
-        private void InvalidateRowsMeasure(bool invalidateIndividualElements)
-        {
-            if (_rowsPresenter != null)
-            {
-                _rowsPresenter.InvalidateMeasure();
-
-                if (invalidateIndividualElements)
-                {
-                    foreach (Control element in _rowsPresenter.Children)
-                    {
-                        element.InvalidateMeasure();
-                    }
-                }
-            }
-        }
-
-        //TODO: Make override?
-        private void DataGrid_GotFocus(object sender, RoutedEventArgs e)
-        {
-            if (!ContainsFocus)
-            {
-                ContainsFocus = true;
-                ApplyDisplayedRowsState(DisplayData.FirstScrollingSlot, DisplayData.LastScrollingSlot);
-                if (CurrentColumnIndex != -1 && IsSlotVisible(CurrentSlot))
-                {
-                    if (DisplayData.GetDisplayedElement(CurrentSlot) is DataGridRow row)
-                    {
-                        row.Cells[CurrentColumnIndex].UpdatePseudoClasses();
-                    }
-                }
-            }
-
-            // Keep track of which row contains the newly focused element
-            DataGridRow focusedRow = null;
-            Visual focusedElement = e.Source as Visual;
-            _focusedObject = focusedElement;
-            while (focusedElement != null)
-            {
-                focusedRow = focusedElement as DataGridRow;
-                if (focusedRow != null && focusedRow.OwningGrid == this && _focusedRow != focusedRow)
-                {
-                    ResetFocusedRow();
-                    _focusedRow = focusedRow.IsVisible ? focusedRow : null;
-                    break;
-                }
-                focusedElement = focusedElement.GetVisualParent();
-            }
-        }
-
-        //TODO: Check
-        private void DataGrid_IsEnabledChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-        }
-
-        private void DataGrid_KeyDown(object sender, KeyEventArgs e)
-        {
-            if (!e.Handled)
-            {
-                e.Handled = ProcessDataGridKey(e);
-            }
-        }
-
-        private void DataGrid_KeyUp(object sender, KeyEventArgs e)
-        {
-            if (e.Key == Key.Tab && CurrentColumnIndex != -1 && e.Source == this)
-            {
-                bool success =
-                    ScrollSlotIntoView(
-                        CurrentColumnIndex, CurrentSlot,
-                        forCurrentCellChange: false,
-                        forceHorizontalScroll: true);
-                Debug.Assert(success);
-                if (CurrentColumnIndex != -1 && SelectedItem == null)
-                {
-                    SetRowSelection(CurrentSlot, isSelected: true, setAnchorSlot: true);
-                }
-            }
-        }
-
-        //TODO: Make override?
-        private void DataGrid_LostFocus(object sender, RoutedEventArgs e)
-        {
-            _focusedObject = null;
-            if (ContainsFocus)
-            {
-                bool focusLeftDataGrid = true;
-                bool dataGridWillReceiveRoutedEvent = true;
-                Visual focusedObject = FocusManager.GetFocusManager(this)?.GetFocusedElement() as Visual;
-                DataGridColumn editingColumn = null;
-
-                while (focusedObject != null)
-                {
-                    if (focusedObject == this)
-                    {
-                        focusLeftDataGrid = false;
-                        break;
-                    }
-
-                    // Walk up the visual tree.  If we hit the root, try using the framework element's
-                    // parent.  We do this because Popups behave differently with respect to the visual tree,
-                    // and it could have a parent even if the VisualTreeHelper doesn't find it.
-                    var parent = focusedObject.Parent as Visual;
-                    if (parent == null)
-                    {
-                        parent = focusedObject.GetVisualParent();
-                    }
-                    else
-                    {
-                        dataGridWillReceiveRoutedEvent = false;
-                    }
-                    focusedObject = parent;
-                }
-
-                if (EditingRow != null && EditingColumnIndex != -1)
-                {
-                    editingColumn = ColumnsItemsInternal[EditingColumnIndex];
-
-                    if (focusLeftDataGrid && editingColumn is DataGridTemplateColumn)
-                    {
-                        dataGridWillReceiveRoutedEvent = false;
-                    }
-                }
-
-                if (focusLeftDataGrid && !(editingColumn is DataGridTemplateColumn))
-                {
-                    ContainsFocus = false;
-                    if (EditingRow != null)
-                    {
-                        CommitEdit(DataGridEditingUnit.Row, exitEditingMode: true);
-                    }
-                    ResetFocusedRow();
-                    ApplyDisplayedRowsState(DisplayData.FirstScrollingSlot, DisplayData.LastScrollingSlot);
-                    if (CurrentColumnIndex != -1 && IsSlotVisible(CurrentSlot))
-                    {
-                        if (DisplayData.GetDisplayedElement(CurrentSlot) is DataGridRow row)
-                        {
-                            row.Cells[CurrentColumnIndex].UpdatePseudoClasses();
-                        }
-                    }
-                }
-                else if (!dataGridWillReceiveRoutedEvent)
-                {
-                    if (focusedObject is Control focusedElement)
-                    {
-                        focusedElement.LostFocus += ExternalEditingElement_LostFocus;
-                    }
-                }
-            }
-        }
-
-        private void EditingElement_Initialized(object sender, EventArgs e)
-        {
-            var element = sender as Control;
-            if (element != null)
-            {
-                element.Initialized -= EditingElement_Initialized;
-            }
-            PreparingCellForEditPrivate(element);
-        }
-
-        //TODO Validation
-        //TODO Binding
-        //TODO TabStop
-        private bool EndCellEdit(DataGridEditAction editAction, bool exitEditingMode, bool keepFocus, bool raiseEvents)
-        {
-            if (_editingColumnIndex == -1)
-            {
-                return true;
-            }
-
-            var editingRow = EditingRow;
-            if (editingRow is null)
-            {
-                return true;
-            }
-
-            Debug.Assert(_editingColumnIndex >= 0);
-            Debug.Assert(_editingColumnIndex < ColumnsItemsInternal.Count);
-            Debug.Assert(_editingColumnIndex == CurrentColumnIndex);
-
-            // Cache these to see if they change later
-            int currentSlot = CurrentSlot;
-            int currentColumnIndex = CurrentColumnIndex;
-
-            // We're ready to start ending, so raise the event
-            DataGridCell editingCell = editingRow.Cells[_editingColumnIndex];
-            var editingElement = editingCell.Content as Control;
-            if (editingElement == null)
-            {
-                return false;
-            }
-            if (raiseEvents)
-            {
-                DataGridCellEditEndingEventArgs e = new DataGridCellEditEndingEventArgs(CurrentColumn, editingRow, editingElement, editAction);
-                OnCellEditEnding(e);
-                if (e.Cancel)
-                {
-                    // CellEditEnding has been cancelled
-                    return false;
-                }
-
-                // Ensure that the current cell wasn't changed in the user's CellEditEnding handler
-                if (_editingColumnIndex == -1 ||
-                    currentSlot != CurrentSlot ||
-                    currentColumnIndex != CurrentColumnIndex)
-                {
-                    return true;
-                }
-                Debug.Assert(EditingRow != null);
-                Debug.Assert(EditingRow.Slot == currentSlot);
-                Debug.Assert(_editingColumnIndex != -1);
-                Debug.Assert(_editingColumnIndex == CurrentColumnIndex);
-            }
-
-            // If we're canceling, let the editing column repopulate its old value if it wants
-            if (editAction == DataGridEditAction.Cancel)
-            {
-                CurrentColumn.CancelCellEditInternal(editingElement, _uneditedValue);
-
-                // Ensure that the current cell wasn't changed in the user column's CancelCellEdit
-                if (_editingColumnIndex == -1 ||
-                    currentSlot != CurrentSlot ||
-                    currentColumnIndex != CurrentColumnIndex)
-                {
-                    return true;
-                }
-                Debug.Assert(EditingRow != null);
-                Debug.Assert(EditingRow.Slot == currentSlot);
-                Debug.Assert(_editingColumnIndex != -1);
-                Debug.Assert(_editingColumnIndex == CurrentColumnIndex);
-            }
-
-            // If we're committing, explicitly update the source but watch out for any validation errors
-            if (editAction == DataGridEditAction.Commit)
-            {
-                void SetValidationStatus(ICellEditBinding binding)
-                {
-                    if (binding.IsValid)
-                    {
-                        ResetValidationStatus();
-                        if (editingElement != null)
-                        {
-                            DataValidationErrors.ClearErrors(editingElement);
-                        }
-                    }
-                    else
-                    {
-                        if (editingRow != null)
-                        {
-                            if (editingCell.IsValid)
-                            {
-                                editingCell.IsValid = false;
-                                editingCell.UpdatePseudoClasses();
-                            }
-
-                            if (editingRow.IsValid)
-                            {
-                                editingRow.IsValid = false;
-                                editingRow.ApplyState();
-                            }
-                        }
-
-                        if (editingElement != null)
-                        {
-                            DataValidationErrors.SetError(editingElement,
-                                new AggregateException(binding.ValidationErrors));
-                        }
-                    }
-                }
-
-                var editBinding = CurrentColumn?.CellEditBinding;
-                if (editBinding != null && !editBinding.CommitEdit())
-                {
-                    SetValidationStatus(editBinding);
-                    _validationSubscription?.Dispose();
-                    _validationSubscription = editBinding.ValidationChanged.Subscribe(v => SetValidationStatus(editBinding));
-
-                    ScrollSlotIntoView(CurrentColumnIndex, CurrentSlot, forCurrentCellChange: false, forceHorizontalScroll: true);
-                    return false;
-                }
-            }
-
-            ResetValidationStatus();
-
-            if (exitEditingMode)
-            {
-                CurrentColumn.EndCellEditInternal();
-                _editingColumnIndex = -1;
-                editingCell.UpdatePseudoClasses();
-
-                //IsTabStop = true;
-                if (keepFocus && editingElement.ContainsFocusedElement())
-                {
-                    Focus();
-                }
-
-                PopulateCellContent(
-                    isCellEdited: !exitEditingMode,
-                    dataGridColumn: CurrentColumn,
-                    dataGridRow: editingRow,
-                    dataGridCell: editingCell);
-
-                editingRow.InvalidateDesiredHeight();
-                var column = editingCell.OwningColumn;
-                if (column.Width.IsSizeToCells || column.Width.IsAuto)
-                {// Invalidate desired width and force recalculation
-                    column.SetWidthDesiredValue(0);
-                    editingRow.OwningGrid.AutoSizeColumn(column, editingCell.DesiredSize.Width);
-                }
-            }
-
-            // We're done, so raise the CellEditEnded event
-            if (raiseEvents)
-            {
-                OnCellEditEnded(new DataGridCellEditEndedEventArgs(CurrentColumn, editingRow, editAction));
-            }
-
-            // There's a chance that somebody reopened this cell for edit within the CellEditEnded handler,
-            // so we should return false if we were supposed to exit editing mode, but we didn't
-            return !(exitEditingMode && currentColumnIndex == _editingColumnIndex);
-        }
-
-        //TODO Validation
-        private bool EndRowEdit(DataGridEditAction editAction, bool exitEditingMode, bool raiseEvents)
-        {
-            if (EditingRow == null || DataConnection.CommittingEdit)
-            {
-                return true;
-            }
-            if (_editingColumnIndex != -1 || (editAction == DataGridEditAction.Cancel && raiseEvents &&
-                !((DataConnection.EditableCollectionView != null && DataConnection.EditableCollectionView.CanCancelEdit) || (EditingRow.DataContext is IEditableObject))))
-            {
-                // Ending the row edit will fail immediately under the following conditions:
-                // 1. We haven't ended the cell edit yet.
-                // 2. We're trying to cancel edit when the underlying DataType is not an IEditableObject,
-                //    because we have no way to properly restore the old value.  We will only allow this to occur
-                //    if raiseEvents == false, which means we're internally forcing a cancel.
-                return false;
-            }
-            DataGridRow editingRow = EditingRow;
-
-            if (raiseEvents)
-            {
-                DataGridRowEditEndingEventArgs e = new DataGridRowEditEndingEventArgs(EditingRow, editAction);
-                OnRowEditEnding(e);
-                if (e.Cancel)
-                {
-                    // RowEditEnding has been cancelled
-                    return false;
-                }
-
-                // Editing states might have been changed in the RowEditEnding handlers
-                if (_editingColumnIndex != -1)
-                {
-                    return false;
-                }
-                if (editingRow != EditingRow)
-                {
-                    return true;
-                }
-            }
-
-            // Call the appropriate commit or cancel methods
-            if (editAction == DataGridEditAction.Commit)
-            {
-                if (!CommitRowEdit(exitEditingMode))
-                {
-                    return false;
-                }
-            }
-            else
-            {
-                if (!CancelRowEdit(exitEditingMode) && raiseEvents)
-                {
-                    // We failed to cancel edit so we should abort unless we're forcing a cancel
-                    return false;
-                }
-            }
-            ResetValidationStatus();
-
-            // Update the previously edited row's state
-            if (exitEditingMode && editingRow == EditingRow)
-            {
-                RemoveEditingElements();
-                ResetEditingRow();
-            }
-
-            // Raise the RowEditEnded event
-            if (raiseEvents)
-            {
-                OnRowEditEnded(new DataGridRowEditEndedEventArgs(editingRow, editAction));
-            }
-
-            return true;
-        }
-
-        private void EnsureColumnHeadersVisibility()
-        {
-            if (_columnHeadersPresenter != null)
-            {
-                _columnHeadersPresenter.IsVisible = AreColumnHeadersVisible;
-            }
-        }
-
-        private void EnsureVerticalGridLines()
-        {
-            if (AreColumnHeadersVisible)
-            {
-                double totalColumnsWidth = 0;
-                foreach (DataGridColumn column in ColumnsInternal)
-                {
-                    totalColumnsWidth += column.ActualWidth;
-
-                    column.HeaderCell.AreSeparatorsVisible = (column != ColumnsInternal.LastVisibleColumn || totalColumnsWidth < CellsWidth);
-                }
-            }
-
-            foreach (DataGridRow row in GetAllRows())
-            {
-                row.EnsureGridLines();
-            }
-        }
-
-        /// <summary>
-        /// Exits editing mode without trying to commit or revert the editing, and
-        /// without repopulating the edited row's cell.
-        /// </summary>
-        //TODO TabStop
-        private void ExitEdit(bool keepFocus)
-        {
-            if (EditingRow == null || DataConnection.CommittingEdit)
-            {
-                Debug.Assert(_editingColumnIndex == -1);
-                return;
-            }
-
-            if (_editingColumnIndex != -1)
-            {
-                Debug.Assert(_editingColumnIndex >= 0);
-                Debug.Assert(_editingColumnIndex < ColumnsItemsInternal.Count);
-                Debug.Assert(_editingColumnIndex == CurrentColumnIndex);
-                Debug.Assert(EditingRow != null && EditingRow.Slot == CurrentSlot);
-
-                _editingColumnIndex = -1;
-                EditingRow.Cells[CurrentColumnIndex].UpdatePseudoClasses();
-            }
-            //IsTabStop = true;
-            if (IsSlotVisible(EditingRow.Slot))
-            {
-                EditingRow.ApplyState();
-            }
-            ResetEditingRow();
-            if (keepFocus)
-            {
-                Focus();
-            }
-        }
-
-        private void ExternalEditingElement_LostFocus(object sender, RoutedEventArgs e)
-        {
-            if (sender is Control element)
-            {
-                element.LostFocus -= ExternalEditingElement_LostFocus;
-                DataGrid_LostFocus(sender, e);
-            }
-        }
-
-        private void FlushCurrentCellChanged()
-        {
-            if (_makeFirstDisplayedCellCurrentCellPending)
-            {
-                return;
-            }
-            if (SelectionHasChanged)
-            {
-                // selection is changing, don't raise CurrentCellChanged until it's done
-                _flushCurrentCellChanged = true;
-                FlushSelectionChanged();
-                return;
-            }
-
-            // We don't want to expand all intermediate currency positions, so we only expand
-            // the last current item before we flush the event
-            if (_collapsedSlotsTable.Contains(CurrentSlot))
-            {
-                DataGridRowGroupInfo rowGroupInfo = RowGroupHeadersTable.GetValueAt(RowGroupHeadersTable.GetPreviousIndex(CurrentSlot));
-                Debug.Assert(rowGroupInfo != null);
-                if (rowGroupInfo != null)
-                {
-                    ExpandRowGroupParentChain(rowGroupInfo.Level, rowGroupInfo.Slot);
-                }
-            }
-
-            if (CurrentColumn != _previousCurrentColumn
-                || CurrentItem != _previousCurrentItem)
-            {
-                CoerceSelectedItem();
-                _previousCurrentColumn = CurrentColumn;
-                _previousCurrentItem = CurrentItem;
-
-                OnCurrentCellChanged(EventArgs.Empty);
-            }
-
-            _flushCurrentCellChanged = false;
-        }
-
-        private void FlushSelectionChanged()
-        {
-            if (SelectionHasChanged && _noSelectionChangeCount == 0 && !_makeFirstDisplayedCellCurrentCellPending)
-            {
-                CoerceSelectedItem();
-                if (NoCurrentCellChangeCount != 0)
-                {
-                    // current cell is changing, don't raise SelectionChanged until it's done
-                    return;
-                }
-                SelectionHasChanged = false;
-
-                if (_flushCurrentCellChanged)
-                {
-                    FlushCurrentCellChanged();
-                }
-
-                SelectionChangedEventArgs e = _selectedItems.GetSelectionChangedEventArgs();
-                if (e.AddedItems.Count > 0 || e.RemovedItems.Count > 0)
-                {
-                    OnSelectionChanged(e);
-                }
-            }
-        }
-
-        //TODO TabStop
-        private bool FocusEditingCell(bool setFocus)
-        {
-            Debug.Assert(CurrentColumnIndex >= 0);
-            Debug.Assert(CurrentColumnIndex < ColumnsItemsInternal.Count);
-            Debug.Assert(CurrentSlot >= -1);
-            Debug.Assert(CurrentSlot < SlotCount);
-            Debug.Assert(EditingRow != null && EditingRow.Slot == CurrentSlot);
-            Debug.Assert(_editingColumnIndex != -1);
-
-            //IsTabStop = false;
-            _focusEditingControl = false;
-
-            bool success = false;
-            DataGridCell dataGridCell = EditingRow.Cells[_editingColumnIndex];
-            if (setFocus)
-            {
-                if (dataGridCell.ContainsFocusedElement())
-                {
-                    success = true;
-                }
-                else
-                {
-                    dataGridCell.Focus();
-                    success = dataGridCell.ContainsFocusedElement();
-                }
-
-                _focusEditingControl = !success;
-            }
-            return success;
-        }
-
-        // Calculates the amount to scroll for the ScrollLeft button
-        // This is a method rather than a property to emphasize a calculation
-        private double GetHorizontalSmallScrollDecrease()
-        {
-            // If the first column is covered up, scroll to the start of it when the user clicks the left button
-            if (_negHorizontalOffset > 0)
-            {
-                return _negHorizontalOffset;
-            }
-            else
-            {
-                // The entire first column is displayed, show the entire previous column when the user clicks
-                // the left button
-                DataGridColumn previousColumn = ColumnsInternal.GetPreviousVisibleScrollingColumn(
-                    ColumnsItemsInternal[DisplayData.FirstDisplayedScrollingCol]);
-                if (previousColumn != null)
-                {
-                    return GetEdgedColumnWidth(previousColumn);
-                }
-                else
-                {
-                    // There's no previous column so don't move
-                    return 0;
-                }
-            }
-        }
-
-        // Calculates the amount to scroll for the ScrollRight button
-        // This is a method rather than a property to emphasize a calculation
-        private double GetHorizontalSmallScrollIncrease()
-        {
-            if (DisplayData.FirstDisplayedScrollingCol >= 0)
-            {
-                return GetEdgedColumnWidth(ColumnsItemsInternal[DisplayData.FirstDisplayedScrollingCol]) - _negHorizontalOffset;
-            }
-            return 0;
-        }
-
-        // Calculates the amount the ScrollDown button should scroll
-        // This is a method rather than a property to emphasize that calculations are taking place
-        private double GetVerticalSmallScrollIncrease()
-        {
-            if (DisplayData.FirstScrollingSlot >= 0)
-            {
-                return GetExactSlotElementHeight(DisplayData.FirstScrollingSlot) - NegVerticalOffset;
-            }
-            return 0;
-        }
-
-        private void HorizontalScrollBar_Scroll(object sender, ScrollEventArgs e)
-        {
-            ProcessHorizontalScroll(e.ScrollEventType);
-            HorizontalScroll?.Invoke(sender, e);
-        }
-
-        private bool IsColumnOutOfBounds(int columnIndex)
-        {
-            return columnIndex >= ColumnsItemsInternal.Count || columnIndex < 0;
-        }
-
-        private bool IsInnerCellOutOfBounds(int columnIndex, int slot)
-        {
-            return IsColumnOutOfBounds(columnIndex) || IsSlotOutOfBounds(slot);
-        }
-
-        private bool IsInnerCellOutOfSelectionBounds(int columnIndex, int slot)
-        {
-            return IsColumnOutOfBounds(columnIndex) || IsSlotOutOfSelectionBounds(slot);
-        }
-
-        private bool IsSlotOutOfBounds(int slot)
-        {
-            return slot >= SlotCount || slot < -1 || _collapsedSlotsTable.Contains(slot);
-        }
-
-        private bool IsSlotOutOfSelectionBounds(int slot)
-        {
-            if (RowGroupHeadersTable.Contains(slot))
-            {
-                Debug.Assert(slot >= 0 && slot < SlotCount);
-                return false;
-            }
-            else
-            {
-                int rowIndex = RowIndexFromSlot(slot);
-                return rowIndex < 0 || rowIndex >= DataConnection.Count;
-            }
-        }
-
-        private void MakeFirstDisplayedCellCurrentCell()
-        {
-            if (CurrentColumnIndex != -1)
-            {
-                _makeFirstDisplayedCellCurrentCellPending = false;
-                _desiredCurrentColumnIndex = -1;
-                FlushCurrentCellChanged();
-                return;
-            }
-            if (SlotCount != SlotFromRowIndex(DataConnection.Count))
-            {
-                _makeFirstDisplayedCellCurrentCellPending = true;
-                return;
-            }
-
-            // No current cell, therefore no selection either - try to set the current cell to the
-            // ItemsSource's ICollectionView.CurrentItem if it exists, otherwise use the first displayed cell.
-            int slot = 0;
-            if (DataConnection.CollectionView != null)
-            {
-                if (DataConnection.CollectionView.IsCurrentBeforeFirst ||
-                    DataConnection.CollectionView.IsCurrentAfterLast)
-                {
-                    slot = RowGroupHeadersTable.Contains(0) ? 0 : -1;
-                }
-                else
-                {
-                    slot = SlotFromRowIndex(DataConnection.CollectionView.CurrentPosition);
-                }
-            }
-            else
-            {
-                if (SelectedIndex == -1)
-                {
-                    // Try to default to the first row
-                    slot = SlotFromRowIndex(0);
-                    if (!IsSlotVisible(slot))
-                    {
-                        slot = -1;
-                    }
-                }
-                else
-                {
-                    slot = SlotFromRowIndex(SelectedIndex);
-                }
-            }
-            int columnIndex = FirstDisplayedNonFillerColumnIndex;
-            if (_desiredCurrentColumnIndex >= 0 && _desiredCurrentColumnIndex < ColumnsItemsInternal.Count)
-            {
-                columnIndex = _desiredCurrentColumnIndex;
-            }
-
-            SetAndSelectCurrentCell(columnIndex, slot, forceCurrentCellSelection: false);
-            AnchorSlot = slot;
-            _makeFirstDisplayedCellCurrentCellPending = false;
-            _desiredCurrentColumnIndex = -1;
-            FlushCurrentCellChanged();
-        }
-
-        private void PopulateCellContent(bool isCellEdited,
-                                         DataGridColumn dataGridColumn,
-                                         DataGridRow dataGridRow,
-                                         DataGridCell dataGridCell)
-        {
-            Debug.Assert(dataGridColumn != null);
-            Debug.Assert(dataGridRow != null);
-            Debug.Assert(dataGridCell != null);
-
-            Control element = null;
-            DataGridBoundColumn dataGridBoundColumn = dataGridColumn as DataGridBoundColumn;
-            if (isCellEdited)
-            {
-                // Generate EditingElement and apply column style if available
-                element = dataGridColumn.GenerateEditingElementInternal(dataGridCell, dataGridRow.DataContext);
-                if (element != null)
-                {
-
-                    dataGridCell.Content = element;
-                    if (element.IsInitialized)
-                    {
-                        PreparingCellForEditPrivate(element as Control);
-                    }
-                    else
-                    {
-                        // Subscribe to the new element's events
-                        element.Initialized += EditingElement_Initialized;
-                    }
-                }
-            }
-            else
-            {
-                // Generate Element and apply column style if available
-                element = dataGridColumn.GenerateElementInternal(dataGridCell, dataGridRow.DataContext);
-                dataGridCell.Content = element;
-            }
-
-
-        }
-
-        private void PreparingCellForEditPrivate(Control editingElement)
-        {
-            if (_editingColumnIndex == -1 ||
-                CurrentColumnIndex == -1 ||
-                EditingRow.Cells[CurrentColumnIndex].Content != editingElement)
-            {
-                // The current cell has changed since the call to BeginCellEdit, so the fact
-                // that this element has loaded is no longer relevant
-                return;
-            }
-
-            Debug.Assert(EditingRow != null);
-            Debug.Assert(_editingColumnIndex >= 0);
-            Debug.Assert(_editingColumnIndex < ColumnsItemsInternal.Count);
-            Debug.Assert(_editingColumnIndex == CurrentColumnIndex);
-            Debug.Assert(EditingRow != null && EditingRow.Slot == CurrentSlot);
-
-            FocusEditingCell(setFocus: ContainsFocus || _focusEditingControl);
-
-            // Prepare the cell for editing and raise the PreparingCellForEdit event for all columns
-            DataGridColumn dataGridColumn = CurrentColumn;
-            _uneditedValue = dataGridColumn.PrepareCellForEditInternal(editingElement, _editingEventArgs);
-            OnPreparingCellForEdit(new DataGridPreparingCellForEditEventArgs(dataGridColumn, EditingRow, _editingEventArgs, editingElement));
-        }
-
-        private bool ProcessAKey(KeyEventArgs e)
-        {
-            KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift, out bool alt);
-
-            if (ctrl && !shift && !alt && SelectionMode == DataGridSelectionMode.Extended)
-            {
-                SelectAll();
-                return true;
-            }
-            return false;
-        }
-
-        //TODO TabStop
-        //TODO FlowDirection
-        private bool ProcessDataGridKey(KeyEventArgs e)
-        {
-            bool focusDataGrid = false;
-            switch (e.Key)
-            {
-                case Key.Tab:
-                    return ProcessTabKey(e);
-
-                case Key.Up:
-                    focusDataGrid = ProcessUpKey(e);
-                    break;
-
-                case Key.Down:
-                    focusDataGrid = ProcessDownKey(e);
-                    break;
-
-                case Key.PageDown:
-                    focusDataGrid = ProcessNextKey(e);
-                    break;
-
-                case Key.PageUp:
-                    focusDataGrid = ProcessPriorKey(e);
-                    break;
-
-                case Key.Left:
-                    focusDataGrid = ProcessLeftKey(e);
-                    break;
-
-                case Key.Right:
-                    focusDataGrid = ProcessRightKey(e);
-                    break;
-
-                case Key.F2:
-                    return ProcessF2Key(e);
-
-                case Key.Home:
-                    focusDataGrid = ProcessHomeKey(e);
-                    break;
-
-                case Key.End:
-                    focusDataGrid = ProcessEndKey(e);
-                    break;
-
-                case Key.Enter:
-                    focusDataGrid = ProcessEnterKey(e);
-                    break;
-
-                case Key.Escape:
-                    return ProcessEscapeKey();
-
-                case Key.A:
-                    return ProcessAKey(e);
-
-                case Key.C:
-                    return ProcessCopyKey(e.KeyModifiers);
-
-                case Key.Insert:
-                    return ProcessCopyKey(e.KeyModifiers);
-            }
-            if (focusDataGrid)
-            {
-                Focus();
-            }
-            return focusDataGrid;
-        }
-
-        private bool ProcessDownKeyInternal(bool shift, bool ctrl)
-        {
-            DataGridColumn dataGridColumn = ColumnsInternal.FirstVisibleColumn;
-            int firstVisibleColumnIndex = (dataGridColumn == null) ? -1 : dataGridColumn.Index;
-            int lastSlot = LastVisibleSlot;
-            if (firstVisibleColumnIndex == -1 || lastSlot == -1)
-            {
-                return false;
-            }
-
-            if (WaitForLostFocus(() => ProcessDownKeyInternal(shift, ctrl)))
-            {
-                return true;
-            }
-
-            int nextSlot = -1;
-            if (CurrentSlot != -1)
-            {
-                nextSlot = GetNextVisibleSlot(CurrentSlot);
-                if (nextSlot >= SlotCount)
-                {
-                    nextSlot = -1;
-                }
-            }
-
-            _noSelectionChangeCount++;
-            try
-            {
-                int desiredSlot;
-                int columnIndex;
-                DataGridSelectionAction action;
-                if (CurrentColumnIndex == -1)
-                {
-                    desiredSlot = FirstVisibleSlot;
-                    columnIndex = firstVisibleColumnIndex;
-                    action = DataGridSelectionAction.SelectCurrent;
-                }
-                else if (ctrl)
-                {
-                    if (shift)
-                    {
-                        // Both Ctrl and Shift
-                        desiredSlot = lastSlot;
-                        columnIndex = CurrentColumnIndex;
-                        action = (SelectionMode == DataGridSelectionMode.Extended)
-                            ? DataGridSelectionAction.SelectFromAnchorToCurrent
-                            : DataGridSelectionAction.SelectCurrent;
-                    }
-                    else
-                    {
-                        // Ctrl without Shift
-                        desiredSlot = lastSlot;
-                        columnIndex = CurrentColumnIndex;
-                        action = DataGridSelectionAction.SelectCurrent;
-                    }
-                }
-                else
-                {
-                    if (nextSlot == -1)
-                    {
-                        return true;
-                    }
-                    if (shift)
-                    {
-                        // Shift without Ctrl
-                        desiredSlot = nextSlot;
-                        columnIndex = CurrentColumnIndex;
-                        action = DataGridSelectionAction.SelectFromAnchorToCurrent;
-                    }
-                    else
-                    {
-                        // Neither Ctrl nor Shift
-                        desiredSlot = nextSlot;
-                        columnIndex = CurrentColumnIndex;
-                        action = DataGridSelectionAction.SelectCurrent;
-                    }
-                }
-
-                UpdateSelectionAndCurrency(columnIndex, desiredSlot, action, scrollIntoView: true);
-            }
-            finally
-            {
-                NoSelectionChangeCount--;
-            }
-            return _successfullyUpdatedSelection;
-        }
-
-        private bool ProcessEndKey(bool shift, bool ctrl)
-        {
-            DataGridColumn dataGridColumn = ColumnsInternal.LastVisibleColumn;
-            int lastVisibleColumnIndex = (dataGridColumn == null) ? -1 : dataGridColumn.Index;
-            int firstVisibleSlot = FirstVisibleSlot;
-            int lastVisibleSlot = LastVisibleSlot;
-            if (lastVisibleColumnIndex == -1 || firstVisibleSlot == -1)
-            {
-                return false;
-            }
-
-            if (WaitForLostFocus(() => ProcessEndKey(shift, ctrl)))
-            {
-                return true;
-            }
-
-            _noSelectionChangeCount++;
-            try
-            {
-                if (!ctrl)
-                {
-                    return ProcessRightMost(lastVisibleColumnIndex, firstVisibleSlot);
-                }
-                else
-                {
-                    DataGridSelectionAction action = (shift && SelectionMode == DataGridSelectionMode.Extended)
-                        ? DataGridSelectionAction.SelectFromAnchorToCurrent
-                        : DataGridSelectionAction.SelectCurrent;
-
-                    UpdateSelectionAndCurrency(lastVisibleColumnIndex, lastVisibleSlot, action, scrollIntoView: true);
-                }
-            }
-            finally
-            {
-                NoSelectionChangeCount--;
-            }
-            return _successfullyUpdatedSelection;
-        }
-
-        private bool ProcessEnterKey(bool shift, bool ctrl)
-        {
-            int oldCurrentSlot = CurrentSlot;
-
-            if (!ctrl)
-            {
-                // If Enter was used by a TextBox, we shouldn't handle the key
-                if (FocusManager.GetFocusManager(this)?.GetFocusedElement() is TextBox focusedTextBox
-                    && focusedTextBox.AcceptsReturn)
-                {
-                    return false;
-                }
-
-                if (WaitForLostFocus(() => ProcessEnterKey(shift, ctrl)))
-                {
-                    return true;
-                }
-
-                // Enter behaves like down arrow - it commits the potential editing and goes down one cell.
-                if (!ProcessDownKeyInternal(false, ctrl))
-                {
-                    return false;
-                }
-            }
-            else if (WaitForLostFocus(() => ProcessEnterKey(shift, ctrl)))
-            {
-                return true;
-            }
-
-            // Try to commit the potential editing
-            if (oldCurrentSlot == CurrentSlot &&
-                EndCellEdit(DataGridEditAction.Commit, exitEditingMode: true, keepFocus: true, raiseEvents: true) &&
-                EditingRow != null)
-            {
-                EndRowEdit(DataGridEditAction.Commit, exitEditingMode: true, raiseEvents: true);
-                ScrollIntoView(CurrentItem, CurrentColumn);
-            }
-
-            return true;
-        }
-
-        private bool ProcessEscapeKey()
-        {
-            if (WaitForLostFocus(() => ProcessEscapeKey()))
-            {
-                return true;
-            }
-
-            if (_editingColumnIndex != -1)
-            {
-                // Revert the potential cell editing and exit cell editing.
-                EndCellEdit(DataGridEditAction.Cancel, exitEditingMode: true, keepFocus: true, raiseEvents: true);
-                return true;
-            }
-            else if (EditingRow != null)
-            {
-                // Revert the potential row editing and exit row editing.
-                EndRowEdit(DataGridEditAction.Cancel, exitEditingMode: true, raiseEvents: true);
-                return true;
-            }
-            return false;
-        }
-
-        private bool ProcessF2Key(KeyEventArgs e)
-        {
-            KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift);
-
-            if (!shift && !ctrl &&
-                _editingColumnIndex == -1 && CurrentColumnIndex != -1 && GetRowSelection(CurrentSlot) &&
-                !GetColumnEffectiveReadOnlyState(CurrentColumn))
-            {
-                if (ScrollSlotIntoView(CurrentColumnIndex, CurrentSlot, forCurrentCellChange: false, forceHorizontalScroll: true))
-                {
-                    BeginCellEdit(e);
-                }
-                return true;
-            }
-
-            return false;
-        }
-
-        private bool ProcessHomeKey(bool shift, bool ctrl)
-        {
-            DataGridColumn dataGridColumn = ColumnsInternal.FirstVisibleNonFillerColumn;
-            int firstVisibleColumnIndex = (dataGridColumn == null) ? -1 : dataGridColumn.Index;
-            int firstVisibleSlot = FirstVisibleSlot;
-            if (firstVisibleColumnIndex == -1 || firstVisibleSlot == -1)
-            {
-                return false;
-            }
-
-            if (WaitForLostFocus(() => ProcessHomeKey(shift, ctrl)))
-            {
-                return true;
-            }
-
-            _noSelectionChangeCount++;
-            try
-            {
-                if (!ctrl)
-                {
-                    return ProcessLeftMost(firstVisibleColumnIndex, firstVisibleSlot);
-                }
-                else
-                {
-                    DataGridSelectionAction action = (shift && SelectionMode == DataGridSelectionMode.Extended)
-                        ? DataGridSelectionAction.SelectFromAnchorToCurrent
-                        : DataGridSelectionAction.SelectCurrent;
-
-                    UpdateSelectionAndCurrency(firstVisibleColumnIndex, firstVisibleSlot, action, scrollIntoView: true);
-                }
-            }
-            finally
-            {
-                NoSelectionChangeCount--;
-            }
-            return _successfullyUpdatedSelection;
-        }
-
-        private bool ProcessLeftKey(bool shift, bool ctrl)
-        {
-            DataGridColumn dataGridColumn = ColumnsInternal.FirstVisibleNonFillerColumn;
-            int firstVisibleColumnIndex = (dataGridColumn == null) ? -1 : dataGridColumn.Index;
-            int firstVisibleSlot = FirstVisibleSlot;
-            if (firstVisibleColumnIndex == -1 || firstVisibleSlot == -1)
-            {
-                return false;
-            }
-
-            if (WaitForLostFocus(() => ProcessLeftKey(shift, ctrl)))
-            {
-                return true;
-            }
-
-            int previousVisibleColumnIndex = -1;
-            if (CurrentColumnIndex != -1)
-            {
-                dataGridColumn = ColumnsInternal.GetPreviousVisibleNonFillerColumn(ColumnsItemsInternal[CurrentColumnIndex]);
-                if (dataGridColumn != null)
-                {
-                    previousVisibleColumnIndex = dataGridColumn.Index;
-                }
-            }
-
-            _noSelectionChangeCount++;
-            try
-            {
-                if (ctrl)
-                {
-                    return ProcessLeftMost(firstVisibleColumnIndex, firstVisibleSlot);
-                }
-                else
-                {
-                    if (RowGroupHeadersTable.Contains(CurrentSlot))
-                    {
-                        CollapseRowGroup(RowGroupHeadersTable.GetValueAt(CurrentSlot).CollectionViewGroup, collapseAllSubgroups: false);
-                    }
-                    else if (CurrentColumnIndex == -1)
-                    {
-                        UpdateSelectionAndCurrency(
-                            firstVisibleColumnIndex,
-                            firstVisibleSlot,
-                            DataGridSelectionAction.SelectCurrent,
-                            scrollIntoView: true);
-                    }
-                    else
-                    {
-                        if (previousVisibleColumnIndex == -1)
-                        {
-                            return true;
-                        }
-
-                        UpdateSelectionAndCurrency(
-                            previousVisibleColumnIndex,
-                            CurrentSlot,
-                            DataGridSelectionAction.None,
-                            scrollIntoView: true);
-                    }
-                }
-            }
-            finally
-            {
-                NoSelectionChangeCount--;
-            }
-            return _successfullyUpdatedSelection;
-        }
-
-        // Ctrl Left <==> Home
-        private bool ProcessLeftMost(int firstVisibleColumnIndex, int firstVisibleSlot)
-        {
-            _noSelectionChangeCount++;
-            try
-            {
-                int desiredSlot;
-                DataGridSelectionAction action;
-                if (CurrentColumnIndex == -1)
-                {
-                    desiredSlot = firstVisibleSlot;
-                    action = DataGridSelectionAction.SelectCurrent;
-                    Debug.Assert(_selectedItems.Count == 0);
-                }
-                else
-                {
-                    desiredSlot = CurrentSlot;
-                    action = DataGridSelectionAction.None;
-                }
-
-                UpdateSelectionAndCurrency(firstVisibleColumnIndex, desiredSlot, action, scrollIntoView: true);
-            }
-            finally
-            {
-                NoSelectionChangeCount--;
-            }
-            return _successfullyUpdatedSelection;
-        }
-
-        private bool ProcessNextKey(bool shift, bool ctrl)
-        {
-            DataGridColumn dataGridColumn = ColumnsInternal.FirstVisibleNonFillerColumn;
-            int firstVisibleColumnIndex = (dataGridColumn == null) ? -1 : dataGridColumn.Index;
-            if (firstVisibleColumnIndex == -1 || DisplayData.FirstScrollingSlot == -1)
-            {
-                return false;
-            }
-
-            if (WaitForLostFocus(() => ProcessNextKey(shift, ctrl)))
-            {
-                return true;
-            }
-
-            int nextPageSlot = CurrentSlot == -1 ? DisplayData.FirstScrollingSlot : CurrentSlot;
-            Debug.Assert(nextPageSlot != -1);
-            int slot = GetNextVisibleSlot(nextPageSlot);
-
-            int scrollCount = DisplayData.NumTotallyDisplayedScrollingElements;
-            while (scrollCount > 0 && slot < SlotCount)
-            {
-                nextPageSlot = slot;
-                scrollCount--;
-                slot = GetNextVisibleSlot(slot);
-            }
-
-            _noSelectionChangeCount++;
-            try
-            {
-                DataGridSelectionAction action;
-                int columnIndex;
-                if (CurrentColumnIndex == -1)
-                {
-                    columnIndex = firstVisibleColumnIndex;
-                    action = DataGridSelectionAction.SelectCurrent;
-                }
-                else
-                {
-                    columnIndex = CurrentColumnIndex;
-                    action = (shift && SelectionMode == DataGridSelectionMode.Extended)
-                        ? action = DataGridSelectionAction.SelectFromAnchorToCurrent
-                        : action = DataGridSelectionAction.SelectCurrent;
-                }
-
-                UpdateSelectionAndCurrency(columnIndex, nextPageSlot, action, scrollIntoView: true);
-            }
-            finally
-            {
-                NoSelectionChangeCount--;
-            }
-            return _successfullyUpdatedSelection;
-        }
-
-        private bool ProcessPriorKey(bool shift, bool ctrl)
-        {
-            DataGridColumn dataGridColumn = ColumnsInternal.FirstVisibleNonFillerColumn;
-            int firstVisibleColumnIndex = (dataGridColumn == null) ? -1 : dataGridColumn.Index;
-            if (firstVisibleColumnIndex == -1 || DisplayData.FirstScrollingSlot == -1)
-            {
-                return false;
-            }
-
-            if (WaitForLostFocus(() => ProcessPriorKey(shift, ctrl)))
-            {
-                return true;
-            }
-
-            int previousPageSlot = (CurrentSlot == -1) ? DisplayData.FirstScrollingSlot : CurrentSlot;
-            Debug.Assert(previousPageSlot != -1);
-
-            int scrollCount = DisplayData.NumTotallyDisplayedScrollingElements;
-            int slot = GetPreviousVisibleSlot(previousPageSlot);
-            while (scrollCount > 0 && slot != -1)
-            {
-                previousPageSlot = slot;
-                scrollCount--;
-                slot = GetPreviousVisibleSlot(slot);
-            }
-            Debug.Assert(previousPageSlot != -1);
-
-            _noSelectionChangeCount++;
-            try
-            {
-                int columnIndex;
-                DataGridSelectionAction action;
-                if (CurrentColumnIndex == -1)
-                {
-                    columnIndex = firstVisibleColumnIndex;
-                    action = DataGridSelectionAction.SelectCurrent;
-                }
-                else
-                {
-                    columnIndex = CurrentColumnIndex;
-                    action = (shift && SelectionMode == DataGridSelectionMode.Extended)
-                        ? DataGridSelectionAction.SelectFromAnchorToCurrent
-                        : DataGridSelectionAction.SelectCurrent;
-                }
-
-                UpdateSelectionAndCurrency(columnIndex, previousPageSlot, action, scrollIntoView: true);
-            }
-            finally
-            {
-                NoSelectionChangeCount--;
-            }
-            return _successfullyUpdatedSelection;
-        }
-
-        private bool ProcessRightKey(bool shift, bool ctrl)
-        {
-            DataGridColumn dataGridColumn = ColumnsInternal.LastVisibleColumn;
-            int lastVisibleColumnIndex = (dataGridColumn == null) ? -1 : dataGridColumn.Index;
-            int firstVisibleSlot = FirstVisibleSlot;
-            if (lastVisibleColumnIndex == -1 || firstVisibleSlot == -1)
-            {
-                return false;
-            }
-
-            if (WaitForLostFocus(delegate { ProcessRightKey(shift, ctrl); }))
-            {
-                return true;
-            }
-
-            int nextVisibleColumnIndex = -1;
-            if (CurrentColumnIndex != -1)
-            {
-                dataGridColumn = ColumnsInternal.GetNextVisibleColumn(ColumnsItemsInternal[CurrentColumnIndex]);
-                if (dataGridColumn != null)
-                {
-                    nextVisibleColumnIndex = dataGridColumn.Index;
-                }
-            }
-            _noSelectionChangeCount++;
-            try
-            {
-                if (ctrl)
-                {
-                    return ProcessRightMost(lastVisibleColumnIndex, firstVisibleSlot);
-                }
-                else
-                {
-                    if (RowGroupHeadersTable.Contains(CurrentSlot))
-                    {
-                        ExpandRowGroup(RowGroupHeadersTable.GetValueAt(CurrentSlot).CollectionViewGroup, expandAllSubgroups: false);
-                    }
-                    else if (CurrentColumnIndex == -1)
-                    {
-                        int firstVisibleColumnIndex = ColumnsInternal.FirstVisibleColumn == null ? -1 : ColumnsInternal.FirstVisibleColumn.Index;
-
-                        UpdateSelectionAndCurrency(
-                            firstVisibleColumnIndex,
-                            firstVisibleSlot,
-                            DataGridSelectionAction.SelectCurrent,
-                            scrollIntoView: true);
-                    }
-                    else
-                    {
-                        if (nextVisibleColumnIndex == -1)
-                        {
-                            return true;
-                        }
-
-                        UpdateSelectionAndCurrency(
-                            nextVisibleColumnIndex,
-                            CurrentSlot,
-                            DataGridSelectionAction.None,
-                            scrollIntoView: true);
-                    }
-                }
-            }
-            finally
-            {
-                NoSelectionChangeCount--;
-            }
-            return _successfullyUpdatedSelection;
-        }
-
-        // Ctrl Right <==> End
-        private bool ProcessRightMost(int lastVisibleColumnIndex, int firstVisibleSlot)
-        {
-            _noSelectionChangeCount++;
-            try
-            {
-                int desiredSlot;
-                DataGridSelectionAction action;
-                if (CurrentColumnIndex == -1)
-                {
-                    desiredSlot = firstVisibleSlot;
-                    action = DataGridSelectionAction.SelectCurrent;
-                }
-                else
-                {
-                    desiredSlot = CurrentSlot;
-                    action = DataGridSelectionAction.None;
-                }
-
-                UpdateSelectionAndCurrency(lastVisibleColumnIndex, desiredSlot, action, scrollIntoView: true);
-            }
-            finally
-            {
-                NoSelectionChangeCount--;
-            }
-            return _successfullyUpdatedSelection;
-        }
-
-        private bool ProcessTabKey(KeyEventArgs e)
-        {
-            KeyboardHelper.GetMetaKeyState(this, e.KeyModifiers, out bool ctrl, out bool shift);
-            return ProcessTabKey(e, shift, ctrl);
-        }
-
-        private bool ProcessTabKey(KeyEventArgs e, bool shift, bool ctrl)
-        {
-            if (ctrl || _editingColumnIndex == -1 || IsReadOnly)
-            {
-                //Go to the next/previous control on the page when
-                // - Ctrl key is used
-                // - Potential current cell is not edited, or the datagrid is read-only.
-                return false;
-            }
-
-            // Try to locate a writable cell before/after the current cell
-            Debug.Assert(CurrentColumnIndex != -1);
-            Debug.Assert(CurrentSlot != -1);
-
-            int neighborVisibleWritableColumnIndex, neighborSlot;
-            DataGridColumn dataGridColumn;
-            if (shift)
-            {
-                dataGridColumn = ColumnsInternal.GetPreviousVisibleWritableColumn(ColumnsItemsInternal[CurrentColumnIndex]);
-                neighborSlot = GetPreviousVisibleSlot(CurrentSlot);
-                if (EditingRow != null)
-                {
-                    while (neighborSlot != -1 && RowGroupHeadersTable.Contains(neighborSlot))
-                    {
-                        neighborSlot = GetPreviousVisibleSlot(neighborSlot);
-                    }
-                }
-            }
-            else
-            {
-                dataGridColumn = ColumnsInternal.GetNextVisibleWritableColumn(ColumnsItemsInternal[CurrentColumnIndex]);
-                neighborSlot = GetNextVisibleSlot(CurrentSlot);
-                if (EditingRow != null)
-                {
-                    while (neighborSlot < SlotCount && RowGroupHeadersTable.Contains(neighborSlot))
-                    {
-                        neighborSlot = GetNextVisibleSlot(neighborSlot);
-                    }
-                }
-            }
-            neighborVisibleWritableColumnIndex = (dataGridColumn == null) ? -1 : dataGridColumn.Index;
-
-            if (neighborVisibleWritableColumnIndex == -1 && (neighborSlot == -1 || neighborSlot >= SlotCount))
-            {
-                // There is no previous/next row and no previous/next writable cell on the current row
-                return false;
-            }
-
-            if (WaitForLostFocus(() => ProcessTabKey(e, shift, ctrl)))
-            {
-                return true;
-            }
-
-            int targetSlot = -1, targetColumnIndex = -1;
-
-            _noSelectionChangeCount++;
-            try
-            {
-                if (neighborVisibleWritableColumnIndex == -1)
-                {
-                    targetSlot = neighborSlot;
-                    if (shift)
-                    {
-                        Debug.Assert(ColumnsInternal.LastVisibleWritableColumn != null);
-                        targetColumnIndex = ColumnsInternal.LastVisibleWritableColumn.Index;
-                    }
-                    else
-                    {
-                        Debug.Assert(ColumnsInternal.FirstVisibleWritableColumn != null);
-                        targetColumnIndex = ColumnsInternal.FirstVisibleWritableColumn.Index;
-                    }
-                }
-                else
-                {
-                    targetSlot = CurrentSlot;
-                    targetColumnIndex = neighborVisibleWritableColumnIndex;
-                }
-
-                DataGridSelectionAction action;
-                if (targetSlot != CurrentSlot || (SelectionMode == DataGridSelectionMode.Extended))
-                {
-                    if (IsSlotOutOfBounds(targetSlot))
-                    {
-                        return true;
-                    }
-                    action = DataGridSelectionAction.SelectCurrent;
-                }
-                else
-                {
-                    action = DataGridSelectionAction.None;
-                }
-
-                UpdateSelectionAndCurrency(targetColumnIndex, targetSlot, action, scrollIntoView: true);
-            }
-            finally
-            {
-                NoSelectionChangeCount--;
-            }
-
-            if (_successfullyUpdatedSelection && !RowGroupHeadersTable.Contains(targetSlot))
-            {
-                BeginCellEdit(e);
-            }
-
-            // Return true to say we handled the key event even if the operation was unsuccessful. If we don't
-            // say we handled this event, the framework will continue to process the tab key and change focus.
-            return true;
-        }
-
-        private bool ProcessUpKey(bool shift, bool ctrl)
-        {
-            DataGridColumn dataGridColumn = ColumnsInternal.FirstVisibleNonFillerColumn;
-            int firstVisibleColumnIndex = (dataGridColumn == null) ? -1 : dataGridColumn.Index;
-            int firstVisibleSlot = FirstVisibleSlot;
-            if (firstVisibleColumnIndex == -1 || firstVisibleSlot == -1)
-            {
-                return false;
-            }
-
-            if (WaitForLostFocus(() => ProcessUpKey(shift, ctrl)))
-            {
-                return true;
-            }
-
-            int previousVisibleSlot = (CurrentSlot != -1) ? GetPreviousVisibleSlot(CurrentSlot) : -1;
-
-            _noSelectionChangeCount++;
-
-            try
-            {
-                int slot;
-                int columnIndex;
-                DataGridSelectionAction action;
-                if (CurrentColumnIndex == -1)
-                {
-                    slot = firstVisibleSlot;
-                    columnIndex = firstVisibleColumnIndex;
-                    action = DataGridSelectionAction.SelectCurrent;
-                }
-                else if (ctrl)
-                {
-                    if (shift)
-                    {
-                        // Both Ctrl and Shift
-                        slot = firstVisibleSlot;
-                        columnIndex = CurrentColumnIndex;
-                        action = (SelectionMode == DataGridSelectionMode.Extended)
-                            ? DataGridSelectionAction.SelectFromAnchorToCurrent
-                            : DataGridSelectionAction.SelectCurrent;
-                    }
-                    else
-                    {
-                        // Ctrl without Shift
-                        slot = firstVisibleSlot;
-                        columnIndex = CurrentColumnIndex;
-                        action = DataGridSelectionAction.SelectCurrent;
-                    }
-                }
-                else
-                {
-                    if (previousVisibleSlot == -1)
-                    {
-                        return true;
-                    }
-                    if (shift)
-                    {
-                        // Shift without Ctrl
-                        slot = previousVisibleSlot;
-                        columnIndex = CurrentColumnIndex;
-                        action = DataGridSelectionAction.SelectFromAnchorToCurrent;
-                    }
-                    else
-                    {
-                        // Neither Shift nor Ctrl
-                        slot = previousVisibleSlot;
-                        columnIndex = CurrentColumnIndex;
-                        action = DataGridSelectionAction.SelectCurrent;
-                    }
-                }
-                UpdateSelectionAndCurrency(columnIndex, slot, action, scrollIntoView: true);
-            }
-            finally
-            {
-                NoSelectionChangeCount--;
-            }
-            return _successfullyUpdatedSelection;
-        }
-
-        private void RemoveDisplayedColumnHeader(DataGridColumn dataGridColumn)
-        {
-            if (_columnHeadersPresenter != null)
-            {
-                _columnHeadersPresenter.Children.Remove(dataGridColumn.HeaderCell);
-            }
-        }
-
-        private void RemoveDisplayedColumnHeaders()
-        {
-            if (_columnHeadersPresenter != null)
-            {
-                _columnHeadersPresenter.Children.Clear();
-            }
-            ColumnsInternal.FillerColumn.IsRepresented = false;
-        }
-
-        private bool ResetCurrentCellCore()
-        {
-            return (CurrentColumnIndex == -1 || SetCurrentCellCore(-1, -1));
-        }
-
-        private void ResetEditingRow()
-        {
-            if (EditingRow != null
-                && EditingRow != _focusedRow
-                && !IsSlotVisible(EditingRow.Slot))
-            {
-                // Unload the old editing row if it's off screen
-                EditingRow.Clip = null;
-                UnloadRow(EditingRow);
-                DisplayData.FullyRecycleElements();
-            }
-            EditingRow = null;
-        }
-
-        private void ResetFocusedRow()
-        {
-            if (_focusedRow != null
-                && _focusedRow != EditingRow
-                && !IsSlotVisible(_focusedRow.Slot))
-            {
-                // Unload the old focused row if it's off screen
-                _focusedRow.Clip = null;
-                UnloadRow(_focusedRow);
-                DisplayData.FullyRecycleElements();
-            }
-            _focusedRow = null;
-        }
-
-        public void SelectAll()
-        {
-            SetRowsSelection(0, SlotCount - 1);
-        }
-
-        private void SetAndSelectCurrentCell(int columnIndex,
-                                             int slot,
-                                             bool forceCurrentCellSelection)
-        {
-            DataGridSelectionAction action = forceCurrentCellSelection ? DataGridSelectionAction.SelectCurrent : DataGridSelectionAction.None;
-            UpdateSelectionAndCurrency(columnIndex, slot, action, scrollIntoView: false);
-        }
-
-        // columnIndex = 2, rowIndex = -1 --> current cell belongs to the 'new row'.
-        // columnIndex = 2, rowIndex = 2 --> current cell is an inner cell
-        // columnIndex = -1, rowIndex = -1 --> current cell is reset
-        // columnIndex = -1, rowIndex = 2 --> Unexpected
-        private bool SetCurrentCellCore(int columnIndex, int slot, bool commitEdit, bool endRowEdit)
-        {
-            Debug.Assert(columnIndex < ColumnsItemsInternal.Count);
-            Debug.Assert(slot < SlotCount);
-            Debug.Assert(columnIndex == -1 || ColumnsItemsInternal[columnIndex].IsVisible);
-            Debug.Assert(!(columnIndex > -1 && slot == -1));
-
-            if (columnIndex == CurrentColumnIndex &&
-                slot == CurrentSlot)
-            {
-                Debug.Assert(DataConnection != null);
-                Debug.Assert(_editingColumnIndex == -1 || _editingColumnIndex == CurrentColumnIndex);
-                Debug.Assert(EditingRow == null || EditingRow.Slot == CurrentSlot || DataConnection.CommittingEdit);
-                return true;
-            }
-
-            Control oldDisplayedElement = null;
-            DataGridCellCoordinates oldCurrentCell = new DataGridCellCoordinates(CurrentCellCoordinates);
-
-            object newCurrentItem = null;
-            if (!RowGroupHeadersTable.Contains(slot))
-            {
-                int rowIndex = RowIndexFromSlot(slot);
-                if (rowIndex >= 0 && rowIndex < DataConnection.Count)
-                {
-                    newCurrentItem = DataConnection.GetDataItem(rowIndex);
-                }
-            }
-
-            if (CurrentColumnIndex > -1)
-            {
-                Debug.Assert(CurrentColumnIndex < ColumnsItemsInternal.Count);
-                Debug.Assert(CurrentSlot < SlotCount);
-
-                if (!IsInnerCellOutOfBounds(oldCurrentCell.ColumnIndex, oldCurrentCell.Slot) &&
-                    IsSlotVisible(oldCurrentCell.Slot))
-                {
-                    oldDisplayedElement = DisplayData.GetDisplayedElement(oldCurrentCell.Slot);
-                }
-
-                if (!RowGroupHeadersTable.Contains(oldCurrentCell.Slot) && !_temporarilyResetCurrentCell)
-                {
-                    bool keepFocus = ContainsFocus;
-                    if (commitEdit)
-                    {
-                        if (!EndCellEdit(DataGridEditAction.Commit, exitEditingMode: true, keepFocus: keepFocus, raiseEvents: true))
-                        {
-                            return false;
-                        }
-                        // Resetting the current cell: setting it to (-1, -1) is not considered setting it out of bounds
-                        if ((columnIndex != -1 && slot != -1 && IsInnerCellOutOfSelectionBounds(columnIndex, slot)) ||
-                            IsInnerCellOutOfSelectionBounds(oldCurrentCell.ColumnIndex, oldCurrentCell.Slot))
-                        {
-                            return false;
-                        }
-
-                        if (endRowEdit && !EndRowEdit(DataGridEditAction.Commit, exitEditingMode: true, raiseEvents: true))
-                        {
-                            return false;
-                        }
-                    }
-                    else
-                    {
-                        CancelEdit(DataGridEditingUnit.Row, false);
-                        ExitEdit(keepFocus);
-                    }
-                }
-            }
-
-            if (newCurrentItem != null)
-            {
-                slot = SlotFromRowIndex(DataConnection.IndexOf(newCurrentItem));
-            }
-            if (slot == -1 && columnIndex != -1)
-            {
-                return false;
-            }
-            CurrentColumnIndex = columnIndex;
-            CurrentSlot = slot;
-
-            if (_temporarilyResetCurrentCell)
-            {
-                if (columnIndex != -1)
-                {
-                    _temporarilyResetCurrentCell = false;
-                }
-            }
-            if (!_temporarilyResetCurrentCell && _editingColumnIndex != -1)
-            {
-                _editingColumnIndex = columnIndex;
-            }
-
-            if (oldDisplayedElement != null)
-            {
-                if (oldDisplayedElement is DataGridRow row)
-                {
-                    // Don't reset the state of the current cell if we're editing it because that would put it in an invalid state
-                    UpdateCurrentState(oldDisplayedElement, oldCurrentCell.ColumnIndex, !(_temporarilyResetCurrentCell && row.IsEditing && _editingColumnIndex == oldCurrentCell.ColumnIndex));
-                }
-                else
-                {
-                    UpdateCurrentState(oldDisplayedElement, oldCurrentCell.ColumnIndex, applyCellState: false);
-                }
-            }
-
-            if (CurrentColumnIndex > -1)
-            {
-                Debug.Assert(CurrentSlot > -1);
-                Debug.Assert(CurrentColumnIndex < ColumnsItemsInternal.Count);
-                Debug.Assert(CurrentSlot < SlotCount);
-                if (IsSlotVisible(CurrentSlot))
-                {
-                    UpdateCurrentState(DisplayData.GetDisplayedElement(CurrentSlot), CurrentColumnIndex, applyCellState: true);
-                }
-            }
-
-            return true;
-        }
-
-        private void SetVerticalOffset(double newVerticalOffset)
-        {
-            _verticalOffset = newVerticalOffset;
-            if (_vScrollBar != null && !MathUtilities.AreClose(newVerticalOffset, _vScrollBar.Value))
-            {
-                _vScrollBar.Value = _verticalOffset;
-            }
-        }
-
-        private void UpdateCurrentState(Control displayedElement, int columnIndex, bool applyCellState)
-        {
-            if (displayedElement is DataGridRow row)
-            {
-                if (AreRowHeadersVisible)
-                {
-                    row.ApplyHeaderStatus();
-                }
-                DataGridCell cell = row.Cells[columnIndex];
-                if (applyCellState)
-                {
-                    cell.UpdatePseudoClasses();
-                }
-            }
-            else if (displayedElement is DataGridRowGroupHeader groupHeader)
-            {
-                groupHeader.UpdatePseudoClasses();
-                if (AreRowHeadersVisible)
-                {
-                    groupHeader.ApplyHeaderStatus();
-                }
-            }
-        }
-
-        private void UpdateHorizontalScrollBar(bool needHorizScrollbar, bool forceHorizScrollbar, double totalVisibleWidth, double totalVisibleFrozenWidth, double cellsWidth)
-        {
-            if (_hScrollBar != null)
-            {
-                if (needHorizScrollbar || forceHorizScrollbar)
-                {
-                    //          viewportSize
-                    //        v---v
-                    //|<|_____|###|>|
-                    //  ^     ^
-                    //  min   max
-
-                    // we want to make the relative size of the thumb reflect the relative size of the viewing area
-                    // viewportSize / (max + viewportSize) = cellsWidth / max
-                    // -> viewportSize = max * cellsWidth / (max - cellsWidth)
-
-                    // always zero
-                    _hScrollBar.Minimum = 0;
-                    if (needHorizScrollbar)
-                    {
-                        // maximum travel distance -- not the total width
-                        _hScrollBar.Maximum = totalVisibleWidth - cellsWidth;
-                        Debug.Assert(totalVisibleFrozenWidth >= 0);
-                        if (_frozenColumnScrollBarSpacer != null)
-                        {
-                            _frozenColumnScrollBarSpacer.Width = totalVisibleFrozenWidth;
-                        }
-                        Debug.Assert(_hScrollBar.Maximum >= 0);
-
-                        // width of the scrollable viewing area
-                        double viewPortSize = Math.Max(0, cellsWidth - totalVisibleFrozenWidth);
-                        _hScrollBar.ViewportSize = viewPortSize;
-                        _hScrollBar.LargeChange = viewPortSize;
-                        // The ScrollBar should be in sync with HorizontalOffset at this point.  There's a resize case
-                        // where the ScrollBar will coerce an old value here, but we don't want that
-                        if (_hScrollBar.Value != _horizontalOffset)
-                        {
-                            _hScrollBar.Value = _horizontalOffset;
-                        }
-                        _hScrollBar.IsEnabled = true;
-                    }
-                    else
-                    {
-                        _hScrollBar.Maximum = 0;
-                        _hScrollBar.ViewportSize = 0;
-                        _hScrollBar.IsEnabled = false;
-                    }
-
-                    if (!_hScrollBar.IsVisible)
-                    {
-                        // This will trigger a call to this method via Cells_SizeChanged for
-                        _ignoreNextScrollBarsLayout = true;
-                        // which no processing is needed.
-                        _hScrollBar.IsVisible = true;
-                        if (_hScrollBar.DesiredSize.Height == 0)
-                        {
-                            // We need to know the height for the rest of layout to work correctly so measure it now
-                            _hScrollBar.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
-                        }
-                    }
-                }
-                else
-                {
-                    _hScrollBar.Maximum = 0;
-                    if (_hScrollBar.IsVisible)
-                    {
-                        // This will trigger a call to this method via Cells_SizeChanged for
-                        // which no processing is needed.
-                        _hScrollBar.IsVisible = false;
-                        _ignoreNextScrollBarsLayout = true;
-                    }
-                }
-            }
-        }
-
-        private void UpdateVerticalScrollBar(bool needVertScrollbar, bool forceVertScrollbar, double totalVisibleHeight, double cellsHeight)
-        {
-            if (_vScrollBar != null)
-            {
-                if (needVertScrollbar || forceVertScrollbar)
-                {
-                    //          viewportSize
-                    //        v---v
-                    //|<|_____|###|>|
-                    //  ^     ^
-                    //  min   max
-
-                    // we want to make the relative size of the thumb reflect the relative size of the viewing area
-                    // viewportSize / (max + viewportSize) = cellsWidth / max
-                    // -> viewportSize = max * cellsHeight / (totalVisibleHeight - cellsHeight)
-                    // ->              = max * cellsHeight / (totalVisibleHeight - cellsHeight)
-                    // ->              = max * cellsHeight / max
-                    // ->              = cellsHeight
-
-                    // always zero
-                    _vScrollBar.Minimum = 0;
-                    if (needVertScrollbar && !double.IsInfinity(cellsHeight))
-                    {
-                        // maximum travel distance -- not the total height
-                        _vScrollBar.Maximum = totalVisibleHeight - cellsHeight;
-                        Debug.Assert(_vScrollBar.Maximum >= 0);
-
-                        // total height of the display area
-                        _vScrollBar.ViewportSize = cellsHeight;
-                        _vScrollBar.IsEnabled = true;
-                    }
-                    else
-                    {
-                        _vScrollBar.Maximum = 0;
-                        _vScrollBar.ViewportSize = 0;
-                        _vScrollBar.IsEnabled = false;
-                    }
-
-                    if (!_vScrollBar.IsVisible)
-                    {
-                        // This will trigger a call to this method via Cells_SizeChanged for
-                        // which no processing is needed.
-                        _vScrollBar.IsVisible = true;
-                        if (_vScrollBar.DesiredSize.Width == 0)
-                        {
-                            // We need to know the width for the rest of layout to work correctly so measure it now
-                            _vScrollBar.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
-                        }
-                        _ignoreNextScrollBarsLayout = true;
-                    }
-                }
-                else
-                {
-                    _vScrollBar.Maximum = 0;
-                    if (_vScrollBar.IsVisible)
-                    {
-                        // This will trigger a call to this method via Cells_SizeChanged for
-                        // which no processing is needed.
-                        _vScrollBar.IsVisible = false;
-                        _ignoreNextScrollBarsLayout = true;
-                    }
-                }
-            }
-        }
-
-        private void VerticalScrollBar_Scroll(object sender, ScrollEventArgs e)
-        {
-            ProcessVerticalScroll(e.ScrollEventType);
-            VerticalScroll?.Invoke(sender, e);
-        }
-
-        //TODO: Ensure right button is checked for
-        private bool UpdateStateOnMouseRightButtonDown(PointerPressedEventArgs pointerPressedEventArgs, int columnIndex, int slot, bool allowEdit, bool shift, bool ctrl)
-        {
-            Debug.Assert(slot >= 0);
-
-            if (shift || ctrl)
-            {
-                return true;
-            }
-            if (IsSlotOutOfBounds(slot))
-            {
-                return true;
-            }
-            if (GetRowSelection(slot))
-            {
-                return true;
-            }
-            // Unselect everything except the row that was clicked on
-            _noSelectionChangeCount++;
-            try
-            {
-                UpdateSelectionAndCurrency(columnIndex, slot, DataGridSelectionAction.SelectCurrent, scrollIntoView: false);
-            }
-            finally
-            {
-                NoSelectionChangeCount--;
-            }
-            return true;
-        }
-
-        //TODO: Ensure left button is checked for
-        private bool UpdateStateOnMouseLeftButtonDown(PointerPressedEventArgs pointerPressedEventArgs, int columnIndex, int slot, bool allowEdit, bool shift, bool ctrl)
-        {
-            bool beginEdit;
-
-            Debug.Assert(slot >= 0);
-
-            // Before changing selection, check if the current cell needs to be committed, and
-            // check if the current row needs to be committed. If any of those two operations are required and fail,
-            // do not change selection, and do not change current cell.
-
-            bool wasInEdit = EditingColumnIndex != -1;
-
-            if (IsSlotOutOfBounds(slot))
-            {
-                return true;
-            }
-
-            if (wasInEdit && (columnIndex != EditingColumnIndex || slot != CurrentSlot) &&
-                WaitForLostFocus(() => UpdateStateOnMouseLeftButtonDown(pointerPressedEventArgs, columnIndex, slot, allowEdit, shift, ctrl)))
-            {
-                return true;
-            }
-
-            try
-            {
-                _noSelectionChangeCount++;
-
-                beginEdit = allowEdit &&
-                            CurrentSlot == slot &&
-                            columnIndex != -1 &&
-                            (wasInEdit || CurrentColumnIndex == columnIndex) &&
-                            !GetColumnEffectiveReadOnlyState(ColumnsItemsInternal[columnIndex]);
-
-                DataGridSelectionAction action;
-                if (SelectionMode == DataGridSelectionMode.Extended && shift)
-                {
-                    // Shift select multiple rows
-                    action = DataGridSelectionAction.SelectFromAnchorToCurrent;
-                }
-                else if (GetRowSelection(slot))  // Unselecting single row or Selecting a previously multi-selected row
-                {
-                    if (!ctrl && SelectionMode == DataGridSelectionMode.Extended && _selectedItems.Count != 0)
-                    {
-                        // Unselect everything except the row that was clicked on
-                        action = DataGridSelectionAction.SelectCurrent;
-                    }
-                    else if (ctrl && EditingRow == null)
-                    {
-                        action = DataGridSelectionAction.RemoveCurrentFromSelection;
-                    }
-                    else
-                    {
-                        action = DataGridSelectionAction.None;
-                    }
-                }
-                else // Selecting a single row or multi-selecting with Ctrl
-                {
-                    if (SelectionMode == DataGridSelectionMode.Single || !ctrl)
-                    {
-                        // Unselect the currently selected rows except the new selected row
-                        action = DataGridSelectionAction.SelectCurrent;
-                    }
-                    else
-                    {
-                        action = DataGridSelectionAction.AddCurrentToSelection;
-                    }
-                }
-
-                UpdateSelectionAndCurrency(columnIndex, slot, action, scrollIntoView: false);
-            }
-            finally
-            {
-                NoSelectionChangeCount--;
-            }
-
-            if (_successfullyUpdatedSelection && beginEdit && BeginCellEdit(pointerPressedEventArgs))
-            {
-                FocusEditingCell(setFocus: true);
-            }
-
-            return true;
-        }
-
-        /// <summary>
-        /// Returns the Group at the indicated level or null if the item is not in the ItemsSource
-        /// </summary>
-        /// <param name="item">item</param>
-        /// <param name="groupLevel">groupLevel</param>
-        /// <returns>The group the given item falls under or null if the item is not in the ItemsSource</returns>
-        public DataGridCollectionViewGroup GetGroupFromItem(object item, int groupLevel)
-        {
-            int itemIndex = DataConnection.IndexOf(item);
-            if (itemIndex == -1)
-            {
-                return null;
-            }
-            int groupHeaderSlot = RowGroupHeadersTable.GetPreviousIndex(SlotFromRowIndex(itemIndex));
-            DataGridRowGroupInfo rowGroupInfo = RowGroupHeadersTable.GetValueAt(groupHeaderSlot);
-            while (rowGroupInfo != null && rowGroupInfo.Level != groupLevel)
-            {
-                groupHeaderSlot = RowGroupHeadersTable.GetPreviousIndex(rowGroupInfo.Slot);
-                rowGroupInfo = RowGroupHeadersTable.GetValueAt(groupHeaderSlot);
-            }
-            return rowGroupInfo?.CollectionViewGroup;
-        }
-
-        /// <summary>
-        /// Raises the LoadingRowGroup event
-        /// </summary>
-        /// <param name="e">EventArgs</param>
-        protected virtual void OnLoadingRowGroup(DataGridRowGroupHeaderEventArgs e)
-        {
-            EventHandler<DataGridRowGroupHeaderEventArgs> handler = LoadingRowGroup;
-            if (handler != null)
-            {
-                LoadingOrUnloadingRow = true;
-                handler(this, e);
-                LoadingOrUnloadingRow = false;
-            }
-        }
-
-        /// <summary>
-        /// Raises the UnLoadingRowGroup event
-        /// </summary>
-        /// <param name="e">EventArgs</param>
-        protected virtual void OnUnloadingRowGroup(DataGridRowGroupHeaderEventArgs e)
-        {
-            EventHandler<DataGridRowGroupHeaderEventArgs> handler = UnloadingRowGroup;
-            if (handler != null)
-            {
-                LoadingOrUnloadingRow = true;
-                handler(this, e);
-                LoadingOrUnloadingRow = false;
-            }
-        }
-
-        /// <summary>
-        /// Occurs before a DataGridRowGroupHeader header is used.
-        /// </summary>
-        public event EventHandler<DataGridRowGroupHeaderEventArgs> LoadingRowGroup;
-
-        /// <summary>
-        /// Occurs when the DataGridRowGroupHeader is available for reuse.
-        /// </summary>
-        public event EventHandler<DataGridRowGroupHeaderEventArgs> UnloadingRowGroup;
-
-        // Recursively expands parent RowGroupHeaders from the top down
-        private void ExpandRowGroupParentChain(int level, int slot)
-        {
-            if (level < 0)
-            {
-                return;
-            }
-            int previousHeaderSlot = RowGroupHeadersTable.GetPreviousIndex(slot + 1);
-            DataGridRowGroupInfo rowGroupInfo = null;
-            while (previousHeaderSlot >= 0)
-            {
-                rowGroupInfo = RowGroupHeadersTable.GetValueAt(previousHeaderSlot);
-                Debug.Assert(rowGroupInfo != null);
-                if (level == rowGroupInfo.Level)
-                {
-                    if (_collapsedSlotsTable.Contains(rowGroupInfo.Slot))
-                    {
-                        // Keep going up the chain
-                        ExpandRowGroupParentChain(level - 1, rowGroupInfo.Slot - 1);
-                    }
-                    if (!rowGroupInfo.IsVisible)
-                    {
-                        EnsureRowGroupVisibility(rowGroupInfo, true, false);
-                    }
-                    return;
-                }
-                else
-                {
-                    previousHeaderSlot = RowGroupHeadersTable.GetPreviousIndex(previousHeaderSlot);
-                }
-            }
-        }
-
-        /// <summary>
-        /// This event is raised by OnCopyingRowClipboardContent method after the default row content is prepared.
-        /// Event listeners can modify or add to the row clipboard content.
-        /// </summary>
-        public event EventHandler<DataGridRowClipboardEventArgs> CopyingRowClipboardContent;
-
-        /// <summary>
-        /// This method raises the CopyingRowClipboardContent event.
-        /// </summary>
-        /// <param name="e">Contains the necessary information for generating the row clipboard content.</param>
-        protected virtual void OnCopyingRowClipboardContent(DataGridRowClipboardEventArgs e)
-        {
-            CopyingRowClipboardContent?.Invoke(this, e);
-        }
-
-        /// <summary>
-        /// This method formats a row (specified by a DataGridRowClipboardEventArgs) into
-        /// a single string to be added to the Clipboard when the DataGrid is copying its contents.
-        /// </summary>
-        /// <param name="e">DataGridRowClipboardEventArgs</param>
-        /// <returns>The formatted string.</returns>
-        private string FormatClipboardContent(DataGridRowClipboardEventArgs e)
-        {
-            var text = StringBuilderCache.Acquire();
-            var clipboardRowContent = e.ClipboardRowContent;
-            var numberOfItem = clipboardRowContent.Count;
-            for (int cellIndex = 0; cellIndex < numberOfItem; cellIndex++)
-            {
-                var cellContent = clipboardRowContent[cellIndex].Content?.ToString();
-                cellContent = cellContent?.Replace("\"", "\"\"");
-                text.Append($"\"{cellContent}\"");
-                if (cellIndex < numberOfItem - 1)
-                {
-                    text.Append('\t');
-                }
-                else
-                {
-                    text.Append('\r');
-                    text.Append('\n');
-                }
-            }
-            return StringBuilderCache.GetStringAndRelease(text);
-        }
-
-        /// <summary>
-        /// Handles the case where a 'Copy' key ('C' or 'Insert') has been pressed.  If pressed in combination with
-        /// the control key, and the necessary prerequisites are met, the DataGrid will copy its contents
-        /// to the Clipboard as text.
-        /// </summary>
-        /// <returns>Whether or not the DataGrid handled the key press.</returns>
-        private bool ProcessCopyKey(KeyModifiers modifiers)
-        {
-            KeyboardHelper.GetMetaKeyState(this, modifiers, out bool ctrl, out bool shift, out bool alt);
-
-            if (ctrl && !shift && !alt && ClipboardCopyMode != DataGridClipboardCopyMode.None && SelectedItems.Count > 0)
-            {
-                var textBuilder = StringBuilderCache.Acquire();
-
-                if (ClipboardCopyMode == DataGridClipboardCopyMode.IncludeHeader)
-                {
-                    DataGridRowClipboardEventArgs headerArgs = new DataGridRowClipboardEventArgs(null, true);
-                    foreach (DataGridColumn column in ColumnsInternal.GetVisibleColumns())
-                    {
-                        headerArgs.ClipboardRowContent.Add(new DataGridClipboardCellContent(null, column, column.Header));
-                    }
-                    OnCopyingRowClipboardContent(headerArgs);
-                    textBuilder.Append(FormatClipboardContent(headerArgs));
-                }
-
-                for (int index = 0; index < SelectedItems.Count; index++)
-                {
-                    object item = SelectedItems[index];
-                    DataGridRowClipboardEventArgs itemArgs = new DataGridRowClipboardEventArgs(item, false);
-                    foreach (DataGridColumn column in ColumnsInternal.GetVisibleColumns())
-                    {
-                        object content = column.GetCellValue(item, column.ClipboardContentBinding);
-                        itemArgs.ClipboardRowContent.Add(new DataGridClipboardCellContent(item, column, content));
-                    }
-                    OnCopyingRowClipboardContent(itemArgs);
-                    textBuilder.Append(FormatClipboardContent(itemArgs));
-                }
-
-                string text = StringBuilderCache.GetStringAndRelease(textBuilder);
-
-                if (!string.IsNullOrEmpty(text))
-                {
-                    CopyToClipboard(text);
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        private async void CopyToClipboard(string text)
-        {
-            var clipboard = TopLevel.GetTopLevel(this)?.Clipboard;
-
-            if (clipboard != null)
-                await clipboard.SetTextAsync(text);
-        }
-
-        /// <summary>
-        /// This is an empty content control that's used during the DataGrid's copy procedure
-        /// to determine the value of a ClipboardContentBinding for a particular column and item.
-        /// </summary>
-        internal ContentControl ClipboardContentControl
-        {
-            get
-            {
-                if (_clipboardContentControl == null)
-                {
-                    _clipboardContentControl = new ContentControl();
-                }
-                return _clipboardContentControl;
-            }
-        }
-
-        //TODO Validation UI
-        private void ResetValidationStatus()
-        {
-            // Clear the invalid status of the Cell, Row and DataGrid
-            if (EditingRow != null)
-            {
-                EditingRow.IsValid = true;
-                if (EditingRow.Index != -1)
-                {
-                    foreach (DataGridCell cell in EditingRow.Cells)
-                    {
-                        if (!cell.IsValid)
-                        {
-                            cell.IsValid = true;
-                            cell.UpdatePseudoClasses();
-                        }
-                    }
-                    EditingRow.ApplyState();
-                }
-            }
-            IsValid = true;
-
-            _validationSubscription?.Dispose();
-            _validationSubscription = null;
-        }
-
-        /// <summary>
-        /// Raises the AutoGeneratingColumn event.
-        /// </summary>
-        protected virtual void OnAutoGeneratingColumn(DataGridAutoGeneratingColumnEventArgs e)
-        {
-            AutoGeneratingColumn?.Invoke(this, e);
-        }
-    }
-}

+ 0 - 153
src/Avalonia.Controls.DataGrid/DataGridBoundColumn.cs

@@ -1,153 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved. 
-
-using Avalonia.Data;
-using System;
-using Avalonia.Controls.Utils;
-using Avalonia.Markup.Xaml.MarkupExtensions;
-using Avalonia.Metadata;
-using Avalonia.Reactive;
-
-namespace Avalonia.Controls
-{
-    /// <summary>
-    /// Represents a <see cref="T:Avalonia.Controls.DataGrid" /> column that can 
-    /// bind to a property in the grid's data source.
-    /// </summary>
-    public abstract class DataGridBoundColumn : DataGridColumn
-    {
-        private IBinding _binding; 
-
-        /// <summary>
-        /// Gets or sets the binding that associates the column with a property in the data source.
-        /// </summary>
-        //TODO Binding
-        [AssignBinding]
-        [InheritDataTypeFromItems(nameof(DataGrid.ItemsSource), AncestorType = typeof(DataGrid))]
-        public virtual IBinding Binding
-        {
-            get
-            {
-                return _binding;
-            }
-            set
-            {
-                if (_binding != value)
-                {
-                    if (OwningGrid != null && !OwningGrid.CommitEdit(DataGridEditingUnit.Row, exitEditingMode: true))
-                    {
-                        // Edited value couldn't be committed, so we force a CancelEdit
-                        OwningGrid.CancelEdit(DataGridEditingUnit.Row, raiseEvents: false);
-                    } 
-
-                    _binding = value; 
-
-                    if (_binding != null)
-                    {
-                        if(_binding is BindingBase binding)
-                        {
-                            if (binding.Mode == BindingMode.OneWayToSource)
-                            {
-                                throw new InvalidOperationException("DataGridColumn doesn't support BindingMode.OneWayToSource. Use BindingMode.TwoWay instead.");
-                            }
-
-                            var path = (binding as Binding)?.Path ?? (binding as CompiledBindingExtension)?.Path.ToString();
-                            if (!string.IsNullOrEmpty(path) && binding.Mode == BindingMode.Default)
-                            {
-                                binding.Mode = BindingMode.TwoWay;
-                            } 
-
-                            if (binding.Converter == null && string.IsNullOrEmpty(binding.StringFormat))
-                            {
-                                binding.Converter = DataGridValueConverter.Instance;
-                            }
-                        }  
-
-                        // Apply the new Binding to existing rows in the DataGrid
-                        if (OwningGrid != null)
-                        {
-                            OwningGrid.OnColumnBindingChanged(this);
-                        }
-                    } 
-
-                    RemoveEditingElement();
-                }
-            }
-        } 
-
-        /// <summary>
-        /// The binding that will be used to get or set cell content for the clipboard.
-        /// If the base ClipboardContentBinding is not explicitly set, this will return the value of Binding.
-        /// </summary>
-        public override IBinding ClipboardContentBinding
-        {
-            get
-            {
-                return base.ClipboardContentBinding ?? Binding;
-            }
-            set
-            {
-                base.ClipboardContentBinding = value;
-            }
-        } 
-
-        //TODO Rename
-        //TODO Validation
-        protected sealed override Control GenerateEditingElement(DataGridCell cell, object dataItem, out ICellEditBinding editBinding)
-        {
-            Control element = GenerateEditingElementDirect(cell, dataItem);
-            editBinding = null; 
-
-            if (Binding != null)
-            {
-                editBinding = BindEditingElement(element, BindingTarget, Binding);
-            } 
-
-            return element;
-        } 
-
-        private static ICellEditBinding BindEditingElement(AvaloniaObject target, AvaloniaProperty property, IBinding binding)
-        {
-            var result = binding.Initiate(target, property, enableDataValidation: true); 
-
-            if (result != null)
-            {
-                if(result.Source is IAvaloniaSubject<object> subject)
-                {
-                    var bindingHelper = new CellEditBinding(subject);
-                    var instanceBinding = new InstancedBinding(bindingHelper.InternalSubject, result.Mode, result.Priority); 
-
-                    BindingOperations.Apply(target, property, instanceBinding, null);
-                    return bindingHelper;
-                } 
-
-                BindingOperations.Apply(target, property, result, null);
-            } 
-
-            return null;
-        } 
-
-        protected abstract Control GenerateEditingElementDirect(DataGridCell cell, object dataItem); 
-
-        protected AvaloniaProperty BindingTarget { get; set; } 
-
-        internal void SetHeaderFromBinding()
-        {
-            if (OwningGrid != null && OwningGrid.DataConnection.DataType != null
-                && Header == null && Binding != null && Binding is BindingBase binding)
-            {
-                var path = (binding as Binding)?.Path ?? (binding as CompiledBindingExtension)?.Path.ToString();
-                if (!string.IsNullOrWhiteSpace(path))
-                {
-                    var header = OwningGrid.DataConnection.DataType.GetDisplayName(path);
-                    if (header != null)
-                    {
-                        Header = header;
-                    }
-                }
-            }
-        }
-    } 
-}

+ 0 - 274
src/Avalonia.Controls.DataGrid/DataGridCell.cs

@@ -1,274 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Automation;
-using Avalonia.Automation.Peers;
-using Avalonia.Controls.Automation.Peers;
-using Avalonia.Controls.Metadata;
-using Avalonia.Controls.Primitives;
-using Avalonia.Controls.Shapes;
-using Avalonia.Input;
-
-namespace Avalonia.Controls
-{
-    /// <summary>
-    /// Represents an individual <see cref="T:Avalonia.Controls.DataGrid" /> cell.
-    /// </summary>
-    [TemplatePart(DATAGRIDCELL_elementRightGridLine, typeof(Rectangle))]
-    [PseudoClasses(":selected", ":current", ":edited", ":invalid", ":focus")]
-    public class DataGridCell : ContentControl
-    {
-        private const string DATAGRIDCELL_elementRightGridLine = "PART_RightGridLine";
-
-        private Rectangle _rightGridLine;
-        private DataGridColumn _owningColumn;
-
-        bool _isValid = true;
-
-        public static readonly DirectProperty<DataGridCell, bool> IsValidProperty =
-            AvaloniaProperty.RegisterDirect<DataGridCell, bool>(
-                nameof(IsValid),
-                o => o.IsValid);
-
-        static DataGridCell()
-        {
-            PointerPressedEvent.AddClassHandler<DataGridCell>(
-                (x,e) => x.DataGridCell_PointerPressed(e), handledEventsToo: true);
-            FocusableProperty.OverrideDefaultValue<DataGridCell>(true);
-            IsTabStopProperty.OverrideDefaultValue<DataGridCell>(false);
-            AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<DataGridCell>(IsOffscreenBehavior.FromClip);
-        }
-        public DataGridCell()
-        { }
-
-        public bool IsValid
-        {
-            get { return _isValid; }
-            internal set { SetAndRaise(IsValidProperty, ref _isValid, value); }
-        }
-
-        internal DataGridColumn OwningColumn
-        {
-            get => _owningColumn;
-            set
-            {
-                if (_owningColumn != value)
-                {
-                    _owningColumn = value;
-                    OnOwningColumnSet(value);
-                }
-            }
-        }
-        internal DataGridRow OwningRow
-        {
-            get;
-            set;
-        }
-
-        internal DataGrid OwningGrid
-        {
-            get { return OwningRow?.OwningGrid ?? OwningColumn?.OwningGrid; }
-        }
-
-        internal double ActualRightGridLineWidth
-        {
-            get { return _rightGridLine?.Bounds.Width ?? 0; }
-        }
-
-        internal int ColumnIndex
-        {
-            get { return OwningColumn?.Index ?? -1; }
-        }
-
-        internal int RowIndex
-        {
-            get { return OwningRow?.Index ?? -1; }
-        }
-
-        internal bool IsCurrent
-        {
-            get
-            {
-                return OwningGrid.CurrentColumnIndex == OwningColumn.Index &&
-                       OwningGrid.CurrentSlot == OwningRow.Slot;
-            }
-        }
-
-        private bool IsEdited
-        {
-            get
-            {
-                return OwningGrid.EditingRow == OwningRow &&
-                       OwningGrid.EditingColumnIndex == ColumnIndex;
-            }
-        }
-
-        private bool IsMouseOver
-        {
-            get
-            {
-                return OwningRow != null && OwningRow.MouseOverColumnIndex == ColumnIndex;
-            }
-            set
-            {
-                if (value != IsMouseOver)
-                {
-                    if (value)
-                    {
-                        OwningRow.MouseOverColumnIndex = ColumnIndex;
-                    }
-                    else
-                    {
-                        OwningRow.MouseOverColumnIndex = null;
-                    }
-                }
-            }
-        }
-
-        protected override AutomationPeer OnCreateAutomationPeer()
-        {
-            return new DataGridCellAutomationPeer(this);
-        }
-
-        /// <summary>
-        /// Builds the visual tree for the cell control when a new template is applied.
-        /// </summary>
-        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
-        {
-            UpdatePseudoClasses();
-            _rightGridLine = e.NameScope.Find<Rectangle>(DATAGRIDCELL_elementRightGridLine);
-            if (_rightGridLine != null && OwningColumn == null)
-            {
-                // Turn off the right GridLine for filler cells
-                _rightGridLine.IsVisible = false;
-            }
-            else
-            {
-                EnsureGridLine(null);
-            }
-
-        }
-        protected override void OnPointerEntered(PointerEventArgs e)
-        {
-            base.OnPointerEntered(e);
-
-            if (OwningRow != null)
-            {
-                IsMouseOver = true;
-            }
-        }
-        protected override void OnPointerExited(PointerEventArgs e)
-        {
-            base.OnPointerExited(e);
-
-            if (OwningRow != null)
-            {
-                IsMouseOver = false;
-            }
-        }
-
-        //TODO TabStop
-        private void DataGridCell_PointerPressed(PointerPressedEventArgs e)
-        {
-            // OwningGrid is null for TopLeftHeaderCell and TopRightHeaderCell because they have no OwningRow
-            if (OwningGrid == null)
-            {
-                return;
-            }
-            OwningGrid.OnCellPointerPressed(new DataGridCellPointerPressedEventArgs(this, OwningRow, OwningColumn, e));
-            if (e.Handled)
-            {
-                return;
-            }
-            if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
-            {
-                if (OwningGrid.IsTabStop)
-                {
-                    OwningGrid.Focus();
-                }
-                if (OwningRow != null)
-                {
-                    var handled = OwningGrid.UpdateStateOnMouseLeftButtonDown(e, ColumnIndex, OwningRow.Slot, !e.Handled);
-
-                    // Do not handle PointerPressed with touch or pen,
-                    // so we can start scroll gesture on the same event.
-                    if (e.Pointer.Type != PointerType.Touch && e.Pointer.Type != PointerType.Pen)
-                    {
-                        e.Handled = handled;
-                    }
-                }
-            }
-            else if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed)
-            {
-                if (OwningGrid.IsTabStop)
-                {
-                    OwningGrid.Focus();
-                }
-                if (OwningRow != null)
-                {
-                    e.Handled = OwningGrid.UpdateStateOnMouseRightButtonDown(e, ColumnIndex, OwningRow.Slot, !e.Handled);
-                }
-            }
-        }
-
-        internal void UpdatePseudoClasses()
-        {
-            if (OwningGrid == null || OwningColumn == null || OwningRow == null || !OwningRow.IsVisible || OwningRow.Slot == -1)
-            {
-                return;
-            }
-
-            PseudoClasses.Set(":selected", OwningRow.IsSelected);
-
-            PseudoClasses.Set(":current", IsCurrent);
-
-            PseudoClasses.Set(":edited", IsEdited);
-
-            PseudoClasses.Set(":invalid", !IsValid);
-            
-            PseudoClasses.Set(":focus", OwningGrid.IsFocused && IsCurrent);
-        }
-
-        // Makes sure the right gridline has the proper stroke and visibility. If lastVisibleColumn is specified, the 
-        // right gridline will be collapsed if this cell belongs to the lastVisibleColumn and there is no filler column
-        internal void EnsureGridLine(DataGridColumn lastVisibleColumn)
-        {
-            if (OwningGrid != null && _rightGridLine != null)
-            {
-                if (OwningGrid.VerticalGridLinesBrush != null && OwningGrid.VerticalGridLinesBrush != _rightGridLine.Fill)
-                {
-                    _rightGridLine.Fill = OwningGrid.VerticalGridLinesBrush;
-                }
-
-                bool newVisibility =
-                    (OwningGrid.GridLinesVisibility == DataGridGridLinesVisibility.Vertical || OwningGrid.GridLinesVisibility == DataGridGridLinesVisibility.All)
-                        && (OwningGrid.ColumnsInternal.FillerColumn.IsActive || OwningColumn != lastVisibleColumn);
-
-                if (newVisibility != _rightGridLine.IsVisible)
-                {
-                    _rightGridLine.IsVisible = newVisibility;
-                }
-            }
-        }
-
-        private void OnOwningColumnSet(DataGridColumn column)
-        {
-            if (column == null)
-            {
-                Classes.Clear();
-                ClearValue(ThemeProperty);
-            }
-            else
-            {
-                if (Theme != column.CellTheme)
-                {
-                    Theme = column.CellTheme;
-                }
-                
-                Classes.Replace(column.CellStyleClasses);
-            }
-        }
-    }
-}

+ 0 - 71
src/Avalonia.Controls.DataGrid/DataGridCellCollection.cs

@@ -1,71 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Diagnostics;
-
-namespace Avalonia.Controls
-{
-    internal class DataGridCellCollection
-    {
-        private List<DataGridCell> _cells;
-        private DataGridRow _owningRow;
-
-        internal event EventHandler<DataGridCellEventArgs> CellAdded;
-        internal event EventHandler<DataGridCellEventArgs> CellRemoved;
-
-        public DataGridCellCollection(DataGridRow owningRow)
-        {
-            _owningRow = owningRow;
-            _cells = new List<DataGridCell>();
-        }
-
-        public int Count
-        {
-            get
-            {
-                return _cells.Count;
-            }
-        }
-
-        public IEnumerator GetEnumerator()
-        {
-            return _cells.GetEnumerator();
-        }
-
-        public void Insert(int cellIndex, DataGridCell cell)
-        {
-            Debug.Assert(cellIndex >= 0 && cellIndex <= _cells.Count);
-            Debug.Assert(cell != null);
-
-            cell.OwningRow = _owningRow;
-            _cells.Insert(cellIndex, cell);
-
-            CellAdded?.Invoke(this, new DataGridCellEventArgs(cell));
-        }
-
-        public void RemoveAt(int cellIndex)
-        {
-            DataGridCell dataGridCell = _cells[cellIndex];
-            _cells.RemoveAt(cellIndex);
-            dataGridCell.OwningRow = null;
-            CellRemoved?.Invoke(this, new DataGridCellEventArgs(dataGridCell));
-        }
-
-        public DataGridCell this[int index]
-        {
-            get
-            {
-                if (index < 0 || index >= _cells.Count)
-                {
-                    throw DataGridError.DataGrid.ValueMustBeBetween("index", "Index", 0, true, _cells.Count, false);
-                }
-                return _cells[index];
-            }
-        }
-    }
-}

+ 0 - 57
src/Avalonia.Controls.DataGrid/DataGridCellCoordinates.cs

@@ -1,57 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System.Globalization;
-
-namespace Avalonia.Controls
-{
-    internal class DataGridCellCoordinates
-    {
-        public DataGridCellCoordinates(int columnIndex, int slot)
-        {
-            ColumnIndex = columnIndex;
-            Slot = slot;
-        }
-
-        public DataGridCellCoordinates(DataGridCellCoordinates dataGridCellCoordinates) : this(dataGridCellCoordinates.ColumnIndex, dataGridCellCoordinates.Slot)
-        {
-        }
-
-        public int ColumnIndex
-        {
-            get;
-            set;
-        }
-
-        public int Slot
-        {
-            get;
-            set;
-        }
-
-        public override bool Equals(object o)
-        {
-            if (o is DataGridCellCoordinates dataGridCellCoordinates)
-            {
-                return dataGridCellCoordinates.ColumnIndex == ColumnIndex && dataGridCellCoordinates.Slot == Slot;
-            }
-            return false;
-        }
-
-        // There is build warning if this is missing
-        public override int GetHashCode()
-        {
-            return base.GetHashCode();
-        }
-
-#if DEBUG
-        public override string ToString()
-        {
-            return "DataGridCellCoordinates {ColumnIndex = " + ColumnIndex.ToString(CultureInfo.CurrentCulture) +
-                   ", Slot = " + Slot.ToString(CultureInfo.CurrentCulture) + "}";
-        }
-#endif
-    }
-}

+ 0 - 334
src/Avalonia.Controls.DataGrid/DataGridCheckBoxColumn.cs

@@ -1,334 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Input;
-using Avalonia.Interactivity;
-using Avalonia.Layout;
-using System;
-using System.Collections.Specialized;
-
-namespace Avalonia.Controls
-{
-    /// <summary>
-    /// Represents a <see cref="T:System.Windows.Controls.DataGrid" /> column that hosts 
-    /// <see cref="T:System.Windows.Controls.CheckBox" /> controls in its cells.
-    /// </summary>
-    public class DataGridCheckBoxColumn : DataGridBoundColumn
-    {
-        private CheckBox _currentCheckBox;
-        private DataGrid _owningGrid;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="T:System.Windows.Controls.DataGridCheckBoxColumn" /> class. 
-        /// </summary>
-        public DataGridCheckBoxColumn()
-        {
-            BindingTarget = CheckBox.IsCheckedProperty;
-        }
-
-        /// <summary>
-        /// Defines the <see cref="IsThreeState"/> property.
-        /// </summary>
-        public static readonly StyledProperty<bool> IsThreeStateProperty =
-            CheckBox.IsThreeStateProperty.AddOwner<DataGridCheckBoxColumn>();
-
-        /// <summary>
-        /// Gets or sets a value that indicates whether the hosted <see cref="T:System.Windows.Controls.CheckBox" /> controls allow three states or two. 
-        /// </summary>
-        /// <returns>
-        /// true if the hosted controls support three states; false if they support two states. The default is false. 
-        /// </returns>
-        public bool IsThreeState
-        {
-            get => GetValue(IsThreeStateProperty);
-            set => SetValue(IsThreeStateProperty, value);
-        }
-
-        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
-        {
-            base.OnPropertyChanged(change);
-
-            if (change.Property == IsThreeStateProperty)
-            {
-                NotifyPropertyChanged(change.Property.Name);
-            }
-        }
-
-        /// <summary>
-        /// Causes the column cell being edited to revert to the specified value.
-        /// </summary>
-        /// <param name="editingElement">
-        /// The element that the column displays for a cell in editing mode.
-        /// </param>
-        /// <param name="uneditedValue">
-        /// The previous, unedited value in the cell being edited.
-        /// </param>
-        protected override void CancelCellEdit(Control editingElement, object uneditedValue)
-        {
-            if (editingElement is CheckBox editingCheckBox)
-            {
-                editingCheckBox.IsChecked = (bool?)uneditedValue;
-            }
-        }
-
-        ///  <summary>
-        ///  Gets a <see cref="T:System.Windows.Controls.CheckBox" /> control that is bound to the column's <see cref="P:System.Windows.Controls.DataGridBoundColumn.Binding" /> property value.
-        ///  </summary>
-        ///  <param name="cell">
-        ///  The cell that will contain the generated element.
-        ///  </param>
-        ///  <param name="dataItem">
-        ///  The data item represented by the row that contains the intended cell.
-        /// </param>
-        ///  <returns>
-        ///  A new <see cref="T:System.Windows.Controls.CheckBox" /> control that is bound to the column's <see cref="P:System.Windows.Controls.DataGridBoundColumn.Binding" /> property value.
-        ///  </returns>
-        protected override Control GenerateEditingElementDirect(DataGridCell cell, object dataItem)
-        {
-            var checkBox = new CheckBox
-            {
-                Margin = new Thickness(0)
-            };
-            ConfigureCheckBox(checkBox);
-            return checkBox;
-        }
-
-        /// <summary>                
-        /// Gets a read-only <see cref="T:System.Windows.Controls.CheckBox" /> control that is bound to the column's <see cref="P:System.Windows.Controls.DataGridBoundColumn.Binding" /> property value.
-        /// </summary>
-        /// <param name="cell">
-        /// The cell that will contain the generated element.
-        /// </param>
-        /// <param name="dataItem">
-        /// The data item represented by the row that contains the intended cell.
-        /// </param>
-        /// <returns>
-        /// A new, read-only <see cref="T:System.Windows.Controls.CheckBox" /> control that is bound to the column's <see cref="P:System.Windows.Controls.DataGridBoundColumn.Binding" /> property value.
-        /// </returns>
-        protected override Control GenerateElement(DataGridCell cell, object dataItem)
-        {
-            bool isEnabled = false;
-            CheckBox checkBoxElement = new CheckBox();
-            if (EnsureOwningGrid())
-            {
-                if (cell.RowIndex != -1 && cell.ColumnIndex != -1 &&
-                    cell.OwningRow != null &&
-                    cell.OwningRow.Slot == this.OwningGrid.CurrentSlot &&
-                    cell.ColumnIndex == this.OwningGrid.CurrentColumnIndex)
-                {
-                    isEnabled = true;
-                    if (_currentCheckBox != null)
-                    {
-                        _currentCheckBox.IsEnabled = false;
-                    }
-                    _currentCheckBox = checkBoxElement;
-                }
-            }
-            checkBoxElement.IsEnabled = isEnabled;
-            checkBoxElement.IsHitTestVisible = false;
-            ConfigureCheckBox(checkBoxElement);
-            if (Binding != null)
-            {
-                checkBoxElement.Bind(BindingTarget, Binding);
-            }
-            return checkBoxElement;
-        }
-
-        /// <summary>
-        /// Called when a cell in the column enters editing mode.
-        /// </summary>
-        /// <param name="editingElement">
-        /// The element that the column displays for a cell in editing mode.
-        /// </param>
-        /// <param name="editingEventArgs">
-        /// Information about the user gesture that is causing a cell to enter editing mode.
-        /// </param>
-        /// <returns>
-        /// The unedited value. 
-        /// </returns>
-        protected override object PrepareCellForEdit(Control editingElement, RoutedEventArgs editingEventArgs)
-        {
-            if (editingElement is CheckBox editingCheckBox)
-            {
-                void EditValue()
-                {
-                    // User clicked the checkbox itself or pressed space, let's toggle the IsChecked value
-                    if (editingCheckBox.IsThreeState)
-                    {
-                        switch (editingCheckBox.IsChecked)
-                        {
-                            case false:
-                                editingCheckBox.IsChecked = true;
-                                break;
-                            case true:
-                                editingCheckBox.IsChecked = null;
-                                break;
-                            case null:
-                                editingCheckBox.IsChecked = false;
-                                break;
-                        }
-                    }
-                    else
-                    {
-                        editingCheckBox.IsChecked = !editingCheckBox.IsChecked;
-                    }
-                }
-
-                bool? uneditedValue = editingCheckBox.IsChecked;
-                if (editingEventArgs is PointerPressedEventArgs args)
-                {
-                    void ProcessPointerArgs()
-                    {
-                        // Editing was triggered by a mouse click
-                        Point position = args.GetPosition(editingCheckBox);
-                        Rect rect = new Rect(0, 0, editingCheckBox.Bounds.Width, editingCheckBox.Bounds.Height);
-                        if (rect.Contains(position))
-                        {
-                            EditValue();
-                        }
-                    }
-                    
-                    void OnLayoutUpdated(object sender, EventArgs e)
-                    {
-                        if (editingCheckBox.Bounds.Width != 0 || editingCheckBox.Bounds.Height != 0)
-                        {
-                            editingCheckBox.LayoutUpdated -= OnLayoutUpdated;
-                            ProcessPointerArgs();
-                        }
-                    }
-
-                    if (editingCheckBox.Bounds.Width == 0 && editingCheckBox.Bounds.Height == 0)
-                    {
-                        editingCheckBox.LayoutUpdated += OnLayoutUpdated;
-                    }
-                    else
-                    {
-                        ProcessPointerArgs();
-                    }
-                }
-
-                return uneditedValue;
-            }
-            return false;
-        }
-
-        /// <summary>
-        /// Called by the DataGrid control when this column asks for its elements to be
-        /// updated, because its CheckBoxContent or IsThreeState property changed.
-        /// </summary>
-        protected internal override void RefreshCellContent(Control element, string propertyName)
-        {
-            if (element == null)
-            {
-                throw new ArgumentNullException(nameof(element));
-            }
-            if (element is CheckBox checkBox)
-            {
-                DataGridHelper.SyncColumnProperty(this, checkBox, IsThreeStateProperty);
-            }
-            else
-            {
-                throw DataGridError.DataGrid.ValueIsNotAnInstanceOf("element", typeof(CheckBox));
-            }
-        }
-
-        private void Columns_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
-        {
-            if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems.Contains(this) && _owningGrid != null)
-            {
-                _owningGrid.Columns.CollectionChanged -= Columns_CollectionChanged;
-                _owningGrid.CurrentCellChanged -= OwningGrid_CurrentCellChanged;
-                _owningGrid.KeyDown -= OwningGrid_KeyDown;
-                _owningGrid.LoadingRow -= OwningGrid_LoadingRow;
-                _owningGrid = null;
-            }
-        }
-
-        private void ConfigureCheckBox(CheckBox checkBox)
-        {
-            checkBox.HorizontalAlignment = HorizontalAlignment.Center;
-            checkBox.VerticalAlignment = VerticalAlignment.Center;
-            DataGridHelper.SyncColumnProperty(this, checkBox, IsThreeStateProperty);
-        }
-
-        private bool EnsureOwningGrid()
-        {
-            if (OwningGrid != null)
-            {
-                if (OwningGrid != _owningGrid)
-                {
-                    _owningGrid = OwningGrid;
-                    _owningGrid.Columns.CollectionChanged += Columns_CollectionChanged;
-                    _owningGrid.CurrentCellChanged += OwningGrid_CurrentCellChanged;
-                    _owningGrid.KeyDown += OwningGrid_KeyDown;
-                    _owningGrid.LoadingRow += OwningGrid_LoadingRow;
-                }
-                return true;
-            }
-            return false;
-        }
-
-        private void OwningGrid_CurrentCellChanged(object sender, EventArgs e)
-        {
-            if (_currentCheckBox != null)
-            {
-                _currentCheckBox.IsEnabled = false;
-            }
-            if (OwningGrid != null && OwningGrid.CurrentColumn == this
-                && OwningGrid.IsSlotVisible(OwningGrid.CurrentSlot))
-            {
-                if (OwningGrid.DisplayData.GetDisplayedElement(OwningGrid.CurrentSlot) is DataGridRow row)
-                {
-                    CheckBox checkBox = GetCellContent(row) as CheckBox;
-                    if (checkBox != null)
-                    {
-                        checkBox.IsEnabled = true;
-                    }
-                    _currentCheckBox = checkBox;
-                }
-            }
-        }
-
-        private void OwningGrid_KeyDown(object sender, KeyEventArgs e)
-        {
-            if (e.Key == Key.Space && OwningGrid != null &&
-                OwningGrid.CurrentColumn == this)
-            {
-                if (OwningGrid.DisplayData.GetDisplayedElement(OwningGrid.CurrentSlot) is DataGridRow row)
-                {
-                    CheckBox checkBox = GetCellContent(row) as CheckBox;
-                    if (checkBox == _currentCheckBox)
-                    {
-                        OwningGrid.BeginEdit();
-                    }
-                }
-            }
-        }
-
-        private void OwningGrid_LoadingRow(object sender, DataGridRowEventArgs e)
-        {
-            if (OwningGrid != null)
-            {
-                if (GetCellContent(e.Row) is CheckBox checkBox)
-                {
-                    if (OwningGrid.CurrentColumnIndex == Index && OwningGrid.CurrentSlot == e.Row.Slot)
-                    {
-                        if (_currentCheckBox != null)
-                        {
-                            _currentCheckBox.IsEnabled = false;
-                        }
-                        checkBox.IsEnabled = true;
-                        _currentCheckBox = checkBox;
-                    }
-                    else
-                    {
-                        checkBox.IsEnabled = false;
-                    }
-                }
-            }
-        }
-
-    }
-}

+ 0 - 204
src/Avalonia.Controls.DataGrid/DataGridClipboard.cs

@@ -1,204 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System;
-using System.Collections.Generic;
-
-namespace Avalonia.Controls
-{
-    /// <summary>
-    /// Defines modes that indicates how DataGrid content is copied to the Clipboard. 
-    /// </summary>
-    public enum DataGridClipboardCopyMode
-    {
-        /// <summary>
-        /// Disable the DataGrid's ability to copy selected items as text.
-        /// </summary>
-        None,
-
-        /// <summary>
-        /// Enable the DataGrid's ability to copy selected items as text, but do not include
-        /// the column header content as the first line in the text that gets copied to the Clipboard.
-        /// </summary>
-        ExcludeHeader,
-
-        /// <summary>
-        /// Enable the DataGrid's ability to copy selected items as text, and include
-        /// the column header content as the first line in the text that gets copied to the Clipboard.
-        /// </summary>
-        IncludeHeader
-    }
-
-    /// <summary>
-    /// This structure encapsulate the cell information necessary when clipboard content is prepared.
-    /// </summary>
-    public struct DataGridClipboardCellContent
-    {
-
-        private DataGridColumn _column;
-        private object _content;
-        private object _item;
-
-        /// <summary>
-        /// Creates a new DataGridClipboardCellValue structure containing information about a DataGrid cell.
-        /// </summary>
-        /// <param name="item">DataGrid row item containing the cell.</param>
-        /// <param name="column">DataGridColumn containing the cell.</param>
-        /// <param name="content">DataGrid cell value.</param>
-        public DataGridClipboardCellContent(object item, DataGridColumn column, object content)
-        {
-            this._item = item;
-            this._column = column;
-            this._content = content;
-        }
-
-        /// <summary>
-        /// DataGridColumn containing the cell.
-        /// </summary>
-        public DataGridColumn Column
-        {
-            get
-            {
-                return _column;
-            }
-        }
-
-        /// <summary>
-        /// Cell content.
-        /// </summary>
-        public object Content
-        {
-            get
-            {
-                return _content;
-            }
-        }
-
-        /// <summary>
-        /// DataGrid row item containing the cell.
-        /// </summary>
-        public object Item
-        {
-            get
-            {
-                return _item;
-            }
-        }
-
-        /// <summary>
-        /// Field-by-field comparison to avoid reflection-based ValueType.Equals.
-        /// </summary>
-        /// <param name="obj">DataGridClipboardCellContent to compare.</param>
-        /// <returns>True iff this and data are equal</returns>
-        public override bool Equals(object obj)
-        {
-            if(obj is DataGridClipboardCellContent content)
-            {
-                return (((_column == content._column) && (_content == content._content)) && (_item == content._item));
-            }
-            else
-            {
-                return false;
-            }
-        }
-
-        /// <summary>
-        /// Returns a deterministic hash code.
-        /// </summary>
-        /// <returns>Hash value.</returns>
-        public override int GetHashCode()
-        {
-            return ((_column.GetHashCode() ^ _content.GetHashCode()) ^ _item.GetHashCode());
-        }
-
-        /// <summary>
-        /// Field-by-field comparison to avoid reflection-based ValueType.Equals.
-        /// </summary>
-        /// <param name="clipboardCellContent1">The first DataGridClipboardCellContent.</param>
-        /// <param name="clipboardCellContent2">The second DataGridClipboardCellContent.</param>
-        /// <returns>True iff clipboardCellContent1 and clipboardCellContent2 are equal.</returns>
-        public static bool operator ==(DataGridClipboardCellContent clipboardCellContent1, DataGridClipboardCellContent clipboardCellContent2)
-        {
-            return (((clipboardCellContent1._column == clipboardCellContent2._column) && (clipboardCellContent1._content == clipboardCellContent2._content)) && (clipboardCellContent1._item == clipboardCellContent2._item));
-        }
-
-        /// <summary>
-        /// Field-by-field comparison to avoid reflection-based ValueType.Equals.
-        /// </summary>
-        /// <param name="clipboardCellContent1">The first DataGridClipboardCellContent.</param>
-        /// <param name="clipboardCellContent2">The second DataGridClipboardCellContent.</param>
-        /// <returns>True iff clipboardCellContent1 and clipboardCellContent2 are NOT equal.</returns>
-        public static bool operator !=(DataGridClipboardCellContent clipboardCellContent1, DataGridClipboardCellContent clipboardCellContent2)
-        {
-            if ((clipboardCellContent1._column == clipboardCellContent2._column) && (clipboardCellContent1._content == clipboardCellContent2._content))
-            {
-                return (clipboardCellContent1._item != clipboardCellContent2._item);
-            }
-            return true;
-        }
-
-    }
-
-    /// <summary>
-    /// This class encapsulates a selected row's information necessary for the CopyingRowClipboardContent event.
-    /// </summary>
-    public class DataGridRowClipboardEventArgs : EventArgs
-    {
-
-        private List<DataGridClipboardCellContent> _clipboardRowContent;
-        private bool _isColumnHeadersRow;
-        private object _item;
-
-        /// <summary>
-        /// Creates a DataGridRowClipboardEventArgs object and initializes the properties.
-        /// </summary>
-        /// <param name="item">The row's associated data item.</param>
-        /// <param name="isColumnHeadersRow">Whether or not this EventArgs is for the column headers.</param>
-        internal DataGridRowClipboardEventArgs(object item, bool isColumnHeadersRow)
-        {
-            _isColumnHeadersRow = isColumnHeadersRow;
-            _item = item;
-        }
-
-        /// <summary>
-        /// This list should be used to modify, add ot remove a cell content before it gets stored into the clipboard.
-        /// </summary>
-        public List<DataGridClipboardCellContent> ClipboardRowContent
-        {
-            get
-            {
-                if (_clipboardRowContent == null)
-                {
-                    _clipboardRowContent = new List<DataGridClipboardCellContent>();
-                }
-                return _clipboardRowContent;
-            }
-        }
-
-        /// <summary>
-        /// This property is true when the ClipboardRowContent represents column headers, in which case the Item is null.
-        /// </summary>
-        public bool IsColumnHeadersRow
-        {
-            get
-            {
-                return _isColumnHeadersRow;
-            }
-        }
-
-        /// <summary>
-        /// DataGrid row item used for preparing the ClipboardRowContent.
-        /// </summary>
-        public object Item
-        {
-            get
-            {
-                return _item;
-            }
-        }
-
-    }
-
-}

+ 0 - 1198
src/Avalonia.Controls.DataGrid/DataGridColumn.cs

@@ -1,1198 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Linq;
-using Avalonia.Collections;
-using Avalonia.Controls.Templates;
-using Avalonia.Controls.Utils;
-using Avalonia.Data;
-using Avalonia.Input;
-using Avalonia.Interactivity;
-using Avalonia.Layout;
-using Avalonia.Markup.Xaml.MarkupExtensions;
-using Avalonia.Styling;
-using Avalonia.VisualTree;
-
-namespace Avalonia.Controls
-{
-    public abstract class DataGridColumn : AvaloniaObject
-    {
-        internal const int DATAGRIDCOLUMN_maximumWidth = 65536;
-        private const bool DATAGRIDCOLUMN_defaultIsReadOnly = false;
-        private bool? _isReadOnly;
-        private double? _maxWidth;
-        private double? _minWidth;
-        private bool _settingWidthInternally;
-        private int _displayIndexWithFiller;
-        private object _header;
-        private IDataTemplate _headerTemplate;
-        private DataGridColumnHeader _headerCell;
-        private Control _editingElement;
-        private ICellEditBinding _editBinding;
-        private IBinding _clipboardContentBinding;
-        private ControlTheme _cellTheme;
-        private Classes _cellStyleClasses;
-        private bool _setWidthInternalNoCallback;
-
-        /// <summary>
-        /// Occurs when the pointer is pressed over the column's header
-        /// </summary>
-        public event EventHandler<PointerPressedEventArgs> HeaderPointerPressed;
-        /// <summary>
-        /// Occurs when the pointer is released over the column's header
-        /// </summary>
-        public event EventHandler<PointerReleasedEventArgs> HeaderPointerReleased;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridColumn" /> class.
-        /// </summary>
-        protected internal DataGridColumn()
-        {
-            _displayIndexWithFiller = -1;
-            IsInitialDesiredWidthDetermined = false;
-            InheritsWidth = true;
-        }
-
-        /// <summary>
-        /// Gets the <see cref="T:Avalonia.Controls.DataGrid"/> control that contains this column.
-        /// </summary>
-        protected internal DataGrid OwningGrid
-        {
-            get;
-            internal set;
-        }
-
-        internal int Index
-        {
-            get;
-            set;
-        }
-
-        internal bool? CanUserReorderInternal
-        {
-            get;
-            set;
-        }
-
-        internal bool? CanUserResizeInternal
-        {
-            get;
-            set;
-        }
-
-        internal bool? CanUserSortInternal
-        {
-            get;
-            set;
-        }
-
-        internal bool ActualCanUserResize
-        {
-            get
-            {
-                if (OwningGrid == null || OwningGrid.CanUserResizeColumns == false || this is DataGridFillerColumn)
-                {
-                    return false;
-                }
-                return CanUserResizeInternal ?? true;
-            }
-        }
-
-        // MaxWidth from local setting or DataGrid setting
-        internal double ActualMaxWidth
-        {
-            get
-            {
-                return _maxWidth ?? OwningGrid?.MaxColumnWidth ?? double.PositiveInfinity;
-            }
-        }
-
-        // MinWidth from local setting or DataGrid setting
-        internal double ActualMinWidth
-        {
-            get
-            {
-                double minWidth = _minWidth ?? OwningGrid?.MinColumnWidth ?? 0;
-                if (Width.IsStar)
-                {
-                    return Math.Max(DataGrid.DATAGRID_minimumStarColumnWidth, minWidth);
-                }
-                return minWidth;
-            }
-        }
-
-        internal bool DisplayIndexHasChanged
-        {
-            get;
-            set;
-        }
-
-        internal int DisplayIndexWithFiller
-        {
-            get { return _displayIndexWithFiller; }
-            set { _displayIndexWithFiller = value; }
-        }
-
-        internal bool HasHeaderCell
-        {
-            get
-            {
-                return _headerCell != null;
-            }
-        }
-
-        internal DataGridColumnHeader HeaderCell
-        {
-            get
-            {
-                if (_headerCell == null)
-                {
-                    _headerCell = CreateHeader();
-                }
-                return _headerCell;
-            }
-        }
-
-        /// <summary>
-        /// Tracks whether or not this column inherits its Width value from the DataGrid.
-        /// </summary>
-        internal bool InheritsWidth
-        {
-            get;
-            private set;
-        }
-
-        /// <summary>
-        /// When a column is initially added, we won't know its initial desired value
-        /// until all rows have been measured.  We use this variable to track whether or
-        /// not the column has been fully measured.
-        /// </summary>
-        internal bool IsInitialDesiredWidthDetermined
-        {
-            get;
-            set;
-        }
-
-        internal double LayoutRoundedWidth
-        {
-            get;
-            private set;
-        }
-
-        internal ICellEditBinding CellEditBinding
-        {
-            get => _editBinding;
-        }
-
-
-        /// <summary>
-        /// Defines the <see cref="IsVisible"/> property.
-        /// </summary>
-        public static readonly StyledProperty<bool> IsVisibleProperty =
-             Control.IsVisibleProperty.AddOwner<DataGridColumn>();
-
-        /// <summary>
-        /// Determines whether or not this column is visible.
-        /// </summary>
-        public bool IsVisible
-        {
-            get => GetValue(IsVisibleProperty);
-            set => SetValue(IsVisibleProperty, value);
-        }
-
-        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
-        {
-            base.OnPropertyChanged(change);
-
-            if (change.Property == IsVisibleProperty)
-            {
-                OwningGrid?.OnColumnVisibleStateChanging(this);
-                var isVisible = change.GetNewValue<bool>();
-
-                if (_headerCell != null)
-                {
-                    _headerCell.IsVisible = isVisible;
-                }
-
-                OwningGrid?.OnColumnVisibleStateChanged(this);
-                NotifyPropertyChanged(change.Property.Name);
-            }
-            else if (change.Property == WidthProperty)
-            {
-                if (!_settingWidthInternally)
-                {
-                    InheritsWidth = false;
-                }
-                if (_setWidthInternalNoCallback == false)
-                {
-                    var grid = OwningGrid;
-                    var width = (change as AvaloniaPropertyChangedEventArgs<DataGridLength>).NewValue.Value;
-                    if (grid != null)
-                    {
-                        var oldWidth = (change as AvaloniaPropertyChangedEventArgs<DataGridLength>).OldValue.Value;
-                        if (width.IsStar != oldWidth.IsStar)
-                        {
-                            SetWidthInternalNoCallback(width);
-                            IsInitialDesiredWidthDetermined = false;
-                            grid.OnColumnWidthChanged(this);
-                        }
-                        else
-                        {
-                            Resize(oldWidth, width, false);
-                        }
-                    }
-                    else
-                    {
-                        SetWidthInternalNoCallback(width);
-                    }
-                }
-            }
-        }
-
-
-        /// <summary>
-        /// Actual visible width after Width, MinWidth, and MaxWidth setting at the Column level and DataGrid level
-        /// have been taken into account
-        /// </summary>
-        public double ActualWidth
-        {
-            get
-            {
-                if (OwningGrid == null || double.IsNaN(Width.DisplayValue))
-                {
-                    return ActualMinWidth;
-                }
-                return Width.DisplayValue;
-            }
-        }
-
-        /// <summary>
-        /// Gets or sets a value that indicates whether the user can change the column display position by
-        /// dragging the column header.
-        /// </summary>
-        /// <returns>
-        /// true if the user can drag the column header to a new position; otherwise, false. The default is the current <see cref="P:Avalonia.Controls.DataGrid.CanUserReorderColumns" /> property value.
-        /// </returns>
-        public bool CanUserReorder
-        {
-            get
-            {
-                return
-                    CanUserReorderInternal ??
-                        OwningGrid?.CanUserReorderColumns ??
-                        DataGrid.DATAGRID_defaultCanUserResizeColumns;
-            }
-            set
-            {
-                CanUserReorderInternal = value;
-            }
-        }
-
-        /// <summary>
-        /// Gets or sets a value that indicates whether the user can adjust the column width using the mouse.
-        /// </summary>
-        /// <returns>
-        /// true if the user can resize the column; false if the user cannot resize the column. The default is the current <see cref="P:Avalonia.Controls.DataGrid.CanUserResizeColumns" /> property value.
-        /// </returns>
-        public bool CanUserResize
-        {
-            get
-            {
-                return
-                    CanUserResizeInternal ??
-                    OwningGrid?.CanUserResizeColumns ??
-                    DataGrid.DATAGRID_defaultCanUserResizeColumns;
-            }
-            set
-            {
-                CanUserResizeInternal = value;
-                OwningGrid?.OnColumnCanUserResizeChanged(this);
-            }
-        }
-
-        /// <summary>
-        /// Gets or sets a value that indicates whether the user can sort the column by clicking the column header.
-        /// </summary>
-        /// <returns>
-        /// true if the user can sort the column; false if the user cannot sort the column. The default is the current <see cref="P:Avalonia.Controls.DataGrid.CanUserSortColumns" /> property value.
-        /// </returns>
-        public bool CanUserSort
-        {
-            get
-            {
-                if (CanUserSortInternal.HasValue)
-                {
-                    return CanUserSortInternal.Value;
-                }
-                else if (OwningGrid != null)
-                {
-                    string propertyPath = GetSortPropertyName();
-                    Type propertyType = OwningGrid.DataConnection.DataType.GetNestedPropertyType(propertyPath);
-
-                    // if the type is nullable, then we will compare the non-nullable type
-                    if (TypeHelper.IsNullableType(propertyType))
-                    {
-                        propertyType = TypeHelper.GetNonNullableType(propertyType);
-                    }
-
-                    // return whether or not the property type can be compared
-                    return typeof(IComparable).IsAssignableFrom(propertyType) ? true : false;
-                }
-                else
-                {
-                    return DataGrid.DATAGRID_defaultCanUserSortColumns;
-                }
-            }
-            set
-            {
-                CanUserSortInternal = value;
-            }
-        }
-
-        /// <summary>
-        /// Gets or sets the display position of the column relative to the other columns in the <see cref="T:Avalonia.Controls.DataGrid" />.
-        /// </summary>
-        /// <returns>
-        /// The zero-based position of the column as it is displayed in the associated <see cref="T:Avalonia.Controls.DataGrid" />. The default is the index of the corresponding <see cref="P:System.Collections.ObjectModel.Collection`1.Item(System.Int32)" /> in the <see cref="P:Avalonia.Controls.DataGrid.Columns" /> collection.
-        /// </returns>
-        /// <exception cref="T:System.ArgumentOutOfRangeException">
-        /// When setting this property, the specified value is less than -1 or equal to <see cref="F:System.Int32.MaxValue" />.
-        ///
-        /// -or-
-        ///
-        /// When setting this property on a column in a <see cref="T:Avalonia.Controls.DataGrid" />, the specified value is less than zero or greater than or equal to the number of columns in the <see cref="T:Avalonia.Controls.DataGrid" />.
-        /// </exception>
-        /// <exception cref="T:System.InvalidOperationException">
-        /// When setting this property, the <see cref="T:Avalonia.Controls.DataGrid" /> is already making <see cref="P:Avalonia.Controls.DataGridColumn.DisplayIndex" /> adjustments. For example, this exception is thrown when you attempt to set <see cref="P:Avalonia.Controls.DataGridColumn.DisplayIndex" /> in a <see cref="E:Avalonia.Controls.DataGrid.ColumnDisplayIndexChanged" /> event handler.
-        ///
-        /// -or-
-        ///
-        /// When setting this property, the specified value would result in a frozen column being displayed in the range of unfrozen columns, or an unfrozen column being displayed in the range of frozen columns.
-        /// </exception>
-        public int DisplayIndex
-        {
-            get
-            {
-                if (OwningGrid != null && OwningGrid.ColumnsInternal.RowGroupSpacerColumn.IsRepresented)
-                {
-                    return _displayIndexWithFiller - 1;
-                }
-                else
-                {
-                    return _displayIndexWithFiller;
-                }
-            }
-            set
-            {
-                if (value == Int32.MaxValue)
-                {
-                    throw DataGridError.DataGrid.ValueMustBeLessThan(nameof(value), nameof(DisplayIndex), Int32.MaxValue);
-                }
-                if (OwningGrid != null)
-                {
-                    if (OwningGrid.ColumnsInternal.RowGroupSpacerColumn.IsRepresented)
-                    {
-                        value++;
-                    }
-                    if (_displayIndexWithFiller != value)
-                    {
-                        if (value < 0 || value >= OwningGrid.ColumnsItemsInternal.Count)
-                        {
-                            throw DataGridError.DataGrid.ValueMustBeBetween(nameof(value), nameof(DisplayIndex), 0, true, OwningGrid.Columns.Count, false);
-                        }
-                        // Will throw an error if a visible frozen column is placed inside a non-frozen area or vice-versa.
-                        OwningGrid.OnColumnDisplayIndexChanging(this, value);
-                        _displayIndexWithFiller = value;
-                        try
-                        {
-                            OwningGrid.InDisplayIndexAdjustments = true;
-                            OwningGrid.OnColumnDisplayIndexChanged(this);
-                            OwningGrid.OnColumnDisplayIndexChanged_PostNotification();
-                        }
-                        finally
-                        {
-                            OwningGrid.InDisplayIndexAdjustments = false;
-                        }
-                    }
-                }
-                else
-                {
-                    if (value < -1)
-                    {
-                        throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo(nameof(value), nameof(DisplayIndex), -1);
-                    }
-                    _displayIndexWithFiller = value;
-                }
-            }
-        }
-
-        public Classes CellStyleClasses => _cellStyleClasses ??= new();
-
-        /// <summary>
-        ///    Backing field for CellTheme property.
-        /// </summary>
-        public static readonly DirectProperty<DataGridColumn, ControlTheme> CellThemeProperty =
-            AvaloniaProperty.RegisterDirect<DataGridColumn, ControlTheme>(
-                nameof(CellTheme),
-                o => o.CellTheme,
-                (o, v) => o.CellTheme = v);
-
-        /// <summary>
-        ///    Gets or sets the <see cref="DataGridColumnHeader"/> cell theme.
-        /// </summary>
-        public ControlTheme CellTheme
-        {
-            get { return _cellTheme; }
-            set { SetAndRaise(CellThemeProperty, ref _cellTheme, value); }
-        }
-
-        /// <summary>
-        ///    Backing field for Header property
-        /// </summary>
-        public static readonly DirectProperty<DataGridColumn, object> HeaderProperty =
-            AvaloniaProperty.RegisterDirect<DataGridColumn, object>(
-                nameof(Header),
-                o => o.Header,
-                (o, v) => o.Header = v);
-
-        /// <summary>
-        ///    Gets or sets the <see cref="DataGridColumnHeader"/> content
-        /// </summary>
-        public object Header
-        {
-            get { return _header; }
-            set { SetAndRaise(HeaderProperty, ref _header, value); }
-        }
-
-        /// <summary>
-        ///    Backing field for Header property
-        /// </summary>
-        public static readonly DirectProperty<DataGridColumn, IDataTemplate> HeaderTemplateProperty =
-            AvaloniaProperty.RegisterDirect<DataGridColumn, IDataTemplate>(
-                nameof(HeaderTemplate),
-                o => o.HeaderTemplate,
-                (o, v) => o.HeaderTemplate = v);
-
-        /// <summary>
-        ///  Gets or sets an <see cref="IDataTemplate"/> for the <see cref="Header"/>
-        /// </summary>
-        public IDataTemplate HeaderTemplate
-        {
-            get { return _headerTemplate; }
-            set { SetAndRaise(HeaderTemplateProperty, ref _headerTemplate, value); }
-        }
-
-        public bool IsAutoGenerated
-        {
-            get;
-            internal set;
-        }
-
-        public bool IsFrozen
-        {
-            get;
-            internal set;
-        }
-
-        public virtual bool IsReadOnly
-        {
-            get
-            {
-                if (OwningGrid == null)
-                {
-                    return _isReadOnly ?? DATAGRIDCOLUMN_defaultIsReadOnly;
-                }
-                if (_isReadOnly != null)
-                {
-                    return _isReadOnly.Value || OwningGrid.IsReadOnly;
-                }
-                return OwningGrid.GetColumnReadOnlyState(this, DATAGRIDCOLUMN_defaultIsReadOnly);
-            }
-            set
-            {
-                if (value != _isReadOnly)
-                {
-                    OwningGrid?.OnColumnReadOnlyStateChanging(this, value);
-                    _isReadOnly = value;
-                }
-            }
-        }
-
-        public double MaxWidth
-        {
-            get
-            {
-                return _maxWidth ?? double.PositiveInfinity;
-            }
-            set
-            {
-                if (value < 0)
-                {
-                    throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("value", "MaxWidth", 0);
-                }
-                if (value < ActualMinWidth)
-                {
-                    throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("value", "MaxWidth", "MinWidth");
-                }
-                if (!_maxWidth.HasValue || _maxWidth.Value != value)
-                {
-                    double oldValue = ActualMaxWidth;
-                    _maxWidth = value;
-                    if (OwningGrid != null && OwningGrid.ColumnsInternal != null)
-                    {
-                        OwningGrid.OnColumnMaxWidthChanged(this, oldValue);
-                    }
-                }
-            }
-        }
-
-        public double MinWidth
-        {
-            get
-            {
-                return _minWidth ?? 0;
-            }
-            set
-            {
-                if (double.IsNaN(value))
-                {
-                    throw DataGridError.DataGrid.ValueCannotBeSetToNAN("MinWidth");
-                }
-                if (value < 0)
-                {
-                    throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("value", "MinWidth", 0);
-                }
-                if (double.IsPositiveInfinity(value))
-                {
-                    throw DataGridError.DataGrid.ValueCannotBeSetToInfinity("MinWidth");
-                }
-                if (value > ActualMaxWidth)
-                {
-                    throw DataGridError.DataGrid.ValueMustBeLessThanOrEqualTo("value", "MinWidth", "MaxWidth");
-                }
-                if (!_minWidth.HasValue || _minWidth.Value != value)
-                {
-                    double oldValue = ActualMinWidth;
-                    _minWidth = value;
-                    if (OwningGrid != null && OwningGrid.ColumnsInternal != null)
-                    {
-                        OwningGrid.OnColumnMinWidthChanged(this, oldValue);
-                    }
-                }
-            }
-        }
-
-        public static readonly StyledProperty<DataGridLength> WidthProperty = AvaloniaProperty
-            .Register<DataGridColumn, DataGridLength>(nameof(Width)
-            , coerce: CoerceWidth
-            );
-
-        public DataGridLength Width
-        {
-            get => this.GetValue(WidthProperty);
-            set => SetValue(WidthProperty, value);
-        }
-
-        /// <summary>
-        /// The binding that will be used to get or set cell content for the clipboard.
-        /// </summary>
-        public virtual IBinding ClipboardContentBinding
-        {
-            get
-            {
-                return _clipboardContentBinding;
-            }
-            set
-            {
-                _clipboardContentBinding = value;
-            }
-        }
-
-        /// <summary>
-        /// Gets the value of a cell according to the specified binding.
-        /// </summary>
-        /// <param name="item">The item associated with a cell.</param>
-        /// <param name="binding">The binding to get the value of.</param>
-        /// <returns>The resultant cell value.</returns>
-        internal object GetCellValue(object item, IBinding binding)
-        {
-            Debug.Assert(OwningGrid != null);
-
-            object content = null;
-            if (binding != null)
-            {
-                OwningGrid.ClipboardContentControl.DataContext = item;
-                var sub = OwningGrid.ClipboardContentControl.Bind(ContentControl.ContentProperty, binding);
-                content = OwningGrid.ClipboardContentControl.GetValue(ContentControl.ContentProperty);
-                sub.Dispose();
-            }
-            return content;
-        }
-
-        public Control GetCellContent(DataGridRow dataGridRow)
-        {
-            dataGridRow = dataGridRow ?? throw new ArgumentNullException(nameof(dataGridRow));
-            if (OwningGrid == null)
-            {
-                throw DataGridError.DataGrid.NoOwningGrid(GetType());
-            }
-            if (dataGridRow.OwningGrid == OwningGrid)
-            {
-                DataGridCell dataGridCell = dataGridRow.Cells[Index];
-                if (dataGridCell != null)
-                {
-                    return dataGridCell.Content as Control;
-                }
-            }
-            return null;
-        }
-
-        public Control GetCellContent(object dataItem)
-        {
-            dataItem = dataItem ?? throw new ArgumentNullException(nameof(dataItem));
-            if (OwningGrid == null)
-            {
-                throw DataGridError.DataGrid.NoOwningGrid(GetType());
-            }
-            DataGridRow dataGridRow = OwningGrid.GetRowFromItem(dataItem);
-            if (dataGridRow == null)
-            {
-                return null;
-            }
-            return GetCellContent(dataGridRow);
-        }
-
-        /// <summary>
-        /// Returns the column which contains the given element
-        /// </summary>
-        /// <param name="element">element contained in a column</param>
-        /// <returns>Column that contains the element, or null if not found
-        /// </returns>
-        public static DataGridColumn GetColumnContainingElement(Control element)
-        {
-            // Walk up the tree to find the DataGridCell or DataGridColumnHeader that contains the element
-            Visual parent = element;
-            while (parent != null)
-            {
-                if (parent is DataGridCell cell)
-                {
-                    return cell.OwningColumn;
-                }
-                if (parent is DataGridColumnHeader columnHeader)
-                {
-                    return columnHeader.OwningColumn;
-                }
-                parent = parent.GetVisualParent();
-            }
-            return null;
-        }
-
-        /// <summary>
-        /// Clears the current sort direction
-        /// </summary>
-        public void ClearSort()
-        {
-            //InvokeProcessSort is already validating if sorting is possible
-            _headerCell?.InvokeProcessSort(KeyboardHelper.GetPlatformCtrlOrCmdKeyModifier(OwningGrid));
-        }
-
-        /// <summary>
-        /// Switches the current state of sort direction
-        /// </summary>
-        public void Sort()
-        {
-            //InvokeProcessSort is already validating if sorting is possible
-            _headerCell?.InvokeProcessSort(Input.KeyModifiers.None);
-        }
-
-        /// <summary>
-        /// Changes the sort direction of this column
-        /// </summary>
-        /// <param name="direction">New sort direction</param>
-        public void Sort(ListSortDirection direction)
-        {
-            //InvokeProcessSort is already validating if sorting is possible
-            _headerCell?.InvokeProcessSort(Input.KeyModifiers.None, direction);
-        }
-
-        /// <summary>
-        /// When overridden in a derived class, causes the column cell being edited to revert to the unedited value.
-        /// </summary>
-        /// <param name="editingElement">
-        /// The element that the column displays for a cell in editing mode.
-        /// </param>
-        /// <param name="uneditedValue">
-        /// The previous, unedited value in the cell being edited.
-        /// </param>
-        protected virtual void CancelCellEdit(Control editingElement, object uneditedValue)
-        { }
-
-        /// <summary>
-        /// When overridden in a derived class, gets an editing element that is bound to the column's <see cref="P:Avalonia.Controls.DataGridBoundColumn.Binding" /> property value.
-        /// </summary>
-        /// <param name="cell">
-        /// The cell that will contain the generated element.
-        /// </param>
-        /// <param name="dataItem">
-        /// The data item represented by the row that contains the intended cell.
-        /// </param>
-        /// <param name="binding">When the method returns, contains the applied binding.</param>
-        /// <returns>
-        /// A new editing element that is bound to the column's <see cref="P:Avalonia.Controls.DataGridBoundColumn.Binding" /> property value.
-        /// </returns>
-        protected abstract Control GenerateEditingElement(DataGridCell cell, object dataItem, out ICellEditBinding binding);
-
-        /// <summary>
-        /// When overridden in a derived class, gets a read-only element that is bound to the column's
-        /// <see cref="P:Avalonia.Controls.DataGridBoundColumn.Binding" /> property value.
-        /// </summary>
-        /// <param name="cell">
-        /// The cell that will contain the generated element.
-        /// </param>
-        /// <param name="dataItem">
-        /// The data item represented by the row that contains the intended cell.
-        /// </param>
-        /// <returns>
-        /// A new, read-only element that is bound to the column's <see cref="P:Avalonia.Controls.DataGridBoundColumn.Binding" /> property value.
-        /// </returns>
-        protected abstract Control GenerateElement(DataGridCell cell, object dataItem);
-
-        /// <summary>
-        /// Called by a specific column type when one of its properties changed,
-        /// and its current cells need to be updated.
-        /// </summary>
-        /// <param name="propertyName">Indicates which property changed and caused this call</param>
-        protected void NotifyPropertyChanged(string propertyName)
-        {
-            OwningGrid?.RefreshColumnElements(this, propertyName);
-        }
-
-        /// <summary>
-        /// When overridden in a derived class, called when a cell in the column enters editing mode.
-        /// </summary>
-        /// <param name="editingElement">
-        /// The element that the column displays for a cell in editing mode.
-        /// </param>
-        /// <param name="editingEventArgs">
-        /// Information about the user gesture that is causing a cell to enter editing mode.
-        /// </param>
-        /// <returns>
-        /// The unedited value.
-        /// </returns>
-        protected abstract object PrepareCellForEdit(Control editingElement, RoutedEventArgs editingEventArgs);
-
-        /// <summary>
-        /// Called by the DataGrid control when a column asked for its
-        /// elements to be refreshed, typically because one of its properties changed.
-        /// </summary>
-        /// <param name="element">Indicates the element that needs to be refreshed</param>
-        /// <param name="propertyName">Indicates which property changed and caused this call</param>
-        protected internal virtual void RefreshCellContent(Control element, string propertyName)
-        { }
-
-        /// <summary>
-        /// When overridden in a derived class, called when a cell in the column exits editing mode.
-        /// </summary>
-        protected virtual void EndCellEdit()
-        { }
-
-        internal void CancelCellEditInternal(Control editingElement, object uneditedValue)
-        {
-            CancelCellEdit(editingElement, uneditedValue);
-        }
-
-        internal void EndCellEditInternal()
-        {
-            EndCellEdit();
-        }
-
-        /// <summary>
-        /// Coerces a DataGridLength to a valid value.  If any value components are double.NaN, this method
-        /// coerces them to a proper initial value.  For star columns, the desired width is calculated based
-        /// on the rest of the star columns.  For pixel widths, the desired value is based on the pixel value.
-        /// For auto widths, the desired value is initialized as the column's minimum width.
-        /// </summary>
-        /// <param name="source"></param>
-        /// <param name="width">The DataGridLength to coerce.</param>
-        /// <returns>The resultant (coerced) DataGridLength.</returns>
-        private static DataGridLength CoerceWidth(AvaloniaObject source, DataGridLength width)
-        {
-            var target = (DataGridColumn)source;
-
-            if (target._setWidthInternalNoCallback)
-            {
-                return width;
-            }
-
-            if (!target.IsSet(WidthProperty))
-            {
-
-                return target.OwningGrid?.ColumnWidth ??
-                        DataGridLength.Auto;
-            }
-
-            double desiredValue = width.DesiredValue;
-            if (double.IsNaN(desiredValue))
-            {
-                if (width.IsStar && target.OwningGrid != null && target.OwningGrid.ColumnsInternal != null)
-                {
-                    double totalStarValues = 0;
-                    double totalStarDesiredValues = 0;
-                    double totalNonStarDisplayWidths = 0;
-                    foreach (DataGridColumn column in target.OwningGrid.ColumnsInternal.GetDisplayedColumns(c => c.IsVisible && c != target && !double.IsNaN(c.Width.DesiredValue)))
-                    {
-                        if (column.Width.IsStar)
-                        {
-                            totalStarValues += column.Width.Value;
-                            totalStarDesiredValues += column.Width.DesiredValue;
-                        }
-                        else
-                        {
-                            totalNonStarDisplayWidths += column.ActualWidth;
-                        }
-                    }
-                    if (totalStarValues == 0)
-                    {
-                        // Compute the new star column's desired value based on the available space if there are no other visible star columns
-                        desiredValue = Math.Max(target.ActualMinWidth, target.OwningGrid.CellsWidth - totalNonStarDisplayWidths);
-                    }
-                    else
-                    {
-                        // Otherwise, compute its desired value based on those of other visible star columns
-                        desiredValue = totalStarDesiredValues * width.Value / totalStarValues;
-                    }
-                }
-                else if (width.IsAbsolute)
-                {
-                    desiredValue = width.Value;
-                }
-                else
-                {
-                    desiredValue = target.ActualMinWidth;
-                }
-            }
-
-            double displayValue = width.DisplayValue;
-            if (double.IsNaN(displayValue))
-            {
-                displayValue = desiredValue;
-            }
-            displayValue = Math.Max(target.ActualMinWidth, Math.Min(target.ActualMaxWidth, displayValue));
-
-            return new DataGridLength(width.Value, width.UnitType, desiredValue, displayValue);
-        }
-
-        /// <summary>
-        /// If the DataGrid is using layout rounding, the pixel snapping will force all widths to
-        /// whole numbers. Since the column widths aren't visual elements, they don't go through the normal
-        /// rounding process, so we need to do it ourselves.  If we don't, then we'll end up with some
-        /// pixel gaps and/or overlaps between columns.
-        /// </summary>
-        /// <param name="leftEdge"></param>
-        internal void ComputeLayoutRoundedWidth(double leftEdge)
-        {
-            if (OwningGrid != null && OwningGrid.UseLayoutRounding)
-            {
-                var scale = LayoutHelper.GetLayoutScale(HeaderCell);
-                var roundSize = LayoutHelper.RoundLayoutSizeUp(new Size(leftEdge + ActualWidth, 1), scale, scale);
-                LayoutRoundedWidth = roundSize.Width - leftEdge;
-            }
-            else
-            {
-                LayoutRoundedWidth = ActualWidth;
-            }
-        }
-
-        internal virtual DataGridColumnHeader CreateHeader()
-        {
-            var result = new DataGridColumnHeader
-            {
-                OwningColumn = this
-            };
-            result[!ContentControl.ContentProperty] = this[!HeaderProperty];
-            result[!ContentControl.ContentTemplateProperty] = this[!HeaderTemplateProperty];
-            if (OwningGrid.ColumnHeaderTheme is { } columnTheme)
-            {
-                result.SetValue(StyledElement.ThemeProperty, columnTheme, BindingPriority.Template);
-            }
-
-            result.PointerPressed += (s, e) => { HeaderPointerPressed?.Invoke(this, e); };
-            result.PointerReleased += (s, e) => { HeaderPointerReleased?.Invoke(this, e); };
-            return result;
-        }
-
-        /// <summary>
-        /// Ensures that this column's width has been coerced to a valid value.
-        /// </summary>
-        internal void EnsureWidth()
-        {
-            SetWidthInternalNoCallback(CoerceWidth(this, Width));
-        }
-
-        internal Control GenerateElementInternal(DataGridCell cell, object dataItem)
-        {
-            return GenerateElement(cell, dataItem);
-        }
-
-        internal object PrepareCellForEditInternal(Control editingElement, RoutedEventArgs editingEventArgs)
-        {
-            var result = PrepareCellForEdit(editingElement, editingEventArgs);
-            editingElement.Focus();
-
-            return result;
-        }
-
-        /// <summary>
-        /// Attempts to resize the column's width to the desired DisplayValue, but limits the final size
-        /// to the column's minimum and maximum values.  If star sizing is being used, then the column
-        /// can only decrease in size by the amount that the columns after it can increase in size.
-        /// Likewise, the column can only increase in size if other columns can spare the width.
-        /// </summary>
-        /// <param name="oldWidth">with before resize.</param>
-        /// <param name="newWidth">with after resize.</param>
-        /// <param name="userInitiated">Whether or not this resize was initiated by a user action.</param>
-
-        //  double value, DataGridLengthUnitType unitType, double desiredValue, double displayValue
-        internal void Resize(DataGridLength oldWidth, DataGridLength newWidth, bool userInitiated)
-        {
-            double newValue = newWidth.Value;
-            double newDesiredValue = newWidth.DesiredValue;
-            double newDisplayValue = Math.Max(ActualMinWidth, Math.Min(ActualMaxWidth, newWidth.DisplayValue));
-            DataGridLengthUnitType newUnitType = newWidth.UnitType;
-
-            int starColumnsCount = 0;
-            double totalDisplayWidth = 0;
-            foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetVisibleColumns())
-            {
-                column.EnsureWidth();
-                totalDisplayWidth += column.ActualWidth;
-                starColumnsCount += (column != this && column.Width.IsStar) ? 1 : 0;
-            }
-            bool hasInfiniteAvailableWidth = !OwningGrid.RowsPresenterAvailableSize.HasValue || double.IsPositiveInfinity(OwningGrid.RowsPresenterAvailableSize.Value.Width);
-
-            // If we're using star sizing, we can only resize the column as much as the columns to the
-            // right will allow (i.e. until they hit their max or min widths).
-            if (!hasInfiniteAvailableWidth && (starColumnsCount > 0 || (newUnitType == DataGridLengthUnitType.Star && newWidth.IsStar && userInitiated)))
-            {
-                double limitedDisplayValue = oldWidth.DisplayValue;
-                double availableIncrease = Math.Max(0, OwningGrid.CellsWidth - totalDisplayWidth);
-                double desiredChange = newDisplayValue - oldWidth.DisplayValue;
-                if (desiredChange > availableIncrease)
-                {
-                    // The desired change is greater than the amount of available space,
-                    // so we need to decrease the widths of columns to the right to make room.
-                    desiredChange -= availableIncrease;
-                    double actualChange = desiredChange + OwningGrid.DecreaseColumnWidths(DisplayIndex + 1, -desiredChange, userInitiated);
-                    limitedDisplayValue += availableIncrease + actualChange;
-                }
-                else if (desiredChange > 0)
-                {
-                    // The desired change is positive but less than the amount of available space,
-                    // so there's no need to decrease the widths of columns to the right.
-                    limitedDisplayValue += desiredChange;
-                }
-                else
-                {
-                    // The desired change is negative, so we need to increase the widths of columns to the right.
-                    limitedDisplayValue += desiredChange + OwningGrid.IncreaseColumnWidths(DisplayIndex + 1, -desiredChange, userInitiated);
-                }
-                if (ActualCanUserResize || (oldWidth.IsStar && !userInitiated))
-                {
-                    newDisplayValue = limitedDisplayValue;
-                }
-            }
-
-            if (userInitiated)
-            {
-                newDesiredValue = newDisplayValue;
-                if (!Width.IsStar)
-                {
-                    InheritsWidth = false;
-                    newValue = newDisplayValue;
-                    newUnitType = DataGridLengthUnitType.Pixel;
-                }
-                else if (starColumnsCount > 0 && !hasInfiniteAvailableWidth)
-                {
-                    // Recalculate star weight of this column based on the new desired value
-                    InheritsWidth = false;
-                    newValue = Width.Value * newDisplayValue / ActualWidth;
-                }
-            }
-
-            newDisplayValue = Math.Min(double.MaxValue, newValue);
-            newWidth = new DataGridLength(newDisplayValue, newUnitType, newDesiredValue, newDisplayValue);
-            SetWidthInternalNoCallback(newWidth);
-            if (newWidth != oldWidth)
-            {
-                OwningGrid.OnColumnWidthChanged(this);
-            }
-        }
-
-        /// <summary>
-        /// Sets the column's Width to a new DataGridLength with a different DesiredValue.
-        /// </summary>
-        /// <param name="desiredValue">The new DesiredValue.</param>
-        internal void SetWidthDesiredValue(double desiredValue)
-        {
-            SetWidthInternalNoCallback(new DataGridLength(Width.Value, Width.UnitType, desiredValue, Width.DisplayValue));
-        }
-
-        /// <summary>
-        /// Sets the column's Width to a new DataGridLength with a different DisplayValue.
-        /// </summary>
-        /// <param name="displayValue">The new DisplayValue.</param>
-        internal void SetWidthDisplayValue(double displayValue)
-        {
-            SetWidthInternalNoCallback(new DataGridLength(Width.Value, Width.UnitType, Width.DesiredValue, displayValue));
-        }
-
-        /// <summary>
-        /// Set the column's Width without breaking inheritance.
-        /// </summary>
-        /// <param name="width">The new Width.</param>
-        internal void SetWidthInternal(DataGridLength width)
-        {
-            bool originalValue = _settingWidthInternally;
-            _settingWidthInternally = true;
-            try
-            {
-                SetCurrentValue(WidthProperty, width);
-            }
-            finally
-            {
-                _settingWidthInternally = originalValue;
-            }
-        }
-
-        /// <summary>
-        /// Sets the column's Width directly, without any callback effects.
-        /// </summary>
-        /// <param name="width">The new Width.</param>
-        internal void SetWidthInternalNoCallback(DataGridLength width)
-        {
-            var originalValue = _setWidthInternalNoCallback;
-            _setWidthInternalNoCallback = true;
-            try
-            {
-                SetCurrentValue(WidthProperty, width);
-            }
-            finally
-            {
-                _setWidthInternalNoCallback = originalValue;
-            }
-
-        }
-
-        /// <summary>
-        /// Set the column's star value.  Whenever the star value changes, width inheritance is broken.
-        /// </summary>
-        /// <param name="value">The new star value.</param>
-        internal void SetWidthStarValue(double value)
-        {
-            InheritsWidth = false;
-            SetWidthInternalNoCallback(new DataGridLength(value, Width.UnitType, Width.DesiredValue, Width.DisplayValue));
-        }
-
-        //TODO Binding
-        internal Control GenerateEditingElementInternal(DataGridCell cell, object dataItem)
-        {
-            if (_editingElement == null)
-            {
-                _editingElement = GenerateEditingElement(cell, dataItem, out _editBinding);
-            }
-
-            return _editingElement;
-        }
-
-        /// <summary>
-        /// Clears the cached editing element.
-        /// </summary>
-        //TODO Binding
-        internal void RemoveEditingElement()
-        {
-            _editingElement = null;
-        }
-
-        /// <summary>
-        /// Holds the name of the member to use for sorting, if not using the default.
-        /// </summary>
-        public string SortMemberPath
-        {
-            get;
-            set;
-        }
-
-        /// <summary>
-        /// Gets or sets an object associated with this column.
-        /// </summary>
-        public object Tag
-        {
-            get;
-            set;
-        }
-
-        /// <summary>
-        /// Holds a Comparer to use for sorting, if not using the default.
-        /// </summary>
-        public System.Collections.IComparer CustomSortComparer
-        {
-            get;
-            set;
-        }
-
-        /// <summary>
-        /// We get the sort description from the data source.  We don't worry whether we can modify sort -- perhaps the sort description
-        /// describes an unchangeable sort that exists on the data.
-        /// </summary>
-        internal DataGridSortDescription GetSortDescription()
-        {
-            if (OwningGrid != null
-                && OwningGrid.DataConnection != null
-                && OwningGrid.DataConnection.SortDescriptions != null)
-            {
-                if (CustomSortComparer != null)
-                {
-                    return
-                        OwningGrid.DataConnection.SortDescriptions
-                                  .OfType<DataGridComparerSortDescription>()
-                                  .FirstOrDefault(s => s.SourceComparer == CustomSortComparer);
-                }
-
-                string propertyName = GetSortPropertyName();
-
-                return OwningGrid.DataConnection.SortDescriptions.FirstOrDefault(s => s.HasPropertyPath && s.PropertyPath == propertyName);
-            }
-
-            return null;
-        }
-
-        internal string GetSortPropertyName()
-        {
-            string result = SortMemberPath;
-
-            if (String.IsNullOrEmpty(result))
-            {
-                if (this is DataGridBoundColumn boundColumn)
-                {
-                    if (boundColumn.Binding is Binding binding)
-                    {
-                        result = binding.Path;
-                    }
-                    else if (boundColumn.Binding is CompiledBindingExtension compiledBinding)
-                    {
-                        result = compiledBinding.Path.ToString();
-                    }
-                }
-            }
-
-            return result;
-        }
-
-    }
-
-}

+ 0 - 581
src/Avalonia.Controls.DataGrid/DataGridColumnCollection.cs

@@ -1,581 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System;
-using System.Collections.Generic;
-using System.Collections.ObjectModel;
-using System.Diagnostics;
-
-namespace Avalonia.Controls
-{
-    internal class DataGridColumnCollection : ObservableCollection<DataGridColumn>
-    {
-        private readonly DataGrid _owningGrid;
-
-        public DataGridColumnCollection(DataGrid owningGrid)
-        {
-            _owningGrid = owningGrid;
-            ItemsInternal = new List<DataGridColumn>();
-            FillerColumn = new DataGridFillerColumn(owningGrid);
-            RowGroupSpacerColumn = new DataGridFillerColumn(owningGrid);
-            DisplayIndexMap = new List<int>();
-        }
-
-        internal int AutogeneratedColumnCount
-        {
-            get;
-            set;
-        }
-
-        internal List<int> DisplayIndexMap
-        {
-            get;
-            set;
-        }
-
-        internal DataGridFillerColumn FillerColumn
-        {
-            get;
-            private set;
-        }
-
-        internal DataGridColumn FirstColumn
-        {
-            get
-            {
-                return GetFirstColumn(null /*isVisible*/, null /*isFrozen*/, null /*isReadOnly*/);
-            }
-        }
-
-        internal DataGridColumn FirstVisibleColumn
-        {
-            get
-            {
-                return GetFirstColumn(true /*isVisible*/, null /*isFrozen*/, null /*isReadOnly*/);
-            }
-        }
-
-        internal DataGridColumn FirstVisibleNonFillerColumn
-        {
-            get
-            {
-                DataGridColumn dataGridColumn = FirstVisibleColumn;
-                if (dataGridColumn == RowGroupSpacerColumn)
-                {
-                    dataGridColumn = GetNextVisibleColumn(dataGridColumn);
-                }
-                return dataGridColumn;
-            }
-        }
-
-        internal DataGridColumn FirstVisibleWritableColumn
-        {
-            get
-            {
-                return GetFirstColumn(true /*isVisible*/, null /*isFrozen*/, false /*isReadOnly*/);
-            }
-        }
-
-        internal DataGridColumn FirstVisibleScrollingColumn
-        {
-            get
-            {
-                return GetFirstColumn(true /*isVisible*/, false /*isFrozen*/, null /*isReadOnly*/);
-            }
-        }
-
-        internal List<DataGridColumn> ItemsInternal
-        {
-            get;
-            private set;
-        }
-
-        internal DataGridColumn LastVisibleColumn
-        {
-            get
-            {
-                return GetLastColumn(true /*isVisible*/, null /*isFrozen*/, null /*isReadOnly*/);
-            }
-        }
-
-        internal DataGridColumn LastVisibleScrollingColumn
-        {
-            get
-            {
-                return GetLastColumn(true /*isVisible*/, false /*isFrozen*/, null /*isReadOnly*/);
-            }
-        }
-
-        internal DataGridColumn LastVisibleWritableColumn
-        {
-            get
-            {
-                return GetLastColumn(true /*isVisible*/, null /*isFrozen*/, false /*isReadOnly*/);
-            }
-        }
-
-        internal DataGridFillerColumn RowGroupSpacerColumn
-        {
-            get;
-            private set;
-        }
-
-        internal int VisibleColumnCount
-        {
-            get;
-            private set;
-        }
-
-        internal double VisibleEdgedColumnsWidth
-        {
-            get;
-            private set;
-        }
-
-        /// <summary>
-        /// The number of star columns that are currently visible.
-        /// NOTE: Requires that EnsureVisibleEdgedColumnsWidth has been called.
-        /// </summary>
-        internal int VisibleStarColumnCount
-        {
-            get;
-            private set;
-        }
-
-        protected override void ClearItems()
-        {
-            try
-            {
-                _owningGrid.NoCurrentCellChangeCount++;
-                if (ItemsInternal.Count > 0)
-                {
-                    if (_owningGrid.InDisplayIndexAdjustments)
-                    {
-                        // We are within columns display indexes adjustments. We do not allow changing the column collection while adjusting display indexes.
-                        throw DataGridError.DataGrid.CannotChangeColumnCollectionWhileAdjustingDisplayIndexes();
-                    }
-
-                    _owningGrid.OnClearingColumns();
-                    for (int columnIndex = 0; columnIndex < ItemsInternal.Count; columnIndex++)
-                    {
-                        // Detach the column...
-                        ItemsInternal[columnIndex].OwningGrid = null;
-                    }
-                    ItemsInternal.Clear();
-                    DisplayIndexMap.Clear();
-                    AutogeneratedColumnCount = 0;
-                    _owningGrid.OnColumnCollectionChanged_PreNotification(false /*columnsGrew*/);
-                    base.ClearItems();
-                    VisibleEdgedColumnsWidth = 0;
-                    _owningGrid.OnColumnCollectionChanged_PostNotification(false /*columnsGrew*/);
-                }
-            }
-            finally
-            {
-                _owningGrid.NoCurrentCellChangeCount--;
-            }
-        }
-
-        protected override void InsertItem(int columnIndex, DataGridColumn dataGridColumn)
-        {
-            try
-            {
-                _owningGrid.NoCurrentCellChangeCount++;
-                if (_owningGrid.InDisplayIndexAdjustments)
-                {
-                    // We are within columns display indexes adjustments. We do not allow changing the column collection while adjusting display indexes.
-                    throw DataGridError.DataGrid.CannotChangeColumnCollectionWhileAdjustingDisplayIndexes();
-                }
-                if (dataGridColumn == null)
-                {
-                    throw new ArgumentNullException(nameof(dataGridColumn));
-                }
-
-                int columnIndexWithFiller = columnIndex;
-                if (dataGridColumn != RowGroupSpacerColumn && RowGroupSpacerColumn.IsRepresented)
-                {
-                    columnIndexWithFiller++;
-                }
-
-                // get the new current cell coordinates
-                DataGridCellCoordinates newCurrentCellCoordinates = _owningGrid.OnInsertingColumn(columnIndex, dataGridColumn);
-
-                // insert the column into our internal list
-                ItemsInternal.Insert(columnIndexWithFiller, dataGridColumn);
-                dataGridColumn.Index = columnIndexWithFiller;
-                dataGridColumn.OwningGrid = _owningGrid;
-                dataGridColumn.RemoveEditingElement();
-                if (dataGridColumn.IsVisible)
-                {
-                    VisibleEdgedColumnsWidth += dataGridColumn.ActualWidth;
-                }
-
-                // continue with the base insert
-                _owningGrid.OnInsertedColumn_PreNotification(dataGridColumn);
-                _owningGrid.OnColumnCollectionChanged_PreNotification(true /*columnsGrew*/);
-
-                if (dataGridColumn != RowGroupSpacerColumn)
-                {
-                    base.InsertItem(columnIndex, dataGridColumn);
-                }
-                _owningGrid.OnInsertedColumn_PostNotification(newCurrentCellCoordinates, dataGridColumn.DisplayIndex);
-                _owningGrid.OnColumnCollectionChanged_PostNotification(true /*columnsGrew*/);
-            }
-            finally
-            {
-                _owningGrid.NoCurrentCellChangeCount--;
-            }
-        }
-
-        protected override void RemoveItem(int columnIndex)
-        {
-            RemoveItemPrivate(columnIndex, false /*isSpacer*/);
-        }
-
-        protected override void SetItem(int columnIndex, DataGridColumn dataGridColumn)
-        {
-            RemoveItem(columnIndex);
-            InsertItem(columnIndex, dataGridColumn);
-        }
-
-        internal bool DisplayInOrder(int columnIndex1, int columnIndex2)
-        {
-            int displayIndex1 = ItemsInternal[columnIndex1].DisplayIndexWithFiller;
-            int displayIndex2 = ItemsInternal[columnIndex2].DisplayIndexWithFiller;
-            return displayIndex1 < displayIndex2;
-        }
-
-        internal bool EnsureRowGrouping(bool rowGrouping)
-        {
-            // The insert below could cause the first column to be added.  That causes a refresh 
-            // which re-enters method so instead of checking RowGroupSpacerColumn.IsRepresented, 
-            // we need to check to see if it's actually in our collection instead.
-            bool spacerRepresented = (ItemsInternal.Count > 0) && (ItemsInternal[0] == RowGroupSpacerColumn);
-            if (rowGrouping && !spacerRepresented)
-            {
-                Insert(0, RowGroupSpacerColumn);
-                RowGroupSpacerColumn.IsRepresented = true;
-                return true;
-            }
-            else if (!rowGrouping && spacerRepresented)
-            {
-                // We need to set IsRepresented to false before removing the RowGroupSpacerColumn
-                // otherwise, we'll remove the column after it
-                RowGroupSpacerColumn.IsRepresented = false;
-                RemoveItemPrivate(0, true /*isSpacer*/);
-                return true;
-            }
-            return false;
-        }
-
-        /// <summary>
-        /// In addition to ensuring that column widths are valid, method updates the values of
-        /// VisibleEdgedColumnsWidth and VisibleStarColumnCount.
-        /// </summary>
-        internal void EnsureVisibleEdgedColumnsWidth()
-        {
-            VisibleStarColumnCount = 0;
-            VisibleEdgedColumnsWidth = 0;
-            VisibleColumnCount = 0;
-
-            for (int columnIndex = 0; columnIndex < ItemsInternal.Count; columnIndex++)
-            {
-                var item = ItemsInternal[columnIndex];
-                if (item.IsVisible)
-                {
-                    VisibleColumnCount++;
-                    item.EnsureWidth();
-                    if (item.Width.IsStar)
-                    {
-                        VisibleStarColumnCount++;
-                    }
-                    VisibleEdgedColumnsWidth += item.ActualWidth;
-                }
-            }
-        }
-
-        internal DataGridColumn GetColumnAtDisplayIndex(int displayIndex)
-        {
-            if (displayIndex < 0 || displayIndex >= ItemsInternal.Count || displayIndex >= DisplayIndexMap.Count)
-            {
-                return null;
-            }
-            int columnIndex = DisplayIndexMap[displayIndex];
-            return ItemsInternal[columnIndex];
-        }
-
-        internal int GetColumnCount(bool isVisible, bool isFrozen, int fromColumnIndex, int toColumnIndex)
-        {
-            int columnCount = 0;
-            DataGridColumn dataGridColumn = ItemsInternal[fromColumnIndex];
-
-            while (dataGridColumn != ItemsInternal[toColumnIndex])
-            {
-                dataGridColumn = GetNextColumn(dataGridColumn, isVisible, isFrozen, null /*isReadOnly*/);
-                columnCount++;
-            }
-            return columnCount;
-        }
-
-        internal IEnumerable<DataGridColumn> GetDisplayedColumns()
-        {
-            foreach (int columnIndex in DisplayIndexMap)
-            {
-                yield return ItemsInternal[columnIndex];
-            }
-        }
-
-        /// <summary>
-        /// Returns an enumeration of all columns that meet the criteria of the filter predicate.
-        /// </summary>
-        /// <param name="filter">Criteria for inclusion.</param>
-        /// <returns>Columns that meet the criteria, in ascending DisplayIndex order.</returns>
-        internal IEnumerable<DataGridColumn> GetDisplayedColumns(Predicate<DataGridColumn> filter)
-        {
-            Debug.Assert(filter != null);
-            Debug.Assert(ItemsInternal.Count == DisplayIndexMap.Count);
-            foreach (int columnIndex in DisplayIndexMap)
-            {
-                DataGridColumn column = ItemsInternal[columnIndex];
-                if (filter(column))
-                {
-                    yield return column;
-                }
-            }
-        }
-
-        /// <summary>
-        /// Returns an enumeration of all columns that meet the criteria of the filter predicate.
-        /// The columns are returned in the order specified by the reverse flag.
-        /// </summary>
-        /// <param name="reverse">Whether or not to return the columns in descending DisplayIndex order.</param>
-        /// <param name="filter">Criteria for inclusion.</param>
-        /// <returns>Columns that meet the criteria, in the order specified by the reverse flag.</returns>
-        internal IEnumerable<DataGridColumn> GetDisplayedColumns(bool reverse, Predicate<DataGridColumn> filter)
-        {
-            return reverse ? GetDisplayedColumnsReverse(filter) : GetDisplayedColumns(filter);
-        }
-
-        /// <summary>
-        /// Returns an enumeration of all columns that meet the criteria of the filter predicate.
-        /// The columns are returned in descending DisplayIndex order.
-        /// </summary>
-        /// <param name="filter">Criteria for inclusion.</param>
-        /// <returns>Columns that meet the criteria, in descending DisplayIndex order.</returns>
-        internal IEnumerable<DataGridColumn> GetDisplayedColumnsReverse(Predicate<DataGridColumn> filter)
-        {
-            for (int displayIndex = DisplayIndexMap.Count - 1; displayIndex >= 0; displayIndex--)
-            {
-                DataGridColumn column = ItemsInternal[DisplayIndexMap[displayIndex]];
-                if (filter(column))
-                {
-                    yield return column;
-                }
-            }
-        }
-
-        internal DataGridColumn GetFirstColumn(bool? isVisible, bool? isFrozen, bool? isReadOnly)
-        {
-            Debug.Assert(ItemsInternal.Count == DisplayIndexMap.Count);
-            int index = 0;
-            while (index < DisplayIndexMap.Count)
-            {
-                DataGridColumn dataGridColumn = GetColumnAtDisplayIndex(index);
-                if ((isVisible == null || (dataGridColumn.IsVisible) == isVisible) &&
-                    (isFrozen == null || dataGridColumn.IsFrozen == isFrozen) &&
-                    (isReadOnly == null || dataGridColumn.IsReadOnly == isReadOnly))
-                {
-                    return dataGridColumn;
-                }
-                index++;
-            }
-            return null;
-        }
-
-        internal DataGridColumn GetLastColumn(bool? isVisible, bool? isFrozen, bool? isReadOnly)
-        {
-            Debug.Assert(ItemsInternal.Count == DisplayIndexMap.Count);
-            int index = DisplayIndexMap.Count - 1;
-            while (index >= 0)
-            {
-                DataGridColumn dataGridColumn = GetColumnAtDisplayIndex(index);
-                if ((isVisible == null || (dataGridColumn.IsVisible) == isVisible) &&
-                    (isFrozen == null || dataGridColumn.IsFrozen == isFrozen) &&
-                    (isReadOnly == null || dataGridColumn.IsReadOnly == isReadOnly))
-                {
-                    return dataGridColumn;
-                }
-                index--;
-            }
-            return null;
-        }
-
-        internal DataGridColumn GetNextColumn(DataGridColumn dataGridColumnStart)
-        {
-            return GetNextColumn(dataGridColumnStart, null /*isVisible*/, null /*isFrozen*/, null /*isReadOnly*/);
-        }
-
-        internal DataGridColumn GetNextColumn(DataGridColumn dataGridColumnStart,
-                                                  bool? isVisible, bool? isFrozen, bool? isReadOnly)
-        {
-            Debug.Assert(dataGridColumnStart != null);
-            Debug.Assert(ItemsInternal.Contains(dataGridColumnStart));
-            Debug.Assert(ItemsInternal.Count == DisplayIndexMap.Count);
-
-            int index = dataGridColumnStart.DisplayIndexWithFiller + 1;
-            while (index < DisplayIndexMap.Count)
-            {
-                DataGridColumn dataGridColumn = GetColumnAtDisplayIndex(index);
-
-                if ((isVisible == null || (dataGridColumn.IsVisible) == isVisible) &&
-                    (isFrozen == null || dataGridColumn.IsFrozen == isFrozen) &&
-                    (isReadOnly == null || dataGridColumn.IsReadOnly == isReadOnly))
-                {
-                    return dataGridColumn;
-                }
-                index++;
-            }
-            return null;
-        }
-
-        internal DataGridColumn GetNextVisibleColumn(DataGridColumn dataGridColumnStart)
-        {
-            return GetNextColumn(dataGridColumnStart, true /*isVisible*/, null /*isFrozen*/, null /*isReadOnly*/);
-        }
-
-        internal DataGridColumn GetNextVisibleFrozenColumn(DataGridColumn dataGridColumnStart)
-        {
-            return GetNextColumn(dataGridColumnStart, true /*isVisible*/, true /*isFrozen*/, null /*isReadOnly*/);
-        }
-
-        internal DataGridColumn GetNextVisibleWritableColumn(DataGridColumn dataGridColumnStart)
-        {
-            return GetNextColumn(dataGridColumnStart, true /*isVisible*/, null /*isFrozen*/, false /*isReadOnly*/);
-        }
-
-        internal DataGridColumn GetPreviousColumn(DataGridColumn dataGridColumnStart,
-                                                      bool? isVisible, bool? isFrozen, bool? isReadOnly)
-        {
-            int index = dataGridColumnStart.DisplayIndexWithFiller - 1;
-            while (index >= 0)
-            {
-                DataGridColumn dataGridColumn = GetColumnAtDisplayIndex(index);
-                if ((isVisible == null || (dataGridColumn.IsVisible) == isVisible) &&
-                    (isFrozen == null || dataGridColumn.IsFrozen == isFrozen) &&
-                    (isReadOnly == null || dataGridColumn.IsReadOnly == isReadOnly))
-                {
-                    return dataGridColumn;
-                }
-                index--;
-            }
-            return null;
-        }
-
-        internal DataGridColumn GetPreviousVisibleNonFillerColumn(DataGridColumn dataGridColumnStart)
-        {
-            DataGridColumn column = GetPreviousColumn(dataGridColumnStart, true /*isVisible*/, null /*isFrozen*/, null /*isReadOnly*/);
-            return (column is DataGridFillerColumn) ? null : column;
-        }
-
-        internal DataGridColumn GetPreviousVisibleScrollingColumn(DataGridColumn dataGridColumnStart)
-        {
-            return GetPreviousColumn(dataGridColumnStart, true /*isVisible*/, false /*isFrozen*/, null /*isReadOnly*/);
-        }
-
-        internal DataGridColumn GetPreviousVisibleWritableColumn(DataGridColumn dataGridColumnStart)
-        {
-            return GetPreviousColumn(dataGridColumnStart, true /*isVisible*/, null /*isFrozen*/, false /*isReadOnly*/);
-        }
-
-        internal int GetVisibleColumnCount(int fromColumnIndex, int toColumnIndex)
-        {
-            int columnCount = 0;
-            DataGridColumn dataGridColumn = ItemsInternal[fromColumnIndex];
-
-            while (dataGridColumn != ItemsInternal[toColumnIndex])
-            {
-                dataGridColumn = GetNextVisibleColumn(dataGridColumn);
-                columnCount++;
-            }
-            return columnCount;
-        }
-
-        internal IEnumerable<DataGridColumn> GetVisibleColumns()
-        {
-            Predicate<DataGridColumn> filter = column => column.IsVisible;
-            return GetDisplayedColumns(filter);
-        }
-
-        internal IEnumerable<DataGridColumn> GetVisibleFrozenColumns()
-        {
-            Predicate<DataGridColumn> filter = column => column.IsVisible && column.IsFrozen;
-            return GetDisplayedColumns(filter);
-        }
-
-        internal double GetVisibleFrozenEdgedColumnsWidth()
-        {
-            double visibleFrozenColumnsWidth = 0;
-            for (int columnIndex = 0; columnIndex < ItemsInternal.Count; columnIndex++)
-            {
-                if (ItemsInternal[columnIndex].IsVisible && ItemsInternal[columnIndex].IsFrozen)
-                {
-                    visibleFrozenColumnsWidth += ItemsInternal[columnIndex].ActualWidth;
-                }
-            }
-            return visibleFrozenColumnsWidth;
-        }
-
-        internal IEnumerable<DataGridColumn> GetVisibleScrollingColumns()
-        {
-            Predicate<DataGridColumn> filter = column => column.IsVisible && !column.IsFrozen;
-            return GetDisplayedColumns(filter);
-        }
-
-        private void RemoveItemPrivate(int columnIndex, bool isSpacer)
-        {
-            try
-            {
-                _owningGrid.NoCurrentCellChangeCount++;
-
-                if (_owningGrid.InDisplayIndexAdjustments)
-                {
-                    // We are within columns display indexes adjustments. We do not allow changing the column collection while adjusting display indexes.
-                    throw DataGridError.DataGrid.CannotChangeColumnCollectionWhileAdjustingDisplayIndexes();
-                }
-
-                int columnIndexWithFiller = columnIndex;
-                if (!isSpacer && RowGroupSpacerColumn.IsRepresented)
-                {
-                    columnIndexWithFiller++;
-                }
-
-                DataGridColumn dataGridColumn = ItemsInternal[columnIndexWithFiller];
-                DataGridCellCoordinates newCurrentCellCoordinates = _owningGrid.OnRemovingColumn(dataGridColumn);
-                ItemsInternal.RemoveAt(columnIndexWithFiller);
-                if (dataGridColumn.IsVisible)
-                {
-                    VisibleEdgedColumnsWidth -= dataGridColumn.ActualWidth;
-                }
-                dataGridColumn.OwningGrid = null;
-                dataGridColumn.RemoveEditingElement();
-
-                // continue with the base remove
-                _owningGrid.OnRemovedColumn_PreNotification(dataGridColumn);
-                _owningGrid.OnColumnCollectionChanged_PreNotification(false /*columnsGrew*/);
-                if (!isSpacer)
-                {
-                    base.RemoveItem(columnIndex);
-                }
-                _owningGrid.OnRemovedColumn_PostNotification(newCurrentCellCoordinates);
-                _owningGrid.OnColumnCollectionChanged_PostNotification(false /*columnsGrew*/);
-            }
-            finally
-            {
-                _owningGrid.NoCurrentCellChangeCount--;
-            }
-        }
-
-    }
-}

+ 0 - 878
src/Avalonia.Controls.DataGrid/DataGridColumnHeader.cs

@@ -1,878 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System;
-using System.ComponentModel;
-using System.Diagnostics;
-using Avalonia.Automation;
-using Avalonia.Automation.Peers;
-using Avalonia.Collections;
-using Avalonia.Controls.Automation.Peers;
-using Avalonia.Controls.Metadata;
-using Avalonia.Controls.Mixins;
-using Avalonia.Controls.Shapes;
-using Avalonia.Controls.Templates;
-using Avalonia.Controls.Utils;
-using Avalonia.Data;
-using Avalonia.Input;
-using Avalonia.Media;
-using Avalonia.Utilities;
-
-namespace Avalonia.Controls
-{
-    /// <summary>
-    /// Represents an individual <see cref="T:Avalonia.Controls.DataGrid" /> column header.
-    /// </summary>
-    [PseudoClasses(":dragIndicator", ":pressed", ":sortascending", ":sortdescending")]
-    public class DataGridColumnHeader : ContentControl
-    {
-        private enum DragMode
-        {
-            None = 0,
-            MouseDown = 1,
-            Drag = 2,
-            Resize = 3,
-            Reorder = 4
-        }
-
-        private const int DATAGRIDCOLUMNHEADER_resizeRegionWidth = 5;
-        private const int DATAGRIDCOLUMNHEADER_columnsDragTreshold = 5;
-
-        private bool _areHandlersSuspended;
-        private static DragMode _dragMode;
-        private static Point? _lastMousePositionHeaders;
-        private static Cursor _originalCursor;
-        private static double _originalHorizontalOffset;
-        private static double _originalWidth;
-        private bool _desiredSeparatorVisibility = true;
-        private static Point? _dragStart;
-        private static DataGridColumn _dragColumn;
-        private static double _frozenColumnsWidth;
-        private static Lazy<Cursor> _resizeCursor = new Lazy<Cursor>(() => new Cursor(StandardCursorType.SizeWestEast));
-
-        public static readonly StyledProperty<IBrush> SeparatorBrushProperty =
-            AvaloniaProperty.Register<DataGridColumnHeader, IBrush>(nameof(SeparatorBrush));
-
-        public IBrush SeparatorBrush
-        {
-            get { return GetValue(SeparatorBrushProperty); }
-            set { SetValue(SeparatorBrushProperty, value); }
-        }
-
-        public static readonly StyledProperty<bool> AreSeparatorsVisibleProperty =
-            AvaloniaProperty.Register<DataGridColumnHeader, bool>(
-                nameof(AreSeparatorsVisible),
-                defaultValue: true);
-
-        public bool AreSeparatorsVisible
-        {
-            get { return GetValue(AreSeparatorsVisibleProperty); }
-            set { SetValue(AreSeparatorsVisibleProperty, value); }
-        }
-
-        static DataGridColumnHeader()
-        {
-            AreSeparatorsVisibleProperty.Changed.AddClassHandler<DataGridColumnHeader>((x, e) => x.OnAreSeparatorsVisibleChanged(e));
-            PressedMixin.Attach<DataGridColumnHeader>();
-            IsTabStopProperty.OverrideDefaultValue<DataGridColumnHeader>(false);
-            AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<DataGridColumnHeader>(IsOffscreenBehavior.FromClip);
-        }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="T:Avalonia.Controls.Primitives.DataGridColumnHeader" /> class.
-        /// </summary>
-        //TODO Implement
-        public DataGridColumnHeader()
-        {
-            PointerPressed += DataGridColumnHeader_PointerPressed;
-            PointerReleased += DataGridColumnHeader_PointerReleased;
-            PointerMoved += DataGridColumnHeader_PointerMoved;
-            PointerEntered += DataGridColumnHeader_PointerEntered;
-            PointerExited += DataGridColumnHeader_PointerExited;
-        }
-
-        protected override AutomationPeer OnCreateAutomationPeer()
-        {
-            return new DataGridColumnHeaderAutomationPeer(this);
-        }
-
-        private void OnAreSeparatorsVisibleChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            if (!_areHandlersSuspended)
-            {
-                _desiredSeparatorVisibility = (bool)e.NewValue;
-                if (OwningGrid != null)
-                {
-                    UpdateSeparatorVisibility(OwningGrid.ColumnsInternal.LastVisibleColumn);
-                }
-                else
-                {
-                    UpdateSeparatorVisibility(null);
-                }
-            }
-        }
-
-        internal DataGridColumn OwningColumn
-        {
-            get;
-            set;
-        }
-        internal DataGrid OwningGrid => OwningColumn?.OwningGrid;
-
-        internal int ColumnIndex
-        {
-            get
-            {
-                if (OwningColumn == null)
-                {
-                    return -1;
-                }
-                return OwningColumn.Index;
-            }
-        }
-
-        internal ListSortDirection? CurrentSortingState
-        {
-            get;
-            private set;
-        }
-
-        private bool IsMouseOver
-        {
-            get;
-            set;
-        }
-
-        private bool IsPressed
-        {
-            get;
-            set;
-        }
-
-        private void SetValueNoCallback<T>(AvaloniaProperty<T> property, T value, BindingPriority priority = BindingPriority.LocalValue)
-        {
-            _areHandlersSuspended = true;
-            try
-            {
-                SetValue(property, value, priority);
-            }
-            finally
-            {
-                _areHandlersSuspended = false;
-            }
-        }
-
-        internal void UpdatePseudoClasses()
-        {
-            CurrentSortingState = null;
-            if (OwningGrid != null
-                && OwningGrid.DataConnection != null
-                && OwningGrid.DataConnection.AllowSort)
-            {
-                var sort = OwningColumn.GetSortDescription();
-                if (sort != null)
-                {
-                    CurrentSortingState = sort.Direction;
-                }
-            }
-
-            PseudoClasses.Set(":sortascending",
-                CurrentSortingState == ListSortDirection.Ascending);
-            PseudoClasses.Set(":sortdescending",
-                CurrentSortingState == ListSortDirection.Descending);
-        }
-
-        internal void UpdateSeparatorVisibility(DataGridColumn lastVisibleColumn)
-        {
-            bool newVisibility = _desiredSeparatorVisibility;
-
-            // Collapse separator for the last column if there is no filler column
-            if (OwningColumn != null &&
-                OwningGrid != null &&
-                _desiredSeparatorVisibility &&
-                OwningColumn == lastVisibleColumn &&
-                !OwningGrid.ColumnsInternal.FillerColumn.IsActive)
-            {
-                newVisibility = false;
-            }
-
-            // Update the public property if it has changed
-            if (AreSeparatorsVisible != newVisibility)
-            {
-                SetValueNoCallback(AreSeparatorsVisibleProperty, newVisibility);
-            }
-        }
-
-        public event EventHandler<KeyModifiers> LeftClick;
-
-        internal void OnMouseLeftButtonUp_Click(KeyModifiers keyModifiers, ref bool handled)
-        {
-            LeftClick?.Invoke(this, keyModifiers);
-
-            // completed a click without dragging, so we're sorting
-            InvokeProcessSort(keyModifiers);
-            handled = true;
-        }
-
-        internal void InvokeProcessSort(KeyModifiers keyModifiers, ListSortDirection? forcedDirection = null)
-        {
-            Debug.Assert(OwningGrid != null);
-            if (OwningGrid.WaitForLostFocus(() => InvokeProcessSort(keyModifiers, forcedDirection)))
-            {
-                return;
-            }
-            if (OwningGrid.CommitEdit(DataGridEditingUnit.Row, exitEditingMode: true))
-            {
-                Avalonia.Threading.Dispatcher.UIThread.Post(() => ProcessSort(keyModifiers, forcedDirection));
-            }
-        }
-
-        //TODO GroupSorting
-        internal void ProcessSort(KeyModifiers keyModifiers, ListSortDirection? forcedDirection = null)
-        {
-            // if we can sort:
-            //  - AllowUserToSortColumns and CanSort are true, and
-            //  - OwningColumn is bound
-            // then try to sort
-            if (OwningColumn != null
-                && OwningGrid != null
-                && OwningGrid.EditingRow == null
-                && OwningColumn != OwningGrid.ColumnsInternal.FillerColumn
-                && OwningGrid.CanUserSortColumns
-                && OwningColumn.CanUserSort)
-            {
-                var ea = new DataGridColumnEventArgs(OwningColumn);
-                OwningGrid.OnColumnSorting(ea);
-
-                if (!ea.Handled && OwningGrid.DataConnection.AllowSort && OwningGrid.DataConnection.SortDescriptions != null)
-                {
-                    // - DataConnection.AllowSort is true, and
-                    // - SortDescriptionsCollection exists, and
-                    // - the column's data type is comparable
-
-                    DataGrid owningGrid = OwningGrid;
-                    DataGridSortDescription newSort;
-
-                    KeyboardHelper.GetMetaKeyState(this, keyModifiers, out bool ctrl, out bool shift);
-
-                    DataGridSortDescription sort = OwningColumn.GetSortDescription();
-                    IDataGridCollectionView collectionView = owningGrid.DataConnection.CollectionView;
-                    Debug.Assert(collectionView != null);
-
-                    using (collectionView.DeferRefresh())
-                    {
-                        // if shift is held down, we multi-sort, therefore if it isn't, we'll clear the sorts beforehand
-                        if (!shift || owningGrid.DataConnection.SortDescriptions.Count == 0)
-                        {
-                            owningGrid.DataConnection.SortDescriptions.Clear();
-                        }
-
-                        // if ctrl is held down, we only clear the sort directions
-                        if (!ctrl)
-                        {
-                            if (sort != null)
-                            {
-                                if (forcedDirection == null || sort.Direction != forcedDirection)
-                                {
-                                    newSort = sort.SwitchSortDirection();
-                                }
-                                else
-                                {
-                                    newSort = sort;
-                                }
-
-                                // changing direction should not affect sort order, so we replace this column's
-                                // sort description instead of just adding it to the end of the collection
-                                int oldIndex = owningGrid.DataConnection.SortDescriptions.IndexOf(sort);
-                                if (oldIndex >= 0)
-                                {
-                                    owningGrid.DataConnection.SortDescriptions.Remove(sort);
-                                    owningGrid.DataConnection.SortDescriptions.Insert(oldIndex, newSort);
-                                }
-                                else
-                                {
-                                    owningGrid.DataConnection.SortDescriptions.Add(newSort);
-                                }
-                            }
-                            else if (OwningColumn.CustomSortComparer != null)
-                            {
-                                newSort = forcedDirection != null ?
-                                    DataGridSortDescription.FromComparer(OwningColumn.CustomSortComparer, forcedDirection.Value) :
-                                    DataGridSortDescription.FromComparer(OwningColumn.CustomSortComparer);
-
-
-                                owningGrid.DataConnection.SortDescriptions.Add(newSort);
-                            }
-                            else
-                            {
-                                string propertyName = OwningColumn.GetSortPropertyName();
-                                // no-opt if we couldn't find a property to sort on
-                                if (string.IsNullOrEmpty(propertyName))
-                                {
-                                    return;
-                                }
-
-                                newSort = DataGridSortDescription.FromPath(propertyName, culture: collectionView.Culture);
-                                if (forcedDirection != null && newSort.Direction != forcedDirection)
-                                {
-                                    newSort = newSort.SwitchSortDirection();
-                                }
-
-                                owningGrid.DataConnection.SortDescriptions.Add(newSort);
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        private bool CanReorderColumn(DataGridColumn column)
-        {
-            return OwningGrid.CanUserReorderColumns
-                && !(column is DataGridFillerColumn)
-                && (column.CanUserReorderInternal.HasValue && column.CanUserReorderInternal.Value || !column.CanUserReorderInternal.HasValue);
-        }
-
-        /// <summary>
-        /// Determines whether a column can be resized by dragging the border of its header.  If star sizing
-        /// is being used, there are special conditions that can prevent a column from being resized:
-        /// 1. The column is the last visible column.
-        /// 2. All columns are constrained by either their maximum or minimum values.
-        /// </summary>
-        /// <param name="column">Column to check.</param>
-        /// <returns>Whether or not the column can be resized by dragging its header.</returns>
-        private static bool CanResizeColumn(DataGridColumn column)
-        {
-            if (column.OwningGrid != null && column.OwningGrid.ColumnsInternal != null && column.OwningGrid.UsesStarSizing &&
-                (column.OwningGrid.ColumnsInternal.LastVisibleColumn == column || !MathUtilities.AreClose(column.OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth, column.OwningGrid.CellsWidth)))
-            {
-                return false;
-            }
-            return column.ActualCanUserResize;
-        }
-
-        private static bool TrySetResizeColumn(DataGridColumn column)
-        {
-            // If datagrid.CanUserResizeColumns == false, then the column can still override it
-            if (CanResizeColumn(column))
-            {
-                _dragColumn = column;
-
-                _dragMode = DragMode.Resize;
-
-                return true;
-            }
-            return false;
-        }
-
-        //TODO DragDrop
-
-        internal void OnMouseLeftButtonDown(ref bool handled, PointerEventArgs args, Point mousePosition)
-        {
-            IsPressed = true;
-
-            if (OwningGrid != null && OwningGrid.ColumnHeaders != null)
-            {
-                _dragMode = DragMode.MouseDown;
-                _frozenColumnsWidth = OwningGrid.ColumnsInternal.GetVisibleFrozenEdgedColumnsWidth();
-                _lastMousePositionHeaders = this.Translate(OwningGrid.ColumnHeaders, mousePosition);
-
-                double distanceFromLeft = mousePosition.X;
-                double distanceFromRight = Bounds.Width - distanceFromLeft;
-                DataGridColumn currentColumn = OwningColumn;
-                DataGridColumn previousColumn = null;
-                if (!(OwningColumn is DataGridFillerColumn))
-                {
-                    previousColumn = OwningGrid.ColumnsInternal.GetPreviousVisibleNonFillerColumn(currentColumn);
-                }
-
-                if (_dragMode == DragMode.MouseDown && _dragColumn == null && (distanceFromRight <= DATAGRIDCOLUMNHEADER_resizeRegionWidth))
-                {
-                    handled = TrySetResizeColumn(currentColumn);
-                }
-                else if (_dragMode == DragMode.MouseDown && _dragColumn == null && distanceFromLeft <= DATAGRIDCOLUMNHEADER_resizeRegionWidth && previousColumn != null)
-                {
-                    handled = TrySetResizeColumn(previousColumn);
-                }
-
-                if (_dragMode == DragMode.Resize && _dragColumn != null)
-                {
-                    _dragStart = _lastMousePositionHeaders;
-                    _originalWidth = _dragColumn.ActualWidth;
-                    _originalHorizontalOffset = OwningGrid.HorizontalOffset;
-
-                    handled = true;
-                }
-            }
-        }
-
-        //TODO DragEvents
-        //TODO MouseCapture
-        internal void OnMouseLeftButtonUp(ref bool handled, PointerEventArgs args, Point mousePosition, Point mousePositionHeaders)
-        {
-            IsPressed = false;
-
-            if (OwningGrid != null && OwningGrid.ColumnHeaders != null)
-            {
-                if (_dragMode == DragMode.MouseDown)
-                {
-                    OnMouseLeftButtonUp_Click(args.KeyModifiers, ref handled);
-                }
-                else if (_dragMode == DragMode.Reorder)
-                {
-                    // Find header we're hovering over
-                    int targetIndex = GetReorderingTargetDisplayIndex(mousePositionHeaders);
-
-                    if (((!OwningColumn.IsFrozen && targetIndex >= OwningGrid.FrozenColumnCount)
-                          || (OwningColumn.IsFrozen && targetIndex < OwningGrid.FrozenColumnCount)))
-                    {
-                        OwningColumn.DisplayIndex = targetIndex;
-
-                        DataGridColumnEventArgs ea = new DataGridColumnEventArgs(OwningColumn);
-                        OwningGrid.OnColumnReordered(ea);
-                    }
-                }
-
-                SetDragCursor(mousePosition);
-
-                // Variables that track drag mode states get reset in DataGridColumnHeader_LostMouseCapture
-                args.Pointer.Capture(null);
-                OnLostMouseCapture();
-                _dragMode = DragMode.None;
-                handled = true;
-            }
-        }
-
-        //TODO DragEvents
-        internal void OnMouseMove(PointerEventArgs args, Point mousePosition, Point mousePositionHeaders)
-        {
-            var handled = args.Handled;
-            if (handled || OwningGrid == null || OwningGrid.ColumnHeaders == null)
-            {
-                return;
-            }
-
-            Debug.Assert(OwningGrid.Parent is InputElement);
-
-            OnMouseMove_Resize(ref handled, mousePositionHeaders);
-
-            OnMouseMove_Reorder(ref handled, mousePosition, mousePositionHeaders);
-
-            SetDragCursor(mousePosition);
-        }
-
-        private void DataGridColumnHeader_PointerEntered(object sender, PointerEventArgs e)
-        {
-            if (!IsEnabled)
-            {
-                return;
-            }
-
-            Point mousePosition = e.GetPosition(this);
-            OnMouseEnter(mousePosition);
-            UpdatePseudoClasses();
-        }
-
-        private void DataGridColumnHeader_PointerExited(object sender, PointerEventArgs e)
-        {
-            if (!IsEnabled)
-            {
-                return;
-            }
-
-            OnMouseLeave();
-            UpdatePseudoClasses();
-        }
-
-        private void DataGridColumnHeader_PointerPressed(object sender, PointerPressedEventArgs e)
-        {
-            if (OwningColumn == null || e.Handled || !IsEnabled || !e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
-            {
-                return;
-            }
-
-            Point mousePosition = e.GetPosition(this);
-            bool handled = e.Handled;
-            OnMouseLeftButtonDown(ref handled, e, mousePosition);
-            e.Handled = handled;
-
-            UpdatePseudoClasses();
-        }
-
-        private void DataGridColumnHeader_PointerReleased(object sender, PointerReleasedEventArgs e)
-        {
-            if (OwningColumn == null || e.Handled || !IsEnabled || e.InitialPressMouseButton != MouseButton.Left)
-            {
-                return;
-            }
-
-            Point mousePosition = e.GetPosition(this);
-            Point mousePositionHeaders = e.GetPosition(OwningGrid.ColumnHeaders);
-            bool handled = e.Handled;
-            OnMouseLeftButtonUp(ref handled, e, mousePosition, mousePositionHeaders);
-            e.Handled = handled;
-
-            UpdatePseudoClasses();
-        }
-
-        private void DataGridColumnHeader_PointerMoved(object sender, PointerEventArgs e)
-        {
-            if (OwningGrid == null || !IsEnabled)
-            {
-                return;
-            }
-
-            Point mousePosition = e.GetPosition(this);
-            Point mousePositionHeaders = e.GetPosition(OwningGrid.ColumnHeaders);
-
-            OnMouseMove(e, mousePosition, mousePositionHeaders);
-        }
-
-        /// <summary>
-        /// Returns the column against whose top-left the reordering caret should be positioned
-        /// </summary>
-        /// <param name="mousePositionHeaders">Mouse position within the ColumnHeadersPresenter</param>
-        /// <param name="scroll">Whether or not to scroll horizontally when a column is dragged out of bounds</param>
-        /// <param name="scrollAmount">If scroll is true, returns the horizontal amount that was scrolled</param>
-        /// <returns></returns>
-        private DataGridColumn GetReorderingTargetColumn(Point mousePositionHeaders, bool scroll, out double scrollAmount)
-        {
-            scrollAmount = 0;
-            double leftEdge = OwningGrid.ColumnsInternal.RowGroupSpacerColumn.IsRepresented ? OwningGrid.ColumnsInternal.RowGroupSpacerColumn.ActualWidth : 0;
-            double rightEdge = OwningGrid.CellsWidth;
-            if (OwningColumn.IsFrozen)
-            {
-                rightEdge = Math.Min(rightEdge, _frozenColumnsWidth);
-            }
-            else if (OwningGrid.FrozenColumnCount > 0)
-            {
-                leftEdge = _frozenColumnsWidth;
-            }
-
-            if (mousePositionHeaders.X < leftEdge)
-            {
-                if (scroll &&
-                    OwningGrid.HorizontalScrollBar != null &&
-                    OwningGrid.HorizontalScrollBar.IsVisible &&
-                    OwningGrid.HorizontalScrollBar.Value > 0)
-                {
-                    double newVal = mousePositionHeaders.X - leftEdge;
-                    scrollAmount = Math.Min(newVal, OwningGrid.HorizontalScrollBar.Value);
-                    OwningGrid.UpdateHorizontalOffset(scrollAmount + OwningGrid.HorizontalScrollBar.Value);
-                }
-                mousePositionHeaders = mousePositionHeaders.WithX(leftEdge);
-            }
-            else if (mousePositionHeaders.X >= rightEdge)
-            {
-                if (scroll &&
-                    OwningGrid.HorizontalScrollBar != null &&
-                    OwningGrid.HorizontalScrollBar.IsVisible &&
-                    OwningGrid.HorizontalScrollBar.Value < OwningGrid.HorizontalScrollBar.Maximum)
-                {
-                    double newVal = mousePositionHeaders.X - rightEdge;
-                    scrollAmount = Math.Min(newVal, OwningGrid.HorizontalScrollBar.Maximum - OwningGrid.HorizontalScrollBar.Value);
-                    OwningGrid.UpdateHorizontalOffset(scrollAmount + OwningGrid.HorizontalScrollBar.Value);
-                }
-                mousePositionHeaders = mousePositionHeaders.WithX(rightEdge - 1);
-            }
-
-            foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetVisibleColumns())
-            {
-                Point mousePosition = OwningGrid.ColumnHeaders.Translate(column.HeaderCell, mousePositionHeaders);
-                double columnMiddle = column.HeaderCell.Bounds.Width / 2;
-                if (mousePosition.X >= 0 && mousePosition.X <= columnMiddle)
-                {
-                    return column;
-                }
-                else if (mousePosition.X > columnMiddle && mousePosition.X < column.HeaderCell.Bounds.Width)
-                {
-                    return OwningGrid.ColumnsInternal.GetNextVisibleColumn(column);
-                }
-            }
-
-            return null;
-        }
-
-        /// <summary>
-        /// Returns the display index to set the column to
-        /// </summary>
-        /// <param name="mousePositionHeaders">Mouse position relative to the column headers presenter</param>
-        /// <returns></returns>
-        private int GetReorderingTargetDisplayIndex(Point mousePositionHeaders)
-        {
-            DataGridColumn targetColumn = GetReorderingTargetColumn(mousePositionHeaders, false /*scroll*/, out double scrollAmount);
-            if (targetColumn != null)
-            {
-                return targetColumn.DisplayIndex > OwningColumn.DisplayIndex ? targetColumn.DisplayIndex - 1 : targetColumn.DisplayIndex;
-            }
-            else
-            {
-                return OwningGrid.Columns.Count - 1;
-            }
-        }
-
-        /// <summary>
-        /// Returns true if the mouse is
-        /// - to the left of the element, or within the left half of the element
-        /// and
-        /// - within the vertical range of the element, or ignoreVertical == true
-        /// </summary>
-        /// <param name="mousePosition"></param>
-        /// <param name="element"></param>
-        /// <param name="ignoreVertical"></param>
-        /// <returns></returns>
-        private bool IsReorderTargeted(Point mousePosition, Control element, bool ignoreVertical)
-        {
-            Point position = this.Translate(element, mousePosition);
-
-            return (position.X < 0 || (position.X >= 0 && position.X <= element.Bounds.Width / 2))
-                && (ignoreVertical || (position.Y >= 0 && position.Y <= element.Bounds.Height));
-        }
-
-        /// <summary>
-        /// Resets the static DataGridColumnHeader properties when a header loses mouse capture
-        /// </summary>
-        private void OnLostMouseCapture()
-        {
-            // When we stop interacting with the column headers, we need to reset the drag mode
-            // and close any popups if they are open.
-
-            if (_dragColumn != null && _dragColumn.HeaderCell != null)
-            {
-                _dragColumn.HeaderCell.Cursor = _originalCursor;
-            }
-            _dragMode = DragMode.None;
-            _dragColumn = null;
-            _dragStart = null;
-            _lastMousePositionHeaders = null;
-
-            if (OwningGrid != null && OwningGrid.ColumnHeaders != null)
-            {
-                OwningGrid.ColumnHeaders.DragColumn = null;
-                OwningGrid.ColumnHeaders.DragIndicator = null;
-                OwningGrid.ColumnHeaders.DropLocationIndicator = null;
-            }
-        }
-
-        /// <summary>
-        /// Sets up the DataGridColumnHeader for the MouseEnter event
-        /// </summary>
-        /// <param name="mousePosition">mouse position relative to the DataGridColumnHeader</param>
-        private void OnMouseEnter(Point mousePosition)
-        {
-            IsMouseOver = true;
-            SetDragCursor(mousePosition);
-        }
-
-        /// <summary>
-        /// Sets up the DataGridColumnHeader for the MouseLeave event
-        /// </summary>
-        private void OnMouseLeave()
-        {
-            IsMouseOver = false;
-        }
-
-        private void OnMouseMove_BeginReorder(Point mousePosition)
-        {
-            var dragIndicator = new DataGridColumnHeader
-            {
-                OwningColumn = OwningColumn,
-                IsEnabled = false,
-                Content = GetDragIndicatorContent(Content, ContentTemplate)
-            };
-            if (OwningGrid.ColumnHeaderTheme is { } columnHeaderTheme)
-            {
-                dragIndicator.SetValue(ThemeProperty, columnHeaderTheme, BindingPriority.Template);
-            }
-
-            dragIndicator.PseudoClasses.Add(":dragIndicator");
-
-            Control dropLocationIndicator = OwningGrid.DropLocationIndicatorTemplate?.Build();
-
-            // If the user didn't style the dropLocationIndicator's Height, default to the column header's height
-            if (dropLocationIndicator != null && double.IsNaN(dropLocationIndicator.Height) && dropLocationIndicator is Control element)
-            {
-                element.Height = Bounds.Height;
-            }
-
-            // pass the caret's data template to the user for modification
-            DataGridColumnReorderingEventArgs columnReorderingEventArgs = new DataGridColumnReorderingEventArgs(OwningColumn)
-            {
-                DropLocationIndicator = dropLocationIndicator,
-                DragIndicator = dragIndicator
-            };
-            OwningGrid.OnColumnReordering(columnReorderingEventArgs);
-            if (columnReorderingEventArgs.Cancel)
-            {
-                return;
-            }
-
-            // The user didn't cancel, so prepare for the reorder
-            _dragColumn = OwningColumn;
-            _dragMode = DragMode.Reorder;
-            _dragStart = mousePosition;
-
-            // Display the reordering thumb
-            OwningGrid.ColumnHeaders.DragColumn = OwningColumn;
-            OwningGrid.ColumnHeaders.DragIndicator = columnReorderingEventArgs.DragIndicator;
-            OwningGrid.ColumnHeaders.DropLocationIndicator = columnReorderingEventArgs.DropLocationIndicator;
-
-            // If the user didn't style the dragIndicator's Width, default it to the column header's width
-            if (double.IsNaN(dragIndicator.Width))
-            {
-                dragIndicator.Width = Bounds.Width;
-            }
-        }
-
-        private object GetDragIndicatorContent(object content, IDataTemplate? dataTemplate)
-        {
-            if (content is ContentControl icc)
-            {
-                content = icc.Content;
-            }
-
-            if (content is Control control)
-            {
-                if (VisualRoot == null) return content;
-                control.Measure(Size.Infinity);
-                var rect = new Rectangle()
-                {
-                    Width = control.DesiredSize.Width,
-                    Height = control.DesiredSize.Height,
-                    Fill = new VisualBrush
-                    {
-                        Visual = control, Stretch = Stretch.None, AlignmentX = AlignmentX.Left,
-                    }
-                };
-                return rect;
-            }
-
-            if (dataTemplate is not null)
-            {
-                return dataTemplate.Build(content);
-            }
-            return content;
-        }
-
-        //TODO DragEvents
-        private void OnMouseMove_Reorder(ref bool handled, Point mousePosition, Point mousePositionHeaders)
-        {
-            if (handled)
-            {
-                return;
-            }
-
-            //handle entry into reorder mode
-            if (_dragMode == DragMode.MouseDown && _dragColumn == null && _lastMousePositionHeaders != null)
-            {
-                var distanceFromInitial = (Vector)(mousePositionHeaders - _lastMousePositionHeaders);
-                if (distanceFromInitial.Length > DATAGRIDCOLUMNHEADER_columnsDragTreshold)
-                {
-                    handled = CanReorderColumn(OwningColumn);
-
-                    if (handled)
-                    {
-                        OnMouseMove_BeginReorder(mousePosition);
-                    }
-                }
-            }
-
-            //handle reorder mode (eg, positioning of the popup)
-            if (_dragMode == DragMode.Reorder && OwningGrid.ColumnHeaders.DragIndicator != null)
-            {
-                // Find header we're hovering over
-                DataGridColumn targetColumn = GetReorderingTargetColumn(mousePositionHeaders, !OwningColumn.IsFrozen /*scroll*/, out double scrollAmount);
-
-                OwningGrid.ColumnHeaders.DragIndicatorOffset = mousePosition.X - _dragStart.Value.X + scrollAmount;
-                OwningGrid.ColumnHeaders.InvalidateArrange();
-
-                if (OwningGrid.ColumnHeaders.DropLocationIndicator != null)
-                {
-                    Point targetPosition = new Point(0, 0);
-                    if (targetColumn == null || targetColumn == OwningGrid.ColumnsInternal.FillerColumn || targetColumn.IsFrozen != OwningColumn.IsFrozen)
-                    {
-                        targetColumn =
-                            OwningGrid.ColumnsInternal.GetLastColumn(
-                                isVisible: true,
-                                isFrozen: OwningColumn.IsFrozen,
-                                isReadOnly: null);
-                        targetPosition = targetColumn.HeaderCell.Translate(OwningGrid.ColumnHeaders, targetPosition);
-
-                        targetPosition = targetPosition.WithX(targetPosition.X + targetColumn.ActualWidth);
-                    }
-                    else
-                    {
-                        targetPosition = targetColumn.HeaderCell.Translate(OwningGrid.ColumnHeaders, targetPosition);
-                    }
-                    OwningGrid.ColumnHeaders.DropLocationIndicatorOffset = targetPosition.X - scrollAmount;
-                }
-
-                handled = true;
-            }
-        }
-
-        private void OnMouseMove_Resize(ref bool handled, Point mousePositionHeaders)
-        {
-            if (handled)
-            {
-                return;
-            }
-
-            if (_dragMode == DragMode.Resize && _dragColumn != null && _dragStart.HasValue)
-            {
-                // resize column
-
-                double mouseDelta = mousePositionHeaders.X - _dragStart.Value.X;
-                double desiredWidth = _originalWidth + mouseDelta;
-
-                desiredWidth = Math.Max(_dragColumn.ActualMinWidth, Math.Min(_dragColumn.ActualMaxWidth, desiredWidth));
-                _dragColumn.Resize(_dragColumn.Width,
-                    new(_dragColumn.Width.Value, _dragColumn.Width.UnitType, _dragColumn.Width.DesiredValue, desiredWidth),
-                    true);
-
-                OwningGrid.UpdateHorizontalOffset(_originalHorizontalOffset);
-
-                handled = true;
-            }
-        }
-
-        private void SetDragCursor(Point mousePosition)
-        {
-            if (_dragMode != DragMode.None || OwningGrid == null || OwningColumn == null)
-            {
-                return;
-            }
-
-            // set mouse if we can resize column
-
-            double distanceFromLeft = mousePosition.X;
-            double distanceFromRight = Bounds.Width - distanceFromLeft;
-            DataGridColumn currentColumn = OwningColumn;
-            DataGridColumn previousColumn = null;
-
-            if (!(OwningColumn is DataGridFillerColumn))
-            {
-                previousColumn = OwningGrid.ColumnsInternal.GetPreviousVisibleNonFillerColumn(currentColumn);
-            }
-
-            if ((distanceFromRight <= DATAGRIDCOLUMNHEADER_resizeRegionWidth && currentColumn != null && CanResizeColumn(currentColumn)) ||
-                (distanceFromLeft <= DATAGRIDCOLUMNHEADER_resizeRegionWidth && previousColumn != null && CanResizeColumn(previousColumn)))
-            {
-                var resizeCursor = _resizeCursor.Value;
-                if (Cursor != resizeCursor)
-                {
-                    _originalCursor = Cursor;
-                    Cursor = resizeCursor;
-                }
-            }
-            else
-            {
-                Cursor = _originalCursor;
-            }
-        }
-
-    }
-
-}

+ 0 - 1790
src/Avalonia.Controls.DataGrid/DataGridColumns.cs

@@ -1,1790 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Controls.Utils;
-using Avalonia.Data;
-using Avalonia.Markup.Xaml.MarkupExtensions;
-using Avalonia.Utilities;
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.Diagnostics;
-using System.Reflection;
-using Avalonia.Layout;
-
-namespace Avalonia.Controls
-{
-    public partial class DataGrid
-    {
-
-        protected virtual void OnColumnDisplayIndexChanged(DataGridColumnEventArgs e)
-        {
-            ColumnDisplayIndexChanged?.Invoke(this, e);
-        }
-
-        protected internal virtual void OnColumnReordered(DataGridColumnEventArgs e)
-        {
-            EnsureVerticalGridLines();
-            ColumnReordered?.Invoke(this, e);
-        }
-
-        protected internal virtual void OnColumnReordering(DataGridColumnReorderingEventArgs e)
-        {
-            ColumnReordering?.Invoke(this, e);
-        }
-
-        protected internal virtual void OnColumnSorting(DataGridColumnEventArgs e)
-        {
-            Sorting?.Invoke(this, e);
-        }
-
-        /// <summary>
-        /// Adjusts the widths of all columns with DisplayIndex >= displayIndex such that the total
-        /// width is adjusted by the given amount, if possible.  If the total desired adjustment amount
-        /// could not be met, the remaining amount of adjustment is returned.
-        /// </summary>
-        /// <param name="displayIndex">Starting column DisplayIndex.</param>
-        /// <param name="amount">Adjustment amount (positive for increase, negative for decrease).</param>
-        /// <param name="userInitiated">Whether or not this adjustment was initiated by a user action.</param>
-        /// <returns>The remaining amount of adjustment.</returns>
-        internal double AdjustColumnWidths(int displayIndex, double amount, bool userInitiated)
-        {
-            if (!MathUtilities.IsZero(amount))
-            {
-                if (amount < 0)
-                {
-                    amount = DecreaseColumnWidths(displayIndex, amount, userInitiated);
-                }
-                else
-                {
-                    amount = IncreaseColumnWidths(displayIndex, amount, userInitiated);
-                }
-            }
-            return amount;
-        }
-
-        /// <summary>
-        /// Grows an auto-column's width to the desired width.
-        /// </summary>
-        /// <param name="column">Auto-column to adjust.</param>
-        /// <param name="desiredWidth">The new desired width of the column.</param>
-        internal void AutoSizeColumn(DataGridColumn column, double desiredWidth)
-        {
-            Debug.Assert(column.Width.IsAuto || column.Width.IsSizeToCells || column.Width.IsSizeToHeader || (!UsesStarSizing && column.Width.IsStar));
-
-            // If we're using star sizing and this is the first time we've measured this particular auto-column,
-            // we want to allow all rows to get measured before we setup the star widths.  We won't know the final
-            // desired value of the column until all rows have been measured.  Because of this, we wait until
-            // an Arrange occurs before we adjust star widths.
-            if (UsesStarSizing && !column.IsInitialDesiredWidthDetermined)
-            {
-                AutoSizingColumns = true;
-            }
-
-            // Update the column's DesiredValue if it needs to grow to fit the new desired value
-            if (desiredWidth > column.Width.DesiredValue || double.IsNaN(column.Width.DesiredValue))
-            {
-                // If this auto-growth occurs after the column's initial desired width has been determined,
-                // then the growth should act like a resize (squish columns to the right).  Otherwise, if
-                // this column is newly added, we'll just set its display value directly.
-                if (UsesStarSizing && column.IsInitialDesiredWidthDetermined)
-                {
-                    var oldWidth = column.Width;
-                    column.Resize(oldWidth,
-                        new(column.Width.Value, column.Width.UnitType, desiredWidth, desiredWidth),
-                        false);
-                }
-                else
-                {
-                    column.SetWidthInternalNoCallback(new DataGridLength(column.Width.Value, column.Width.UnitType, desiredWidth, desiredWidth));
-                    OnColumnWidthChanged(column);
-                }
-            }
-        }
-
-        internal bool ColumnRequiresRightGridLine(DataGridColumn dataGridColumn, bool includeLastRightGridLineWhenPresent)
-        {
-            return (GridLinesVisibility == DataGridGridLinesVisibility.Vertical || GridLinesVisibility == DataGridGridLinesVisibility.All) && VerticalGridLinesBrush != null &&
-                   (dataGridColumn != ColumnsInternal.LastVisibleColumn || (includeLastRightGridLineWhenPresent && ColumnsInternal.FillerColumn.IsActive));
-        }
-
-        internal DataGridColumnCollection CreateColumnsInstance()
-        {
-            return new DataGridColumnCollection(this);
-        }
-
-        /// <summary>
-        /// Decreases the widths of all columns with DisplayIndex >= displayIndex such that the total
-        /// width is decreased by the given amount, if possible.  If the total desired adjustment amount
-        /// could not be met, the remaining amount of adjustment is returned.
-        /// </summary>
-        /// <param name="displayIndex">Starting column DisplayIndex.</param>
-        /// <param name="amount">Amount to decrease (in pixels).</param>
-        /// <param name="userInitiated">Whether or not this adjustment was initiated by a user action.</param>
-        /// <returns>The remaining amount of adjustment.</returns>
-        internal double DecreaseColumnWidths(int displayIndex, double amount, bool userInitiated)
-        {
-            // 1. Take space from non-star columns with widths larger than desired widths (left to right).
-            amount = DecreaseNonStarColumnWidths(displayIndex, c => c.Width.DesiredValue, amount, false, false);
-
-            // 2. Take space from star columns until they reach their min.
-            amount = AdjustStarColumnWidths(displayIndex, amount, userInitiated);
-
-            // 3. Take space from non-star columns that have already been initialized, until they reach their min (right to left).
-            amount = DecreaseNonStarColumnWidths(displayIndex, c => c.ActualMinWidth, amount, true, false);
-
-            // 4. Take space from all non-star columns until they reach their min, even if they are new (right to left).
-            amount = DecreaseNonStarColumnWidths(displayIndex, c => c.ActualMinWidth, amount, true, true);
-
-            return amount;
-        }
-
-        internal bool GetColumnReadOnlyState(DataGridColumn dataGridColumn, bool isReadOnly)
-        {
-            Debug.Assert(dataGridColumn != null);
-
-            if (dataGridColumn is DataGridBoundColumn dataGridBoundColumn &&
-                dataGridBoundColumn.Binding is BindingBase binding)
-            {
-                var path = (binding as Binding)?.Path ?? (binding as CompiledBindingExtension)?.Path.ToString();
-
-                if (string.IsNullOrWhiteSpace(path))
-                {
-                    return true;
-                }
-                else
-                {
-                    return DataConnection.GetPropertyIsReadOnly(path) || isReadOnly;
-                }
-            }
-
-            return isReadOnly;
-        }
-
-        // Returns the column's width
-        internal static double GetEdgedColumnWidth(DataGridColumn dataGridColumn)
-        {
-            Debug.Assert(dataGridColumn != null);
-            return dataGridColumn.ActualWidth;
-        }
-
-        /// <summary>
-        /// Increases the widths of all columns with DisplayIndex >= displayIndex such that the total
-        /// width is increased by the given amount, if possible.  If the total desired adjustment amount
-        /// could not be met, the remaining amount of adjustment is returned.
-        /// </summary>
-        /// <param name="displayIndex">Starting column DisplayIndex.</param>
-        /// <param name="amount">Amount of increase (in pixels).</param>
-        /// <param name="userInitiated">Whether or not this adjustment was initiated by a user action.</param>
-        /// <returns>The remaining amount of adjustment.</returns>
-        internal double IncreaseColumnWidths(int displayIndex, double amount, bool userInitiated)
-        {
-            // 1. Give space to non-star columns that are smaller than their desired widths (left to right).
-            amount = IncreaseNonStarColumnWidths(displayIndex, c => c.Width.DesiredValue, amount, false, false);
-
-            // 2. Give space to star columns until they reach their max.
-            amount = AdjustStarColumnWidths(displayIndex, amount, userInitiated);
-
-            // 3. Give space to non-star columns that have already been initialized, until they reach their max (right to left).
-            amount = IncreaseNonStarColumnWidths(displayIndex, c => c.ActualMaxWidth, amount, true, false);
-
-            // 4. Give space to all non-star columns until they reach their max, even if they are new (right to left).
-            amount = IncreaseNonStarColumnWidths(displayIndex, c => c.ActualMaxWidth, amount, true, false);
-
-            return amount;
-        }
-
-        internal void OnClearingColumns()
-        {
-            // Rows need to be cleared first. There cannot be rows without also having columns.
-            ClearRows(false);
-
-            // Removing all the column header cells
-            RemoveDisplayedColumnHeaders();
-
-            _horizontalOffset = _negHorizontalOffset = 0;
-            if (_hScrollBar != null && _hScrollBar.IsVisible) // 
-            {
-                _hScrollBar.Value = 0;
-            }
-        }
-
-        /// <summary>
-        /// Invalidates the widths of all columns because the resizing behavior of an individual column has changed.
-        /// </summary>
-        /// <param name="column">Column with CanUserResize property that has changed.</param>
-        internal void OnColumnCanUserResizeChanged(DataGridColumn column)
-        {
-            if (column.IsVisible)
-            {
-                EnsureHorizontalLayout();
-            }
-        }
-
-        internal void OnColumnCollectionChanged_PostNotification(bool columnsGrew)
-        {
-            if (columnsGrew &&
-                CurrentColumnIndex == -1)
-            {
-                MakeFirstDisplayedCellCurrentCell();
-            }
-
-            if (_autoGeneratingColumnOperationCount == 0)
-            {
-                EnsureRowsPresenterVisibility();
-                InvalidateRowHeightEstimate();
-            }
-        }
-
-        internal void OnColumnCollectionChanged_PreNotification(bool columnsGrew)
-        {
-            // dataGridColumn==null means the collection was refreshed.
-
-            if (columnsGrew && _autoGeneratingColumnOperationCount == 0 && ColumnsItemsInternal.Count == 1)
-            {
-                RefreshRows(false /*recycleRows*/, true /*clearRows*/);
-            }
-            else
-            {
-                InvalidateMeasure();
-            }
-        }
-
-        internal void OnColumnDisplayIndexChanged(DataGridColumn dataGridColumn)
-        {
-            Debug.Assert(dataGridColumn != null);
-            DataGridColumnEventArgs e = new DataGridColumnEventArgs(dataGridColumn);
-
-            // Call protected method to raise event
-            if (dataGridColumn != ColumnsInternal.RowGroupSpacerColumn)
-            {
-                OnColumnDisplayIndexChanged(e);
-            }
-        }
-
-        internal void OnColumnDisplayIndexChanged_PostNotification()
-        {
-            // Notifications for adjusted display indexes.
-            FlushDisplayIndexChanged(true /*raiseEvent*/);
-
-            // Our displayed columns may have changed so recompute them
-            UpdateDisplayedColumns();
-
-            // Invalidate layout
-            CorrectColumnFrozenStates();
-            EnsureHorizontalLayout();
-        }
-
-        internal void OnColumnDisplayIndexChanging(DataGridColumn targetColumn, int newDisplayIndex)
-        {
-            Debug.Assert(targetColumn != null);
-            Debug.Assert(newDisplayIndex != targetColumn.DisplayIndexWithFiller);
-
-            if (InDisplayIndexAdjustments)
-            {
-                // We are within columns display indexes adjustments. We do not allow changing display indexes while adjusting them.
-                throw DataGridError.DataGrid.CannotChangeColumnCollectionWhileAdjustingDisplayIndexes();
-            }
-
-            try
-            {
-                InDisplayIndexAdjustments = true;
-
-                bool trackChange = targetColumn != ColumnsInternal.RowGroupSpacerColumn;
-
-                DataGridColumn column;
-                // Move is legal - let's adjust the affected display indexes.
-                if (newDisplayIndex < targetColumn.DisplayIndexWithFiller)
-                {
-                    // DisplayIndex decreases. All columns with newDisplayIndex <= DisplayIndex < targetColumn.DisplayIndex
-                    // get their DisplayIndex incremented.
-                    for (int i = newDisplayIndex; i < targetColumn.DisplayIndexWithFiller; i++)
-                    {
-                        column = ColumnsInternal.GetColumnAtDisplayIndex(i);
-                        column.DisplayIndexWithFiller = column.DisplayIndexWithFiller + 1;
-                        if (trackChange)
-                        {
-                            column.DisplayIndexHasChanged = true; // OnColumnDisplayIndexChanged needs to be raised later on
-                        }
-                    }
-                }
-                else
-                {
-                    // DisplayIndex increases. All columns with targetColumn.DisplayIndex < DisplayIndex <= newDisplayIndex
-                    // get their DisplayIndex decremented.
-                    for (int i = newDisplayIndex; i > targetColumn.DisplayIndexWithFiller; i--)
-                    {
-                        column = ColumnsInternal.GetColumnAtDisplayIndex(i);
-                        column.DisplayIndexWithFiller = column.DisplayIndexWithFiller - 1;
-                        if (trackChange)
-                        {
-                            column.DisplayIndexHasChanged = true; // OnColumnDisplayIndexChanged needs to be raised later on
-                        }
-                    }
-                }
-                // Now let's actually change the order of the DisplayIndexMap
-                if (targetColumn.DisplayIndexWithFiller != -1)
-                {
-                    ColumnsInternal.DisplayIndexMap.Remove(targetColumn.Index);
-                }
-                ColumnsInternal.DisplayIndexMap.Insert(newDisplayIndex, targetColumn.Index);
-            }
-            finally
-            {
-                InDisplayIndexAdjustments = false;
-            }
-
-            // Note that displayIndex of moved column is updated by caller.
-        }
-
-        internal void OnColumnBindingChanged(DataGridBoundColumn column)
-        {
-            // Update Binding in Displayed rows by regenerating the affected elements
-            if (_rowsPresenter != null)
-            {
-                foreach (DataGridRow row in GetAllRows())
-                {
-                    PopulateCellContent(false /*isCellEdited*/, column, row, row.Cells[column.Index]);
-                }
-            }
-        }
-
-        /// <summary>
-        /// Adjusts the specified column's width according to its new maximum value.
-        /// </summary>
-        /// <param name="column">The column to adjust.</param>
-        /// <param name="oldValue">The old ActualMaxWidth of the column.</param>
-        internal void OnColumnMaxWidthChanged(DataGridColumn column, double oldValue)
-        {
-            Debug.Assert(column != null);
-
-            if (column.IsVisible && oldValue != column.ActualMaxWidth)
-            {
-                DataGridLength oldWitdh = new(oldValue, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue);
-                if (column.ActualMaxWidth < column.Width.DisplayValue)
-                {
-                    // If the maximum width has caused the column to decrease in size, try first to resize
-                    // the columns to the right to make up for the difference in width, but don't limit the column's
-                    // final display value to how much they could be resized.
-                    AdjustColumnWidths(column.DisplayIndex + 1, column.Width.DisplayValue - column.ActualMaxWidth, false);
-                    column.SetWidthDisplayValue(column.ActualMaxWidth);
-                }
-                else if (column.Width.DisplayValue == oldValue && column.Width.DesiredValue > column.Width.DisplayValue)
-                {
-                    // If the column was previously limited by its maximum value but has more room now, 
-                    // attempt to resize the column to its desired width.
-                    column.Resize(oldWitdh,
-                        new (column.Width.Value, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue),
-                        false);
-                }
-                OnColumnWidthChanged(column);
-            }
-        }
-
-        /// <summary>
-        /// Adjusts the specified column's width according to its new minimum value.
-        /// </summary>
-        /// <param name="column">The column to adjust.</param>
-        /// <param name="oldValue">The old ActualMinWidth of the column.</param>
-        internal void OnColumnMinWidthChanged(DataGridColumn column, double oldValue)
-        {
-            Debug.Assert(column != null);
-
-            if (column.IsVisible && oldValue != column.ActualMinWidth)
-            {
-                DataGridLength oldWitdh = new(oldValue, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue);
-                if (column.ActualMinWidth > column.Width.DisplayValue)
-                {
-                    // If the minimum width has caused the column to increase in size, try first to resize
-                    // the columns to the right to make up for the difference in width, but don't limit the column's
-                    // final display value to how much they could be resized.
-                    AdjustColumnWidths(column.DisplayIndex + 1, column.Width.DisplayValue - column.ActualMinWidth, false);
-                    column.SetWidthDisplayValue(column.ActualMinWidth);
-                }
-                else if (column.Width.DisplayValue == oldValue && column.Width.DesiredValue < column.Width.DisplayValue)
-                {
-                    // If the column was previously limited by its minimum value but but can be smaller now, 
-                    // attempt to resize the column to its desired width.
-                    column.Resize(oldWitdh,
-                       new(column.Width.Value, column.Width.UnitType, column.Width.DesiredValue, column.Width.DesiredValue),
-                       false);
-                }
-                OnColumnWidthChanged(column);
-            }
-        }
-
-        internal void OnColumnReadOnlyStateChanging(DataGridColumn dataGridColumn, bool isReadOnly)
-        {
-            Debug.Assert(dataGridColumn != null);
-            if (isReadOnly && CurrentColumnIndex == dataGridColumn.Index)
-            {
-                // Edited column becomes read-only. Exit editing mode.
-                if (!EndCellEdit(DataGridEditAction.Commit, true /*exitEditingMode*/, ContainsFocus /*keepFocus*/, true /*raiseEvents*/))
-                {
-                    EndCellEdit(DataGridEditAction.Cancel, true /*exitEditingMode*/, ContainsFocus /*keepFocus*/, false /*raiseEvents*/);
-                }
-            }
-        }
-
-        internal void OnColumnVisibleStateChanged(DataGridColumn updatedColumn)
-        {
-            Debug.Assert(updatedColumn != null);
-
-            CorrectColumnFrozenStates();
-            UpdateDisplayedColumns();
-            EnsureRowsPresenterVisibility();
-            EnsureHorizontalLayout();
-            InvalidateColumnHeadersMeasure();
-
-            if (updatedColumn.IsVisible &&
-                ColumnsInternal.VisibleColumnCount == 1 && CurrentColumnIndex == -1)
-            {
-                Debug.Assert(SelectedIndex == DataConnection.IndexOf(SelectedItem));
-                if (SelectedIndex != -1)
-                {
-                    SetAndSelectCurrentCell(updatedColumn.Index, SelectedIndex, true /*forceCurrentCellSelection*/);
-                }
-                else
-                {
-                    MakeFirstDisplayedCellCurrentCell();
-                }
-            }
-
-            // We need to explicitly collapse the cells of the invisible column because layout only goes through
-            // visible ones
-            ColumnHeaders?.InvalidateChildIndex();
-            foreach (var row in GetAllRows())
-            {
-                row.Cells[updatedColumn.Index].IsVisible = updatedColumn.IsVisible;
-                row.InvalidateCellsIndex();
-            }
-        }
-
-        internal void OnColumnVisibleStateChanging(DataGridColumn targetColumn)
-        {
-            Debug.Assert(targetColumn != null);
-
-            if (targetColumn.IsVisible &&
-                CurrentColumn == targetColumn)
-            {
-                // Column of the current cell is made invisible. Trying to move the current cell to a neighbor column. May throw an exception.
-                DataGridColumn dataGridColumn = ColumnsInternal.GetNextVisibleColumn(targetColumn);
-                if (dataGridColumn == null)
-                {
-                    dataGridColumn = ColumnsInternal.GetPreviousVisibleNonFillerColumn(targetColumn);
-                }
-                if (dataGridColumn == null)
-                {
-                    SetCurrentCellCore(-1, -1);
-                }
-                else
-                {
-                    SetCurrentCellCore(dataGridColumn.Index, CurrentSlot);
-                }
-            }
-        }
-
-        internal void OnColumnWidthChanged(DataGridColumn updatedColumn)
-        {
-            Debug.Assert(updatedColumn != null);
-            if (updatedColumn.IsVisible)
-            {
-                EnsureHorizontalLayout();
-            }
-        }
-
-        internal void OnFillerColumnWidthNeeded(double finalWidth)
-        {
-            DataGridFillerColumn fillerColumn = ColumnsInternal.FillerColumn;
-            double totalColumnsWidth = ColumnsInternal.VisibleEdgedColumnsWidth;
-            if (finalWidth - totalColumnsWidth > LayoutHelper.LayoutEpsilon)
-            {
-                fillerColumn.FillerWidth = finalWidth - totalColumnsWidth;
-            }
-            else
-            {
-                fillerColumn.FillerWidth = 0;
-            }
-        }
-
-        internal void OnInsertedColumn_PostNotification(DataGridCellCoordinates newCurrentCellCoordinates, int newDisplayIndex)
-        {
-            // Update current cell if needed
-            if (newCurrentCellCoordinates.ColumnIndex != -1)
-            {
-                Debug.Assert(CurrentColumnIndex == -1);
-                SetAndSelectCurrentCell(newCurrentCellCoordinates.ColumnIndex,
-                                        newCurrentCellCoordinates.Slot,
-                                        ColumnsInternal.VisibleColumnCount == 1 /*forceCurrentCellSelection*/);
-
-                if (newDisplayIndex < FrozenColumnCountWithFiller)
-                {
-                    CorrectColumnFrozenStates();
-                }
-            }
-        }
-
-        internal void OnInsertedColumn_PreNotification(DataGridColumn insertedColumn)
-        {
-            // Fix the Index of all following columns
-            CorrectColumnIndexesAfterInsertion(insertedColumn, 1);
-
-            Debug.Assert(insertedColumn.Index >= 0);
-            Debug.Assert(insertedColumn.Index < ColumnsItemsInternal.Count);
-            Debug.Assert(insertedColumn.OwningGrid == this);
-
-            CorrectColumnDisplayIndexesAfterInsertion(insertedColumn);
-
-            InsertDisplayedColumnHeader(insertedColumn);
-
-            // Insert the missing data cells
-            if (SlotCount > 0)
-            {
-                int newColumnCount = ColumnsItemsInternal.Count;
-
-                foreach (DataGridRow row in GetAllRows())
-                {
-                    if (row.Cells.Count < newColumnCount)
-                    {
-                        AddNewCellPrivate(row, insertedColumn);
-                    }
-                }
-            }
-
-            if (insertedColumn.IsVisible)
-            {
-                EnsureHorizontalLayout();
-            }
-
-            if (insertedColumn is DataGridBoundColumn boundColumn && !boundColumn.IsAutoGenerated)
-            {
-                boundColumn.SetHeaderFromBinding();
-            }
-        }
-
-        internal DataGridCellCoordinates OnInsertingColumn(int columnIndexInserted, DataGridColumn insertColumn)
-        {
-            DataGridCellCoordinates newCurrentCellCoordinates;
-            Debug.Assert(insertColumn != null);
-
-            if (insertColumn.OwningGrid != null && insertColumn != ColumnsInternal.RowGroupSpacerColumn)
-            {
-                throw DataGridError.DataGrid.ColumnCannotBeReassignedToDifferentDataGrid();
-            }
-
-            // Reset current cell if there is one, no matter the relative position of the columns involved
-            if (CurrentColumnIndex != -1)
-            {
-                _temporarilyResetCurrentCell = true;
-                newCurrentCellCoordinates = new DataGridCellCoordinates(columnIndexInserted <= CurrentColumnIndex ? CurrentColumnIndex + 1 : CurrentColumnIndex,
-                     CurrentSlot);
-                ResetCurrentCellCore();
-            }
-            else
-            {
-                newCurrentCellCoordinates = new DataGridCellCoordinates(-1, -1);
-            }
-            return newCurrentCellCoordinates;
-        }
-
-        internal void OnRemovedColumn_PostNotification(DataGridCellCoordinates newCurrentCellCoordinates)
-        {
-            // Update current cell if needed
-            if (newCurrentCellCoordinates.ColumnIndex != -1)
-            {
-                Debug.Assert(CurrentColumnIndex == -1);
-                SetAndSelectCurrentCell(newCurrentCellCoordinates.ColumnIndex, newCurrentCellCoordinates.Slot, false /*forceCurrentCellSelection*/);
-            }
-        }
-
-        internal void OnRemovedColumn_PreNotification(DataGridColumn removedColumn)
-        {
-            Debug.Assert(removedColumn.Index >= 0);
-            Debug.Assert(removedColumn.OwningGrid == null);
-
-            // Intentionally keep the DisplayIndex intact after detaching the column.
-            CorrectColumnIndexesAfterDeletion(removedColumn);
-
-            CorrectColumnDisplayIndexesAfterDeletion(removedColumn);
-
-            // If the detached column was frozen, a new column needs to take its place
-            if (removedColumn.IsFrozen)
-            {
-                removedColumn.IsFrozen = false;
-                CorrectColumnFrozenStates();
-            }
-
-            UpdateDisplayedColumns();
-
-            // Fix the existing rows by removing cells at correct index
-            int newColumnCount = ColumnsItemsInternal.Count;
-
-            if (_rowsPresenter != null)
-            {
-                foreach (DataGridRow row in GetAllRows())
-                {
-                    if (row.Cells.Count > newColumnCount)
-                    {
-                        row.Cells.RemoveAt(removedColumn.Index);
-                    }
-                }
-                _rowsPresenter.InvalidateArrange();
-            }
-
-            RemoveDisplayedColumnHeader(removedColumn);
-        }
-
-        internal DataGridCellCoordinates OnRemovingColumn(DataGridColumn dataGridColumn)
-        {
-            Debug.Assert(dataGridColumn != null);
-            Debug.Assert(dataGridColumn.Index >= 0 && dataGridColumn.Index < ColumnsItemsInternal.Count);
-
-            DataGridCellCoordinates newCurrentCellCoordinates;
-
-            _temporarilyResetCurrentCell = false;
-            int columnIndex = dataGridColumn.Index;
-
-            // Reset the current cell's address if there is one.
-            if (CurrentColumnIndex != -1)
-            {
-                int newCurrentColumnIndex = CurrentColumnIndex;
-                if (columnIndex == newCurrentColumnIndex)
-                {
-                    DataGridColumn dataGridColumnNext = ColumnsInternal.GetNextVisibleColumn(ColumnsItemsInternal[columnIndex]);
-                    if (dataGridColumnNext != null)
-                    {
-                        if (dataGridColumnNext.Index > columnIndex)
-                        {
-                            newCurrentColumnIndex = dataGridColumnNext.Index - 1;
-                        }
-                        else
-                        {
-                            newCurrentColumnIndex = dataGridColumnNext.Index;
-                        }
-                    }
-                    else
-                    {
-                        DataGridColumn dataGridColumnPrevious = ColumnsInternal.GetPreviousVisibleNonFillerColumn(ColumnsItemsInternal[columnIndex]);
-                        if (dataGridColumnPrevious != null)
-                        {
-                            if (dataGridColumnPrevious.Index > columnIndex)
-                            {
-                                newCurrentColumnIndex = dataGridColumnPrevious.Index - 1;
-                            }
-                            else
-                            {
-                                newCurrentColumnIndex = dataGridColumnPrevious.Index;
-                            }
-                        }
-                        else
-                        {
-                            newCurrentColumnIndex = -1;
-                        }
-                    }
-                }
-                else if (columnIndex < newCurrentColumnIndex)
-                {
-                    newCurrentColumnIndex--;
-                }
-                newCurrentCellCoordinates = new DataGridCellCoordinates(newCurrentColumnIndex, (newCurrentColumnIndex == -1) ? -1 : CurrentSlot);
-                if (columnIndex == CurrentColumnIndex)
-                {
-                    // If the commit fails, force a cancel edit
-                    if (!CommitEdit(DataGridEditingUnit.Row, false /*exitEditingMode*/))
-                    {
-                        CancelEdit(DataGridEditingUnit.Row, false /*raiseEvents*/);
-                    }
-                    bool success = SetCurrentCellCore(-1, -1);
-                    Debug.Assert(success);
-                }
-                else
-                {
-                    // Underlying data of deleted column is gone. It cannot be accessed anymore.
-                    // Do not end editing mode so that CellValidation doesn't get raised, since that event needs the current formatted value.
-                    _temporarilyResetCurrentCell = true;
-                    bool success = SetCurrentCellCore(-1, -1);
-                    Debug.Assert(success);
-                }
-            }
-            else
-            {
-                newCurrentCellCoordinates = new DataGridCellCoordinates(-1, -1);
-            }
-
-            // If the last column is removed, delete all the rows first.
-            if (ColumnsItemsInternal.Count == 1)
-            {
-                ClearRows(false);
-            }
-
-            // Is deleted column scrolled off screen?
-            if (dataGridColumn.IsVisible &&
-                !dataGridColumn.IsFrozen &&
-                DisplayData.FirstDisplayedScrollingCol >= 0)
-            {
-                // Deleted column is part of scrolling columns.
-                if (DisplayData.FirstDisplayedScrollingCol == dataGridColumn.Index)
-                {
-                    // Deleted column is first scrolling column
-                    _horizontalOffset -= _negHorizontalOffset;
-                    _negHorizontalOffset = 0;
-                }
-                else if (!ColumnsInternal.DisplayInOrder(DisplayData.FirstDisplayedScrollingCol, dataGridColumn.Index))
-                {
-                    // Deleted column is displayed before first scrolling column
-                    Debug.Assert(_horizontalOffset >= GetEdgedColumnWidth(dataGridColumn));
-                    _horizontalOffset -= GetEdgedColumnWidth(dataGridColumn);
-                }
-
-                if (_hScrollBar != null && _hScrollBar.IsVisible) // 
-                {
-                    _hScrollBar.Value = _horizontalOffset;
-                }
-            }
-
-            return newCurrentCellCoordinates;
-        }
-
-        /// <summary>
-        /// Called when a column property changes, and its cells need to 
-        /// adjust that that column change.
-        /// </summary>
-        internal void RefreshColumnElements(DataGridColumn dataGridColumn, string propertyName)
-        {
-            Debug.Assert(dataGridColumn != null);
-
-            // Take care of the non-displayed loaded rows
-            for (int index = 0; index < _loadedRows.Count;)
-            {
-                DataGridRow dataGridRow = _loadedRows[index];
-                Debug.Assert(dataGridRow != null);
-                if (!IsSlotVisible(dataGridRow.Slot))
-                {
-                    RefreshCellElement(dataGridColumn, dataGridRow, propertyName);
-                }
-                index++;
-            }
-
-            // Take care of the displayed rows
-            if (_rowsPresenter != null)
-            {
-                foreach (DataGridRow row in GetAllRows())
-                {
-                    RefreshCellElement(dataGridColumn, row, propertyName);
-                }
-                // This update could change layout so we need to update our estimate and invalidate
-                InvalidateRowHeightEstimate();
-                InvalidateMeasure();
-            }
-        }
-
-        /// <summary>
-        /// Adjusts the widths of all star columns with DisplayIndex >= displayIndex such that the total
-        /// width is adjusted by the given amount, if possible.  If the total desired adjustment amount
-        /// could not be met, the remaining amount of adjustment is returned.
-        /// </summary>
-        /// <param name="displayIndex">Starting column DisplayIndex.</param>
-        /// <param name="adjustment">Adjustment amount (positive for increase, negative for decrease).</param>
-        /// <param name="userInitiated">Whether or not this adjustment was initiated by a user action.</param>
-        /// <returns>The remaining amount of adjustment.</returns>
-        private double AdjustStarColumnWidths(int displayIndex, double adjustment, bool userInitiated)
-        {
-            double remainingAdjustment = adjustment;
-            if (MathUtilities.IsZero(remainingAdjustment))
-            {
-                return remainingAdjustment;
-            }
-            bool increase = remainingAdjustment > 0;
-
-            // Make an initial pass through the star columns to total up some values.
-            bool scaleStarWeights = false;
-            double totalStarColumnsWidth = 0;
-            double totalStarColumnsWidthLimit = 0;
-            double totalStarWeights = 0;
-            List<DataGridColumn> starColumns = new List<DataGridColumn>();
-            foreach (DataGridColumn column in ColumnsInternal.GetDisplayedColumns(c => c.Width.IsStar && c.IsVisible && (c.ActualCanUserResize || !userInitiated)))
-            {
-                if (column.DisplayIndex < displayIndex)
-                {
-                    scaleStarWeights = true;
-                    continue;
-                }
-                starColumns.Add(column);
-                totalStarWeights += column.Width.Value;
-                totalStarColumnsWidth += column.Width.DisplayValue;
-                totalStarColumnsWidthLimit += increase ? column.ActualMaxWidth : column.ActualMinWidth;
-            }
-
-            // Set the new desired widths according to how much all the star columns can be adjusted without any
-            // of them being limited by their minimum or maximum widths (as that would distort their ratios).
-            double adjustmentLimit = totalStarColumnsWidthLimit - totalStarColumnsWidth;
-            adjustmentLimit = increase ? Math.Min(adjustmentLimit, adjustment) : Math.Max(adjustmentLimit, adjustment);
-            foreach (DataGridColumn starColumn in starColumns)
-            {
-                starColumn.SetWidthDesiredValue((totalStarColumnsWidth + adjustmentLimit) * starColumn.Width.Value / totalStarWeights);
-            }
-
-            // Adjust the star column widths first towards their desired values, and then towards their limits.
-            remainingAdjustment = AdjustStarColumnWidths(displayIndex, remainingAdjustment, userInitiated, c => c.Width.DesiredValue);
-            remainingAdjustment = AdjustStarColumnWidths(displayIndex, remainingAdjustment, userInitiated, c => increase ? c.ActualMaxWidth : c.ActualMinWidth);
-
-            // Set the new star value weights according to how much the total column widths have changed.
-            // Only do this if there were other star columns to the left, though.  If there weren't any then that means
-            // all the star columns were adjusted at the same time, and therefore, their ratios have not changed.
-            if (scaleStarWeights)
-            {
-                double starRatio = (totalStarColumnsWidth + adjustment - remainingAdjustment) / totalStarColumnsWidth;
-                foreach (DataGridColumn starColumn in starColumns)
-                {
-                    starColumn.SetWidthStarValue(Math.Min(double.MaxValue, starRatio * starColumn.Width.Value));
-                }
-            }
-
-            return remainingAdjustment;
-        }
-
-        /// <summary>
-        /// Adjusts the widths of all star columns with DisplayIndex >= displayIndex such that the total
-        /// width is adjusted by the given amount, if possible.  If the total desired adjustment amount
-        /// could not be met, the remaining amount of adjustment is returned.  The columns will stop adjusting
-        /// once they hit their target widths.
-        /// </summary>
-        /// <param name="displayIndex">Starting column DisplayIndex.</param>
-        /// <param name="remainingAdjustment">Adjustment amount (positive for increase, negative for decrease).</param>
-        /// <param name="userInitiated">Whether or not this adjustment was initiated by a user action.</param>
-        /// <param name="targetWidth">The target width of the column.</param>
-        /// <returns>The remaining amount of adjustment.</returns>
-        private double AdjustStarColumnWidths(int displayIndex, double remainingAdjustment, bool userInitiated, Func<DataGridColumn, double> targetWidth)
-        {
-            if (MathUtilities.IsZero(remainingAdjustment))
-            {
-                return remainingAdjustment;
-            }
-            bool increase = remainingAdjustment > 0;
-
-            double totalStarWeights = 0;
-            double totalStarColumnsWidth = 0;
-
-            // Order the star columns according to which one will hit their target width (or min/max limit) first.
-            // Each KeyValuePair represents a column (as the key) and an ordering factor (as the value).  The ordering factor
-            // is computed based on the distance from each column's current display width to its target width.  Because each column
-            // could have different star ratios, though, this distance is then adjusted according to its star value.  A column with
-            // a larger star value, for example, will change size more rapidly than a column with a lower star value.
-            List<KeyValuePair<DataGridColumn, double>> starColumnPairs = new List<KeyValuePair<DataGridColumn, double>>();
-            foreach (DataGridColumn column in ColumnsInternal.GetDisplayedColumns(
-                c => c.Width.IsStar && c.DisplayIndex >= displayIndex && c.IsVisible && c.Width.Value > 0 && (c.ActualCanUserResize || !userInitiated)))
-            {
-                int insertIndex = 0;
-                double distanceToTarget = Math.Min(column.ActualMaxWidth, Math.Max(targetWidth(column), column.ActualMinWidth)) - column.Width.DisplayValue;
-                double factor = (increase ? Math.Max(0, distanceToTarget) : Math.Min(0, distanceToTarget)) / column.Width.Value;
-                foreach (KeyValuePair<DataGridColumn, double> starColumnPair in starColumnPairs)
-                {
-                    if (increase ? factor <= starColumnPair.Value : factor >= starColumnPair.Value)
-                    {
-                        break;
-                    }
-                    insertIndex++;
-                }
-                starColumnPairs.Insert(insertIndex, new KeyValuePair<DataGridColumn, double>(column, factor));
-                totalStarWeights += column.Width.Value;
-                totalStarColumnsWidth += column.Width.DisplayValue;
-            }
-
-            // Adjust the column widths one at a time until they either hit their individual target width
-            // or the total remaining amount to adjust has been depleted.
-            foreach (KeyValuePair<DataGridColumn, double> starColumnPair in starColumnPairs)
-            {
-                double distanceToTarget = starColumnPair.Value * starColumnPair.Key.Width.Value;
-                double distanceAvailable = (starColumnPair.Key.Width.Value * remainingAdjustment) / totalStarWeights;
-                double adjustment = increase ? Math.Min(distanceToTarget, distanceAvailable) : Math.Max(distanceToTarget, distanceAvailable);
-
-                remainingAdjustment -= adjustment;
-                totalStarWeights -= starColumnPair.Key.Width.Value;
-                starColumnPair.Key.SetWidthDisplayValue(Math.Max(DataGrid.DATAGRID_minimumStarColumnWidth, starColumnPair.Key.Width.DisplayValue + adjustment));
-            }
-
-            return remainingAdjustment;
-        }
-
-        private bool ComputeDisplayedColumns()
-        {
-            bool invalidate = false;
-            int numVisibleScrollingCols = 0;
-            int visibleScrollingColumnsTmp = 0;
-            double displayWidth = CellsWidth;
-            double cx = 0;
-            int firstDisplayedFrozenCol = -1;
-            int firstDisplayedScrollingCol = DisplayData.FirstDisplayedScrollingCol;
-
-            // the same problem with negative numbers:
-            // if the width passed in is negative, then return 0
-            if (displayWidth <= 0 || ColumnsInternal.VisibleColumnCount == 0)
-            {
-                DisplayData.FirstDisplayedScrollingCol = -1;
-                DisplayData.LastTotallyDisplayedScrollingCol = -1;
-                return invalidate;
-            }
-
-            foreach (DataGridColumn dataGridColumn in ColumnsInternal.GetVisibleFrozenColumns())
-            {
-                if (firstDisplayedFrozenCol == -1)
-                {
-                    firstDisplayedFrozenCol = dataGridColumn.Index;
-                }
-                cx += GetEdgedColumnWidth(dataGridColumn);
-                if (cx >= displayWidth)
-                {
-                    break;
-                }
-            }
-
-            Debug.Assert(cx <= ColumnsInternal.GetVisibleFrozenEdgedColumnsWidth());
-
-            if (cx < displayWidth && firstDisplayedScrollingCol >= 0)
-            {
-                DataGridColumn dataGridColumn = ColumnsItemsInternal[firstDisplayedScrollingCol];
-                if (dataGridColumn.IsFrozen)
-                {
-                    dataGridColumn = ColumnsInternal.FirstVisibleScrollingColumn;
-                    _negHorizontalOffset = 0;
-                    if (dataGridColumn == null)
-                    {
-                        DisplayData.FirstDisplayedScrollingCol = DisplayData.LastTotallyDisplayedScrollingCol = -1;
-                        return invalidate;
-                    }
-                    else
-                    {
-                        firstDisplayedScrollingCol = dataGridColumn.Index;
-                    }
-                }
-
-                cx -= _negHorizontalOffset;
-                while (cx < displayWidth && dataGridColumn != null)
-                {
-                    cx += GetEdgedColumnWidth(dataGridColumn);
-                    visibleScrollingColumnsTmp++;
-                    dataGridColumn = ColumnsInternal.GetNextVisibleColumn(dataGridColumn);
-                }
-                numVisibleScrollingCols = visibleScrollingColumnsTmp;
-
-                // if we inflate the data area then we paint columns to the left of firstDisplayedScrollingCol
-                if (cx < displayWidth)
-                {
-                    Debug.Assert(firstDisplayedScrollingCol >= 0);
-                    //first minimize value of _negHorizontalOffset
-                    if (_negHorizontalOffset > 0)
-                    {
-                        invalidate = true;
-                        if (displayWidth - cx > _negHorizontalOffset)
-                        {
-                            cx += _negHorizontalOffset;
-                            _horizontalOffset -= _negHorizontalOffset;
-                            if (_horizontalOffset < LayoutHelper.LayoutEpsilon)
-                            {
-                                // Snap to zero to avoid trying to partially scroll in first scrolled off column below
-                                _horizontalOffset = 0;
-                            }
-
-                            _negHorizontalOffset = 0;
-                        }
-                        else
-                        {
-                            _horizontalOffset -= displayWidth - cx;
-                            _negHorizontalOffset -= displayWidth - cx;
-                            cx = displayWidth;
-                        }
-
-                        // Make sure the HorizontalAdjustment is not greater than the new HorizontalOffset
-                        // since it would cause an assertion failure in DataGridCellsPresenter.ShouldDisplayCell
-                        // called by DataGridCellsPresenter.MeasureOverride.
-                        HorizontalAdjustment = Math.Min(HorizontalAdjustment, _horizontalOffset);
-                    }
-                    // second try to scroll entire columns
-                    if (cx < displayWidth && _horizontalOffset > 0)
-                    {
-                        Debug.Assert(_negHorizontalOffset == 0);
-                        dataGridColumn = ColumnsInternal.GetPreviousVisibleScrollingColumn(ColumnsItemsInternal[firstDisplayedScrollingCol]);
-                        while (dataGridColumn != null && cx + GetEdgedColumnWidth(dataGridColumn) <= displayWidth)
-                        {
-                            cx += GetEdgedColumnWidth(dataGridColumn);
-                            visibleScrollingColumnsTmp++;
-                            invalidate = true;
-                            firstDisplayedScrollingCol = dataGridColumn.Index;
-                            _horizontalOffset -= GetEdgedColumnWidth(dataGridColumn);
-                            dataGridColumn = ColumnsInternal.GetPreviousVisibleScrollingColumn(dataGridColumn);
-                        }
-                    }
-                    // third try to partially scroll in first scrolled off column
-                    if (cx < displayWidth && _horizontalOffset > 0)
-                    {
-                        Debug.Assert(_negHorizontalOffset == 0);
-                        dataGridColumn = ColumnsInternal.GetPreviousVisibleScrollingColumn(ColumnsItemsInternal[firstDisplayedScrollingCol]);
-                        Debug.Assert(dataGridColumn != null);
-                        Debug.Assert(GetEdgedColumnWidth(dataGridColumn) > displayWidth - cx);
-                        firstDisplayedScrollingCol = dataGridColumn.Index;
-                        _negHorizontalOffset = GetEdgedColumnWidth(dataGridColumn) - displayWidth + cx;
-                        _horizontalOffset -= displayWidth - cx;
-                        visibleScrollingColumnsTmp++;
-                        invalidate = true;
-                        cx = displayWidth;
-                        Debug.Assert(_negHorizontalOffset == GetNegHorizontalOffsetFromHorizontalOffset(_horizontalOffset));
-                    }
-
-                    // update the number of visible columns to the new reality
-                    Debug.Assert(numVisibleScrollingCols <= visibleScrollingColumnsTmp, "the number of displayed columns can only grow");
-                    numVisibleScrollingCols = visibleScrollingColumnsTmp;
-                }
-
-                int jumpFromFirstVisibleScrollingCol = numVisibleScrollingCols - 1;
-                if (cx > displayWidth)
-                {
-                    jumpFromFirstVisibleScrollingCol--;
-                }
-
-                Debug.Assert(jumpFromFirstVisibleScrollingCol >= -1);
-
-                if (jumpFromFirstVisibleScrollingCol < 0)
-                {
-                    DisplayData.LastTotallyDisplayedScrollingCol = -1; // no totally visible scrolling column at all
-                }
-                else
-                {
-                    Debug.Assert(firstDisplayedScrollingCol >= 0);
-                    dataGridColumn = ColumnsItemsInternal[firstDisplayedScrollingCol];
-                    for (int jump = 0; jump < jumpFromFirstVisibleScrollingCol; jump++)
-                    {
-                        dataGridColumn = ColumnsInternal.GetNextVisibleColumn(dataGridColumn);
-                        Debug.Assert(dataGridColumn != null);
-                    }
-                    DisplayData.LastTotallyDisplayedScrollingCol = dataGridColumn.Index;
-                }
-            }
-            else
-            {
-                DisplayData.LastTotallyDisplayedScrollingCol = -1;
-            }
-            DisplayData.FirstDisplayedScrollingCol = firstDisplayedScrollingCol;
-
-            return invalidate;
-        }
-
-        private int ComputeFirstVisibleScrollingColumn()
-        {
-            if (ColumnsInternal.GetVisibleFrozenEdgedColumnsWidth() >= CellsWidth)
-            {
-                // Not enough room for scrolling columns.
-                _negHorizontalOffset = 0;
-                return -1;
-            }
-
-            DataGridColumn dataGridColumn = ColumnsInternal.FirstVisibleScrollingColumn;
-
-            if (_horizontalOffset == 0)
-            {
-                _negHorizontalOffset = 0;
-                return (dataGridColumn == null) ? -1 : dataGridColumn.Index;
-            }
-
-            double cx = 0;
-            while (dataGridColumn != null)
-            {
-                cx += GetEdgedColumnWidth(dataGridColumn);
-                if (cx > _horizontalOffset)
-                {
-                    break;
-                }
-                dataGridColumn = ColumnsInternal.GetNextVisibleColumn(dataGridColumn);
-            }
-
-            if (dataGridColumn == null)
-            {
-                Debug.Assert(cx <= _horizontalOffset);
-                dataGridColumn = ColumnsInternal.FirstVisibleScrollingColumn;
-                if (dataGridColumn == null)
-                {
-                    _negHorizontalOffset = 0;
-                    return -1;
-                }
-                else
-                {
-                    if (_negHorizontalOffset != _horizontalOffset)
-                    {
-                        _negHorizontalOffset = 0;
-                    }
-                    return dataGridColumn.Index;
-                }
-            }
-            else
-            {
-                _negHorizontalOffset = GetEdgedColumnWidth(dataGridColumn) - (cx - _horizontalOffset);
-                return dataGridColumn.Index;
-            }
-        }
-
-        private void CorrectColumnDisplayIndexesAfterDeletion(DataGridColumn deletedColumn)
-        {
-            // Column indexes have already been adjusted.
-            // This column has already been detached and has retained its old Index and DisplayIndex
-
-            Debug.Assert(deletedColumn != null);
-            Debug.Assert(deletedColumn.OwningGrid == null);
-            Debug.Assert(deletedColumn.Index >= 0);
-            Debug.Assert(deletedColumn.DisplayIndexWithFiller >= 0);
-
-            try
-            {
-                InDisplayIndexAdjustments = true;
-
-                // The DisplayIndex of columns greater than the deleted column need to be decremented,
-                // as do the DisplayIndexMap values of modified column Indexes
-                DataGridColumn column;
-                ColumnsInternal.DisplayIndexMap.RemoveAt(deletedColumn.DisplayIndexWithFiller);
-                for (int displayIndex = 0; displayIndex < ColumnsInternal.DisplayIndexMap.Count; displayIndex++)
-                {
-                    if (ColumnsInternal.DisplayIndexMap[displayIndex] > deletedColumn.Index)
-                    {
-                        ColumnsInternal.DisplayIndexMap[displayIndex]--;
-                    }
-                    if (displayIndex >= deletedColumn.DisplayIndexWithFiller)
-                    {
-                        column = ColumnsInternal.GetColumnAtDisplayIndex(displayIndex);
-                        column.DisplayIndexWithFiller = column.DisplayIndexWithFiller - 1;
-                        column.DisplayIndexHasChanged = true; // OnColumnDisplayIndexChanged needs to be raised later on
-                    }
-                }
-
-                // Now raise all the OnColumnDisplayIndexChanged events
-                FlushDisplayIndexChanged(true /*raiseEvent*/);
-            }
-            finally
-            {
-                InDisplayIndexAdjustments = false;
-                FlushDisplayIndexChanged(false /*raiseEvent*/);
-            }
-        }
-
-        private void CorrectColumnDisplayIndexesAfterInsertion(DataGridColumn insertedColumn)
-        {
-            Debug.Assert(insertedColumn != null);
-            Debug.Assert(insertedColumn.OwningGrid == this);
-            if (insertedColumn.DisplayIndexWithFiller == -1 || insertedColumn.DisplayIndexWithFiller >= ColumnsItemsInternal.Count)
-            {
-                // Developer did not assign a DisplayIndex or picked a large number.
-                // Choose the Index as the DisplayIndex.
-                insertedColumn.DisplayIndexWithFiller = insertedColumn.Index;
-            }
-
-            try
-            {
-                InDisplayIndexAdjustments = true;
-
-                // The DisplayIndex of columns greater than the inserted column need to be incremented,
-                // as do the DisplayIndexMap values of modified column Indexes
-                DataGridColumn column;
-                for (int displayIndex = 0; displayIndex < ColumnsInternal.DisplayIndexMap.Count; displayIndex++)
-                {
-                    if (ColumnsInternal.DisplayIndexMap[displayIndex] >= insertedColumn.Index)
-                    {
-                        ColumnsInternal.DisplayIndexMap[displayIndex]++;
-                    }
-                    if (displayIndex >= insertedColumn.DisplayIndexWithFiller)
-                    {
-                        column = ColumnsInternal.GetColumnAtDisplayIndex(displayIndex);
-                        column.DisplayIndexWithFiller++;
-                        column.DisplayIndexHasChanged = true; // OnColumnDisplayIndexChanged needs to be raised later on
-                    }
-                }
-                ColumnsInternal.DisplayIndexMap.Insert(insertedColumn.DisplayIndexWithFiller, insertedColumn.Index);
-
-                // Now raise all the OnColumnDisplayIndexChanged events
-                FlushDisplayIndexChanged(true /*raiseEvent*/);
-            }
-            finally
-            {
-                InDisplayIndexAdjustments = false;
-                FlushDisplayIndexChanged(false /*raiseEvent*/);
-            }
-        }
-
-        private void CorrectColumnFrozenStates()
-        {
-            int index = 0;
-            double frozenColumnWidth = 0;
-            double oldFrozenColumnWidth = 0;
-            foreach (DataGridColumn column in ColumnsInternal.GetDisplayedColumns())
-            {
-                if (column.IsFrozen)
-                {
-                    oldFrozenColumnWidth += column.ActualWidth;
-                }
-                column.IsFrozen = index < FrozenColumnCountWithFiller;
-                if (column.IsFrozen)
-                {
-                    frozenColumnWidth += column.ActualWidth;
-                }
-                index++;
-            }
-            if (HorizontalOffset > Math.Max(0, frozenColumnWidth - oldFrozenColumnWidth))
-            {
-                UpdateHorizontalOffset(HorizontalOffset - frozenColumnWidth + oldFrozenColumnWidth);
-            }
-            else
-            {
-                UpdateHorizontalOffset(0);
-            }
-        }
-
-        private void CorrectColumnIndexesAfterDeletion(DataGridColumn deletedColumn)
-        {
-            Debug.Assert(deletedColumn != null);
-            for (int columnIndex = deletedColumn.Index; columnIndex < ColumnsItemsInternal.Count; columnIndex++)
-            {
-                ColumnsItemsInternal[columnIndex].Index = ColumnsItemsInternal[columnIndex].Index - 1;
-                Debug.Assert(ColumnsItemsInternal[columnIndex].Index == columnIndex);
-            }
-        }
-
-        private void CorrectColumnIndexesAfterInsertion(DataGridColumn insertedColumn, int insertionCount)
-        {
-            Debug.Assert(insertedColumn != null);
-            Debug.Assert(insertionCount > 0);
-            for (int columnIndex = insertedColumn.Index + insertionCount; columnIndex < ColumnsItemsInternal.Count; columnIndex++)
-            {
-                ColumnsItemsInternal[columnIndex].Index = columnIndex;
-            }
-        }
-
-        /// <summary>
-        /// Decreases the width of a non-star column by the given amount, if possible.  If the total desired
-        /// adjustment amount could not be met, the remaining amount of adjustment is returned.  The adjustment
-        /// stops when the column's target width has been met.
-        /// </summary>
-        /// <param name="column">Column to adjust.</param>
-        /// <param name="targetWidth">The target width of the column (in pixels).</param>
-        /// <param name="amount">Amount to decrease (in pixels).</param>
-        /// <returns>The remaining amount of adjustment.</returns>
-        private static double DecreaseNonStarColumnWidth(DataGridColumn column, double targetWidth, double amount)
-        {
-            Debug.Assert(amount < 0);
-            Debug.Assert(column.Width.UnitType != DataGridLengthUnitType.Star);
-
-            if (MathUtilities.GreaterThanOrClose(targetWidth, column.Width.DisplayValue))
-            {
-                return amount;
-            }
-
-            double adjustment = Math.Max(
-                column.ActualMinWidth - column.Width.DisplayValue,
-                Math.Max(targetWidth - column.Width.DisplayValue, amount));
-
-            column.SetWidthDisplayValue(column.Width.DisplayValue + adjustment);
-            return amount - adjustment;
-        }
-
-        /// <summary>
-        /// Decreases the widths of all non-star columns with DisplayIndex >= displayIndex such that the total
-        /// width is decreased by the given amount, if possible.  If the total desired adjustment amount
-        /// could not be met, the remaining amount of adjustment is returned.  The adjustment stops when
-        /// the column's target width has been met.
-        /// </summary>
-        /// <param name="displayIndex">Starting column DisplayIndex.</param>
-        /// <param name="targetWidth">The target width of the column (in pixels).</param>
-        /// <param name="amount">Amount to decrease (in pixels).</param>
-        /// <param name="reverse">Whether or not to reverse the order in which the columns are traversed.</param>
-        /// <param name="affectNewColumns">Whether or not to adjust widths of columns that do not yet have their initial desired width.</param>
-        /// <returns>The remaining amount of adjustment.</returns>
-        private double DecreaseNonStarColumnWidths(int displayIndex, Func<DataGridColumn, double> targetWidth, double amount, bool reverse, bool affectNewColumns)
-        {
-            if (MathUtilities.GreaterThanOrClose(amount, 0))
-            {
-                return amount;
-            }
-
-            foreach (DataGridColumn column in ColumnsInternal.GetDisplayedColumns(reverse,
-                column =>
-                    column.IsVisible &&
-                    column.Width.UnitType != DataGridLengthUnitType.Star &&
-                    column.DisplayIndex >= displayIndex &&
-                    column.ActualCanUserResize &&
-                    (affectNewColumns || column.IsInitialDesiredWidthDetermined)))
-            {
-                amount = DecreaseNonStarColumnWidth(column, Math.Max(column.ActualMinWidth, targetWidth(column)), amount);
-                if (MathUtilities.IsZero(amount))
-                {
-                    break;
-                }
-            }
-            return amount;
-        }
-
-        private void FlushDisplayIndexChanged(bool raiseEvent)
-        {
-            foreach (DataGridColumn column in ColumnsItemsInternal)
-            {
-                if (column.DisplayIndexHasChanged)
-                {
-                    column.DisplayIndexHasChanged = false;
-                    if (raiseEvent)
-                    {
-                        Debug.Assert(column != ColumnsInternal.RowGroupSpacerColumn);
-                        OnColumnDisplayIndexChanged(column);
-                    }
-                }
-            }
-        }
-
-        private bool GetColumnEffectiveReadOnlyState(DataGridColumn dataGridColumn)
-        {
-            Debug.Assert(dataGridColumn != null);
-
-            return IsReadOnly || dataGridColumn.IsReadOnly || dataGridColumn is DataGridFillerColumn;
-        }
-
-        /// <devdoc>
-        ///      Returns the absolute coordinate of the left edge of the given column (including
-        ///      the potential gridline - that is the left edge of the gridline is returned). Note that
-        ///      the column does not need to be in the display area.
-        /// </devdoc>
-        private double GetColumnXFromIndex(int index)
-        {
-            Debug.Assert(index < ColumnsItemsInternal.Count);
-            Debug.Assert(ColumnsItemsInternal[index].IsVisible);
-
-            double x = 0;
-            foreach (DataGridColumn column in ColumnsInternal.GetVisibleColumns())
-            {
-                if (index == column.Index)
-                {
-                    break;
-                }
-                x += GetEdgedColumnWidth(column);
-            }
-            return x;
-        }
-
-        private double GetNegHorizontalOffsetFromHorizontalOffset(double horizontalOffset)
-        {
-            foreach (DataGridColumn column in ColumnsInternal.GetVisibleScrollingColumns())
-            {
-                if (GetEdgedColumnWidth(column) > horizontalOffset)
-                {
-                    break;
-                }
-                horizontalOffset -= GetEdgedColumnWidth(column);
-            }
-            return horizontalOffset;
-        }
-
-        /// <summary>
-        /// Increases the width of a non-star column by the given amount, if possible.  If the total desired
-        /// adjustment amount could not be met, the remaining amount of adjustment is returned.  The adjustment
-        /// stops when the column's target width has been met.
-        /// </summary>
-        /// <param name="column">Column to adjust.</param>
-        /// <param name="targetWidth">The target width of the column (in pixels).</param>
-        /// <param name="amount">Amount to increase (in pixels).</param>
-        /// <returns>The remaining amount of adjustment.</returns>
-        private static double IncreaseNonStarColumnWidth(DataGridColumn column, double targetWidth, double amount)
-        {
-            Debug.Assert(amount > 0);
-            Debug.Assert(column.Width.UnitType != DataGridLengthUnitType.Star);
-
-            if (targetWidth <= column.Width.DisplayValue)
-            {
-                return amount;
-            }
-
-            double adjustment = Math.Min(
-                column.ActualMaxWidth - column.Width.DisplayValue,
-                Math.Min(targetWidth - column.Width.DisplayValue, amount));
-
-            column.SetWidthDisplayValue(column.Width.DisplayValue + adjustment);
-            return amount - adjustment;
-        }
-
-        /// <summary>
-        /// Increases the widths of all non-star columns with DisplayIndex >= displayIndex such that the total
-        /// width is increased by the given amount, if possible.  If the total desired adjustment amount
-        /// could not be met, the remaining amount of adjustment is returned.  The adjustment stops when
-        /// the column's target width has been met.
-        /// </summary>
-        /// <param name="displayIndex">Starting column DisplayIndex.</param>
-        /// <param name="targetWidth">The target width of the column (in pixels).</param>
-        /// <param name="amount">Amount to increase (in pixels).</param>
-        /// <param name="reverse">Whether or not to reverse the order in which the columns are traversed.</param>
-        /// <param name="affectNewColumns">Whether or not to adjust widths of columns that do not yet have their initial desired width.</param>
-        /// <returns>The remaining amount of adjustment.</returns>
-        private double IncreaseNonStarColumnWidths(int displayIndex, Func<DataGridColumn, double> targetWidth, double amount, bool reverse, bool affectNewColumns)
-        {
-            if (MathUtilities.LessThanOrClose(amount, 0))
-            {
-                return amount;
-            }
-
-            foreach (DataGridColumn column in ColumnsInternal.GetDisplayedColumns(reverse,
-                column =>
-                    column.IsVisible &&
-                    column.Width.UnitType != DataGridLengthUnitType.Star &&
-                    column.DisplayIndex >= displayIndex &&
-                    column.ActualCanUserResize &&
-                    (affectNewColumns || column.IsInitialDesiredWidthDetermined)))
-            {
-                amount = IncreaseNonStarColumnWidth(column, Math.Min(column.ActualMaxWidth, targetWidth(column)), amount);
-                if (MathUtilities.IsZero(amount))
-                {
-                    break;
-                }
-            }
-            return amount;
-        }
-
-        private void InsertDisplayedColumnHeader(DataGridColumn dataGridColumn)
-        {
-            Debug.Assert(dataGridColumn != null);
-            if (_columnHeadersPresenter != null)
-            {
-                dataGridColumn.HeaderCell.IsVisible = dataGridColumn.IsVisible;
-                Debug.Assert(!_columnHeadersPresenter.Children.Contains(dataGridColumn.HeaderCell));
-                _columnHeadersPresenter.Children.Insert(dataGridColumn.DisplayIndexWithFiller, dataGridColumn.HeaderCell);
-            }
-        }
-
-        private static void RefreshCellElement(DataGridColumn dataGridColumn, DataGridRow dataGridRow, string propertyName)
-        {
-            Debug.Assert(dataGridColumn != null);
-            Debug.Assert(dataGridRow != null);
-
-            DataGridCell dataGridCell = dataGridRow.Cells[dataGridColumn.Index];
-            Debug.Assert(dataGridCell != null);
-            if (dataGridCell.Content is Control element)
-            {
-                dataGridColumn.RefreshCellContent(element, propertyName);
-            }
-        }
-
-        private void RemoveAutoGeneratedColumns()
-        {
-            int index = 0;
-            _autoGeneratingColumnOperationCount++;
-            try
-            {
-                while (index < ColumnsInternal.Count)
-                {
-                    // Skip over the user columns
-                    while (index < ColumnsInternal.Count && !ColumnsInternal[index].IsAutoGenerated)
-                    {
-                        index++;
-                    }
-                    // Remove the autogenerated columns
-                    while (index < ColumnsInternal.Count && ColumnsInternal[index].IsAutoGenerated)
-                    {
-                        ColumnsInternal.RemoveAt(index);
-                    }
-                }
-                ColumnsInternal.AutogeneratedColumnCount = 0;
-            }
-            finally
-            {
-                _autoGeneratingColumnOperationCount--;
-            }
-        }
-
-        private bool ScrollColumnIntoView(int columnIndex)
-        {
-            Debug.Assert(columnIndex >= 0 && columnIndex < ColumnsItemsInternal.Count);
-
-            if (DisplayData.FirstDisplayedScrollingCol != -1 &&
-                !ColumnsItemsInternal[columnIndex].IsFrozen &&
-                (columnIndex != DisplayData.FirstDisplayedScrollingCol || _negHorizontalOffset > 0))
-            {
-                int columnsToScroll;
-                if (ColumnsInternal.DisplayInOrder(columnIndex, DisplayData.FirstDisplayedScrollingCol))
-                {
-                    columnsToScroll = ColumnsInternal.GetColumnCount(true /* isVisible */, false /* isFrozen */, columnIndex, DisplayData.FirstDisplayedScrollingCol);
-                    if (_negHorizontalOffset > 0)
-                    {
-                        columnsToScroll++;
-                    }
-                    ScrollColumns(-columnsToScroll);
-                }
-                else if (columnIndex == DisplayData.FirstDisplayedScrollingCol && _negHorizontalOffset > 0)
-                {
-                    ScrollColumns(-1);
-                }
-                else if (DisplayData.LastTotallyDisplayedScrollingCol == -1 ||
-                         (DisplayData.LastTotallyDisplayedScrollingCol != columnIndex &&
-                          ColumnsInternal.DisplayInOrder(DisplayData.LastTotallyDisplayedScrollingCol, columnIndex)))
-                {
-                    double xColumnLeftEdge = GetColumnXFromIndex(columnIndex);
-                    double xColumnRightEdge = xColumnLeftEdge + GetEdgedColumnWidth(ColumnsItemsInternal[columnIndex]);
-                    double change = xColumnRightEdge - HorizontalOffset - CellsWidth;
-                    double widthRemaining = change;
-
-                    DataGridColumn newFirstDisplayedScrollingCol = ColumnsItemsInternal[DisplayData.FirstDisplayedScrollingCol];
-                    DataGridColumn nextColumn = ColumnsInternal.GetNextVisibleColumn(newFirstDisplayedScrollingCol);
-                    double newColumnWidth = GetEdgedColumnWidth(newFirstDisplayedScrollingCol) - _negHorizontalOffset;
-                    while (nextColumn != null && widthRemaining >= newColumnWidth)
-                    {
-                        widthRemaining -= newColumnWidth;
-                        newFirstDisplayedScrollingCol = nextColumn;
-                        newColumnWidth = GetEdgedColumnWidth(newFirstDisplayedScrollingCol);
-                        nextColumn = ColumnsInternal.GetNextVisibleColumn(newFirstDisplayedScrollingCol);
-                        _negHorizontalOffset = 0;
-                    }
-                    _negHorizontalOffset += widthRemaining;
-                    DisplayData.LastTotallyDisplayedScrollingCol = columnIndex;
-                    if (newFirstDisplayedScrollingCol.Index == columnIndex)
-                    {
-                        _negHorizontalOffset = 0;
-                        double frozenColumnWidth = ColumnsInternal.GetVisibleFrozenEdgedColumnsWidth();
-                        // If the entire column cannot be displayed, we want to start showing it from its LeftEdge
-                        if (newColumnWidth > (CellsWidth - frozenColumnWidth))
-                        {
-                            DisplayData.LastTotallyDisplayedScrollingCol = -1;
-                            change = xColumnLeftEdge - HorizontalOffset - frozenColumnWidth;
-                        }
-                    }
-                    DisplayData.FirstDisplayedScrollingCol = newFirstDisplayedScrollingCol.Index;
-
-                    // At this point DisplayData.FirstDisplayedScrollingColumn and LastDisplayedScrollingColumn 
-                    // should be correct
-                    if (change != 0)
-                    {
-                        UpdateHorizontalOffset(HorizontalOffset + change);
-                    }
-                }
-            }
-            return true;
-        }
-
-        private void ScrollColumns(int columns)
-        {
-            DataGridColumn newFirstVisibleScrollingCol = null;
-            DataGridColumn dataGridColumnTmp;
-            int colCount = 0;
-            if (columns > 0)
-            {
-                if (DisplayData.LastTotallyDisplayedScrollingCol >= 0)
-                {
-                    dataGridColumnTmp = ColumnsItemsInternal[DisplayData.LastTotallyDisplayedScrollingCol];
-                    while (colCount < columns && dataGridColumnTmp != null)
-                    {
-                        dataGridColumnTmp = ColumnsInternal.GetNextVisibleColumn(dataGridColumnTmp);
-                        colCount++;
-                    }
-
-                    if (dataGridColumnTmp == null)
-                    {
-                        // no more column to display on the right of the last totally seen column
-                        return;
-                    }
-                }
-                Debug.Assert(DisplayData.FirstDisplayedScrollingCol >= 0);
-                dataGridColumnTmp = ColumnsItemsInternal[DisplayData.FirstDisplayedScrollingCol];
-                colCount = 0;
-                while (colCount < columns && dataGridColumnTmp != null)
-                {
-                    dataGridColumnTmp = ColumnsInternal.GetNextVisibleColumn(dataGridColumnTmp);
-                    colCount++;
-                }
-                newFirstVisibleScrollingCol = dataGridColumnTmp;
-            }
-
-            if (columns < 0)
-            {
-                Debug.Assert(DisplayData.FirstDisplayedScrollingCol >= 0);
-                dataGridColumnTmp = ColumnsItemsInternal[DisplayData.FirstDisplayedScrollingCol];
-                if (_negHorizontalOffset > 0)
-                {
-                    colCount++;
-                }
-                while (colCount < -columns && dataGridColumnTmp != null)
-                {
-                    dataGridColumnTmp = ColumnsInternal.GetPreviousVisibleScrollingColumn(dataGridColumnTmp);
-                    colCount++;
-                }
-                newFirstVisibleScrollingCol = dataGridColumnTmp;
-                if (newFirstVisibleScrollingCol == null)
-                {
-                    if (_negHorizontalOffset == 0)
-                    {
-                        // no more column to display on the left of the first seen column
-                        return;
-                    }
-                    else
-                    {
-                        newFirstVisibleScrollingCol = ColumnsItemsInternal[DisplayData.FirstDisplayedScrollingCol];
-                    }
-                }
-            }
-
-            double newColOffset = 0;
-            foreach (DataGridColumn dataGridColumn in ColumnsInternal.GetVisibleScrollingColumns())
-            {
-                if (dataGridColumn == newFirstVisibleScrollingCol)
-                {
-                    break;
-                }
-                newColOffset += GetEdgedColumnWidth(dataGridColumn);
-            }
-
-            UpdateHorizontalOffset(newColOffset);
-        }
-
-        private void UpdateDisplayedColumns()
-        {
-            DisplayData.FirstDisplayedScrollingCol = ComputeFirstVisibleScrollingColumn();
-            ComputeDisplayedColumns();
-        }
-
-        private static DataGridBoundColumn GetDataGridColumnFromType(Type type)
-        {
-            Debug.Assert(type != null);
-            if (type == typeof(bool))
-            {
-                return new DataGridCheckBoxColumn();
-            }
-            else if (type == typeof(bool?))
-            {
-                return new DataGridCheckBoxColumn
-                {
-                    IsThreeState = true
-                };
-            }
-            return new DataGridTextColumn();
-        }
-
-        private void AutoGenerateColumnsPrivate()
-        {
-            if (!_measured || (_autoGeneratingColumnOperationCount > 0))
-            {
-                // Reading the DataType when we generate columns could cause the CollectionView to 
-                // raise a Reset if its Enumeration changed.  In that case, we don't want to generate again.
-                return;
-            }
-
-            _autoGeneratingColumnOperationCount++;
-            try
-            {
-                // Always remove existing autogenerated columns before generating new ones
-                RemoveAutoGeneratedColumns();
-                GenerateColumnsFromProperties();
-                EnsureRowsPresenterVisibility();
-                InvalidateRowHeightEstimate();
-            }
-            finally
-            {
-                _autoGeneratingColumnOperationCount--;
-            }
-        }
-
-        private void GenerateColumnsFromProperties()
-        {
-            // Autogenerated Columns are added at the end so the user columns appear first
-            if (DataConnection.DataProperties != null && DataConnection.DataProperties.Length > 0)
-            {
-                List<KeyValuePair<int, DataGridAutoGeneratingColumnEventArgs>> columnOrderPairs = new List<KeyValuePair<int, DataGridAutoGeneratingColumnEventArgs>>();
-
-                // Generate the columns
-                foreach (PropertyInfo propertyInfo in DataConnection.DataProperties)
-                {
-                    string columnHeader = propertyInfo.Name;
-                    int columnOrder = DATAGRID_defaultColumnDisplayOrder;
-
-                    // Check if DisplayAttribute is defined on the property
-                    object[] attributes = propertyInfo.GetCustomAttributes(typeof(DisplayAttribute), true);
-                    if (attributes != null && attributes.Length > 0)
-                    {
-                        DisplayAttribute displayAttribute = attributes[0] as DisplayAttribute;
-                        Debug.Assert(displayAttribute != null);
-
-                        bool? autoGenerateField = displayAttribute.GetAutoGenerateField();
-                        if (autoGenerateField.HasValue && autoGenerateField.Value == false)
-                        {
-                            // Abort column generation because we aren't supposed to auto-generate this field
-                            continue;
-                        }
-
-                        string header = displayAttribute.GetShortName();
-                        if (header != null)
-                        {
-                            columnHeader = header;
-                        }
-
-                        int? order = displayAttribute.GetOrder();
-                        if (order.HasValue)
-                        {
-                            columnOrder = order.Value;
-                        }
-                    }
-
-                    // Generate a single column and determine its relative order
-                    int insertIndex = 0;
-                    if (columnOrder == int.MaxValue)
-                    {
-                        insertIndex = columnOrderPairs.Count;
-                    }
-                    else
-                    {
-                        foreach (KeyValuePair<int, DataGridAutoGeneratingColumnEventArgs> columnOrderPair in columnOrderPairs)
-                        {
-                            if (columnOrderPair.Key > columnOrder)
-                            {
-                                break;
-                            }
-                            insertIndex++;
-                        }
-                    }
-                    DataGridAutoGeneratingColumnEventArgs columnArgs = GenerateColumn(propertyInfo.PropertyType, propertyInfo.Name, columnHeader);
-                    columnOrderPairs.Insert(insertIndex, new KeyValuePair<int, DataGridAutoGeneratingColumnEventArgs>(columnOrder, columnArgs));
-                }
-
-                // Add the columns to the DataGrid in the correct order
-                foreach (KeyValuePair<int, DataGridAutoGeneratingColumnEventArgs> columnOrderPair in columnOrderPairs)
-                {
-                    AddGeneratedColumn(columnOrderPair.Value);
-                }
-            }
-            else if (DataConnection.DataIsPrimitive)
-            {
-                AddGeneratedColumn(GenerateColumn(DataConnection.DataType, string.Empty, DataConnection.DataType.Name));
-            }
-        }
-
-        private static DataGridAutoGeneratingColumnEventArgs GenerateColumn(Type propertyType, string propertyName, string header)
-        {
-            // Create a new DataBoundColumn for the Property
-            DataGridBoundColumn newColumn = GetDataGridColumnFromType(propertyType);
-            newColumn.Binding = new Binding(propertyName);
-            newColumn.Header = header;
-            newColumn.IsAutoGenerated = true;
-            return new DataGridAutoGeneratingColumnEventArgs(propertyName, propertyType, newColumn);
-        }
-
-        private bool AddGeneratedColumn(DataGridAutoGeneratingColumnEventArgs e)
-        {
-            // Raise the AutoGeneratingColumn event in case the user wants to Cancel or Replace the
-            // column being generated
-            OnAutoGeneratingColumn(e);
-            if (e.Cancel)
-            {
-                return false;
-            }
-            else
-            {
-                if (e.Column != null)
-                {
-                    // Set the IsAutoGenerated flag here in case the user provides a custom autogenerated column
-                    e.Column.IsAutoGenerated = true;
-                }
-                ColumnsInternal.Add(e.Column);
-                ColumnsInternal.AutogeneratedColumnCount++;
-                return true;
-            }
-        }
-
-    }
-
-}

+ 0 - 710
src/Avalonia.Controls.DataGrid/DataGridDataConnection.cs

@@ -1,710 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Collections;
-using System;
-using System.Linq;
-using System.Collections;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Reflection;
-using System.ComponentModel.DataAnnotations;
-using Avalonia.Utilities;
-using Avalonia.Controls.Utils;
-
-namespace Avalonia.Controls
-{
-    internal class DataGridDataConnection
-    {
-
-        private int _backupSlotForCurrentChanged;
-        private int _columnForCurrentChanged;
-        private PropertyInfo[] _dataProperties;
-        private IEnumerable _dataSource;
-        private Type _dataType;
-        private bool _expectingCurrentChanged;
-        private object _itemToSelectOnCurrentChanged;
-        private DataGrid _owner;
-        private bool _scrollForCurrentChanged;
-        private DataGridSelectionAction _selectionActionForCurrentChanged;
-
-        public DataGridDataConnection(DataGrid owner)
-        {
-            _owner = owner;
-        }
-
-        public bool AllowEdit
-        {
-            get
-            {
-                if (List == null)
-                {
-                    return true;
-                }
-                else
-                {
-                    return !List.IsReadOnly;
-                }
-            }
-        }
-
-        /// <summary>
-        /// True if the collection view says it can sort.
-        /// </summary>
-        public bool AllowSort
-        {
-            get
-            {
-                if (CollectionView == null ||
-                    (EditableCollectionView != null && (EditableCollectionView.IsAddingNew || EditableCollectionView.IsEditingItem)))
-                {
-                    return false;
-                }
-                else
-                {
-                    return CollectionView.CanSort;
-                }
-            }
-        }
-
-        public bool CommittingEdit
-        {
-            get;
-            private set;
-        }
-
-        public int Count => TryGetCount(true, false, out var count) ? count : 0;
-
-        public bool DataIsPrimitive
-        {
-            get
-            {
-                return DataTypeIsPrimitive(DataType);
-            }
-        }
-
-        public PropertyInfo[] DataProperties
-        {
-            get
-            {
-                if (_dataProperties == null)
-                {
-                    UpdateDataProperties();
-                }
-                return _dataProperties;
-            }
-        }
-
-        public IEnumerable DataSource
-        {
-            get
-            {
-                return _dataSource;
-            }
-            set
-            {
-                _dataSource = value;
-                // Because the DataSource is changing, we need to reset our cached values for DataType and DataProperties,
-                // which are dependent on the current DataSource
-                _dataType = null;
-                UpdateDataProperties();
-            }
-        }
-
-        public Type DataType
-        {
-            get
-            {
-                // We need to use the raw ItemsSource as opposed to DataSource because DataSource
-                // may be the ItemsSource wrapped in a collection view, in which case we wouldn't
-                // be able to take T to be the type if we're given IEnumerable<T>
-                if (_dataType == null && _owner.ItemsSource != null)
-                {
-                    _dataType = _owner.ItemsSource.GetItemType();
-                }
-                return _dataType;
-            }
-        }
-
-        public bool EventsWired
-        {
-            get;
-            private set;
-        }
-
-        private bool IsGrouping
-        {
-            get
-            {
-                return (CollectionView != null)
-                    && CollectionView.CanGroup
-                    && CollectionView.IsGrouping
-                    && (CollectionView.GroupingDepth > 0);
-            }
-        }
-
-        public IList List
-        {
-            get
-            {
-                return DataSource as IList;
-            }
-        }
-
-        public bool ShouldAutoGenerateColumns
-        {
-            get
-            {
-                return false;
-            }
-        }
-
-        public IDataGridCollectionView CollectionView
-        {
-            get
-            {
-                return DataSource as IDataGridCollectionView;
-            }
-        }
-        public IDataGridEditableCollectionView EditableCollectionView
-        {
-            get
-            {
-                return DataSource as IDataGridEditableCollectionView;
-            }
-        }
-
-        public DataGridSortDescriptionCollection SortDescriptions
-        {
-            get
-            {
-                if (CollectionView != null && CollectionView.CanSort)
-                {
-                    return CollectionView.SortDescriptions;
-                }
-                else
-                {
-                    return null;
-                }
-            }
-        }
-
-        /// <summary>Try get number of DataSource items.</summary>
-        /// <param name="allowSlow">When "allowSlow" is false, method will not use Linq.Count() method and will return 0 or 1 instead.</param>
-        /// <param name="getAny">If "getAny" is true, method can use Linq.Any() method to speedup.</param>
-        /// <param name="count">number of DataSource items.</param>
-        /// <returns>true if able to retrieve number of DataSource items; otherwise, false.</returns>
-        internal bool TryGetCount(bool allowSlow, bool getAny, out int count)
-        {
-            bool result;
-            (result, count) = DataSource switch
-            {
-                ICollection collection => (true, collection.Count),
-                IEnumerable enumerable when allowSlow && !getAny => (true, enumerable.Cast<object>().Count()),
-                IEnumerable enumerable when getAny => (true, enumerable.Cast<object>().Any() ? 1 : 0),
-                _ => (false, 0)
-            };
-            return result;
-        }
-
-        internal bool Any()
-        {
-            return TryGetCount(false, true, out var count) && count > 0;
-        }
-
-        /// <summary>
-        /// Puts the entity into editing mode if possible
-        /// </summary>
-        /// <param name="dataItem">The entity to edit</param>
-        /// <returns>True if editing was started</returns>
-        public bool BeginEdit(object dataItem)
-        {
-            if (dataItem == null)
-            {
-                return false;
-            }
-
-            IDataGridEditableCollectionView editableCollectionView = EditableCollectionView;
-            if (editableCollectionView != null)
-            {
-                if (editableCollectionView.IsEditingItem && (dataItem == editableCollectionView.CurrentEditItem))
-                {
-                    return true;
-                }
-                else
-                {
-                    editableCollectionView.EditItem(dataItem);
-                    return editableCollectionView.IsEditingItem || editableCollectionView.IsAddingNew;
-                }
-            }
-
-            if (dataItem is IEditableObject editableDataItem)
-            {
-                editableDataItem.BeginEdit();
-                return true;
-            }
-
-            return true;
-        }
-
-        /// <summary>
-        /// Cancels the current entity editing and exits the editing mode.
-        /// </summary>
-        /// <param name="dataItem">The entity being edited</param>
-        /// <returns>True if a cancellation operation was invoked.</returns>
-        public bool CancelEdit(object dataItem)
-        {
-            IDataGridEditableCollectionView editableCollectionView = EditableCollectionView;
-            if (editableCollectionView != null)
-            {
-                if (editableCollectionView.CanCancelEdit)
-                {
-                    editableCollectionView.CancelEdit();
-                    return true;
-                }
-                return false;
-            }
-
-            if (dataItem is IEditableObject editableDataItem)
-            {
-                editableDataItem.CancelEdit();
-                return true;
-            }
-
-            return true;
-        }
-
-        public static bool CanEdit(Type type)
-        {
-            Debug.Assert(type != null);
-
-            type = type.GetNonNullableType();
-
-            return
-                type.IsEnum
-                || type == typeof(System.String)
-                || type == typeof(System.Char)
-                || type == typeof(System.DateTime)
-                || type == typeof(System.Boolean)
-                || type == typeof(System.Byte)
-                || type == typeof(System.SByte)
-                || type == typeof(System.Single)
-                || type == typeof(System.Double)
-                || type == typeof(System.Decimal)
-                || type == typeof(System.Int16)
-                || type == typeof(System.Int32)
-                || type == typeof(System.Int64)
-                || type == typeof(System.UInt16)
-                || type == typeof(System.UInt32)
-                || type == typeof(System.UInt64);
-        }
-
-        /// <summary>
-        /// Commits the current entity editing and exits the editing mode.
-        /// </summary>
-        /// <param name="dataItem">The entity being edited</param>
-        /// <returns>True if a commit operation was invoked.</returns>
-        public bool EndEdit(object dataItem)
-        {
-            IDataGridEditableCollectionView editableCollectionView = EditableCollectionView;
-            if (editableCollectionView != null)
-            {
-                // IEditableCollectionView.CommitEdit can potentially change currency. If it does,
-                // we don't want to attempt a second commit inside our CurrentChanging event handler.
-                _owner.NoCurrentCellChangeCount++;
-                CommittingEdit = true;
-                try
-                {
-                    if (editableCollectionView.IsAddingNew)
-                    {
-                        editableCollectionView.CommitNew();
-                    }
-                    else
-                    {
-                        editableCollectionView.CommitEdit();
-                    }                    
-                }
-                finally
-                {
-                    _owner.NoCurrentCellChangeCount--;
-                    CommittingEdit = false;
-                }
-                return true;
-            }
-
-            if (dataItem is IEditableObject editableDataItem)
-            {
-                editableDataItem.EndEdit();
-            }
-
-            return true;
-        }
-
-        // Assumes index >= 0, returns null if index >= Count
-        public object GetDataItem(int index)
-        {
-            Debug.Assert(index >= 0);
-
-            if (DataSource is DataGridCollectionView collectionView)
-            {
-                return (index < collectionView.Count) ? collectionView.GetItemAt(index) : null;
-            }
-
-            IList list = List;
-            if (list != null)
-            {
-                return (index < list.Count) ? list[index] : null;
-            }
-
-            IEnumerable enumerable = DataSource;
-            if (enumerable != null)
-            {
-                IEnumerator enumerator = enumerable.GetEnumerator();
-                int i = -1;
-                while (enumerator.MoveNext() && i < index)
-                {
-                    i++;
-                    if (i == index)
-                    {
-                        return enumerator.Current;
-                    }
-                }
-            }
-            return null;
-        }
-
-        public bool GetPropertyIsReadOnly(string propertyName)
-        {
-            if (DataType != null)
-            {
-                if (!String.IsNullOrEmpty(propertyName))
-                {
-                    Type propertyType = DataType;
-                    PropertyInfo propertyInfo = null;
-                    List<string> propertyNames = TypeHelper.SplitPropertyPath(propertyName);
-                    for (int i = 0; i < propertyNames.Count; i++)
-                    {
-                        propertyInfo = propertyType.GetPropertyOrIndexer(propertyNames[i], out _);
-                        if (propertyInfo == null || propertyType.GetIsReadOnly() || propertyInfo.GetIsReadOnly())
-                        {
-                            // Either the data type is read-only, the property doesn't exist, or it does exist but is read-only
-                            return true;
-                        }
-
-                        // Check if EditableAttribute is defined on the property and if it indicates uneditable
-                        var attributes = propertyInfo.GetCustomAttributes(typeof(EditableAttribute), true);
-                        if (attributes != null && attributes.Length > 0)
-                        {
-                            var editableAttribute = (EditableAttribute)attributes[0];
-                            if (!editableAttribute.AllowEdit)
-                            {
-                                return true;
-                            }
-                        }
-                        propertyType = propertyInfo.PropertyType.GetNonNullableType();
-                    }
-                    return propertyInfo == null || !propertyInfo.CanWrite || !AllowEdit || !CanEdit(propertyType);
-                }
-                else if (DataType.GetIsReadOnly())
-                {
-                    return true;
-                }
-            }
-            return !AllowEdit;
-        }
-
-        public int IndexOf(object dataItem)
-        {
-            if (DataSource is DataGridCollectionView cv)
-            {
-                return cv.IndexOf(dataItem);
-            }
-
-            IList list = List;
-            if (list != null)
-            {
-                return list.IndexOf(dataItem);
-            }
-
-            IEnumerable enumerable = DataSource;
-            if (enumerable != null && dataItem != null)
-            {
-                int index = 0;
-                foreach (object dataItemTmp in enumerable)
-                {
-                    if ((dataItem == null && dataItemTmp == null) ||
-                        dataItem.Equals(dataItemTmp))
-                    {
-                        return index;
-                    }
-                    index++;
-                }
-            }
-            return -1;
-        }
-
-        internal void ClearDataProperties()
-        {
-            _dataProperties = null;
-        }
-
-        /// <summary>
-        /// Creates a collection view around the DataGrid's source. ICollectionViewFactory is
-        /// used if the source implements it. Otherwise a PagedCollectionView is returned.
-        /// </summary>
-        /// <param name="source">Enumerable source for which to create a view</param>
-        /// <returns>ICollectionView view over the provided source</returns>
-        internal static IDataGridCollectionView CreateView(IEnumerable source)
-        {
-            Debug.Assert(source != null, "source unexpectedly null");
-            Debug.Assert(!(source is IDataGridCollectionView), "source is an ICollectionView");
-
-            IDataGridCollectionView collectionView = null;
-
-            if (source is IDataGridCollectionViewFactory collectionViewFactory)
-            {
-                // If the source is a collection view factory, give it a chance to produce a custom collection view.
-                collectionView = collectionViewFactory.CreateView();
-                // Intentionally not catching potential exception thrown by ICollectionViewFactory.CreateView().
-            }
-            if (collectionView == null)
-            {
-                // If we still do not have a collection view, default to a PagedCollectionView.
-                collectionView = new DataGridCollectionView(source);
-            }
-            return collectionView;
-        }
-
-        internal static bool DataTypeIsPrimitive(Type dataType)
-        {
-            if (dataType != null)
-            {
-                Type type = TypeHelper.GetNonNullableType(dataType);  // no-opt if dataType isn't nullable
-                return type.IsPrimitive || type == typeof(string) || type == typeof(DateTime) || type == typeof(Decimal);
-            }
-            else
-            {
-                return false;
-            }
-        }
-
-        internal void MoveCurrentTo(object item, int backupSlot, int columnIndex, DataGridSelectionAction action, bool scrollIntoView)
-        {
-            if (CollectionView != null)
-            {
-                _expectingCurrentChanged = true;
-                _columnForCurrentChanged = columnIndex;
-                _itemToSelectOnCurrentChanged = item;
-                _selectionActionForCurrentChanged = action;
-                _scrollForCurrentChanged = scrollIntoView;
-                _backupSlotForCurrentChanged = backupSlot;
-
-                CollectionView.MoveCurrentTo(item is DataGridCollectionViewGroup ? null : item);
-
-                _expectingCurrentChanged = false;
-            }
-        }
-
-        internal void UnWireEvents(IEnumerable value)
-        {
-            if (value is INotifyCollectionChanged notifyingDataSource)
-            {
-                notifyingDataSource.CollectionChanged -= NotifyingDataSource_CollectionChanged;
-            }
-
-            if (SortDescriptions != null)
-            {
-                SortDescriptions.CollectionChanged -= CollectionView_SortDescriptions_CollectionChanged;
-            }
-
-            if (CollectionView != null)
-            {
-                CollectionView.CurrentChanged -= CollectionView_CurrentChanged;
-                CollectionView.CurrentChanging -= CollectionView_CurrentChanging;
-            }
-
-            EventsWired = false;
-        }
-
-        internal void WireEvents(IEnumerable value)
-        {
-            if (value is INotifyCollectionChanged notifyingDataSource)
-            {
-                notifyingDataSource.CollectionChanged += NotifyingDataSource_CollectionChanged;
-            }
-
-            if (SortDescriptions != null)
-            {
-                SortDescriptions.CollectionChanged += CollectionView_SortDescriptions_CollectionChanged;
-            }
-
-            if (CollectionView != null)
-            {
-                CollectionView.CurrentChanged += CollectionView_CurrentChanged;
-                CollectionView.CurrentChanging += CollectionView_CurrentChanging;
-            }
-
-            EventsWired = true;
-        }
-
-        private void CollectionView_CurrentChanged(object sender, EventArgs e)
-        {
-            if (_expectingCurrentChanged)
-            {
-                // Committing Edit could cause our item to move to a group that no longer exists.  In
-                // this case, we need to update the item.
-                if (_itemToSelectOnCurrentChanged is DataGridCollectionViewGroup collectionViewGroup)
-                {
-                    DataGridRowGroupInfo groupInfo = _owner.RowGroupInfoFromCollectionViewGroup(collectionViewGroup);
-                    if (groupInfo == null)
-                    {
-                        // Move to the next slot if the target slot isn't visible                        
-                        if (!_owner.IsSlotVisible(_backupSlotForCurrentChanged))
-                        {
-                            _backupSlotForCurrentChanged = _owner.GetNextVisibleSlot(_backupSlotForCurrentChanged);
-                        }
-                        // Move to the next best slot if we've moved past all the slots.  This could happen if multiple
-                        // groups were removed.
-                        if (_backupSlotForCurrentChanged >= _owner.SlotCount)
-                        {
-                            _backupSlotForCurrentChanged = _owner.GetPreviousVisibleSlot(_owner.SlotCount);
-                        }
-                        // Update the itemToSelect
-                        int newCurrentPosition = -1;
-                        _itemToSelectOnCurrentChanged = _owner.ItemFromSlot(_backupSlotForCurrentChanged, ref newCurrentPosition);
-                    }
-                }
-
-                _owner.ProcessSelectionAndCurrency(
-                    _columnForCurrentChanged,
-                    _itemToSelectOnCurrentChanged,
-                    _backupSlotForCurrentChanged,
-                    _selectionActionForCurrentChanged,
-                    _scrollForCurrentChanged);
-            }
-            else if (CollectionView != null)
-            {
-                _owner.UpdateStateOnCurrentChanged(CollectionView.CurrentItem, CollectionView.CurrentPosition);
-            }
-        }
-
-        private void CollectionView_CurrentChanging(object sender, DataGridCurrentChangingEventArgs e)
-        {
-            if (_owner.NoCurrentCellChangeCount == 0 &&
-                !_expectingCurrentChanged &&
-                !CommittingEdit &&
-                !_owner.CommitEdit())
-            {
-                // If CommitEdit failed, then the user has most likely input invalid data.
-                // We should cancel the current change if we can, otherwise we have to abort the edit.
-                if (e.IsCancelable)
-                {
-                    e.Cancel = true;
-                }
-                else
-                {
-                    _owner.CancelEdit(DataGridEditingUnit.Row, false);
-                }
-            }
-        }
-
-        private void CollectionView_SortDescriptions_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
-        {
-            if (_owner.ColumnsItemsInternal.Count == 0)
-            {
-                return;
-            }
-
-            // refresh sort description
-            foreach (DataGridColumn column in _owner.ColumnsItemsInternal)
-            {
-                column.HeaderCell.UpdatePseudoClasses();
-            }
-        }
-
-        private void NotifyingDataSource_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
-        {
-            if (_owner.LoadingOrUnloadingRow)
-            {
-                throw DataGridError.DataGrid.CannotChangeItemsWhenLoadingRows();
-            }
-            switch (e.Action)
-            {
-                case NotifyCollectionChangedAction.Add:
-                    Debug.Assert(e.NewItems != null, "Unexpected NotifyCollectionChangedAction.Add notification");
-                    if (ShouldAutoGenerateColumns)
-                    {
-                        // The columns are also affected (not just rows) in this case so we need to reset everything
-                        _owner.InitializeElements(false /*recycleRows*/);
-                    }
-                    else if (!IsGrouping)
-                    {
-                        // If we're grouping then we handle this through the CollectionViewGroup notifications
-                        // According to WPF, Add is a single item operation
-                        Debug.Assert(e.NewItems.Count == 1);
-                        _owner.InsertRowAt(e.NewStartingIndex);
-                    }
-                    break;
-                case NotifyCollectionChangedAction.Remove:
-                    IList removedItems = e.OldItems;
-                    if (removedItems == null || e.OldStartingIndex < 0)
-                    {
-                        Debug.Assert(false, "Unexpected NotifyCollectionChangedAction.Remove notification");
-                        return;
-                    }
-                    if (!IsGrouping)
-                    {
-                        // If we're grouping then we handle this through the CollectionViewGroup notifications
-                        // According to WPF, Remove is a single item operation
-                        foreach (object item in e.OldItems)
-                        {
-                            Debug.Assert(item != null);
-                            _owner.RemoveRowAt(e.OldStartingIndex, item);
-                        }
-                    }
-                    break;
-                case NotifyCollectionChangedAction.Replace:
-                    throw new NotSupportedException(); // 
-
-                case NotifyCollectionChangedAction.Reset:
-                    // Did the data type change during the reset?  If not, we can recycle
-                    // the existing rows instead of having to clear them all.  We still need to clear our cached
-                    // values for DataType and DataProperties, though, because the collection has been reset.
-                    Type previousDataType = _dataType;
-                    _dataType = null;
-                    if (previousDataType != DataType)
-                    {
-                        ClearDataProperties();
-                        _owner.InitializeElements(false /*recycleRows*/);
-                    }
-                    else
-                    {
-                        _owner.InitializeElements(!ShouldAutoGenerateColumns /*recycleRows*/);
-                    }
-                    break;
-            }
-
-            _owner.UpdatePseudoClasses();
-        }
-
-        private void UpdateDataProperties()
-        {
-            Type dataType = DataType;
-
-            if (DataSource != null && dataType != null && !DataTypeIsPrimitive(dataType))
-            {
-                _dataProperties = dataType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
-                Debug.Assert(_dataProperties != null);
-            }
-            else
-            {
-                _dataProperties = null;
-            }
-        }
-
-    }
-}

+ 0 - 364
src/Avalonia.Controls.DataGrid/DataGridDisplayData.cs

@@ -1,364 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Media;
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-
-namespace Avalonia.Controls
-{
-    internal class DataGridDisplayData
-    {
-        private Stack<DataGridRow> _fullyRecycledRows; // list of Rows that have been fully recycled (Collapsed)
-        private int _headScrollingElements; // index of the row in _scrollingRows that is the first displayed row
-        private DataGrid _owner;
-        private Stack<DataGridRow> _recyclableRows; // list of Rows which have not been fully recycled (avoids Measure in several cases)
-        private List<Control> _scrollingElements; // circular list of displayed elements
-        private Stack<DataGridRowGroupHeader> _fullyRecycledGroupHeaders; // list of GroupHeaders that have been fully recycled (Collapsed)
-        private Stack<DataGridRowGroupHeader> _recyclableGroupHeaders; // list of GroupHeaders which have not been fully recycled (avoids Measure in several cases)
-
-        public DataGridDisplayData(DataGrid owner)
-        {
-            _owner = owner;
-
-            ResetSlotIndexes();
-            FirstDisplayedScrollingCol = -1;
-            LastTotallyDisplayedScrollingCol = -1;
-
-            _scrollingElements = new List<Control>();
-            _recyclableRows = new Stack<DataGridRow>();
-            _fullyRecycledRows = new Stack<DataGridRow>();
-            _recyclableGroupHeaders = new Stack<DataGridRowGroupHeader>();
-            _fullyRecycledGroupHeaders = new Stack<DataGridRowGroupHeader>();
-        }
-
-        public int FirstDisplayedScrollingCol
-        {
-            get;
-            set;
-        }
-
-        public int FirstScrollingSlot
-        {
-            get;
-            set;
-        }
-
-        public int LastScrollingSlot
-        {
-            get;
-            set;
-        }
-
-        public int LastTotallyDisplayedScrollingCol
-        {
-            get;
-            set;
-        }
-
-        public int NumDisplayedScrollingElements
-        {
-            get
-            {
-                return _scrollingElements.Count;
-            }
-        }
-
-        public int NumTotallyDisplayedScrollingElements
-        {
-            get;
-            set;
-        }
-
-        internal double PendingVerticalScrollHeight
-        {
-            get;
-            set;
-        }
-
-        internal void AddRecyclableRow(DataGridRow row)
-        {
-            Debug.Assert(!_recyclableRows.Contains(row));
-            row.DetachFromDataGrid(true);
-            _recyclableRows.Push(row);
-        }
-
-        internal DataGridRowGroupHeader GetUsedGroupHeader()
-        {
-            if (_recyclableGroupHeaders.Count > 0)
-            {
-                return _recyclableGroupHeaders.Pop();
-            }
-            else if (_fullyRecycledGroupHeaders.Count > 0)
-            {
-                // For fully recycled rows, we need to set the Visibility back to Visible
-                DataGridRowGroupHeader groupHeader = _fullyRecycledGroupHeaders.Pop();
-                groupHeader.IsVisible = true;
-                return groupHeader;
-            }
-            return null;
-        }
-
-        internal void AddRecylableRowGroupHeader(DataGridRowGroupHeader groupHeader)
-        {
-            Debug.Assert(!_recyclableGroupHeaders.Contains(groupHeader));
-            groupHeader.IsRecycled = true;
-            _recyclableGroupHeaders.Push(groupHeader);
-        }
-
-        internal void ClearElements(bool recycle)
-        {
-            ResetSlotIndexes();
-            if (recycle)
-            {
-                foreach (Control element in _scrollingElements)
-                {
-                    if (element is DataGridRow row)
-                    {
-                        if (row.IsRecyclable)
-                        {
-                            AddRecyclableRow(row);
-                        }
-                        else
-                        {
-                            row.Clip = new RectangleGeometry();
-                        }
-                    }
-                    else if (element is DataGridRowGroupHeader groupHeader)
-                    {
-                        AddRecylableRowGroupHeader(groupHeader);
-                    }
-                }
-            }
-            else
-            {
-                _recyclableRows.Clear();
-                _fullyRecycledRows.Clear();
-                _recyclableGroupHeaders.Clear();
-                _fullyRecycledGroupHeaders.Clear();
-            }
-            _scrollingElements.Clear();
-        }
-
-        internal void CorrectSlotsAfterDeletion(int slot, bool wasCollapsed)
-        {
-            if (wasCollapsed)
-            {
-                if (slot > FirstScrollingSlot)
-                {
-                    LastScrollingSlot--;
-                }
-            }
-            else if (_owner.IsSlotVisible(slot))
-            {
-                UnloadScrollingElement(slot, true /*updateSlotInformation*/, true /*wasDeleted*/);
-            }
-            // This cannot be an else condition because if there are 2 rows left, and you delete the first one
-            // then these indexes need to be updated as well
-            if (slot < FirstScrollingSlot)
-            {
-                FirstScrollingSlot--;
-                LastScrollingSlot--;
-            }
-        }
-
-        internal void CorrectSlotsAfterInsertion(int slot, Control element, bool isCollapsed)
-        {
-            if (slot < FirstScrollingSlot)
-            {
-                // The row was inserted above our viewport, just update our indexes
-                FirstScrollingSlot++;
-                LastScrollingSlot++;
-            }
-            else if (isCollapsed && (slot <= LastScrollingSlot))
-            {
-                LastScrollingSlot++;
-            }
-            else if ((_owner.GetPreviousVisibleSlot(slot) <= LastScrollingSlot) || (LastScrollingSlot == -1))
-            {
-                Debug.Assert(element != null);
-                // The row was inserted in our viewport, add it as a scrolling row
-                LoadScrollingSlot(slot, element, true /*updateSlotInformation*/);
-            }
-        }
-
-        private int GetCircularListIndex(int slot, bool wrap)
-        {
-            int index = slot - FirstScrollingSlot - _headScrollingElements - _owner.GetCollapsedSlotCount(FirstScrollingSlot, slot);
-            return wrap ? index % _scrollingElements.Count : index;
-        }
-
-        internal void FullyRecycleElements()
-        {
-            // Fully recycle Recyclable rows and transfer them to Recycled rows
-            while (_recyclableRows.Count > 0)
-            {
-                DataGridRow row = _recyclableRows.Pop();
-                Debug.Assert(row != null);
-                row.IsVisible = false;
-                Debug.Assert(!_fullyRecycledRows.Contains(row));
-                _fullyRecycledRows.Push(row);
-            }
-            // Fully recycle Recyclable GroupHeaders and transfer them to Recycled GroupHeaders
-            while (_recyclableGroupHeaders.Count > 0)
-            {
-                DataGridRowGroupHeader groupHeader = _recyclableGroupHeaders.Pop();
-                Debug.Assert(groupHeader != null);
-                groupHeader.IsVisible = false;
-                Debug.Assert(!_fullyRecycledGroupHeaders.Contains(groupHeader));
-                _fullyRecycledGroupHeaders.Push(groupHeader);
-            }
-        }
-
-        internal Control GetDisplayedElement(int slot)
-        {
-            Debug.Assert(slot >= FirstScrollingSlot);
-            Debug.Assert(slot <= LastScrollingSlot);
-
-            return _scrollingElements[GetCircularListIndex(slot, true /*wrap*/)];
-        }
-
-        internal DataGridRow GetDisplayedRow(int rowIndex)
-        {
-
-            return GetDisplayedElement(_owner.SlotFromRowIndex(rowIndex)) as DataGridRow;
-        }
-
-        // Returns an enumeration of the displayed scrolling rows in order starting with the FirstDisplayedScrollingRow
-        internal IEnumerable<Control> GetScrollingElements()
-        {
-            return GetScrollingElements(null);
-        }
-
-        internal IEnumerable<Control> GetScrollingElements(Predicate<object> filter)
-        {
-            for (int i = 0; i < _scrollingElements.Count; i++)
-            {
-                Control element = _scrollingElements[(_headScrollingElements + i) % _scrollingElements.Count];
-                if (filter == null || filter(element))
-                {
-                    // _scrollingRows is a circular list that wraps
-                    yield return element;
-                }
-            }
-        }
-
-        internal IEnumerable<Control> GetScrollingRows()
-        {
-            return GetScrollingElements(element => element is DataGridRow);
-        }
-
-        internal DataGridRow GetUsedRow()
-        {
-            if (_recyclableRows.Count > 0)
-            {
-                return _recyclableRows.Pop();
-            }
-            else if (_fullyRecycledRows.Count > 0)
-            {
-                // For fully recycled rows, we need to set the Visibility back to Visible
-                DataGridRow row = _fullyRecycledRows.Pop();
-                row.IsVisible = true;
-                return row;
-            }
-            return null;
-        }
-
-        // Tracks the row at index rowIndex as a scrolling row
-        internal void LoadScrollingSlot(int slot, Control element, bool updateSlotInformation)
-        {
-            if (_scrollingElements.Count == 0)
-            {
-                SetScrollingSlots(slot);
-                _scrollingElements.Add(element);
-            }
-            else
-            {
-                // The slot should be adjacent to the other slots being displayed
-                Debug.Assert(slot >= _owner.GetPreviousVisibleSlot(FirstScrollingSlot) && slot <= _owner.GetNextVisibleSlot(LastScrollingSlot));
-                if (updateSlotInformation)
-                {
-                    if (slot < FirstScrollingSlot)
-                    {
-                        FirstScrollingSlot = slot;
-                    }
-                    else
-                    {
-                        LastScrollingSlot = _owner.GetNextVisibleSlot(LastScrollingSlot);
-                    }
-                }
-                int insertIndex = GetCircularListIndex(slot, false /*wrap*/);
-                if (insertIndex > _scrollingElements.Count)
-                {
-                    // We need to wrap around from the bottom to the top of our circular list; as a result the head of the list moves forward
-                    insertIndex -= _scrollingElements.Count;
-                    _headScrollingElements++;
-                }
-                _scrollingElements.Insert(insertIndex, element);
-            }
-        }
-
-        private void ResetSlotIndexes()
-        {
-            SetScrollingSlots(-1);
-            NumTotallyDisplayedScrollingElements = 0;
-            _headScrollingElements = 0;
-        }
-
-        private void SetScrollingSlots(int newValue)
-        {
-            FirstScrollingSlot = newValue;
-            LastScrollingSlot = newValue;
-        }
-
-        // Stops tracking the element at the given slot as a scrolling element
-        internal void UnloadScrollingElement(int slot, bool updateSlotInformation, bool wasDeleted)
-        {
-            Debug.Assert(_owner.IsSlotVisible(slot));
-            int elementIndex = GetCircularListIndex(slot, false /*wrap*/);
-            if (elementIndex > _scrollingElements.Count)
-            {
-                // We need to wrap around from the top to the bottom of our circular list
-                elementIndex -= _scrollingElements.Count;
-                _headScrollingElements--;
-            }
-            _scrollingElements.RemoveAt(elementIndex);
-
-            if (updateSlotInformation)
-            {
-                if (slot == FirstScrollingSlot && !wasDeleted)
-                {
-                    FirstScrollingSlot = _owner.GetNextVisibleSlot(FirstScrollingSlot);
-                }
-                else
-                {
-                    LastScrollingSlot = _owner.GetPreviousVisibleSlot(LastScrollingSlot);
-                }
-                if (LastScrollingSlot < FirstScrollingSlot)
-                {
-                    ResetSlotIndexes();
-                }
-            }
-        }
-
-#if DEBUG
-        internal void PrintDisplay()
-        {
-            foreach (Control element in GetScrollingElements())
-            {
-                if (element is DataGridRow row)
-                {
-                    Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Slot: {0} Row: {1} ", row.Slot, row.Index));
-                }
-                else if (element is DataGridRowGroupHeader groupHeader)
-                {
-                    Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Slot: {0} GroupHeader: {1}", groupHeader.RowGroupInfo.Slot, groupHeader.RowGroupInfo.CollectionViewGroup.Key));
-                }
-            }
-        }
-#endif
-    }
-}

+ 0 - 106
src/Avalonia.Controls.DataGrid/DataGridEnumerations.cs

@@ -1,106 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System;
-
-namespace Avalonia.Controls
-{
-    /// <summary>
-    /// Used to specify action to take out of edit mode.
-    /// </summary>
-    public enum DataGridEditAction
-    {
-        /// <summary>
-        /// Cancel the changes.
-        /// </summary>
-        Cancel,
-
-        /// <summary>
-        /// Commit edited value.
-        /// </summary>
-        Commit
-    }
-
-    // Determines the location and visibility of the editing row.
-    internal enum DataGridEditingRowLocation
-    {
-        Bottom = 0, // The editing row is collapsed below the displayed rows
-        Inline = 1, // The editing row is visible and displayed
-        Top = 2     // The editing row is collapsed above the displayed rows
-    }
-
-    /// <summary>
-    /// Determines whether the inner cells' vertical/horizontal gridlines are shown or not.
-    /// </summary>
-    [Flags]
-    public enum DataGridGridLinesVisibility
-    {
-        None = 0,
-        Horizontal = 1,
-        Vertical = 2,
-        All = 3,
-    }
-
-    public enum DataGridEditingUnit
-    {
-        Cell = 0,
-        Row = 1,
-    }
-
-    /// <summary>
-    /// Determines whether the row/column headers are shown or not.
-    /// </summary>
-    [Flags]
-    public enum DataGridHeadersVisibility
-    {
-        /// <summary>
-        /// Show Row, Column, and Corner Headers
-        /// </summary>
-        All = Row | Column,
-
-        /// <summary>
-        /// Show only Column Headers with top-right corner Header
-        /// </summary>
-        Column = 0x01,
-
-        /// <summary>
-        /// Show only Row Headers with bottom-left corner
-        /// </summary>
-        Row = 0x02,
-
-        /// <summary>
-        /// Don’t show any Headers
-        /// </summary>
-        None = 0x00
-    }
-
-    public enum DataGridRowDetailsVisibilityMode
-    {
-        Collapsed = 2,          // Show no details.  Developer is in charge of toggling visibility.
-        Visible = 1,	        // Show the details section for all rows.
-        VisibleWhenSelected = 0	// Show the details section only for the selected row(s).
-    }
-
-    /// <summary>
-    /// Determines the type of action to take when selecting items
-    /// </summary>
-    internal enum DataGridSelectionAction
-    {
-        AddCurrentToSelection,
-        None,
-        RemoveCurrentFromSelection,
-        SelectCurrent,
-        SelectFromAnchorToCurrent
-    }
-
-    /// <summary>
-    /// Determines the selection model
-    /// </summary>
-    public enum DataGridSelectionMode
-    {
-        Extended = 0,
-        Single = 1
-    }
-}

+ 0 - 190
src/Avalonia.Controls.DataGrid/DataGridError.cs

@@ -1,190 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System;
-using System.Globalization;
-
-namespace Avalonia.Controls
-{
-
-    internal static class DataGridError
-    {
-        public static class DataGrid
-        {
-            public static InvalidOperationException CannotChangeItemsWhenLoadingRows()
-            {
-                return new InvalidOperationException("Items cannot be added, removed or reset while rows are loading or unloading.");
-            }
-
-            public static InvalidOperationException CannotChangeColumnCollectionWhileAdjustingDisplayIndexes()
-            {
-                return new InvalidOperationException("Column collection cannot be changed while adjusting display indexes.");
-            }
-
-            public static InvalidOperationException ColumnCannotBeCollapsed()
-            {
-                return new InvalidOperationException("Column cannot be collapsed.");
-            }
-
-            public static InvalidOperationException ColumnCannotBeReassignedToDifferentDataGrid()
-            {
-                return new InvalidOperationException("Column already belongs to a DataGrid instance and cannot be reassigned.");
-            }
-
-            public static ArgumentException ColumnNotInThisDataGrid()
-            {
-                return new ArgumentException("Provided column does not belong to this DataGrid.");
-            }
-
-            public static ArgumentException ItemIsNotContainedInTheItemsSource(string paramName)
-            {
-                return new ArgumentException("The item is not contained in the ItemsSource.", paramName);
-            }
-
-            public static InvalidOperationException NoCurrentRow()
-            {
-                return new InvalidOperationException("There is no current row.  Operation cannot be completed.");
-            }
-
-            public static InvalidOperationException NoOwningGrid(Type type)
-            {
-                return new InvalidOperationException(Format("There is no instance of DataGrid assigned to this {0}.  Operation cannot be completed.", type.FullName));
-            }
-
-            public static InvalidOperationException UnderlyingPropertyIsReadOnly(string paramName)
-            {
-                return new InvalidOperationException(Format("{0} cannot be set because the underlying property is read only.", paramName));
-            }
-
-            public static ArgumentException ValueCannotBeSetToInfinity(string paramName)
-            {
-                return new ArgumentException(Format("{0} cannot be set to infinity.", paramName));
-            }
-
-            public static ArgumentException ValueCannotBeSetToNAN(string paramName)
-            {
-                return new ArgumentException(Format("{0} cannot be set to double.NAN.", paramName));
-            }
-
-            public static ArgumentNullException ValueCannotBeSetToNull(string paramName, string valueName)
-            {
-                return new ArgumentNullException(paramName, Format("{0} cannot be set to a null value.", valueName));
-            }
-
-            public static ArgumentException ValueIsNotAnInstanceOf(string paramName, Type type)
-            {
-                return new ArgumentException(paramName, Format("The value is not an instance of {0}.", type.FullName));
-            }
-
-            public static ArgumentException ValueIsNotAnInstanceOfEitherOr(string paramName, Type type1, Type type2)
-            {
-                return new ArgumentException(paramName, Format("The value is not an instance of {0} or {1}.", type1.FullName, type2.FullName));
-            }
-
-            public static ArgumentOutOfRangeException ValueMustBeBetween(string paramName, string valueName, object lowValue, bool lowInclusive, object highValue, bool highInclusive)
-            {
-                string message = null;
-
-                if (lowInclusive && highInclusive)
-                {
-                    message = "{0} must be greater than or equal to {1} and less than or equal to {2}.";
-                }
-                else if (lowInclusive && !highInclusive)
-                {
-                    message = "{0} must be greater than or equal to {1} and less than {2}.";
-                }
-                else if (!lowInclusive && highInclusive)
-                {
-                    message = "{0} must be greater than {1} and less than or equal to {2}.";
-                }
-                else
-                {
-                    message = "{0} must be greater than {1} and less than {2}.";
-                }
-
-                return new ArgumentOutOfRangeException(paramName, Format(message, valueName, lowValue, highValue));
-            }
-
-            public static ArgumentOutOfRangeException ValueMustBeGreaterThanOrEqualTo(string paramName, string valueName, object value)
-            {
-                return new ArgumentOutOfRangeException(paramName, Format("{0} must be greater than or equal to {1}.", valueName, value));
-            }
-
-            public static ArgumentOutOfRangeException ValueMustBeLessThanOrEqualTo(string paramName, string valueName, object value)
-            {
-                return new ArgumentOutOfRangeException(paramName, Format("{0} must be less than or equal to {1}.", valueName, value));
-            }
-
-            public static ArgumentOutOfRangeException ValueMustBeLessThan(string paramName, string valueName, object value)
-            {
-                return new ArgumentOutOfRangeException(paramName, Format("{0} must be less than {1}.", valueName, value));
-            }
-
-        }
-
-        public static class DataGridColumnHeader
-        {
-            public static NotSupportedException ContentDoesNotSupportUIElements()
-            {
-                return new NotSupportedException("Content does not support Controls; use ContentTemplate instead.");
-            }
-        }
-
-        public static class DataGridLength
-        {
-            public static ArgumentException InvalidUnitType(string paramName)
-            {
-                return new ArgumentException(Format("{0} is not a valid DataGridLengthUnitType.", paramName), paramName);
-            }
-        }
-
-        public static class DataGridLengthConverter
-        {
-            public static NotSupportedException CannotConvertFrom(string paramName)
-            {
-                return new NotSupportedException(Format("DataGridLengthConverter cannot convert from {0}.", paramName));
-            }
-
-            public static NotSupportedException CannotConvertTo(string paramName)
-            {
-                return new NotSupportedException(Format("Cannot convert from DataGridLength to {0}.", paramName));
-            }
-
-            public static NotSupportedException InvalidDataGridLength(string paramName)
-            {
-                return new NotSupportedException(Format("Invalid DataGridLength.", paramName));
-            }
-        }
-
-        public static class DataGridRow
-        {
-            public static InvalidOperationException InvalidRowIndexCannotCompleteOperation()
-            {
-                return new InvalidOperationException("Invalid row index. Operation cannot be completed.");
-            }
-        }
-
-        public static class DataGridSelectedItemsCollection
-        {
-            public static InvalidOperationException CannotChangeSelectedItemsCollectionInSingleMode()
-            {
-                return new InvalidOperationException("Can only change SelectedItems collection in Extended selection mode.  Use SelectedItem property in Single selection mode.");
-            }
-        }
-
-        public static class DataGridTemplateColumn
-        {
-            public static TypeInitializationException MissingTemplateForType(Type type)
-            {
-                return new TypeInitializationException(Format("Missing template.  Cannot initialize {0}.", type.FullName), null);
-            }
-        }
-
-        private static string Format(string formatString, params object[] args)
-        {
-            return String.Format(CultureInfo.CurrentCulture, formatString, args);
-        }
-    }
-}

+ 0 - 70
src/Avalonia.Controls.DataGrid/DataGridFillerColumn.cs

@@ -1,70 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Controls.Utils;
-using Avalonia.Interactivity;
-using Avalonia.Utilities;
-
-namespace Avalonia.Controls
-{
-    internal class DataGridFillerColumn : DataGridColumn
-    {
-        public DataGridFillerColumn(DataGrid owningGrid)
-        {
-            IsReadOnly = true;
-            OwningGrid = owningGrid;
-            MinWidth = 0;
-            MaxWidth = int.MaxValue;
-        }
-
-        internal double FillerWidth
-        {
-            get;
-            set;
-        }
-
-        // True if there is room for the filler column; otherwise, false
-        internal bool IsActive
-        {
-            get
-            {
-                return FillerWidth > 0;
-            }
-        }
-
-        // True if the FillerColumn's header cell is contained in the visual tree
-        internal bool IsRepresented
-        {
-            get;
-            set;
-        } 
-
-        internal override DataGridColumnHeader CreateHeader()
-        {
-            DataGridColumnHeader headerCell = base.CreateHeader();
-            if (headerCell != null)
-            {
-                headerCell.IsEnabled = false;
-            }
-            return headerCell;
-        }
-
-        protected override Control GenerateElement(DataGridCell cell, object dataItem)
-        {
-            return null;
-        }
-
-        protected override Control GenerateEditingElement(DataGridCell cell, object dataItem, out ICellEditBinding editBinding)
-        {
-            editBinding = null;
-            return null;
-        } 
-
-        protected override object PrepareCellForEdit(Control editingElement, RoutedEventArgs editingEventArgs)
-        {
-            return null;
-        }
-    }
-}

+ 0 - 541
src/Avalonia.Controls.DataGrid/DataGridLength.cs

@@ -1,541 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Utilities;
-using System;
-using System.ComponentModel;
-using System.Globalization;
-
-namespace Avalonia.Controls
-{
-    public enum DataGridLengthUnitType
-    {
-        Auto = 0,
-        Pixel = 1,
-        SizeToCells = 2,
-        SizeToHeader = 3,
-        Star = 4
-    }
-
-    /// <summary>
-    /// Represents the lengths of elements within the <see cref="T:Avalonia.Controls.DataGrid" /> control.
-    /// </summary>
-    [TypeConverter(typeof(DataGridLengthConverter))]
-    public struct DataGridLength : IEquatable<DataGridLength>
-    {
-
-        private double _desiredValue;   //  desired value storage
-        private double _displayValue;   //  display value storage
-        private double _unitValue;      //  unit value storage
-        private DataGridLengthUnitType _unitType; //  unit type storage
-
-        //  static instances of value invariant DataGridLengths
-        private static readonly DataGridLength _auto = new DataGridLength(DATAGRIDLENGTH_DefaultValue, DataGridLengthUnitType.Auto);
-        private static readonly DataGridLength _sizeToCells = new DataGridLength(DATAGRIDLENGTH_DefaultValue, DataGridLengthUnitType.SizeToCells);
-        private static readonly DataGridLength _sizeToHeader = new DataGridLength(DATAGRIDLENGTH_DefaultValue, DataGridLengthUnitType.SizeToHeader);
-
-        // WPF uses 1.0 as the default value as well
-        internal const double DATAGRIDLENGTH_DefaultValue = 1.0;
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridLength" /> class. 
-        /// </summary>
-        /// <param name="value"></param>
-        public DataGridLength(double value)
-            : this(value, DataGridLengthUnitType.Pixel)
-        {
-        }
-        /// <summary>
-        ///     Initializes to a specified value and unit.
-        /// </summary>
-        /// <param name="value">The value to hold.</param>
-        /// <param name="type">The unit of <c>value</c>.</param>
-        /// <remarks> 
-        ///     <c>value</c> is ignored unless <c>type</c> is
-        ///     <c>DataGridLengthUnitType.Pixel</c> or
-        ///     <c>DataGridLengthUnitType.Star</c>
-        /// </remarks>
-        /// <exception cref="ArgumentException">
-        ///     If <c>value</c> parameter is <c>double.NaN</c>
-        ///     or <c>value</c> parameter is <c>double.NegativeInfinity</c>
-        ///     or <c>value</c> parameter is <c>double.PositiveInfinity</c>.
-        /// </exception>
-        public DataGridLength(double value, DataGridLengthUnitType type)
-            : this(value, type, (type == DataGridLengthUnitType.Pixel ? value : Double.NaN), (type == DataGridLengthUnitType.Pixel ? value : Double.NaN))
-        {
-        }
-
-        /// <summary>
-        ///     Initializes to a specified value and unit.
-        /// </summary>
-        /// <param name="value">The value to hold.</param>
-        /// <param name="type">The unit of <c>value</c>.</param>
-        /// <param name="desiredValue"></param>
-        /// <param name="displayValue"></param>
-        /// <remarks> 
-        ///     <c>value</c> is ignored unless <c>type</c> is
-        ///     <c>DataGridLengthUnitType.Pixel</c> or
-        ///     <c>DataGridLengthUnitType.Star</c>
-        /// </remarks>
-        /// <exception cref="ArgumentException">
-        ///     If <c>value</c> parameter is <c>double.NaN</c>
-        ///     or <c>value</c> parameter is <c>double.NegativeInfinity</c>
-        ///     or <c>value</c> parameter is <c>double.PositiveInfinity</c>.
-        /// </exception>
-        public DataGridLength(double value, DataGridLengthUnitType type, double desiredValue, double displayValue)
-        {
-            if (double.IsNaN(value))
-            {
-                throw DataGridError.DataGrid.ValueCannotBeSetToNAN("value");
-            }
-            if (double.IsInfinity(value))
-            {
-                throw DataGridError.DataGrid.ValueCannotBeSetToInfinity("value");
-            }
-            if (double.IsInfinity(desiredValue))
-            {
-                throw DataGridError.DataGrid.ValueCannotBeSetToInfinity("desiredValue");
-            }
-            if (double.IsInfinity(displayValue))
-            {
-                throw DataGridError.DataGrid.ValueCannotBeSetToInfinity("displayValue");
-            }
-            if (value < 0)
-            {
-                throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("value", "value", 0);
-            }
-            if (desiredValue < 0)
-            {
-                throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("desiredValue", "desiredValue", 0);
-            }
-            if (displayValue < 0)
-            {
-                throw DataGridError.DataGrid.ValueMustBeGreaterThanOrEqualTo("displayValue", "displayValue", 0);
-            }
-
-            if (type != DataGridLengthUnitType.Auto &&
-                type != DataGridLengthUnitType.SizeToCells &&
-                type != DataGridLengthUnitType.SizeToHeader &&
-                type != DataGridLengthUnitType.Star &&
-                type != DataGridLengthUnitType.Pixel)
-            {
-                throw DataGridError.DataGridLength.InvalidUnitType("type");
-            }
-
-            _desiredValue = desiredValue;
-            _displayValue = displayValue;
-            _unitValue = (type == DataGridLengthUnitType.Auto) ? DATAGRIDLENGTH_DefaultValue : value;
-            _unitType = type;
-        }
-
-        /// <summary>
-        /// Gets a <see cref="T:Avalonia.Controls.DataGridLength" /> structure that represents the standard automatic sizing mode.
-        /// </summary>
-        /// <returns>
-        /// A <see cref="T:Avalonia.Controls.DataGridLength" /> structure that represents the standard automatic sizing mode.
-        /// </returns>
-        public static DataGridLength Auto
-        {
-            get
-            {
-                return _auto;
-            }
-        }
-
-        /// <summary>
-        ///     Returns the desired value of this instance.
-        /// </summary>
-        public double DesiredValue
-        {
-            get
-            {
-                return _desiredValue;
-            }
-        }
-
-        /// <summary>
-        ///     Returns the display value of this instance.
-        /// </summary>
-        public double DisplayValue
-        {
-            get
-            {
-                return _displayValue;
-            }
-        }
-
-        /// <summary>
-        ///     Returns <c>true</c> if this DataGridLength instance holds 
-        ///     an absolute (pixel) value.
-        /// </summary>
-        public bool IsAbsolute
-        {
-            get
-            {
-                return _unitType == DataGridLengthUnitType.Pixel;
-            }
-        }
-
-        /// <summary>
-        ///     Returns <c>true</c> if this DataGridLength instance is 
-        ///     automatic (not specified).
-        /// </summary>
-        public bool IsAuto
-        {
-            get
-            {
-                return _unitType == DataGridLengthUnitType.Auto;
-            }
-        }
-
-        /// <summary>
-        ///     Returns <c>true</c> if this instance is to size to the cells of a column or row.
-        /// </summary>
-        public bool IsSizeToCells
-        {
-            get
-            {
-                return _unitType == DataGridLengthUnitType.SizeToCells;
-            }
-        }
-
-        /// <summary>
-        ///     Returns <c>true</c> if this instance is to size to the header of a column or row.
-        /// </summary>
-        public bool IsSizeToHeader
-        {
-            get
-            {
-                return _unitType == DataGridLengthUnitType.SizeToHeader;
-            }
-        }
-
-        /// <summary>
-        ///     Returns <c>true</c> if this DataGridLength instance holds a weighted proportion
-        ///     of available space.
-        /// </summary>
-        public bool IsStar
-        {
-            get
-            {
-                return _unitType == DataGridLengthUnitType.Star;
-            }
-        }
-
-        /// <summary>
-        /// Gets a <see cref="T:Avalonia.Controls.DataGridLength" /> structure that represents the cell-based automatic sizing mode.
-        /// </summary>
-        /// <returns>
-        /// A <see cref="T:Avalonia.Controls.DataGridLength" /> structure that represents the cell-based automatic sizing mode.
-        /// </returns>
-        public static DataGridLength SizeToCells
-        {
-            get
-            {
-                return _sizeToCells;
-            }
-        }
-
-        /// <summary>
-        /// Gets a <see cref="T:Avalonia.Controls.DataGridLength" /> structure that represents the header-based automatic sizing mode.
-        /// </summary>
-        /// <returns>
-        /// A <see cref="T:Avalonia.Controls.DataGridLength" /> structure that represents the header-based automatic sizing mode.
-        /// </returns>
-        public static DataGridLength SizeToHeader
-        {
-            get
-            {
-                return _sizeToHeader;
-            }
-        }
-
-        /// <summary>
-        /// Gets the <see cref="T:Avalonia.Controls.DataGridLengthUnitType" /> that represents the current sizing mode.
-        /// </summary>
-        public DataGridLengthUnitType UnitType
-        {
-            get
-            {
-                return _unitType;
-            }
-        }
-
-        /// <summary>
-        /// Gets the absolute value of the <see cref="T:Avalonia.Controls.DataGridLength" /> in pixels.
-        /// </summary>
-        /// <returns>
-        /// The absolute value of the <see cref="T:Avalonia.Controls.DataGridLength" /> in pixels.
-        /// </returns>
-        public double Value
-        {
-            get
-            {
-                return _unitValue;
-            }
-        }
-
-        /// <summary>
-        /// Overloaded operator, compares 2 DataGridLength's.
-        /// </summary>
-        /// <param name="gl1">first DataGridLength to compare.</param>
-        /// <param name="gl2">second DataGridLength to compare.</param>
-        /// <returns>true if specified DataGridLength have same value, 
-        /// unit type, desired value, and display value.</returns>
-        public static bool operator ==(DataGridLength gl1, DataGridLength gl2)
-        {
-            return (gl1.UnitType == gl2.UnitType
-                    && gl1.Value == gl2.Value
-                    && gl1.DesiredValue == gl2.DesiredValue
-                    && gl1.DisplayValue == gl2.DisplayValue);
-        }
-
-        /// <summary>
-        /// Overloaded operator, compares 2 DataGridLength's.
-        /// </summary>
-        /// <param name="gl1">first DataGridLength to compare.</param>
-        /// <param name="gl2">second DataGridLength to compare.</param>
-        /// <returns>true if specified DataGridLength have either different value, 
-        /// unit type, desired value, or display value.</returns>
-        public static bool operator !=(DataGridLength gl1, DataGridLength gl2)
-        {
-            return (gl1.UnitType != gl2.UnitType
-                    || gl1.Value != gl2.Value
-                    || gl1.DesiredValue != gl2.DesiredValue
-                    || gl1.DisplayValue != gl2.DisplayValue);
-        }
-
-        /// <summary>
-        /// Compares this instance of DataGridLength with another instance.
-        /// </summary>
-        /// <param name="other">DataGridLength length instance to compare.</param>
-        /// <returns><c>true</c> if this DataGridLength instance has the same value 
-        /// and unit type as gridLength.</returns>
-        public bool Equals(DataGridLength other)
-        {
-            return (this == other);
-        }
-
-        /// <summary>
-        /// Compares this instance of DataGridLength with another object.
-        /// </summary>
-        /// <param name="obj">Reference to an object for comparison.</param>
-        /// <returns><c>true</c> if this DataGridLength instance has the same value 
-        /// and unit type as oCompare.</returns>
-        public override bool Equals(object obj)
-        {
-            DataGridLength? dataGridLength = obj as DataGridLength?;
-            if (dataGridLength.HasValue)
-            {
-                return (this == dataGridLength);
-            }
-            return false;
-        }
-
-        /// <summary>
-        /// Returns a unique hash code for this DataGridLength
-        /// </summary>
-        /// <returns>hash code</returns>
-        public override int GetHashCode()
-        {
-            return ((int)_unitValue + (int)_unitType) + (int)_desiredValue + (int)_displayValue;
-        }
-
-    }
-    /// <summary>
-    /// DataGridLengthConverter - Converter class for converting instances of other types to and from DataGridLength instances.
-    /// </summary> 
-    public class DataGridLengthConverter : TypeConverter
-    {
-        private static string _starSuffix = "*";
-        private static string[] _valueInvariantUnitStrings = { "auto", "sizetocells", "sizetoheader" };
-        private static DataGridLength[] _valueInvariantDataGridLengths = { DataGridLength.Auto, DataGridLength.SizeToCells, DataGridLength.SizeToHeader };
-
-        /// <summary>
-        /// Checks whether or not this class can convert from a given type.
-        /// </summary>
-        /// <param name="context">
-        /// An ITypeDescriptorContext that provides a format context. 
-        /// </param>
-        /// <param name="sourceType">The Type being queried for support.</param>
-        /// <returns>
-        /// <c>true</c> if this converter can convert from the provided type, 
-        /// <c>false</c> otherwise.
-        /// </returns>
-        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
-        {
-            // We can only handle strings, integral and floating types
-            TypeCode tc = Type.GetTypeCode(sourceType);
-            switch (tc)
-            {
-                case TypeCode.String:
-                case TypeCode.Decimal:
-                case TypeCode.Single:
-                case TypeCode.Double:
-                case TypeCode.Int16:
-                case TypeCode.Int32:
-                case TypeCode.Int64:
-                case TypeCode.UInt16:
-                case TypeCode.UInt32:
-                case TypeCode.UInt64:
-                    return true;
-                default:
-                    return false;
-            }
-        }
-
-        /// <summary>
-        /// Checks whether or not this class can convert to a given type.
-        /// </summary>
-        /// <param name="context">
-        /// An ITypeDescriptorContext that provides a format context. 
-        /// </param>
-        /// <param name="destinationType">The Type being queried for support.</param>
-        /// <returns>
-        /// <c>true</c> if this converter can convert to the provided type, 
-        /// <c>false</c> otherwise.
-        /// </returns>
-        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
-        {
-            return destinationType == typeof(string);
-        }
-
-        /// <summary>
-        /// Attempts to convert to a DataGridLength from the given object.
-        /// </summary>
-        /// <param name="context">
-        /// An ITypeDescriptorContext that provides a format context. 
-        /// </param>
-        /// <param name="culture">
-        /// The CultureInfo to use for the conversion. 
-        /// </param>
-        /// <param name="value">The object to convert to a GridLength.</param>
-        /// <returns>
-        /// The GridLength instance which was constructed.
-        /// </returns>
-        /// <exception cref="NotSupportedException">
-        /// A NotSupportedException is thrown if the example object is null.
-        /// </exception>
-        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
-        {
-            // GridLengthConverter in WPF throws a NotSupportedException on a null value as well.
-            if (value == null)
-            {
-                throw DataGridError.DataGridLengthConverter.CannotConvertFrom("(null)");
-            }
-
-            if (value is string stringValue)
-            {
-                stringValue = stringValue.Trim();
-
-                if (stringValue.EndsWith(_starSuffix, StringComparison.Ordinal))
-                {
-                    string stringValueWithoutSuffix = stringValue.Substring(0, stringValue.Length - _starSuffix.Length);
-
-                    double starWeight;
-                    if (string.IsNullOrEmpty(stringValueWithoutSuffix))
-                    {
-                        starWeight = 1;
-                    }
-                    else
-                    {
-                        starWeight = Convert.ToDouble(stringValueWithoutSuffix, culture ?? CultureInfo.CurrentCulture);
-                    }
-
-                    return new DataGridLength(starWeight, DataGridLengthUnitType.Star);
-                }
-
-                for (int index = 0; index < _valueInvariantUnitStrings.Length; index++)
-                {
-                    if (stringValue.Equals(_valueInvariantUnitStrings[index], StringComparison.OrdinalIgnoreCase))
-                    {
-                        return _valueInvariantDataGridLengths[index];
-                    }
-                }
-            }
-
-            // Conversion from numeric type, WPF lets Convert exceptions bubble out here as well
-            double doubleValue = Convert.ToDouble(value, culture ?? CultureInfo.CurrentCulture);
-            if (double.IsNaN(doubleValue))
-            {
-                // WPF returns Auto in this case as well
-                return DataGridLength.Auto;
-            }
-            else
-            {
-                return new DataGridLength(doubleValue);
-            }
-        }
-
-        /// <summary>
-        /// Attempts to convert a DataGridLength instance to the given type.
-        /// </summary>
-        /// <param name="context">
-        /// An ITypeDescriptorContext that provides a format context. 
-        /// </param>
-        /// <param name="culture">
-        /// The CultureInfo to use for the conversion. 
-        /// </param>
-        /// <param name="value">The DataGridLength to convert.</param>
-        /// <param name="destinationType">The type to which to convert the DataGridLength instance.</param>
-        /// <returns>
-        /// The object which was constructed.
-        /// </returns>
-        /// <exception cref="ArgumentNullException">
-        /// An ArgumentNullException is thrown if the example object is null.
-        /// </exception>
-        /// <exception cref="NotSupportedException">
-        /// A NotSupportedException is thrown if the object is not null and is not a DataGridLength,
-        /// or if the destinationType isn't one of the valid destination types.
-        /// </exception>
-        ///<SecurityNote>
-        ///     Critical: calls InstanceDescriptor ctor which LinkDemands
-        ///     PublicOK: can only make an InstanceDescriptor for DataGridLength, not an arbitrary class
-        ///</SecurityNote> 
-        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
-        {
-            if (destinationType == null)
-            {
-                throw new ArgumentNullException(nameof(destinationType));
-            }
-            if (destinationType != typeof(string))
-            {
-                throw DataGridError.DataGridLengthConverter.CannotConvertTo(destinationType.ToString());
-            }
-            DataGridLength? dataGridLength = value as DataGridLength?;
-            if (!dataGridLength.HasValue)
-            {
-                throw DataGridError.DataGridLengthConverter.InvalidDataGridLength("value");
-            }
-            else
-            {
-                // Convert dataGridLength to a string
-                switch (dataGridLength.Value.UnitType)
-                {
-                    //  for Auto print out "Auto". value is always "1.0"
-                    case DataGridLengthUnitType.Auto:
-                        return "Auto";
-
-                    case DataGridLengthUnitType.SizeToHeader:
-                        return "SizeToHeader";
-
-                    case DataGridLengthUnitType.SizeToCells:
-                        return "SizeToCells";
-
-                    //  Star has one special case when value is "1.0".
-                    //  in this case drop value part and print only "Star"
-                    case DataGridLengthUnitType.Star:
-                        return (
-                            MathUtilities.AreClose(1.0, dataGridLength.Value.Value)
-                            ? _starSuffix
-                            : Convert.ToString(dataGridLength.Value.Value, culture ?? CultureInfo.CurrentCulture) + DataGridLengthConverter._starSuffix);
-
-                    default:
-                        return (Convert.ToString(dataGridLength.Value.Value, culture ?? CultureInfo.CurrentCulture));
-                }
-            }
-        }
-    }
-}

+ 0 - 1106
src/Avalonia.Controls.DataGrid/DataGridRow.cs

@@ -1,1106 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Controls.Metadata;
-using Avalonia.Controls.Primitives;
-using Avalonia.Controls.Shapes;
-using Avalonia.Controls.Templates;
-using Avalonia.Controls.Utils;
-using Avalonia.Data;
-using Avalonia.Input;
-using Avalonia.Media;
-using Avalonia.Utilities;
-using Avalonia.VisualTree;
-using System;
-using System.Diagnostics;
-using Avalonia.Automation.Peers;
-using Avalonia.Reactive;
-using Avalonia.Automation;
-
-namespace Avalonia.Controls
-{
-    /// <summary>
-    /// Represents a <see cref="T:Avalonia.Controls.DataGrid" /> row.
-    /// </summary>
-    [TemplatePart(DATAGRIDROW_elementBottomGridLine, typeof(Rectangle))]
-    [TemplatePart(DATAGRIDROW_elementCells,          typeof(DataGridCellsPresenter))]
-    [TemplatePart(DATAGRIDROW_elementDetails,        typeof(DataGridDetailsPresenter))]
-    [TemplatePart(DATAGRIDROW_elementRoot,           typeof(Panel))]
-    [TemplatePart(DATAGRIDROW_elementRowHeader,      typeof(DataGridRowHeader))]
-    [PseudoClasses(":selected", ":editing", ":invalid")]
-    public class DataGridRow : TemplatedControl
-    {
-
-        private const byte DATAGRIDROW_defaultMinHeight = 0;
-        internal const int DATAGRIDROW_maximumHeight = 65536;
-        internal const double DATAGRIDROW_minimumHeight = 0;
-
-        private const string DATAGRIDROW_elementBottomGridLine = "PART_BottomGridLine";
-        private const string DATAGRIDROW_elementCells = "PART_CellsPresenter";
-        private const string DATAGRIDROW_elementDetails = "PART_DetailsPresenter";
-        internal const string DATAGRIDROW_elementRoot = "PART_Root";
-        internal const string DATAGRIDROW_elementRowHeader = "PART_RowHeader";
-
-        private DataGridCellsPresenter _cellsElement;
-        private DataGridCell _fillerCell;
-        private DataGridRowHeader _headerElement;
-        private double _lastHorizontalOffset;
-        private int? _mouseOverColumnIndex;
-        private bool _isValid = true;
-        private Rectangle _bottomGridLine;
-        private bool _areHandlersSuspended;
-
-        // In the case where Details scales vertically when it's arranged at a different width, we
-        // get the wrong height measurement so we need to check it again after arrange
-        private bool _checkDetailsContentHeight;
-
-        // Optimal height of the details based on the Element created by the DataTemplate
-        private double _detailsDesiredHeight;
-
-        private bool _detailsLoaded;
-        private bool _detailsVisibilityNotificationPending;
-        private Control _detailsContent;
-        private IDisposable _detailsContentSizeSubscription;
-        private DataGridDetailsPresenter _detailsElement;
-        private bool _isSelected;
-
-        // Locally cache whether or not details are visible so we don't run redundant storyboards
-        // The Details Template that is actually applied to the Row
-        private IDataTemplate _appliedDetailsTemplate;
-
-        private bool? _appliedDetailsVisibility;
-
-        /// <summary>
-        /// Identifies the Header dependency property.
-        /// </summary>
-        public static readonly StyledProperty<object> HeaderProperty =
-            AvaloniaProperty.Register<DataGridRow, object>(nameof(Header));
-
-        /// <summary>
-        /// Gets or sets the row header.
-        /// </summary>
-        public object Header
-        {
-            get { return GetValue(HeaderProperty); }
-            set { SetValue(HeaderProperty, value); }
-        }
-
-        public static readonly DirectProperty<DataGridRow, bool> IsSelectedProperty =
-            AvaloniaProperty.RegisterDirect<DataGridRow, bool>(
-                nameof(IsSelected),
-                o => o.IsSelected,
-                (o, v) => o.IsSelected = v);
-
-        public bool IsSelected
-        {
-            get => _isSelected;
-            set => SetAndRaise(IsSelectedProperty, ref _isSelected, value);
-        }
-
-        public static readonly DirectProperty<DataGridRow, bool> IsValidProperty =
-            AvaloniaProperty.RegisterDirect<DataGridRow, bool>(
-                nameof(IsValid),
-                o => o.IsValid);
-
-        /// <summary>
-        /// Gets a value that indicates whether the data in a row is valid.
-        /// </summary>
-        public bool IsValid
-        {
-            get { return _isValid; }
-            internal set { SetAndRaise(IsValidProperty, ref _isValid, value); }
-        }
-
-        public static readonly StyledProperty<IDataTemplate> DetailsTemplateProperty =
-            AvaloniaProperty.Register<DataGridRow, IDataTemplate>(nameof(DetailsTemplate));
-
-        /// <summary>
-        /// Gets or sets the template that is used to display the details section of the row.
-        /// </summary>
-        public IDataTemplate DetailsTemplate
-        {
-            get { return GetValue(DetailsTemplateProperty); }
-            set { SetValue(DetailsTemplateProperty, value); }
-        }
-
-        public static readonly StyledProperty<bool> AreDetailsVisibleProperty =
-            AvaloniaProperty.Register<DataGridRow, bool>(nameof(AreDetailsVisible));
-
-        /// <summary>
-        /// Gets or sets a value that indicates when the details section of the row is displayed.
-        /// </summary>
-        public bool AreDetailsVisible
-        {
-            get { return GetValue(AreDetailsVisibleProperty); }
-            set { SetValue(AreDetailsVisibleProperty, value); }
-        }
-
-        static DataGridRow()
-        {
-            HeaderProperty.Changed.AddClassHandler<DataGridRow>((x, e) => x.OnHeaderChanged(e));
-            DetailsTemplateProperty.Changed.AddClassHandler<DataGridRow>((x, e) => x.OnDetailsTemplateChanged(e));
-            AreDetailsVisibleProperty.Changed.AddClassHandler<DataGridRow>((x, e) => x.OnAreDetailsVisibleChanged(e));
-            PointerPressedEvent.AddClassHandler<DataGridRow>((x, e) => x.DataGridRow_PointerPressed(e), handledEventsToo: true);
-            IsTabStopProperty.OverrideDefaultValue<DataGridRow>(false);
-            AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<DataGridRow>(IsOffscreenBehavior.FromClip);
-        }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridRow" /> class.
-        /// </summary>
-        public DataGridRow()
-        {
-            MinHeight = DATAGRIDROW_defaultMinHeight;
-
-            Index = -1;
-            IsValid = true;
-            Slot = -1;
-            _mouseOverColumnIndex = null;
-            _detailsDesiredHeight = double.NaN;
-            _detailsLoaded = false;
-            _appliedDetailsVisibility = false;
-            Cells = new DataGridCellCollection(this);
-            Cells.CellAdded += DataGridCellCollection_CellAdded;
-            Cells.CellRemoved += DataGridCellCollection_CellRemoved;
-        }
-
-        protected override AutomationPeer OnCreateAutomationPeer()
-        {
-            return new DataGridRowAutomationPeer(this);
-        }
-
-        private void SetValueNoCallback<T>(AvaloniaProperty<T> property, T value, BindingPriority priority = BindingPriority.LocalValue)
-        {
-            _areHandlersSuspended = true;
-            try
-            {
-                SetValue(property, value, priority);
-            }
-            finally
-            {
-                _areHandlersSuspended = false;
-            }
-        }
-
-        private void OnHeaderChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            if (_headerElement != null)
-            {
-                _headerElement.Content = e.NewValue;
-            }
-        }
-
-        private void OnDetailsTemplateChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            var oldValue = (IDataTemplate)e.OldValue;
-            var newValue = (IDataTemplate)e.NewValue;
-
-            if (!_areHandlersSuspended && OwningGrid != null)
-            {
-                IDataTemplate actualDetailsTemplate(IDataTemplate template) => (template ?? OwningGrid.RowDetailsTemplate);
-
-                // We don't always want to apply the new Template because they might have set the same one
-                // we inherited from the DataGrid
-                if (actualDetailsTemplate(newValue) != actualDetailsTemplate(oldValue))
-                {
-                    ApplyDetailsTemplate(initializeDetailsPreferredHeight: false);
-                }
-            }
-        }
-
-        private void OnAreDetailsVisibleChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            if (!_areHandlersSuspended)
-            {
-                if (OwningGrid == null)
-                {
-                    throw DataGridError.DataGrid.NoOwningGrid(this.GetType());
-                }
-                if (Index == -1)
-                {
-                    throw DataGridError.DataGridRow.InvalidRowIndexCannotCompleteOperation();
-                }
-
-                var newValue = (bool)e.NewValue;
-                OwningGrid.OnRowDetailsVisibilityPropertyChanged(Index, newValue);
-                SetDetailsVisibilityInternal(newValue, raiseNotification: true, animate: true);
-            }
-        }
-
-        internal DataGrid OwningGrid
-        {
-            get;
-            set;
-        }
-
-        private int _index;
-
-        public static readonly DirectProperty<DataGridRow, int> IndexProperty = AvaloniaProperty.RegisterDirect<DataGridRow, int>(
-            nameof(Index), o => o.Index, (o, v) => o.Index = v);
-
-        /// <summary>
-        /// Index of the row
-        /// </summary>
-        public int Index
-        {
-            get => _index;
-            internal set => SetAndRaise(IndexProperty, ref _index, value);
-        }
-
-        internal double ActualBottomGridLineHeight
-        {
-            get
-            {
-                if (_bottomGridLine != null && OwningGrid != null && OwningGrid.AreRowBottomGridLinesRequired)
-                {
-                    // Unfortunately, _bottomGridLine has no size yet so we can't get its actualheight
-                    return DataGrid.HorizontalGridLinesThickness;
-                }
-                return 0;
-            }
-        }
-
-        internal DataGridCellCollection Cells
-        {
-            get;
-            private set;
-        }
-
-        internal DataGridCell FillerCell
-        {
-            get
-            {
-                if (_fillerCell == null)
-                {
-                    _fillerCell = new DataGridCell
-                    {
-                        IsVisible = false,
-                        OwningRow = this
-                    };
-                    if (OwningGrid.CellTheme is {} cellTheme)
-                    {
-                        _fillerCell.SetValue(ThemeProperty, cellTheme, BindingPriority.Template);
-                    }
-                    if (_cellsElement != null)
-                    {
-                        _cellsElement.Children.Add(_fillerCell);
-                    }
-                }
-                return _fillerCell;
-            }
-        }
-
-        internal bool HasBottomGridLine
-        {
-            get
-            {
-                return _bottomGridLine != null;
-            }
-        }
-
-        internal bool HasHeaderCell
-        {
-            get
-            {
-                return _headerElement != null;
-            }
-        }
-
-        internal DataGridRowHeader HeaderCell
-        {
-            get
-            {
-                return _headerElement;
-            }
-        }
-
-        internal bool IsEditing => OwningGrid != null && OwningGrid.EditingRow == this;
-
-        /// <summary>
-        /// Layout when template is applied
-        /// </summary>
-        internal bool IsLayoutDelayed
-        {
-            get;
-            private set;
-        }
-
-        internal bool IsMouseOver
-        {
-            get
-            {
-                return OwningGrid != null && OwningGrid.MouseOverRowIndex == Index;
-            }
-            set
-            {
-                if (OwningGrid != null && value != IsMouseOver)
-                {
-                    if (value)
-                    {
-                        OwningGrid.MouseOverRowIndex = Index;
-                    }
-                    else
-                    {
-                        OwningGrid.MouseOverRowIndex = null;
-                    }
-                }
-            }
-        }
-
-        internal bool IsRecycled
-        {
-            get;
-            private set;
-        }
-
-        internal bool IsRecyclable
-        {
-            get
-            {
-                if (OwningGrid != null)
-                {
-                    return OwningGrid.IsRowRecyclable(this);
-                }
-                return true;
-            }
-        }
-
-        internal int? MouseOverColumnIndex
-        {
-            get
-            {
-                return _mouseOverColumnIndex;
-            }
-            set
-            {
-                if (_mouseOverColumnIndex != value)
-                {
-                    DataGridCell oldMouseOverCell = null;
-                    if (_mouseOverColumnIndex != null && OwningGrid.IsSlotVisible(Slot))
-                    {
-                        if (_mouseOverColumnIndex > -1)
-                        {
-                            oldMouseOverCell = Cells[_mouseOverColumnIndex.Value];
-                        }
-                    }
-                    _mouseOverColumnIndex = value;
-                    if (oldMouseOverCell != null && IsVisible)
-                    {
-                        oldMouseOverCell.UpdatePseudoClasses();
-                    }
-                    if (_mouseOverColumnIndex != null && OwningGrid != null && OwningGrid.IsSlotVisible(Slot))
-                    {
-                        if (_mouseOverColumnIndex > -1)
-                        {
-                            Cells[_mouseOverColumnIndex.Value].UpdatePseudoClasses();
-                        }
-                    }
-                }
-            }
-        }
-
-        internal Panel RootElement
-        {
-            get;
-            private set;
-        }
-
-        internal int Slot
-        {
-            get;
-            set;
-        }
-
-        // Height that the row will eventually end up at after a possible details animation has completed
-        internal double TargetHeight
-        {
-            get
-            {
-                if (!double.IsNaN(Height))
-                {
-                    return Height;
-                }
-                else if (_detailsElement != null && _appliedDetailsVisibility == true && _appliedDetailsTemplate != null)
-                {
-                    Debug.Assert(!double.IsNaN(_detailsElement.ContentHeight));
-                    Debug.Assert(!double.IsNaN(_detailsDesiredHeight));
-                    return DesiredSize.Height + _detailsDesiredHeight - _detailsElement.ContentHeight;
-                }
-                else
-                {
-                    return DesiredSize.Height;
-                }
-            }
-        }
-
-        /// <summary>
-        /// Returns the index of the current row.
-        /// </summary>
-        /// <returns>
-        /// The index of the current row.
-        /// </returns>
-        [Obsolete("This API is going to be removed in a future version. Use the Index property instead.")]
-        public int GetIndex()
-        {
-            return Index;
-        }
-
-        /// <summary>
-        /// Returns the row which contains the given element
-        /// </summary>
-        /// <param name="element">element contained in a row</param>
-        /// <returns>Row that contains the element, or null if not found
-        /// </returns>
-        public static DataGridRow GetRowContainingElement(Control element)
-        {
-            // Walk up the tree to find the DataGridRow that contains the element
-            Visual parent = element;
-            DataGridRow row = parent as DataGridRow;
-            while ((parent != null) && (row == null))
-            {
-                parent = parent.GetVisualParent();
-                row = parent as DataGridRow;
-            }
-            return row;
-        }
-
-        /// <summary>
-        /// Arranges the content of the <see cref="T:Avalonia.Controls.DataGridRow" />.
-        /// </summary>
-        /// <returns>
-        /// The actual size used by the <see cref="T:Avalonia.Controls.DataGridRow" />.
-        /// </returns>
-        /// <param name="finalSize">
-        /// The final area within the parent that this element should use to arrange itself and its children.
-        /// </param>
-        protected override Size ArrangeOverride(Size finalSize)
-        {
-            if (OwningGrid == null)
-            {
-                return base.ArrangeOverride(finalSize);
-            }
-
-            // If the DataGrid was scrolled horizontally after our last Arrange, we need to make sure
-            // the Cells and Details are Arranged again
-            if (_lastHorizontalOffset != OwningGrid.HorizontalOffset)
-            {
-                _lastHorizontalOffset = OwningGrid.HorizontalOffset;
-                InvalidateHorizontalArrange();
-            }
-
-            Size size = base.ArrangeOverride(finalSize);
-
-            if (_checkDetailsContentHeight)
-            {
-                _checkDetailsContentHeight = false;
-                EnsureDetailsContentHeight();
-            }
-
-            if (RootElement != null)
-            {
-                foreach (Control child in RootElement.Children)
-                {
-                    if (DataGridFrozenGrid.GetIsFrozen(child))
-                    {
-                        TranslateTransform transform = new TranslateTransform();
-                        // Automatic layout rounding doesn't apply to transforms so we need to Round this
-                        transform.X = Math.Round(OwningGrid.HorizontalOffset);
-                        child.RenderTransform = transform;
-                    }
-                }
-            }
-
-            if (_bottomGridLine != null)
-            {
-                RectangleGeometry gridlineClipGeometry = new RectangleGeometry();
-                gridlineClipGeometry.Rect = new Rect(OwningGrid.HorizontalOffset, 0, Math.Max(0, DesiredSize.Width - OwningGrid.HorizontalOffset), _bottomGridLine.DesiredSize.Height);
-                _bottomGridLine.Clip = gridlineClipGeometry;
-            }
-
-            return size;
-        }
-
-        /// <summary>
-        /// Measures the children of a <see cref="T:Avalonia.Controls.DataGridRow" /> to
-        /// prepare for arranging them during the <see cref="M:System.Windows.FrameworkElement.ArrangeOverride(System.Windows.Size)" /> pass.
-        /// </summary>
-        /// <param name="availableSize">
-        /// The available size that this element can give to child elements. Indicates an upper limit that child elements should not exceed.
-        /// </param>
-        /// <returns>
-        /// The size that the <see cref="T:Avalonia.Controls.Primitives.DataGridRow" /> determines it needs during layout, based on its calculations of child object allocated sizes.
-        /// </returns>
-        protected override Size MeasureOverride(Size availableSize)
-        {
-            if (OwningGrid == null)
-            {
-                return base.MeasureOverride(availableSize);
-            }
-
-            //Allow the DataGrid specific components to adjust themselves based on new values
-            if (_headerElement != null)
-            {
-                _headerElement.InvalidateMeasure();
-            }
-            if (_cellsElement != null)
-            {
-                _cellsElement.InvalidateMeasure();
-            }
-            if (_detailsElement != null)
-            {
-                _detailsElement.InvalidateMeasure();
-            }
-
-            Size desiredSize = base.MeasureOverride(availableSize);
-            return desiredSize.WithWidth(Math.Max(desiredSize.Width, OwningGrid.CellsWidth));
-        }
-
-        /// <summary>
-        /// Builds the visual tree for the column header when a new template is applied.
-        /// </summary>
-        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
-        {
-            RootElement = e.NameScope.Find<Panel>(DATAGRIDROW_elementRoot);
-            if (RootElement != null)
-            {
-                ApplyState();
-            }
-
-            bool updateVerticalScrollBar = false;
-            if (_cellsElement != null)
-            {
-                // If we're applying a new template, we  want to remove the cells from the previous _cellsElement
-                _cellsElement.Children.Clear();
-                updateVerticalScrollBar = true;
-            }
-
-            _cellsElement = e.NameScope.Find<DataGridCellsPresenter>(DATAGRIDROW_elementCells);
-            if (_cellsElement != null)
-            {
-                _cellsElement.OwningRow = this;
-                // Cells that were already added before the Template was applied need to
-                // be added to the Canvas
-                if (Cells.Count > 0)
-                {
-                    foreach (DataGridCell cell in Cells)
-                    {
-                        _cellsElement.Children.Add(cell);
-                    }
-                }
-            }
-
-            _detailsElement = e.NameScope.Find<DataGridDetailsPresenter>(DATAGRIDROW_elementDetails);
-            if (_detailsElement != null && OwningGrid != null)
-            {
-                _detailsElement.OwningRow = this;
-                if (ActualDetailsVisibility && ActualDetailsTemplate != null && _appliedDetailsTemplate == null)
-                {
-                    // Apply the DetailsTemplate now that the row template is applied.
-                    SetDetailsVisibilityInternal(ActualDetailsVisibility, raiseNotification: _detailsVisibilityNotificationPending, animate: false);
-                    _detailsVisibilityNotificationPending = false;
-                }
-            }
-
-            _bottomGridLine = e.NameScope.Find<Rectangle>(DATAGRIDROW_elementBottomGridLine);
-            EnsureGridLines();
-
-            _headerElement = e.NameScope.Find<DataGridRowHeader>(DATAGRIDROW_elementRowHeader);
-            if (_headerElement != null)
-            {
-                _headerElement.Owner = this;
-                if (Header != null)
-                {
-                    _headerElement.Content = Header;
-                }
-                EnsureHeaderStyleAndVisibility(null);
-            }
-
-            //The height of this row might have changed after applying a new style, so fix the vertical scroll bar
-            if (OwningGrid != null && updateVerticalScrollBar)
-            {
-                OwningGrid.UpdateVerticalScrollBar();
-            }
-        }
-
-        protected override void OnPointerEntered(PointerEventArgs e)
-        {
-            base.OnPointerEntered(e);
-            IsMouseOver = true;
-        }
-        protected override void OnPointerExited(PointerEventArgs e)
-        {
-            IsMouseOver = false;
-            base.OnPointerExited(e);
-        }
-
-        internal void ApplyCellsState()
-        {
-            foreach (DataGridCell dataGridCell in Cells)
-            {
-                dataGridCell.UpdatePseudoClasses();
-            }
-        }
-
-        internal void ApplyHeaderStatus()
-        {
-            if (_headerElement != null && OwningGrid.AreRowHeadersVisible)
-            {
-                _headerElement.UpdatePseudoClasses();
-            }
-        }
-
-        internal void ApplyState()
-        {
-            if (RootElement != null && OwningGrid != null && IsVisible)
-            {
-                var isSelected = Slot != -1 && OwningGrid.GetRowSelection(Slot);
-                IsSelected = isSelected;
-                PseudoClasses.Set(":editing", IsEditing);
-                PseudoClasses.Set(":invalid", !IsValid);
-                ApplyHeaderStatus();
-            }
-        }
-
-        //TODO Animation
-        internal void DetachFromDataGrid(bool recycle)
-        {
-            UnloadDetailsTemplate(recycle);
-
-            if (recycle)
-            {
-                IsRecycled = true;
-
-                if (_cellsElement != null)
-                {
-                    _cellsElement.Recycle();
-                }
-
-                _checkDetailsContentHeight = false;
-
-                // Clear out the old Details cache so it won't be reused for other data
-                //_detailsDesiredHeight = double.NaN;
-                if (_detailsElement != null)
-                {
-                    _detailsElement.ClearValue(DataGridDetailsPresenter.ContentHeightProperty);
-                }
-            }
-
-            Slot = -1;
-        }
-
-        internal void InvalidateCellsIndex()
-        {
-            _cellsElement?.InvalidateChildIndex();
-        }
-
-        internal void EnsureFillerVisibility()
-        {
-            if (_cellsElement != null)
-            {
-                _cellsElement.EnsureFillerVisibility();
-            }
-        }
-
-        internal void EnsureGridLines()
-        {
-            if (OwningGrid != null)
-            {
-                if (_bottomGridLine != null)
-                {
-                    // It looks like setting Visibility sometimes has side effects so make sure the value is actually
-                    // different before setting it
-                    bool newVisibility = OwningGrid.GridLinesVisibility == DataGridGridLinesVisibility.Horizontal || OwningGrid.GridLinesVisibility == DataGridGridLinesVisibility.All;
-
-                    if (newVisibility != _bottomGridLine.IsVisible)
-                    {
-                        _bottomGridLine.IsVisible = newVisibility;
-                    }
-                    _bottomGridLine.Fill = OwningGrid.HorizontalGridLinesBrush;
-                }
-
-                foreach (DataGridCell cell in Cells)
-                {
-                    cell.EnsureGridLine(OwningGrid.ColumnsInternal.LastVisibleColumn);
-                }
-            }
-        }
-
-        internal void EnsureHeaderStyleAndVisibility(Styling.Style previousStyle)
-        {
-            if (_headerElement != null && OwningGrid != null)
-            {
-                _headerElement.IsVisible = OwningGrid.AreRowHeadersVisible;
-            }
-        }
-
-        internal void EnsureHeaderVisibility()
-        {
-            if (_headerElement != null && OwningGrid != null)
-            {
-                _headerElement.IsVisible = OwningGrid.AreRowHeadersVisible;
-            }
-        }
-
-        internal void InvalidateHorizontalArrange()
-        {
-            if (_cellsElement != null)
-            {
-                _cellsElement.InvalidateArrange();
-            }
-            if (_detailsElement != null)
-            {
-                _detailsElement.InvalidateArrange();
-            }
-        }
-
-        internal void InvalidateDesiredHeight()
-        {
-            _cellsElement?.InvalidateDesiredHeight();
-        }
-
-        internal void ResetGridLine()
-        {
-            _bottomGridLine = null;
-        }
-
-        private void DataGridCellCollection_CellAdded(object sender, DataGridCellEventArgs e)
-        {
-            _cellsElement?.Children.Add(e.Cell);
-        }
-
-        private void DataGridCellCollection_CellRemoved(object sender, DataGridCellEventArgs e)
-        {
-            _cellsElement?.Children.Remove(e.Cell);
-        }
-
-        private void DataGridRow_PointerPressed(PointerPressedEventArgs e)
-        {
-            if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
-            {
-                return;
-            }
-
-            if (OwningGrid != null)
-            {
-                OwningGrid.IsDoubleClickRecordsClickOnCall(this);
-            }
-        }
-
-        private void OnRowDetailsChanged()
-        {
-            OwningGrid?.OnRowDetailsChanged();
-        }
-
-        // Returns the actual template that should be sued for Details: either explicity set on this row
-        // or inherited from the DataGrid
-        private IDataTemplate ActualDetailsTemplate
-        {
-            get
-            {
-                Debug.Assert(OwningGrid != null);
-                return DetailsTemplate ?? OwningGrid.RowDetailsTemplate;
-            }
-        }
-
-        private bool ActualDetailsVisibility
-        {
-            get
-            {
-                if (OwningGrid == null)
-                {
-                    throw DataGridError.DataGrid.NoOwningGrid(GetType());
-                }
-                if (Index == -1)
-                {
-                    throw DataGridError.DataGridRow.InvalidRowIndexCannotCompleteOperation();
-                }
-                return OwningGrid.GetRowDetailsVisibility(Index);
-            }
-        }
-
-        private void UnloadDetailsTemplate(bool recycle)
-        {
-            if (_detailsElement != null)
-            {
-                if (_detailsContent != null)
-                {
-                    if (_detailsLoaded)
-                    {
-                        OwningGrid.OnUnloadingRowDetails(this, _detailsContent);
-                    }
-                    _detailsContent.DataContext = null;
-                    if (!recycle)
-                    {
-                        _detailsContentSizeSubscription?.Dispose();
-                        _detailsContentSizeSubscription = null;
-                        _detailsContent = null;
-                    }
-                }
-
-                if (!recycle)
-                {
-                    _detailsElement.Children.Clear();
-                }
-                _detailsElement.ContentHeight = 0;
-            }
-            if (!recycle)
-            {
-                _appliedDetailsTemplate = null;
-                SetValueNoCallback(DetailsTemplateProperty, null);
-            }
-
-            _detailsLoaded = false;
-            _appliedDetailsVisibility = null;
-            SetValueNoCallback(AreDetailsVisibleProperty, false);
-        }
-
-        //TODO Animation
-        internal void EnsureDetailsContentHeight()
-        {
-            if ((_detailsElement != null)
-                && (_detailsContent != null)
-                && (double.IsNaN(_detailsContent.Height))
-                && (AreDetailsVisible)
-                && (!double.IsNaN(_detailsDesiredHeight))
-                && !MathUtilities.AreClose(_detailsContent.Bounds.Inflate(_detailsContent.Margin).Height, _detailsDesiredHeight)
-                && Slot != -1)
-            {
-                _detailsDesiredHeight = _detailsContent.Bounds.Inflate(_detailsContent.Margin).Height;
-
-                if (true)
-                {
-                    _detailsElement.ContentHeight = _detailsDesiredHeight;
-                }
-            }
-        }
-
-        // Makes sure the _detailsDesiredHeight is initialized.  We need to measure it to know what
-        // height we want to animate to.  Subsequently, we just update that height in response to SizeChanged
-        private void EnsureDetailsDesiredHeight()
-        {
-            Debug.Assert(_detailsElement != null && OwningGrid != null);
-
-            if (_detailsContent != null)
-            {
-                Debug.Assert(_detailsElement.Children.Contains(_detailsContent));
-
-                _detailsContent.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
-                _detailsDesiredHeight = _detailsContent.DesiredSize.Height;
-            }
-            else
-            {
-                _detailsDesiredHeight = 0;
-            }
-        }
-
-        //TODO Cleanup
-        double? _previousDetailsHeight = null;
-
-        //TODO Animation
-        private void DetailsContent_HeightChanged(double newValue)
-        {
-            if (_previousDetailsHeight.HasValue)
-            {
-                var oldValue = _previousDetailsHeight.Value;
-                _previousDetailsHeight = newValue;
-                if (newValue != oldValue && newValue != _detailsDesiredHeight)
-                {
-
-                    if (AreDetailsVisible && _appliedDetailsTemplate != null)
-                    {
-                        // Update the new desired height for RowDetails
-                        _detailsDesiredHeight = newValue;
-
-                        _detailsElement.ContentHeight = newValue;
-
-                        // Calling this when details are not visible invalidates during layout when we have no work
-                        // to do.  In certain scenarios, this could cause a layout cycle
-                        OnRowDetailsChanged();
-                    }
-                }
-            }
-            else
-            {
-                _previousDetailsHeight = newValue;
-            }
-        }
-
-        private void DetailsContent_SizeChanged(Rect newValue)
-        {
-            DetailsContent_HeightChanged(newValue.Height);
-        }
-        private void DetailsContent_MarginChanged(Thickness newValue)
-        {
-            if (_detailsContent != null)
-                DetailsContent_SizeChanged(_detailsContent.Bounds.Inflate(newValue));
-        }
-        private void DetailsContent_LayoutUpdated(object sender, EventArgs e)
-        {
-            if (_detailsContent != null)
-            {
-                var margin = _detailsContent.Margin;
-                var height = _detailsContent.DesiredSize.Height + margin.Top + margin.Bottom;
-
-                DetailsContent_HeightChanged(height);
-            }
-        }
-
-        //TODO Animation
-        // Sets AreDetailsVisible on the row and animates if necessary
-        internal void SetDetailsVisibilityInternal(bool isVisible, bool raiseNotification, bool animate)
-        {
-            Debug.Assert(OwningGrid != null);
-            Debug.Assert(Index != -1);
-
-            if (_appliedDetailsVisibility != isVisible)
-            {
-                if (_detailsElement == null)
-                {
-                    if (raiseNotification)
-                    {
-                        _detailsVisibilityNotificationPending = true;
-                    }
-                    return;
-                }
-
-                _appliedDetailsVisibility = isVisible;
-                SetValueNoCallback(AreDetailsVisibleProperty, isVisible);
-
-                // Applies a new DetailsTemplate only if it has changed either here or at the DataGrid level
-                ApplyDetailsTemplate(initializeDetailsPreferredHeight: true);
-
-                // no template to show
-                if (_appliedDetailsTemplate == null)
-                {
-                    if (_detailsElement.ContentHeight > 0)
-                    {
-                        _detailsElement.ContentHeight = 0;
-                    }
-                    return;
-                }
-
-                if (AreDetailsVisible)
-                {
-                    // Set the details height directly
-                    _detailsElement.ContentHeight = _detailsDesiredHeight;
-                    _checkDetailsContentHeight = true;
-                }
-                else
-                {
-                    _detailsElement.ContentHeight = 0;
-                }
-
-                OnRowDetailsChanged();
-
-                if (raiseNotification)
-                {
-                    OwningGrid.OnRowDetailsVisibilityChanged(new DataGridRowDetailsEventArgs(this, _detailsContent));
-                }
-            }
-        }
-
-        internal void ApplyDetailsTemplate(bool initializeDetailsPreferredHeight)
-        {
-            if (_detailsElement != null && AreDetailsVisible)
-            {
-                IDataTemplate oldDetailsTemplate = _appliedDetailsTemplate;
-                if (ActualDetailsTemplate != null && ActualDetailsTemplate != _appliedDetailsTemplate)
-                {
-                    if (_detailsContent != null)
-                    {
-                        _detailsContentSizeSubscription?.Dispose();
-                        _detailsContentSizeSubscription = null;
-                        if (_detailsLoaded)
-                        {
-                            OwningGrid.OnUnloadingRowDetails(this, _detailsContent);
-                            _detailsLoaded = false;
-                        }
-                    }
-                    _detailsElement.Children.Clear();
-
-                    _detailsContent = ActualDetailsTemplate.Build(DataContext);
-                    _appliedDetailsTemplate = ActualDetailsTemplate;
-
-                    if (_detailsContent != null)
-                    {
-                        if (_detailsContent is Layout.Layoutable layoutableContent)
-                        {
-                            layoutableContent.LayoutUpdated += DetailsContent_LayoutUpdated;
-
-                            _detailsContentSizeSubscription = new CompositeDisposable(2)
-                            {
-                                Disposable.Create(() => layoutableContent.LayoutUpdated -= DetailsContent_LayoutUpdated),
-                                _detailsContent.GetObservable(MarginProperty).Subscribe(DetailsContent_MarginChanged)
-                            };
-
-
-                        }
-                        else
-                        {
-                            _detailsContentSizeSubscription =
-                                _detailsContent.GetObservable(MarginProperty)
-                                               .Subscribe(DetailsContent_MarginChanged);
-
-                        }
-
-                        _detailsElement.Children.Add(_detailsContent);
-                    }
-                }
-
-                if (_detailsContent != null && !_detailsLoaded)
-                {
-                    _detailsLoaded = true;
-                    _detailsContent.DataContext = DataContext;
-                    OwningGrid.OnLoadingRowDetails(this, _detailsContent);
-                }
-                if (initializeDetailsPreferredHeight && double.IsNaN(_detailsDesiredHeight) &&
-                    _appliedDetailsTemplate != null && _detailsElement.Children.Count > 0)
-                {
-                    EnsureDetailsDesiredHeight();
-                }
-                else if (oldDetailsTemplate == null)
-                {
-                    _detailsDesiredHeight = double.NaN;
-                    EnsureDetailsDesiredHeight();
-                    _detailsElement.ContentHeight = _detailsDesiredHeight;
-                }
-            }
-        }
-
-        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
-        {
-            if (change.Property == DataContextProperty)
-            {
-                var owner = OwningGrid;
-                if (owner != null && this.IsRecycled)
-                {
-                    var columns = owner.ColumnsItemsInternal;
-                    var nc = columns.Count;
-                    for (int ci = 0; ci < nc; ci++)
-                    {
-                        if (columns[ci] is DataGridTemplateColumn column)
-                        {
-                            column.RefreshCellContent((Control)this.Cells[column.Index].Content, nameof(DataGridTemplateColumn.CellTemplate));
-                        }
-                    }
-                }
-            }
-            else if (change.Property == IsSelectedProperty)
-            {
-                var value = change.GetNewValue<bool>();
-
-                if (OwningGrid != null && Slot != -1)
-                {
-                    OwningGrid.SetRowSelection(Slot, value, false);
-                }
-
-                PseudoClasses.Set(":selected", value);
-            }
-
-            base.OnPropertyChanged(change);
-        }
-
-    }
-}

+ 0 - 471
src/Avalonia.Controls.DataGrid/DataGridRowGroupHeader.cs

@@ -1,471 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Controls.Metadata;
-using Avalonia.Controls.Mixins;
-using Avalonia.Controls.Primitives;
-using Avalonia.Input;
-using Avalonia.Media;
-using System;
-using System.Diagnostics;
-using Avalonia.Reactive;
-
-namespace Avalonia.Controls
-{
-    [TemplatePart(DATAGRIDROWGROUPHEADER_expanderButton,      typeof(ToggleButton))]
-    [TemplatePart(DATAGRIDROWGROUPHEADER_indentSpacer,        typeof(Control))]
-    [TemplatePart(DATAGRIDROWGROUPHEADER_itemCountElement,    typeof(TextBlock))]
-    [TemplatePart(DATAGRIDROWGROUPHEADER_propertyNameElement, typeof(TextBlock))]
-    [TemplatePart(DataGridRow.DATAGRIDROW_elementRoot,        typeof(Panel))]
-    [TemplatePart(DataGridRow.DATAGRIDROW_elementRowHeader,   typeof(DataGridRowHeader))]
-    [PseudoClasses(":pressed", ":current", ":expanded")]
-    public class DataGridRowGroupHeader : TemplatedControl
-    {
-        private const string DATAGRIDROWGROUPHEADER_expanderButton = "PART_ExpanderButton";
-        private const string DATAGRIDROWGROUPHEADER_indentSpacer = "PART_IndentSpacer";
-        private const string DATAGRIDROWGROUPHEADER_itemCountElement = "PART_ItemCountElement";
-        private const string DATAGRIDROWGROUPHEADER_propertyNameElement = "PART_PropertyNameElement";
-
-        private bool _areIsCheckedHandlersSuspended;
-        private ToggleButton _expanderButton;
-        private DataGridRowHeader _headerElement;
-        private Control _indentSpacer;
-        private TextBlock _itemCountElement;
-        private TextBlock _propertyNameElement;
-        private Panel _rootElement;
-        private double _totalIndent;
-
-        public static readonly StyledProperty<bool> IsItemCountVisibleProperty =
-            AvaloniaProperty.Register<DataGridRowGroupHeader, bool>(nameof(IsItemCountVisible));
-
-        /// <summary>
-        /// Gets or sets a value that indicates whether the item count is visible.
-        /// </summary>
-        public bool IsItemCountVisible
-        {
-            get { return GetValue(IsItemCountVisibleProperty); }
-            set { SetValue(IsItemCountVisibleProperty, value); }
-        }
-
-
-        public static readonly StyledProperty<string> ItemCountFormatProperty =
-            AvaloniaProperty.Register<DataGridRowGroupHeader, string>(nameof(ItemCountFormat));
-
-        /// <summary>
-        /// Gets or sets a value that indicates number format of items count
-        /// </summary>
-        public string ItemCountFormat
-        {
-            get { return GetValue(ItemCountFormatProperty); }
-            set { SetValue(ItemCountFormatProperty, value); }
-        }
-
-
-        public static readonly StyledProperty<string> PropertyNameProperty =
-            AvaloniaProperty.Register<DataGridRowGroupHeader, string>(nameof(PropertyName));
-
-        /// <summary>
-        /// Gets or sets the name of the property that this <see cref="T:Avalonia.Controls.DataGrid" /> row is bound to.
-        /// </summary>
-        public string PropertyName
-        {
-            get { return GetValue(PropertyNameProperty); }
-            set { SetValue(PropertyNameProperty, value); }
-        }
-
-        public static readonly StyledProperty<bool> IsPropertyNameVisibleProperty =
-            AvaloniaProperty.Register<DataGridRowGroupHeader, bool>(nameof(IsPropertyNameVisible));
-
-        /// <summary>
-        /// Gets or sets a value that indicates whether the property name is visible.
-        /// </summary>
-        public bool IsPropertyNameVisible
-        {
-            get { return GetValue(IsPropertyNameVisibleProperty); }
-            set { SetValue(IsPropertyNameVisibleProperty, value); }
-        }
-
-        public static readonly StyledProperty<double> SublevelIndentProperty =
-            AvaloniaProperty.Register<DataGridRowGroupHeader, double>(
-                nameof(SublevelIndent),
-                defaultValue: DataGrid.DATAGRID_defaultRowGroupSublevelIndent,
-                validate: IsValidSublevelIndent);
-
-        private static bool IsValidSublevelIndent(double value)
-        {
-            return !double.IsNaN(value) && !double.IsInfinity(value) && value >= 0;
-        }
-
-        /// <summary>
-        /// Gets or sets a value that indicates the amount that the
-        /// children of the <see cref="T:Avalonia.Controls.RowGroupHeader" /> are indented.
-        /// </summary>
-        public double SublevelIndent
-        {
-            get { return GetValue(SublevelIndentProperty); }
-            set { SetValue(SublevelIndentProperty, value); }
-        }
-
-        private void OnSublevelIndentChanged(AvaloniaPropertyChangedEventArgs e)
-        {
-            if (OwningGrid != null)
-            {
-                OwningGrid.OnSublevelIndentUpdated(this, (double)e.NewValue);
-            }
-        }
-
-        static DataGridRowGroupHeader()
-        {
-            SublevelIndentProperty.Changed.AddClassHandler<DataGridRowGroupHeader>((x,e) => x.OnSublevelIndentChanged(e));
-            PressedMixin.Attach<DataGridRowGroupHeader>();
-            IsTabStopProperty.OverrideDefaultValue<DataGridRowGroupHeader>(false);
-        }
-
-        /// <summary>
-        /// Constructs a DataGridRowGroupHeader
-        /// </summary>
-        public DataGridRowGroupHeader()
-        {
-            AddHandler(InputElement.PointerPressedEvent, (s, e) => DataGridRowGroupHeader_PointerPressed(e), handledEventsToo: true);
-        }
-
-        internal DataGridRowHeader HeaderCell
-        {
-            get
-            {
-                return _headerElement;
-            }
-        }
-
-        private bool IsCurrent
-        {
-            get
-            {
-                Debug.Assert(OwningGrid != null);
-                return (RowGroupInfo.Slot == OwningGrid.CurrentSlot);
-            }
-        }
-
-        private bool IsMouseOver
-        {
-            get;
-            set;
-        }
-
-        internal bool IsRecycled
-        {
-            get;
-            set;
-        }
-
-        internal int Level
-        {
-            get;
-            set;
-        }
-
-        internal DataGrid OwningGrid
-        {
-            get;
-            set;
-        }
-
-        internal DataGridRowGroupInfo RowGroupInfo
-        {
-            get;
-            set;
-        }
-
-        internal double TotalIndent
-        {
-            set
-            {
-                _totalIndent = value;
-                if (_indentSpacer != null)
-                {
-                    _indentSpacer.Width = _totalIndent;
-                }
-            }
-        }
-
-        private IDisposable _expanderButtonSubscription;
-
-        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
-        {
-            _rootElement = e.NameScope.Find<Panel>(DataGridRow.DATAGRIDROW_elementRoot);
-
-            _expanderButtonSubscription?.Dispose();
-            _expanderButton = e.NameScope.Find<ToggleButton>(DATAGRIDROWGROUPHEADER_expanderButton);
-            if(_expanderButton != null)
-            {
-                EnsureExpanderButtonIsChecked();
-                _expanderButtonSubscription =
-                    _expanderButton.GetObservable(ToggleButton.IsCheckedProperty)
-                                   .Skip(1)
-                                   .Subscribe(v => OnExpanderButtonIsCheckedChanged(v));
-            }
-
-            _headerElement = e.NameScope.Find<DataGridRowHeader>(DataGridRow.DATAGRIDROW_elementRowHeader);
-            if(_headerElement != null)
-            {
-                _headerElement.Owner = this;
-                EnsureHeaderVisibility();
-            }
-
-            _indentSpacer = e.NameScope.Find<Control>(DATAGRIDROWGROUPHEADER_indentSpacer);
-            if(_indentSpacer != null)
-            {
-                _indentSpacer.Width = _totalIndent;
-            }
-
-            _itemCountElement = e.NameScope.Find<TextBlock>(DATAGRIDROWGROUPHEADER_itemCountElement);
-            _propertyNameElement = e.NameScope.Find<TextBlock>(DATAGRIDROWGROUPHEADER_propertyNameElement);
-            UpdateTitleElements();
-        }
-
-        internal void ApplyHeaderStatus()
-        {
-            if (_headerElement != null && OwningGrid.AreRowHeadersVisible)
-            {
-                _headerElement.UpdatePseudoClasses();
-            }
-        }
-
-        internal void UpdatePseudoClasses()
-        {
-            PseudoClasses.Set(":current", IsCurrent);
-
-            if (RowGroupInfo?.CollectionViewGroup != null)
-            {
-                PseudoClasses.Set(":expanded", RowGroupInfo.IsVisible && RowGroupInfo.CollectionViewGroup.ItemCount > 0);
-            }
-        }
-
-        protected override Size ArrangeOverride(Size finalSize)
-        {
-            if (OwningGrid == null)
-            {
-                return base.ArrangeOverride(finalSize);
-            }
-
-            Size size = base.ArrangeOverride(finalSize);
-            if (_rootElement != null)
-            {
-                if (OwningGrid.AreRowGroupHeadersFrozen)
-                {
-                    foreach (Control child in _rootElement.Children)
-                    {
-                        child.Clip = null;
-                    }
-                }
-                else
-                {
-                    double frozenLeftEdge = 0;
-                    foreach (Control child in _rootElement.Children)
-                    {
-                        if (DataGridFrozenGrid.GetIsFrozen(child) && child.IsVisible)
-                        {
-                            TranslateTransform transform = new TranslateTransform();
-                            // Automatic layout rounding doesn't apply to transforms so we need to Round this
-                            transform.X = Math.Round(OwningGrid.HorizontalOffset);
-                            child.RenderTransform = transform;
-
-                            double childLeftEdge = child.Translate(this, new Point(child.Bounds.Width, 0)).X - transform.X;
-                            frozenLeftEdge = Math.Max(frozenLeftEdge, childLeftEdge + OwningGrid.HorizontalOffset);
-                        }
-                    }
-                    // Clip the non-frozen elements so they don't overlap the frozen ones
-                    foreach (Control child in _rootElement.Children)
-                    {
-                        if (!DataGridFrozenGrid.GetIsFrozen(child))
-                        {
-                            EnsureChildClip(child, frozenLeftEdge);
-                        }
-                    }
-                }
-            }
-            return size;
-        }
-
-        internal void ClearFrozenStates()
-        {
-            if (_rootElement != null)
-            {
-                foreach (Control child in _rootElement.Children)
-                {
-                    child.RenderTransform = null;
-                }
-            }
-        }
-
-        //TODO TabStop
-        private void DataGridRowGroupHeader_PointerPressed(PointerPressedEventArgs e)
-        {
-            if (OwningGrid == null)
-            {
-                return;
-            }
-            if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
-            {
-                if (OwningGrid.IsDoubleClickRecordsClickOnCall(this) && !e.Handled)
-                {
-                    ToggleExpandCollapse(!RowGroupInfo.IsVisible, true);
-                    e.Handled = true;
-                }
-                else
-                {
-                    if (!e.Handled && OwningGrid.IsTabStop)
-                    {
-                        OwningGrid.Focus();
-                    }
-                    e.Handled = OwningGrid.UpdateStateOnMouseLeftButtonDown(e, OwningGrid.CurrentColumnIndex, RowGroupInfo.Slot, allowEdit: false);
-                }
-            }
-            else if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed)
-            {
-                if (!e.Handled)
-                {
-                    OwningGrid.Focus();
-                }
-                e.Handled = OwningGrid.UpdateStateOnMouseRightButtonDown(e, OwningGrid.CurrentColumnIndex, RowGroupInfo.Slot, allowEdit: false);
-            }
-
-        }
-
-        private void EnsureChildClip(Visual child, double frozenLeftEdge)
-        {
-            double childLeftEdge = child.Translate(this, new Point(0, 0)).X;
-            if (frozenLeftEdge > childLeftEdge)
-            {
-                double xClip = Math.Round(frozenLeftEdge - childLeftEdge);
-                var rg = new RectangleGeometry();
-                rg.Rect =
-                    new Rect(xClip, 0,
-                        Math.Max(0, child.Bounds.Width - xClip),
-                        child.Bounds.Height);
-                child.Clip = rg;
-            }
-            else
-            {
-                child.Clip = null;
-            }
-        }
-
-        internal void EnsureExpanderButtonIsChecked()
-        {
-            if (_expanderButton != null && RowGroupInfo != null && RowGroupInfo.CollectionViewGroup != null &&
-                RowGroupInfo.CollectionViewGroup.ItemCount != 0)
-            {
-                SetIsCheckedNoCallBack(RowGroupInfo.IsVisible);
-            }
-        }
-
-        internal void EnsureHeaderVisibility()
-        {
-            if (_headerElement != null && OwningGrid != null)
-            {
-                _headerElement.IsVisible = OwningGrid.AreRowHeadersVisible;
-            }
-        }
-
-        private void OnExpanderButtonIsCheckedChanged(bool? value)
-        {
-            if(!_areIsCheckedHandlersSuspended)
-            {
-                ToggleExpandCollapse(value ?? false, true);
-            }
-        }
-
-        internal void LoadVisualsForDisplay()
-        {
-            EnsureExpanderButtonIsChecked();
-            EnsureHeaderVisibility();
-            UpdatePseudoClasses();
-            ApplyHeaderStatus();
-        }
-
-        protected override void OnPointerEntered(PointerEventArgs e)
-        {
-            if (IsEnabled)
-            {
-                IsMouseOver = true;
-                UpdatePseudoClasses();
-            }
-
-            base.OnPointerEntered(e);
-        }
-
-        protected override void OnPointerExited(PointerEventArgs e)
-        {
-            if (IsEnabled)
-            {
-                IsMouseOver = false;
-                UpdatePseudoClasses();
-            }
-
-            base.OnPointerExited(e);
-        }
-
-        private void SetIsCheckedNoCallBack(bool value)
-        {
-            if (_expanderButton != null && _expanderButton.IsChecked != value)
-            {
-                _areIsCheckedHandlersSuspended = true;
-                try
-                {
-                    _expanderButton.IsChecked = value;
-                }
-                finally
-                {
-                    _areIsCheckedHandlersSuspended = false;
-                }
-            }
-        }
-
-        internal void ToggleExpandCollapse(bool isVisible, bool setCurrent)
-        {
-            if (RowGroupInfo.CollectionViewGroup.ItemCount != 0)
-            {
-                if (OwningGrid == null)
-                {
-                    // Do these even if the OwningGrid is null in case it could improve the Designer experience for a standalone DataGridRowGroupHeader
-                    RowGroupInfo.IsVisible = isVisible;
-                }
-                else if(RowGroupInfo.IsVisible != isVisible)
-                {
-                    OwningGrid.OnRowGroupHeaderToggled(this, isVisible, setCurrent);
-                }
-
-                EnsureExpanderButtonIsChecked();
-
-                UpdatePseudoClasses();
-            }
-        }
-
-        internal void UpdateTitleElements()
-        {
-            if (_propertyNameElement != null)
-            {
-                string txt;
-                if (string.IsNullOrWhiteSpace(PropertyName))
-                    txt = String.Empty;
-                else
-                    txt = String.Format("{0}:", PropertyName);
-                _propertyNameElement.Text = txt;
-            }
-            if (_itemCountElement != null && RowGroupInfo != null && RowGroupInfo.CollectionViewGroup != null)
-            {
-                string formatString;
-                if (RowGroupInfo.CollectionViewGroup.ItemCount == 1)
-                    formatString = (ItemCountFormat == null ? "({0} Item)" : ItemCountFormat);
-                else
-                    formatString = (ItemCountFormat == null ? "({0} Items)" : ItemCountFormat);
-
-                _itemCountElement.Text = String.Format(formatString, RowGroupInfo.CollectionViewGroup.ItemCount);
-            }
-        }
-
-    }
-}

+ 0 - 57
src/Avalonia.Controls.DataGrid/DataGridRowGroupInfo.cs

@@ -1,57 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Collections;
-
-namespace Avalonia.Controls
-{
-
-    internal class DataGridRowGroupInfo
-    {
-        public DataGridRowGroupInfo(
-            DataGridCollectionViewGroup collectionViewGroup,
-            bool isVisible,
-            int level,
-            int slot,
-            int lastSubItemSlot)
-        {
-            CollectionViewGroup = collectionViewGroup;
-            IsVisible = isVisible;
-            Level = level;
-            Slot = slot;
-            LastSubItemSlot = lastSubItemSlot;
-        }
-
-        public DataGridCollectionViewGroup CollectionViewGroup
-        {
-            get;
-            private set;
-        }
-
-        public int LastSubItemSlot
-        {
-            get;
-            set;
-        }
-
-        public int Level
-        {
-            get;
-            private set;
-        }
-
-        public int Slot
-        {
-            get;
-            set;
-        }
-
-        public bool IsVisible
-        {
-            get;
-            set;
-        }
-    }
-}

+ 0 - 224
src/Avalonia.Controls.DataGrid/DataGridRowHeader.cs

@@ -1,224 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Automation;
-using Avalonia.Controls.Metadata;
-using Avalonia.Input;
-using Avalonia.Media;
-using System.Diagnostics;
-
-namespace Avalonia.Controls.Primitives
-{
-    /// <summary>
-    /// Represents an individual <see cref="T:Avalonia.Controls.DataGrid" /> row header. 
-    /// </summary>
-    [TemplatePart(DATAGRIDROWHEADER_elementRootName, typeof(Control))]
-    [PseudoClasses(":invalid", ":selected", ":editing", ":current")]
-    public class DataGridRowHeader : ContentControl
-    {
-        private const string DATAGRIDROWHEADER_elementRootName = "PART_Root";
-        private Control _rootElement;
-
-        public static readonly StyledProperty<IBrush> SeparatorBrushProperty =
-            AvaloniaProperty.Register<DataGridRowHeader, IBrush>(nameof(SeparatorBrush));
-
-        public IBrush SeparatorBrush
-        {
-            get { return GetValue(SeparatorBrushProperty); }
-            set { SetValue(SeparatorBrushProperty, value); }
-        }
-
-        public static readonly StyledProperty<bool> AreSeparatorsVisibleProperty =
-            AvaloniaProperty.Register<DataGridRowHeader, bool>(
-                nameof(AreSeparatorsVisible));
-
-        /// <summary>
-        /// Gets or sets a value indicating whether the row header separator lines are visible.
-        /// </summary>
-        public bool AreSeparatorsVisible
-        {
-            get { return GetValue(AreSeparatorsVisibleProperty); }
-            set { SetValue(AreSeparatorsVisibleProperty, value); }
-        }
-
-        /// <summary>
-        /// Initializes a new instance of the <see cref="T:Avalonia.Controls.Primitives.DataGridRowHeader" /> class. 
-        /// </summary>
-        public DataGridRowHeader()
-        {
-            AddHandler(PointerPressedEvent, DataGridRowHeader_PointerPressed, handledEventsToo: true);
-        }
-
-        static DataGridRowHeader()
-        {
-            AutomationProperties.IsOffscreenBehaviorProperty.OverrideDefaultValue<DataGridRowHeader>(IsOffscreenBehavior.FromClip);
-        }
-
-        internal Control Owner
-        {
-            get;
-            set;
-        }
-
-        private DataGridRow OwningRow => Owner as DataGridRow;
-
-        private DataGridRowGroupHeader OwningRowGroupHeader => Owner as DataGridRowGroupHeader;
-
-        private DataGrid OwningGrid
-        {
-            get
-            {
-                if (OwningRow != null)
-                {
-                    return OwningRow.OwningGrid;
-                }
-                else if (OwningRowGroupHeader != null)
-                {
-                    return OwningRowGroupHeader.OwningGrid;
-                }
-                return null;
-            }
-        }
-
-        private int Slot
-        {
-            get
-            {
-                if (OwningRow != null)
-                {
-                    return OwningRow.Slot;
-                }
-                else if (OwningRowGroupHeader != null)
-                {
-                    return OwningRowGroupHeader.RowGroupInfo.Slot;
-                }
-                return -1;
-            }
-        }
-
-        /// <summary>
-        /// Builds the visual tree for the row header when a new template is applied. 
-        /// </summary>
-        protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
-        {
-            _rootElement = e.NameScope.Find<Control>(DATAGRIDROWHEADER_elementRootName);
-            if (_rootElement != null)
-            {
-                UpdatePseudoClasses();
-            }
-        } 
-
-        /// <summary>
-        /// Measures the children of a <see cref="T:Avalonia.Controls.Primitives.DataGridRowHeader" /> to prepare for arranging them during the <see cref="M:System.Windows.FrameworkElement.ArrangeOverride(System.Windows.Size)" /> pass.
-        /// </summary>
-        /// <param name="availableSize">
-        /// The available size that this element can give to child elements. Indicates an upper limit that child elements should not exceed.
-        /// </param>
-        /// <returns>
-        /// The size that the <see cref="T:Avalonia.Controls.Primitives.DataGridRowHeader" /> determines it needs during layout, based on its calculations of child object allocated sizes.
-        /// </returns>
-        protected override Size MeasureOverride(Size availableSize)
-        {
-            if (OwningRow == null || OwningGrid == null)
-            {
-                return base.MeasureOverride(availableSize);
-            }
-            double measureHeight = double.IsNaN(OwningGrid.RowHeight) ? availableSize.Height : OwningGrid.RowHeight;
-            double measureWidth = double.IsNaN(OwningGrid.RowHeaderWidth) ? availableSize.Width : OwningGrid.RowHeaderWidth;
-            Size measuredSize = base.MeasureOverride(new Size(measureWidth, measureHeight));
-
-            // Auto grow the row header or force it to a fixed width based on the DataGrid's setting
-            if (!double.IsNaN(OwningGrid.RowHeaderWidth) || measuredSize.Width < OwningGrid.ActualRowHeaderWidth)
-            {
-                return new Size(OwningGrid.ActualRowHeaderWidth, measuredSize.Height);
-            }
-
-            return measuredSize;
-        }
-
-        internal void UpdatePseudoClasses()
-        {
-            if (_rootElement != null && Owner != null && Owner.IsVisible)
-            {
-                if (OwningRow != null)
-                {
-                    PseudoClasses.Set(":invalid", !OwningRow.IsValid);
-
-                    PseudoClasses.Set(":selected", OwningRow.IsSelected);
-
-                    PseudoClasses.Set(":editing", OwningRow.IsEditing);
-
-                    if (OwningGrid != null)
-                    {
-                        PseudoClasses.Set(":current", OwningRow.Slot == OwningGrid.CurrentSlot);
-                    }
-                }
-                else if (OwningRowGroupHeader != null && OwningGrid != null)
-                {
-                    PseudoClasses.Set(":current", OwningRowGroupHeader.RowGroupInfo.Slot == OwningGrid.CurrentSlot);
-                }
-            }
-        }
-
-        protected override void OnPointerEntered(PointerEventArgs e)
-        {
-            if (OwningRow != null)
-            {
-                OwningRow.IsMouseOver = true;
-            }
-
-            base.OnPointerEntered(e);
-        }
-        protected override void OnPointerExited(PointerEventArgs e)
-        {
-            if (OwningRow != null)
-            {
-                OwningRow.IsMouseOver = false;
-            }
-
-            base.OnPointerExited(e);
-        }
-
-        //TODO TabStop
-        private void DataGridRowHeader_PointerPressed(object sender, PointerPressedEventArgs e)
-        {
-            if (OwningGrid == null)
-            {
-                return;
-            }
-
-            if (e.GetCurrentPoint(this).Properties.IsLeftButtonPressed)
-            {
-                if (!e.Handled)
-                //if (!e.Handled && OwningGrid.IsTabStop)
-                {
-                    OwningGrid.Focus();
-                }
-                if (OwningRow != null)
-                {
-                    Debug.Assert(sender is DataGridRowHeader);
-                    Debug.Assert(sender == this);
-                    e.Handled = OwningGrid.UpdateStateOnMouseLeftButtonDown(e, -1, Slot, false);
-                }
-            }
-            else if (e.GetCurrentPoint(this).Properties.IsRightButtonPressed)
-            {
-                if (!e.Handled)
-                {
-                    OwningGrid.Focus();
-                }
-                if (OwningRow != null)
-                {
-                    Debug.Assert(sender is DataGridRowHeader);
-                    Debug.Assert(sender == this);
-                    e.Handled = OwningGrid.UpdateStateOnMouseRightButtonDown(e, -1, Slot, false);
-                }
-            }
-        } 
-
-    }
-
-}
-

+ 0 - 3045
src/Avalonia.Controls.DataGrid/DataGridRows.cs

@@ -1,3045 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Collections;
-using Avalonia.Controls.Utils;
-using Avalonia.LogicalTree;
-using Avalonia.Media;
-using Avalonia.Utilities;
-using System;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.ComponentModel;
-using System.Diagnostics;
-using System.Linq;
-using Avalonia.Data;
-using Avalonia.Styling;
-
-namespace Avalonia.Controls
-{
-    public partial class DataGrid
-    {
-
-        internal bool AreRowBottomGridLinesRequired
-        {
-            get
-            {
-                return (GridLinesVisibility == DataGridGridLinesVisibility.Horizontal || GridLinesVisibility == DataGridGridLinesVisibility.All) && HorizontalGridLinesBrush != null;
-            }
-        }
-
-        internal int FirstVisibleSlot
-        {
-            get
-            {
-                return (SlotCount > 0) ? GetNextVisibleSlot(-1) : -1;
-            }
-        }
-
-        internal int FrozenColumnCountWithFiller
-        {
-            get
-            {
-                int count = FrozenColumnCount;
-                if (ColumnsInternal.RowGroupSpacerColumn.IsRepresented && (AreRowGroupHeadersFrozen || count > 0))
-                {
-                    // Either the RowGroupHeaders are frozen by default or the user set a frozen column count.  In both cases, we need to freeze
-                    // one more column than the what the public value says
-                    count++;
-                }
-                return count;
-            }
-        }
-
-        internal int LastVisibleSlot
-        {
-            get
-            {
-                return (SlotCount > 0) ? GetPreviousVisibleSlot(SlotCount) : -1;
-            }
-        }
-
-        // Cumulated height of all known rows, including the gridlines and details section.
-        // This property returns an approximation of the actual total row heights and also
-        // updates the RowHeightEstimate
-        private double EdgedRowsHeightCalculated
-        {
-            get
-            {
-                // If we're not displaying any rows or if we have infinite space the, relative height of our rows is 0
-                if (DisplayData.LastScrollingSlot == -1 || double.IsPositiveInfinity(AvailableSlotElementRoom))
-                {
-                    return 0;
-                }
-                Debug.Assert(DisplayData.LastScrollingSlot >= 0);
-                Debug.Assert(_verticalOffset >= 0);
-                Debug.Assert(NegVerticalOffset >= 0);
-
-                // Height of all rows above the viewport
-                double totalRowsHeight = _verticalOffset - NegVerticalOffset;
-
-                // Add the height of all the rows currently displayed, AvailableRowRoom
-                // is not always up to date enough for this
-                foreach (Control element in DisplayData.GetScrollingElements())
-                {
-                    if (element is DataGridRow row)
-                    {
-                        totalRowsHeight += row.TargetHeight;
-                    }
-                    else
-                    {
-                        totalRowsHeight += element.DesiredSize.Height;
-                    }
-                }
-
-                // Details up to and including viewport
-                int detailsCount = GetDetailsCountInclusive(0, DisplayData.LastScrollingSlot);
-
-                // Subtract details that were accounted for from the totalRowsHeight
-                totalRowsHeight -= detailsCount * RowDetailsHeightEstimate;
-
-                // Update the RowHeightEstimate if we have more row information
-                if (DisplayData.LastScrollingSlot >= _lastEstimatedRow)
-                {
-                    _lastEstimatedRow = DisplayData.LastScrollingSlot;
-                    RowHeightEstimate = totalRowsHeight / (_lastEstimatedRow + 1 - _collapsedSlotsTable.GetIndexCount(0, _lastEstimatedRow));
-                }
-
-                // Calculate estimates for what's beyond the viewport
-                if (VisibleSlotCount > DisplayData.NumDisplayedScrollingElements)
-                {
-                    int remainingRowCount = (SlotCount - DisplayData.LastScrollingSlot - _collapsedSlotsTable.GetIndexCount(DisplayData.LastScrollingSlot, SlotCount - 1) - 1);
-
-                    // Add estimation for the cell heights of all rows beyond our viewport
-                    totalRowsHeight += RowHeightEstimate * remainingRowCount;
-
-                    // Add the rest of the details beyond the viewport
-                    detailsCount += GetDetailsCountInclusive(DisplayData.LastScrollingSlot + 1, SlotCount - 1);
-                }
-
-                //
-                double totalDetailsHeight = detailsCount * RowDetailsHeightEstimate;
-
-                return totalRowsHeight + totalDetailsHeight;
-            }
-        }
-
-        /// <summary>
-        /// Clears the entire selection. Displayed rows are deselected explicitly to visualize
-        /// potential transition effects
-        /// </summary>
-        internal void ClearRowSelection(bool resetAnchorSlot)
-        {
-            if (resetAnchorSlot)
-            {
-                AnchorSlot = -1;
-            }
-            if (_selectedItems.Count > 0)
-            {
-                _noSelectionChangeCount++;
-                try
-                {
-                    // Individually deselecting displayed rows to view potential transitions
-                    for (int slot = DisplayData.FirstScrollingSlot;
-                         slot > -1 && slot <= DisplayData.LastScrollingSlot;
-                         slot++)
-                    {
-                        if (DisplayData.GetDisplayedElement(slot) is DataGridRow row)
-                        {
-                            if (_selectedItems.ContainsSlot(row.Slot))
-                            {
-                                SelectSlot(row.Slot, false);
-                            }
-                        }
-                    }
-                    _selectedItems.ClearRows();
-                    SelectionHasChanged = true;
-                }
-                finally
-                {
-                    NoSelectionChangeCount--;
-                }
-            }
-        }
-
-        /// <summary>
-        /// Clears the entire selection except the indicated row. Displayed rows are deselected explicitly to
-        /// visualize potential transition effects. The row indicated is selected if it is not already.
-        /// </summary>
-        internal void ClearRowSelection(int slotException, bool setAnchorSlot)
-        {
-            _noSelectionChangeCount++;
-            try
-            {
-                bool exceptionAlreadySelected = false;
-                if (_selectedItems.Count > 0)
-                {
-                    // Individually deselecting displayed rows to view potential transitions
-                    for (int slot = DisplayData.FirstScrollingSlot;
-                         slot > -1 && slot <= DisplayData.LastScrollingSlot;
-                         slot++)
-                    {
-                        if (slot != slotException && _selectedItems.ContainsSlot(slot))
-                        {
-                            SelectSlot(slot, false);
-                            SelectionHasChanged = true;
-                        }
-                    }
-                    exceptionAlreadySelected = _selectedItems.ContainsSlot(slotException);
-                    int selectedCount = _selectedItems.Count;
-                    if (selectedCount > 0)
-                    {
-                        if (selectedCount > 1)
-                        {
-                            SelectionHasChanged = true;
-                        }
-                        else
-                        {
-                            int currentlySelectedSlot = _selectedItems.GetIndexes().First();
-                            if (currentlySelectedSlot != slotException)
-                            {
-                                SelectionHasChanged = true;
-                            }
-                        }
-                        _selectedItems.ClearRows();
-                    }
-                }
-                if (exceptionAlreadySelected)
-                {
-                    // Exception row was already selected. It just needs to be marked as selected again.
-                    // No transition involved.
-                    _selectedItems.SelectSlot(slotException, true /*select*/);
-                    if (setAnchorSlot)
-                    {
-                        AnchorSlot = slotException;
-                    }
-                }
-                else
-                {
-                    // Exception row was not selected. It needs to be selected with potential transition
-                    SetRowSelection(slotException, true /*isSelected*/, setAnchorSlot);
-                }
-            }
-            finally
-            {
-                NoSelectionChangeCount--;
-            }
-        }
-
-        internal int GetCollapsedSlotCount(int startSlot, int endSlot)
-        {
-            return _collapsedSlotsTable.GetIndexCount(startSlot, endSlot);
-        }
-
-        internal int GetNextVisibleSlot(int slot)
-        {
-            return _collapsedSlotsTable.GetNextGap(slot);
-        }
-
-        internal int GetPreviousVisibleSlot(int slot)
-        {
-            return _collapsedSlotsTable.GetPreviousGap(slot);
-        }
-
-        /// <summary>
-        /// Returns the row associated to the provided backend data item.
-        /// </summary>
-        /// <param name="dataItem">backend data item</param>
-        /// <returns>null if the DataSource is null, the provided item in not in the source, or the item is not displayed; otherwise, the associated Row</returns>
-        internal DataGridRow GetRowFromItem(object dataItem)
-        {
-            int rowIndex = DataConnection.IndexOf(dataItem);
-            if (rowIndex < 0)
-            {
-                return null;
-            }
-            int slot = SlotFromRowIndex(rowIndex);
-            return IsSlotVisible(slot) ? DisplayData.GetDisplayedElement(slot) as DataGridRow : null;
-        }
-
-        internal bool GetRowSelection(int slot)
-        {
-            Debug.Assert(slot != -1);
-            return _selectedItems.ContainsSlot(slot);
-        }
-
-        internal void InsertElementAt(int slot, int rowIndex, object item, DataGridRowGroupInfo groupInfo, bool isCollapsed)
-        {
-            Debug.Assert(slot >= 0 && slot <= SlotCount);
-
-            bool isRow = rowIndex != -1;
-            if (isCollapsed)
-            {
-                InsertElement(slot,
-                    element: null,
-                    updateVerticalScrollBarOnly: true,
-                    isCollapsed: true,
-                    isRow: isRow);
-            }
-            else if (SlotIsDisplayed(slot))
-            {
-                // Row at that index needs to be displayed
-                if (isRow)
-                {
-                    InsertElement(slot, GenerateRow(rowIndex, slot, item), false /*updateVerticalScrollBarOnly*/, false /*isCollapsed*/, isRow);
-                }
-                else
-                {
-                    InsertElement(slot, GenerateRowGroupHeader(slot, groupInfo),
-                        updateVerticalScrollBarOnly: false,
-                        isCollapsed: false,
-                        isRow: isRow);
-                }
-            }
-            else
-            {
-                InsertElement(slot,
-                    element: null,
-                    updateVerticalScrollBarOnly: _vScrollBar == null || _vScrollBar.IsVisible,
-                    isCollapsed: false,
-                    isRow: isRow);
-            }
-        }
-
-        internal void InsertRowAt(int rowIndex)
-        {
-            int slot = SlotFromRowIndex(rowIndex);
-            object item = DataConnection.GetDataItem(rowIndex);
-
-            // isCollapsed below is always false because we only use the method if we're not grouping
-            InsertElementAt(slot, rowIndex, item, null/*DataGridRowGroupInfo*/, false /*isCollapsed*/);
-        }
-
-        internal bool IsColumnDisplayed(int columnIndex)
-        {
-            return columnIndex >= FirstDisplayedNonFillerColumnIndex && columnIndex <= DisplayData.LastTotallyDisplayedScrollingCol;
-        }
-
-        internal bool IsRowRecyclable(DataGridRow row)
-        {
-            return (row != EditingRow && row != _focusedRow);
-        }
-
-        internal bool IsSlotVisible(int slot)
-        {
-            return slot >= DisplayData.FirstScrollingSlot
-               && slot <= DisplayData.LastScrollingSlot
-               && slot != -1
-               && !_collapsedSlotsTable.Contains(slot);
-        }
-
-        internal void OnRowsMeasure()
-        {
-            if (!MathUtilities.IsZero(DisplayData.PendingVerticalScrollHeight))
-            {
-                ScrollSlotsByHeight(DisplayData.PendingVerticalScrollHeight);
-                DisplayData.PendingVerticalScrollHeight = 0;
-            }
-        }
-
-        internal void RefreshRows(bool recycleRows, bool clearRows)
-        {
-            if (_measured)
-            {
-                // _desiredCurrentColumnIndex is used in MakeFirstDisplayedCellCurrentCell to set the
-                // column position back to what it was before the refresh
-                _desiredCurrentColumnIndex = CurrentColumnIndex;
-                double verticalOffset = _verticalOffset;
-                if (DisplayData.PendingVerticalScrollHeight > 0)
-                {
-                    // Use the pending vertical scrollbar position if there is one, in the case that the collection
-                    // has been reset multiple times in a row.
-                    verticalOffset = DisplayData.PendingVerticalScrollHeight;
-                }
-                _verticalOffset = 0;
-                NegVerticalOffset = 0;
-
-                if (clearRows)
-                {
-                    ClearRows(recycleRows);
-                    ClearRowGroupHeadersTable();
-                    PopulateRowGroupHeadersTable();
-                }
-
-                RefreshRowGroupHeaders();
-
-                // Update the CurrentSlot because it might have changed
-                if (recycleRows && DataConnection.CollectionView != null)
-                {
-                    CurrentSlot = DataConnection.CollectionView.CurrentPosition == -1
-                        ? -1 : SlotFromRowIndex(DataConnection.CollectionView.CurrentPosition);
-                    if (CurrentSlot == -1)
-                    {
-                        SetCurrentCellCore(-1, -1);
-                    }
-                }
-
-                if (DataConnection != null && ColumnsItemsInternal.Count > 0)
-                {
-                    AddSlots(DataConnection.Count);
-                    AddSlots(DataConnection.Count + RowGroupHeadersTable.IndexCount);
-
-                    InvalidateMeasure();
-                }
-
-                EnsureRowGroupSpacerColumn();
-
-                if (VerticalScrollBar != null)
-                {
-                    DisplayData.PendingVerticalScrollHeight = Math.Min(verticalOffset, VerticalScrollBar.Maximum);
-                }
-            }
-            else
-            {
-                if (clearRows)
-                {
-                    ClearRows(recycleRows);
-                }
-                ClearRowGroupHeadersTable();
-                PopulateRowGroupHeadersTable();
-            }
-        }
-
-        internal void RemoveRowAt(int rowIndex, object item)
-        {
-            RemoveElementAt(SlotFromRowIndex(rowIndex), item, true);
-        }
-
-        internal int RowIndexFromSlot(int slot)
-        {
-            return slot - RowGroupHeadersTable.GetIndexCount(0, slot);
-        }
-
-        internal bool ScrollSlotIntoView(int slot, bool scrolledHorizontally)
-        {
-            Debug.Assert(_collapsedSlotsTable.Contains(slot) || !IsSlotOutOfBounds(slot));
-
-            if (scrolledHorizontally && DisplayData.FirstScrollingSlot <= slot && DisplayData.LastScrollingSlot >= slot)
-            {
-                // If the slot is displayed and we scrolled horizontally, column virtualization could cause the rows to grow.
-                // As a result we need to force measure on the rows we're displaying and recalculate our First and Last slots
-                // so they're accurate
-                foreach (DataGridRow row in DisplayData.GetScrollingRows())
-                {
-                    row.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
-                }
-                UpdateDisplayedRows(DisplayData.FirstScrollingSlot, CellsEstimatedHeight);
-            }
-
-            if (DisplayData.FirstScrollingSlot < slot && (DisplayData.LastScrollingSlot > slot || DisplayData.LastScrollingSlot == -1))
-            {
-                // The row is already displayed in its entirety
-                return true;
-            }
-            else if (DisplayData.FirstScrollingSlot == slot && slot != -1)
-            {
-                if (!MathUtilities.IsZero(NegVerticalOffset))
-                {
-                    // First displayed row is partially scrolled of. Let's scroll it so that NegVerticalOffset becomes 0.
-                    DisplayData.PendingVerticalScrollHeight = -NegVerticalOffset;
-                    InvalidateRowsMeasure(false /*invalidateIndividualRows*/);
-                }
-                return true;
-            }
-
-            double deltaY = 0;
-            int firstFullSlot;
-            if (DisplayData.FirstScrollingSlot > slot)
-            {
-                // Scroll up to the new row so it becomes the first displayed row
-                firstFullSlot = DisplayData.FirstScrollingSlot - 1;
-                if (MathUtilities.GreaterThan(NegVerticalOffset, 0))
-                {
-                    deltaY = -NegVerticalOffset;
-                }
-                deltaY -= GetSlotElementsHeight(slot, firstFullSlot);
-                if (DisplayData.FirstScrollingSlot - slot > 1)
-                {
-                    //
-
-                    ResetDisplayedRows();
-                }
-                NegVerticalOffset = 0;
-                UpdateDisplayedRows(slot, CellsEstimatedHeight);
-            }
-            else if (DisplayData.LastScrollingSlot <= slot)
-            {
-                // Scroll down to the new row so it's entirely displayed.  If the height of the row
-                // is greater than the height of the DataGrid, then show the top of the row at the top
-                // of the grid
-                firstFullSlot = DisplayData.LastScrollingSlot;
-                // Figure out how much of the last row is cut off
-                double rowHeight = GetExactSlotElementHeight(DisplayData.LastScrollingSlot);
-                double availableHeight = AvailableSlotElementRoom + rowHeight;
-                if (MathUtilities.AreClose(rowHeight, availableHeight))
-                {
-                    if (DisplayData.LastScrollingSlot == slot)
-                    {
-                        // We're already at the very bottom so we don't need to scroll down further
-                        return true;
-                    }
-                    else
-                    {
-                        // We're already showing the entire last row so don't count it as part of the delta
-                        firstFullSlot++;
-                    }
-                }
-                else if (rowHeight > availableHeight)
-                {
-                    firstFullSlot++;
-                    deltaY += rowHeight - availableHeight;
-                }
-                // sum up the height of the rest of the full rows
-                if (slot >= firstFullSlot)
-                {
-                    deltaY += GetSlotElementsHeight(firstFullSlot, slot);
-                }
-                // If the first row we're displaying is no longer adjacent to the rows we have
-                // simply discard the ones we have
-                if (slot - DisplayData.LastScrollingSlot > 1)
-                {
-                    ResetDisplayedRows();
-                }
-                if (MathUtilities.GreaterThanOrClose(GetExactSlotElementHeight(slot), CellsEstimatedHeight))
-                {
-                    // The entire row won't fit in the DataGrid so we start showing it from the top
-                    NegVerticalOffset = 0;
-                    UpdateDisplayedRows(slot, CellsEstimatedHeight);
-                }
-                else
-                {
-                    UpdateDisplayedRowsFromBottom(slot);
-                }
-            }
-
-            _verticalOffset += deltaY;
-            if (_verticalOffset < 0 || DisplayData.FirstScrollingSlot == 0)
-            {
-                // We scrolled too far because a row's height was larger than its approximation
-                _verticalOffset = NegVerticalOffset;
-            }
-
-            //
-            Debug.Assert(MathUtilities.LessThanOrClose(NegVerticalOffset, _verticalOffset));
-
-            SetVerticalOffset(_verticalOffset);
-
-            InvalidateMeasure();
-            InvalidateRowsMeasure(false /*invalidateIndividualRows*/);
-
-            return true;
-        }
-
-        internal void SetRowSelection(int slot, bool isSelected, bool setAnchorSlot)
-        {
-            Debug.Assert(!(!isSelected && setAnchorSlot));
-            Debug.Assert(!IsSlotOutOfSelectionBounds(slot));
-            _noSelectionChangeCount++;
-            try
-            {
-                if (SelectionMode == DataGridSelectionMode.Single && isSelected)
-                {
-                    Debug.Assert(_selectedItems.Count <= 1);
-                    if (_selectedItems.Count > 0)
-                    {
-                        int currentlySelectedSlot = _selectedItems.GetIndexes().First();
-                        if (currentlySelectedSlot != slot)
-                        {
-                            SelectSlot(currentlySelectedSlot, false);
-                            SelectionHasChanged = true;
-                        }
-                    }
-                }
-                if (_selectedItems.ContainsSlot(slot) != isSelected)
-                {
-                    SelectSlot(slot, isSelected);
-                    SelectionHasChanged = true;
-                }
-                if (setAnchorSlot)
-                {
-                    AnchorSlot = slot;
-                }
-            }
-            finally
-            {
-                NoSelectionChangeCount--;
-            }
-        }
-
-        // For now, all scenarios are for isSelected == true.
-        internal void SetRowsSelection(int startSlot, int endSlot /*, bool isSelected*/)
-        {
-            Debug.Assert(startSlot >= 0 && startSlot < SlotCount);
-            Debug.Assert(endSlot >= 0 && endSlot < SlotCount);
-            Debug.Assert(startSlot <= endSlot);
-
-            _noSelectionChangeCount++;
-            try
-            {
-                if (/*isSelected &&*/ !_selectedItems.ContainsAll(startSlot, endSlot))
-                {
-                    // At least one row gets selected
-                    SelectSlots(startSlot, endSlot, true);
-                    SelectionHasChanged = true;
-                }
-            }
-            finally
-            {
-                NoSelectionChangeCount--;
-            }
-        }
-
-        internal int SlotFromRowIndex(int rowIndex)
-        {
-            return rowIndex + RowGroupHeadersTable.GetIndexCountBeforeGap(0, rowIndex);
-        }
-
-        private void AddSlotElement(int slot, Control element)
-        {
-#if DEBUG
-            if (element is DataGridRow row)
-            {
-                Debug.Assert(row.OwningGrid == this);
-                Debug.Assert(row.Cells.Count == ColumnsItemsInternal.Count);
-
-                int columnIndex = 0;
-                foreach (DataGridCell dataGridCell in row.Cells)
-                {
-                    Debug.Assert(dataGridCell.OwningRow == row);
-                    Debug.Assert(dataGridCell.OwningColumn == ColumnsItemsInternal[columnIndex]);
-                    columnIndex++;
-                }
-            }
-#endif
-            Debug.Assert(slot == SlotCount);
-
-            OnAddedElement_Phase1(slot, element);
-            SlotCount++;
-            VisibleSlotCount++;
-            OnAddedElement_Phase2(slot, updateVerticalScrollBarOnly: false);
-            OnElementsChanged(grew: true);
-        }
-
-        private void AddSlots(int totalSlots)
-        {
-            SlotCount = 0;
-            VisibleSlotCount = 0;
-            IEnumerator<int> groupSlots = null;
-            int nextGroupSlot = -1;
-            if (RowGroupHeadersTable.RangeCount > 0)
-            {
-                groupSlots = RowGroupHeadersTable.GetIndexes().GetEnumerator();
-                if (groupSlots != null && groupSlots.MoveNext())
-                {
-                    nextGroupSlot = groupSlots.Current;
-                }
-            }
-            int slot = 0;
-            int addedRows = 0;
-            while (slot < totalSlots && AvailableSlotElementRoom > 0)
-            {
-                if (slot == nextGroupSlot)
-                {
-                    DataGridRowGroupInfo groupRowInfo = RowGroupHeadersTable.GetValueAt(slot);
-                    AddSlotElement(slot, GenerateRowGroupHeader(slot, groupRowInfo));
-                    nextGroupSlot = groupSlots.MoveNext() ? groupSlots.Current : -1;
-                }
-                else
-                {
-                    AddSlotElement(slot, GenerateRow(addedRows, slot));
-                    addedRows++;
-                }
-                slot++;
-            }
-
-            if (slot < totalSlots)
-            {
-                SlotCount += totalSlots - slot;
-                VisibleSlotCount += totalSlots - slot;
-                OnAddedElement_Phase2(0,
-                    updateVerticalScrollBarOnly: _vScrollBar == null || _vScrollBar.IsVisible);
-                OnElementsChanged(grew: true);
-            }
-        }
-
-        private void ApplyDisplayedRowsState(int startSlot, int endSlot)
-        {
-            int firstSlot = Math.Max(DisplayData.FirstScrollingSlot, startSlot);
-            int lastSlot = Math.Min(DisplayData.LastScrollingSlot, endSlot);
-
-            if (firstSlot >= 0)
-            {
-                Debug.Assert(lastSlot >= firstSlot);
-                int slot = GetNextVisibleSlot(firstSlot - 1);
-                while (slot <= lastSlot)
-                {
-                    if (DisplayData.GetDisplayedElement(slot) is DataGridRow row)
-                    {
-                        row.ApplyState();
-                    }
-                    slot = GetNextVisibleSlot(slot);
-                }
-            }
-        }
-
-        private void ClearRows(bool recycle)
-        {
-            // Need to clean up recycled rows even if the RowCount is 0
-            SetCurrentCellCore(-1, -1, commitEdit: false, endRowEdit: false);
-            ClearRowSelection(resetAnchorSlot: true);
-            UnloadElements(recycle);
-
-            _showDetailsTable.Clear();
-            SlotCount = 0;
-            NegVerticalOffset = 0;
-            SetVerticalOffset(0);
-            ComputeScrollBarsLayout();
-        }
-
-        // Updates _collapsedSlotsTable and returns the number of pixels that were collapsed
-        private double CollapseSlotsInTable(int startSlot, int endSlot, ref int slotsExpanded, int lastDisplayedSlot, ref double heightChangeBelowLastDisplayedSlot)
-        {
-            int firstSlot = startSlot;
-            int lastSlot;
-            double totalHeightChange = 0;
-            // Figure out which slots actually need to be expanded since some might already be collapsed
-            while (firstSlot <= endSlot)
-            {
-                firstSlot = _collapsedSlotsTable.GetNextGap(firstSlot - 1);
-                int nextCollapsedSlot = _collapsedSlotsTable.GetNextIndex(firstSlot) - 1;
-                lastSlot = nextCollapsedSlot == -2 ? endSlot : Math.Min(endSlot, nextCollapsedSlot);
-
-                if (firstSlot <= lastSlot)
-                {
-                    double heightChange = GetHeightEstimate(firstSlot, lastSlot);
-                    totalHeightChange -= heightChange;
-                    slotsExpanded -= lastSlot - firstSlot + 1;
-
-                    if (lastSlot > lastDisplayedSlot)
-                    {
-                        if (firstSlot > lastDisplayedSlot)
-                        {
-                            heightChangeBelowLastDisplayedSlot -= heightChange;
-                        }
-                        else
-                        {
-                            heightChangeBelowLastDisplayedSlot -= GetHeightEstimate(lastDisplayedSlot + 1, lastSlot);
-                        }
-                    }
-
-                    firstSlot = lastSlot + 1;
-                }
-            }
-
-            // Update _collapsedSlotsTable in one bulk operation
-            _collapsedSlotsTable.AddValues(startSlot, endSlot - startSlot + 1, false);
-
-            return totalHeightChange;
-        }
-
-        private static void CorrectRowAfterDeletion(DataGridRow row, bool rowDeleted)
-        {
-            row.Slot--;
-            if (rowDeleted)
-            {
-                row.Index--;
-            }
-        }
-
-        private static void CorrectRowAfterInsertion(DataGridRow row, bool rowInserted)
-        {
-            row.Slot++;
-            if (rowInserted)
-            {
-                row.Index++;
-            }
-        }
-
-        /// <summary>
-        /// Adjusts the index of all displayed, loaded and edited rows after a row was deleted.
-        /// Removes the deleted row from the list of loaded rows if present.
-        /// </summary>
-        private void CorrectSlotsAfterDeletion(int slotDeleted, bool wasRow)
-        {
-            Debug.Assert(slotDeleted >= 0);
-
-            // Take care of the non-visible loaded rows
-            for (int index = 0; index < _loadedRows.Count;)
-            {
-                DataGridRow dataGridRow = _loadedRows[index];
-                if (IsSlotVisible(dataGridRow.Slot))
-                {
-                    index++;
-                }
-                else
-                {
-                    if (dataGridRow.Slot > slotDeleted)
-                    {
-                        CorrectRowAfterDeletion(dataGridRow, wasRow);
-                        index++;
-                    }
-                    else if (dataGridRow.Slot == slotDeleted)
-                    {
-                        _loadedRows.RemoveAt(index);
-                    }
-                    else
-                    {
-                        index++;
-                    }
-                }
-            }
-
-            // Take care of the non-visible edited row
-            if (EditingRow != null &&
-                !IsSlotVisible(EditingRow.Slot) &&
-                EditingRow.Slot > slotDeleted)
-            {
-                CorrectRowAfterDeletion(EditingRow, wasRow);
-            }
-
-            // Take care of the non-visible focused row
-            if (_focusedRow != null &&
-                _focusedRow != EditingRow &&
-                !IsSlotVisible(_focusedRow.Slot) &&
-                _focusedRow.Slot > slotDeleted)
-            {
-                CorrectRowAfterDeletion(_focusedRow, wasRow);
-            }
-
-            // Take care of the visible rows
-            foreach (DataGridRow row in DisplayData.GetScrollingRows())
-            {
-                if (row.Slot > slotDeleted)
-                {
-                    CorrectRowAfterDeletion(row, wasRow);
-                    _rowsPresenter?.InvalidateChildIndex(row);
-                }
-            }
-
-            // Update the RowGroupHeaders
-            foreach (int slot in RowGroupHeadersTable.GetIndexes())
-            {
-                DataGridRowGroupInfo rowGroupInfo = RowGroupHeadersTable.GetValueAt(slot);
-                if (rowGroupInfo.Slot > slotDeleted)
-                {
-                    rowGroupInfo.Slot--;
-                }
-                if (rowGroupInfo.LastSubItemSlot >= slotDeleted)
-                {
-                    rowGroupInfo.LastSubItemSlot--;
-                }
-            }
-
-            // Update which row we've calculated the RowHeightEstimate up to
-            if (_lastEstimatedRow >= slotDeleted)
-            {
-                _lastEstimatedRow--;
-            }
-        }
-
-        /// <summary>
-        /// Adjusts the index of all displayed, loaded and edited rows after rows were deleted.
-        /// </summary>
-        private void CorrectSlotsAfterInsertion(int slotInserted, bool isCollapsed, bool rowInserted)
-        {
-            Debug.Assert(slotInserted >= 0);
-
-            // Take care of the non-visible loaded rows
-            foreach (DataGridRow dataGridRow in _loadedRows)
-            {
-                if (!IsSlotVisible(dataGridRow.Slot) && dataGridRow.Slot >= slotInserted)
-                {
-                    DataGrid.CorrectRowAfterInsertion(dataGridRow, rowInserted);
-                }
-            }
-
-            // Take care of the non-visible focused row
-            if (_focusedRow != null &&
-                _focusedRow != EditingRow &&
-                !(IsSlotVisible(_focusedRow.Slot) || ((_focusedRow.Slot == slotInserted) && isCollapsed)) &&
-                _focusedRow.Slot >= slotInserted)
-            {
-                DataGrid.CorrectRowAfterInsertion(_focusedRow, rowInserted);
-            }
-
-            // Take care of the visible rows
-            foreach (DataGridRow row in DisplayData.GetScrollingRows())
-            {
-                if (row.Slot >= slotInserted)
-                {
-                    DataGrid.CorrectRowAfterInsertion(row, rowInserted);
-                    _rowsPresenter?.InvalidateChildIndex(row);
-                }
-            }
-
-            // Re-calculate the EditingRow's Slot and Index and ensure that it is still selected.
-            if (EditingRow != null)
-            {
-                EditingRow.Index = DataConnection.IndexOf(EditingRow.DataContext);
-                EditingRow.Slot = SlotFromRowIndex(EditingRow.Index);
-            }
-
-            // Update the RowGroupHeaders
-            foreach (int slot in RowGroupHeadersTable.GetIndexes(slotInserted))
-            {
-                DataGridRowGroupInfo rowGroupInfo = RowGroupHeadersTable.GetValueAt(slot);
-                if (rowGroupInfo.Slot >= slotInserted)
-                {
-                    rowGroupInfo.Slot++;
-                }
-
-                // We are purposefully checking GT and not GTE because the equality case is handled
-                // by the CorrectLastSubItemSlotsAfterInsertion method
-                if (rowGroupInfo.LastSubItemSlot > slotInserted)
-                {
-                    rowGroupInfo.LastSubItemSlot++;
-                }
-            }
-
-            // Update which row we've calculated the RowHeightEstimate up to
-            if (_lastEstimatedRow >= slotInserted)
-            {
-                _lastEstimatedRow++;
-            }
-        }
-
-        private IEnumerable<DataGridRow> GetAllRows()
-        {
-            if (_rowsPresenter != null)
-            {
-                foreach (Control element in _rowsPresenter.Children)
-                {
-                    if (element is DataGridRow row)
-                    {
-                        yield return row;
-                    }
-                }
-            }
-        }
-
-        // Expands slots from startSlot to endSlot inclusive and adds the amount expanded in this suboperation to
-        // the given totalHeightChanged of the entire operation
-        private void ExpandSlots(int startSlot, int endSlot, bool isDisplayed, ref int slotsExpanded, ref double totalHeightChange)
-        {
-            double heightAboveStartSlot = 0;
-            if (isDisplayed)
-            {
-                int slot = DisplayData.FirstScrollingSlot;
-                while (slot < startSlot)
-                {
-                    heightAboveStartSlot += GetExactSlotElementHeight(slot);
-                    slot = GetNextVisibleSlot(slot);
-                }
-
-                // First make the bottom rows available for recycling so we minimize element creation when expanding
-                for (int i = 0; (i < endSlot - startSlot + 1) && (DisplayData.LastScrollingSlot > endSlot); i++)
-                {
-                    RemoveDisplayedElement(DisplayData.LastScrollingSlot, wasDeleted: false, updateSlotInformation: true);
-                }
-            }
-
-            // Figure out which slots actually need to be expanded since some might already be collapsed
-            double currentHeightChange = 0;
-            int firstSlot = startSlot;
-            int lastSlot = endSlot;
-            while (firstSlot <= endSlot)
-            {
-                firstSlot = _collapsedSlotsTable.GetNextIndex(firstSlot - 1);
-                if (firstSlot == -1)
-                {
-                    break;
-                }
-                lastSlot = Math.Min(endSlot, _collapsedSlotsTable.GetNextGap(firstSlot) - 1);
-
-                if (firstSlot <= lastSlot)
-                {
-                    if (!isDisplayed)
-                    {
-                        // Estimate the height change if the slots aren't displayed.  If they are displayed, we can add real values
-                        double rowCount = lastSlot - firstSlot - GetRowGroupHeaderCount(firstSlot, lastSlot, false, out double headerHeight) + 1;
-                        double detailsCount = GetDetailsCountInclusive(firstSlot, lastSlot);
-                        currentHeightChange += headerHeight + (detailsCount * RowDetailsHeightEstimate) + (rowCount * RowHeightEstimate);
-                    }
-                    slotsExpanded += lastSlot - firstSlot + 1;
-                    firstSlot = lastSlot + 1;
-                }
-            }
-
-            // Update _collapsedSlotsTable in one bulk operation
-            _collapsedSlotsTable.RemoveValues(startSlot, endSlot - startSlot + 1);
-
-            if (isDisplayed)
-            {
-                double availableHeight = CellsEstimatedHeight - heightAboveStartSlot;
-                // Actually expand the displayed slots up to what we can display
-                for (int i = startSlot; (i <= endSlot) && (currentHeightChange < availableHeight); i++)
-                {
-                    Control insertedElement = InsertDisplayedElement(i, updateSlotInformation: false);
-                    currentHeightChange += insertedElement.DesiredSize.Height;
-                    if (i > DisplayData.LastScrollingSlot)
-                    {
-                        DisplayData.LastScrollingSlot = i;
-                    }
-                }
-            }
-
-            // Update the total height for the entire Expand operation
-            totalHeightChange += currentHeightChange;
-        }
-
-        /// <summary>
-        /// Creates all the editing elements for the current editing row, so the bindings
-        /// all exist during validation.
-        /// </summary>
-        private void GenerateEditingElements()
-        {
-            if (EditingRow != null && EditingRow.Cells != null)
-            {
-                Debug.Assert(EditingRow.Cells.Count == ColumnsItemsInternal.Count);
-                foreach (DataGridColumn column in ColumnsInternal.GetDisplayedColumns(c => c.IsVisible && !c.IsReadOnly))
-                {
-                    column.GenerateEditingElementInternal(EditingRow.Cells[column.Index], EditingRow.DataContext);
-                }
-            }
-        }
-
-        /// <summary>
-        /// Returns a row for the provided index. The row gets first loaded through the LoadingRow event.
-        /// </summary>
-        private DataGridRow GenerateRow(int rowIndex, int slot)
-        {
-            return GenerateRow(rowIndex, slot, DataConnection.GetDataItem(rowIndex));
-        }
-
-        /// <summary>
-        /// Returns a row for the provided index. The row gets first loaded through the LoadingRow event.
-        /// </summary>
-        private DataGridRow GenerateRow(int rowIndex, int slot, object dataContext)
-        {
-            Debug.Assert(rowIndex > -1);
-            DataGridRow dataGridRow = GetGeneratedRow(dataContext);
-            if (dataGridRow == null)
-            {
-                dataGridRow = DisplayData.GetUsedRow() ?? new DataGridRow();
-                dataGridRow.Index = rowIndex;
-                dataGridRow.Slot = slot;
-                dataGridRow.OwningGrid = this;
-                dataGridRow.DataContext = dataContext;
-                if (RowTheme is {} rowTheme)
-                {
-                    dataGridRow.SetValue(ThemeProperty, rowTheme, BindingPriority.Template);
-                }
-                CompleteCellsCollection(dataGridRow);
-
-                OnLoadingRow(new DataGridRowEventArgs(dataGridRow));
-            }
-            return dataGridRow;
-        }
-
-        /// <summary>
-        /// Returns the exact row height, whether it is currently displayed or not.
-        /// The row is generated and added to the displayed rows in case it is not already displayed.
-        /// The horizontal gridlines thickness are added.
-        /// </summary>
-        private double GetExactSlotElementHeight(int slot)
-        {
-            Debug.Assert((slot >= 0) && slot < SlotCount);
-
-            if (IsSlotVisible(slot))
-            {
-                Debug.Assert(DisplayData.GetDisplayedElement(slot) != null);
-                return DisplayData.GetDisplayedElement(slot).DesiredSize.Height;
-            }
-
-            Control slotElement = InsertDisplayedElement(slot, true /*updateSlotInformation*/);
-            Debug.Assert(slotElement != null);
-            return slotElement.DesiredSize.Height;
-        }
-
-        // Returns an estimate for the height of the slots between fromSlot and toSlot
-        private double GetHeightEstimate(int fromSlot, int toSlot)
-        {
-            double rowCount = toSlot - fromSlot - GetRowGroupHeaderCount(fromSlot, toSlot, true, out double headerHeight) + 1;
-            double detailsCount = GetDetailsCountInclusive(fromSlot, toSlot);
-
-            return headerHeight + (detailsCount * RowDetailsHeightEstimate) + (rowCount * RowHeightEstimate);
-        }
-
-        /// <summary>
-        /// If the provided slot is displayed, returns the exact height.
-        /// If the slot is not displayed, returns a default height.
-        /// </summary>
-        private double GetSlotElementHeight(int slot)
-        {
-            Debug.Assert(slot >= 0 && slot < SlotCount);
-            if (IsSlotVisible(slot))
-            {
-                Debug.Assert(DisplayData.GetDisplayedElement(slot) != null);
-                return DisplayData.GetDisplayedElement(slot).DesiredSize.Height;
-            }
-            else
-            {
-                DataGridRowGroupInfo rowGroupInfo = RowGroupHeadersTable.GetValueAt(slot);
-                if (rowGroupInfo != null)
-                {
-                    return _rowGroupHeightsByLevel[rowGroupInfo.Level];
-                }
-
-                // Assume it's a row since we're either not grouping or it wasn't a RowGroupHeader
-                return RowHeightEstimate + (GetRowDetailsVisibility(slot) ? RowDetailsHeightEstimate : 0);
-            }
-        }
-
-        /// <summary>
-        /// Cumulates the approximate height of the rows from fromRowIndex to toRowIndex included.
-        /// Including the potential gridline thickness.
-        /// </summary>
-        private double GetSlotElementsHeight(int fromSlot, int toSlot)
-        {
-            Debug.Assert(toSlot >= fromSlot);
-
-            double height = 0;
-            for (int slot = fromSlot; slot <= toSlot; slot++)
-            {
-                height += GetSlotElementHeight(slot);
-            }
-            return height;
-        }
-
-        /// <summary>
-        /// Checks if the row for the provided dataContext has been generated and is present
-        /// in either the loaded rows, pre-fetched rows, or editing row.
-        /// The displayed rows are *not* searched. Returns null if the row does not belong to those 3 categories.
-        /// </summary>
-        private DataGridRow GetGeneratedRow(object dataContext)
-        {
-            // Check the list of rows being loaded via the LoadingRow event.
-            DataGridRow dataGridRow = GetLoadedRow(dataContext);
-            if (dataGridRow != null)
-            {
-                return dataGridRow;
-            }
-
-            // Check the potential editing row.
-            if (EditingRow != null && dataContext == EditingRow.DataContext)
-            {
-                return EditingRow;
-            }
-
-            // Check the potential focused row.
-            if (_focusedRow != null && dataContext == _focusedRow.DataContext)
-            {
-                return _focusedRow;
-            }
-
-            return null;
-        }
-
-        private DataGridRow GetLoadedRow(object dataContext)
-        {
-            foreach (DataGridRow dataGridRow in _loadedRows)
-            {
-                if (dataGridRow.DataContext == dataContext)
-                {
-                    return dataGridRow;
-                }
-            }
-            return null;
-        }
-
-        private Control InsertDisplayedElement(int slot, bool updateSlotInformation)
-        {
-            Control slotElement;
-            if (RowGroupHeadersTable.Contains(slot))
-            {
-                slotElement = GenerateRowGroupHeader(slot, rowGroupInfo: RowGroupHeadersTable.GetValueAt(slot));
-            }
-            else
-            {
-                // If we're grouping, the GroupLevel needs to be fixed later by methods calling this
-                // which end up inserting rows. We don't do it here because elements could be inserted
-                // from top to bottom or bottom to up so it's better to do in one pass
-                slotElement = GenerateRow(RowIndexFromSlot(slot), slot);
-            }
-            InsertDisplayedElement(slot, slotElement, wasNewlyAdded: false, updateSlotInformation: updateSlotInformation);
-            return slotElement;
-        }
-
-        private void InsertDisplayedElement(int slot, Control element, bool wasNewlyAdded, bool updateSlotInformation)
-        {
-            // We can only support creating new rows that are adjacent to the currently visible rows
-            // since they need to be added to the visual tree for us to Measure them.
-            Debug.Assert(DisplayData.FirstScrollingSlot == -1 || slot >= GetPreviousVisibleSlot(DisplayData.FirstScrollingSlot) && slot <= GetNextVisibleSlot(DisplayData.LastScrollingSlot));
-            Debug.Assert(element != null);
-
-            if (_rowsPresenter != null)
-            {
-                DataGridRowGroupHeader groupHeader = null;
-                DataGridRow row = element as DataGridRow;
-                if (row != null)
-                {
-                    LoadRowVisualsForDisplay(row);
-
-                    if (IsRowRecyclable(row))
-                    {
-                        if (!row.IsRecycled)
-                        {
-                            Debug.Assert(!_rowsPresenter.Children.Contains(element));
-                            _rowsPresenter.Children.Add(row);
-                        }
-                    }
-                    else
-                    {
-                        element.Clip = null;
-                        Debug.Assert(row.Index == RowIndexFromSlot(slot));
-                    }
-                }
-                else
-                {
-                    groupHeader = element as DataGridRowGroupHeader;
-                    Debug.Assert(groupHeader != null);  // Nothing other and Rows and RowGroups now
-                    if (groupHeader != null)
-                    {
-                        groupHeader.TotalIndent = (groupHeader.Level == 0) ? 0 : RowGroupSublevelIndents[groupHeader.Level - 1];
-                        if (!groupHeader.IsRecycled)
-                        {
-                            _rowsPresenter.Children.Add(element);
-                        }
-                        groupHeader.LoadVisualsForDisplay();
-                    }
-                }
-
-                // Measure the element and update AvailableRowRoom
-                element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
-                AvailableSlotElementRoom -= element.DesiredSize.Height;
-
-                if (groupHeader != null)
-                {
-                    _rowGroupHeightsByLevel[groupHeader.Level] = groupHeader.DesiredSize.Height;
-                }
-
-                if (row != null && RowHeightEstimate == DataGrid.DATAGRID_defaultRowHeight && double.IsNaN(row.Height))
-                {
-                    RowHeightEstimate = element.DesiredSize.Height;
-                }
-            }
-
-            if (wasNewlyAdded)
-            {
-                DisplayData.CorrectSlotsAfterInsertion(slot, element, isCollapsed: false);
-            }
-            else
-            {
-                DisplayData.LoadScrollingSlot(slot, element, updateSlotInformation);
-            }
-        }
-
-        private void InsertElement(int slot, Control element, bool updateVerticalScrollBarOnly, bool isCollapsed, bool isRow)
-        {
-            Debug.Assert(slot >= 0 && slot <= SlotCount);
-
-            OnInsertingElement(slot, true /*firstInsertion*/, isCollapsed);   // will throw an exception if the insertion is illegal
-
-            OnInsertedElement_Phase1(slot, element, isCollapsed, isRow);
-            SlotCount++;
-            if (!isCollapsed)
-            {
-                VisibleSlotCount++;
-            }
-            OnInsertedElement_Phase2(slot, updateVerticalScrollBarOnly, isCollapsed);
-        }
-
-        private void InvalidateRowHeightEstimate()
-        {
-            // Start from scratch and assume that we haven't estimated any rows
-            _lastEstimatedRow = -1;
-        }
-
-        private void OnAddedElement_Phase1(int slot, Control element)
-        {
-            Debug.Assert(slot >= 0);
-
-            // Row needs to be potentially added to the displayed rows
-            if (SlotIsDisplayed(slot))
-            {
-                InsertDisplayedElement(slot, element, true /*wasNewlyAdded*/, true);
-            }
-        }
-
-        private void OnAddedElement_Phase2(int slot, bool updateVerticalScrollBarOnly)
-        {
-            if (slot < DisplayData.FirstScrollingSlot - 1)
-            {
-                // The element was added above our viewport so it pushes the VerticalOffset down
-                double elementHeight = RowGroupHeadersTable.Contains(slot) ? RowGroupHeaderHeightEstimate : RowHeightEstimate;
-
-                SetVerticalOffset(_verticalOffset + elementHeight);
-            }
-            if (updateVerticalScrollBarOnly)
-            {
-                UpdateVerticalScrollBar();
-            }
-            else
-            {
-                ComputeScrollBarsLayout();
-                // Reposition rows in case we use a recycled one
-                InvalidateRowsArrange();
-            }
-        }
-
-        private void OnElementsChanged(bool grew)
-        {
-            if (grew &&
-                ColumnsItemsInternal.Count > 0 &&
-                CurrentColumnIndex == -1)
-            {
-                MakeFirstDisplayedCellCurrentCell();
-            }
-        }
-
-        private void OnInsertedElement_Phase1(int slot, Control element, bool isCollapsed, bool isRow)
-        {
-            Debug.Assert(slot >= 0);
-
-            // Fix the Index of all following rows
-            CorrectSlotsAfterInsertion(slot, isCollapsed, isRow);
-
-            // Next, same effect as adding a row
-            if (element != null)
-            {
-#if DEBUG
-                if (element is DataGridRow dataGridRow)
-                {
-                    Debug.Assert(dataGridRow.Cells.Count == ColumnsItemsInternal.Count);
-
-                    int columnIndex = 0;
-                    foreach (DataGridCell dataGridCell in dataGridRow.Cells)
-                    {
-                        Debug.Assert(dataGridCell.OwningRow == dataGridRow);
-                        Debug.Assert(dataGridCell.OwningColumn == ColumnsItemsInternal[columnIndex]);
-                        columnIndex++;
-                    }
-                }
-#endif
-                Debug.Assert(!isCollapsed);
-                OnAddedElement_Phase1(slot, element);
-            }
-            else if ((slot <= DisplayData.FirstScrollingSlot) || (isCollapsed && (slot <= DisplayData.LastScrollingSlot)))
-            {
-                DisplayData.CorrectSlotsAfterInsertion(slot, null /*row*/, isCollapsed);
-            }
-        }
-
-        private void OnInsertedElement_Phase2(int slot, bool updateVerticalScrollBarOnly, bool isCollapsed)
-        {
-            Debug.Assert(slot >= 0);
-
-            if (!isCollapsed)
-            {
-                // Same effect as adding a row
-                OnAddedElement_Phase2(slot, updateVerticalScrollBarOnly);
-            }
-        }
-
-        private void OnInsertingElement(int slotInserted,
-                                    bool firstInsertion,
-                                    bool isCollapsed)
-        {
-            // Reset the current cell's address if it's after the inserted row.
-            if (firstInsertion)
-            {
-                if (CurrentSlot != -1 && slotInserted <= CurrentSlot)
-                {
-                    // The underlying data was already added, therefore we need to avoid accessing any back-end data since we might be off by 1 row.
-                    _temporarilyResetCurrentCell = true;
-                    bool success = SetCurrentCellCore(-1, -1);
-                    Debug.Assert(success);
-                }
-            }
-
-            _showDetailsTable.InsertIndex(slotInserted);
-            // Update the slot ranges for the RowGroupHeaders before updating the _selectedItems table,
-            // because it's dependent on the slots being correct with regards to grouping.
-            RowGroupHeadersTable.InsertIndex(slotInserted);
-            _selectedItems.InsertIndex(slotInserted);
-
-            if (isCollapsed)
-            {
-                _collapsedSlotsTable.InsertIndexAndValue(slotInserted, false);
-            }
-            else
-            {
-                _collapsedSlotsTable.InsertIndex(slotInserted);
-            }
-
-            // If we've inserted rows before the current selected item, update its index
-            if (slotInserted <= SelectedIndex)
-            {
-                SetValueNoCallback(SelectedIndexProperty, SelectedIndex + 1);
-            }
-        }
-
-        private void OnRemovedElement(int slotDeleted, object itemDeleted)
-        {
-            SlotCount--;
-            bool wasCollapsed = _collapsedSlotsTable.Contains(slotDeleted);
-            if (!wasCollapsed)
-            {
-                VisibleSlotCount--;
-            }
-
-            // If we're deleting the focused row, we need to clear the cached value
-            if (_focusedRow != null && _focusedRow.Slot == slotDeleted)
-            {
-                ResetFocusedRow();
-            }
-
-            // The element needs to be potentially removed from the displayed elements
-            Control elementDeleted = null;
-            if (slotDeleted <= DisplayData.LastScrollingSlot)
-            {
-                if ((slotDeleted >= DisplayData.FirstScrollingSlot) && !wasCollapsed)
-                {
-                    elementDeleted = DisplayData.GetDisplayedElement(slotDeleted);
-                    // We need to retrieve the Element before updating the tables, but we need
-                    // to update the tables before updating DisplayData in RemoveDisplayedElement
-                    UpdateTablesForRemoval(slotDeleted, itemDeleted);
-
-                    // Displayed row is removed
-                    RemoveDisplayedElement(elementDeleted, slotDeleted, true /*wasDeleted*/, true /*updateSlotInformation*/);
-                }
-                else
-                {
-                    UpdateTablesForRemoval(slotDeleted, itemDeleted);
-
-                    // Removed row is not in view, just update the DisplayData
-                    DisplayData.CorrectSlotsAfterDeletion(slotDeleted, wasCollapsed);
-                }
-            }
-            else
-            {
-                // The element was removed beyond the viewport so we just need to update the tables
-                UpdateTablesForRemoval(slotDeleted, itemDeleted);
-            }
-
-            // If a row was removed before the currently selected row, update its index
-            if (slotDeleted < SelectedIndex)
-            {
-                SetValueNoCallback(SelectedIndexProperty, SelectedIndex - 1);
-            }
-
-            if (!wasCollapsed)
-            {
-                if (slotDeleted >= DisplayData.LastScrollingSlot && elementDeleted == null)
-                {
-                    // Deleted Row is below our Viewport, we just need to adjust the scrollbar
-                    UpdateVerticalScrollBar();
-                }
-                else
-                {
-                    if (elementDeleted != null)
-                    {
-                        // Deleted Row is within our Viewport, update the AvailableRowRoom
-                        AvailableSlotElementRoom += elementDeleted.DesiredSize.Height;
-                    }
-                    else
-                    {
-                        // Deleted Row is above our Viewport, update the vertical offset
-                        SetVerticalOffset(Math.Max(0, _verticalOffset - RowHeightEstimate));
-                    }
-
-                    ComputeScrollBarsLayout();
-                    // Reposition rows in case we use a recycled one
-                    InvalidateRowsArrange();
-                }
-            }
-        }
-
-        private void OnRemovingElement(int slotDeleted)
-        {
-            // Note that the row needs to be deleted no matter what. The underlying data row was already deleted.
-
-            Debug.Assert(slotDeleted >= 0 && slotDeleted < SlotCount);
-            _temporarilyResetCurrentCell = false;
-
-            // Reset the current cell's address if it's on the deleted row, or after it.
-            if (CurrentSlot != -1 && slotDeleted <= CurrentSlot)
-            {
-                _desiredCurrentColumnIndex = CurrentColumnIndex;
-                if (slotDeleted == CurrentSlot)
-                {
-                    // No editing is committed since the underlying entity was already deleted.
-                    bool success = SetCurrentCellCore(-1, -1, false /*commitEdit*/, false /*endRowEdit*/);
-                    Debug.Assert(success);
-                }
-                else
-                {
-                    // Underlying data of deleted row is gone. It cannot be accessed anymore. Skip the commit of the editing.
-                    _temporarilyResetCurrentCell = true;
-                    bool success = SetCurrentCellCore(-1, -1);
-                    Debug.Assert(success);
-                }
-            }
-        }
-
-        // Makes sure the row shows the proper visuals for selection, currency, details, etc.
-        private void LoadRowVisualsForDisplay(DataGridRow row)
-        {
-            // If the row has been recycled, reapply the BackgroundBrush
-            if (row.IsRecycled)
-            {
-                row.ApplyCellsState();
-                _rowsPresenter?.InvalidateChildIndex(row);
-            }
-            else if (row == EditingRow)
-            {
-                row.ApplyCellsState();
-            }
-
-            // Set the Row's Style if we one's defined at the DataGrid level and the user didn't
-            // set one at the row level
-            //EnsureElementStyle(row, null, RowStyle);
-            row.EnsureHeaderStyleAndVisibility(null);
-
-            // Check to see if the row contains the CurrentCell, apply its state.
-            if (CurrentColumnIndex != -1 &&
-                CurrentSlot != -1 &&
-                row.Index == CurrentSlot)
-            {
-                row.Cells[CurrentColumnIndex].UpdatePseudoClasses();
-            }
-
-            if (row.IsSelected || row.IsRecycled)
-            {
-                row.ApplyState();
-            }
-
-            // Show or hide RowDetails based on DataGrid settings
-            EnsureRowDetailsVisibility(row, raiseNotification: false, animate: false);
-        }
-
-        private void RemoveDisplayedElement(int slot, bool wasDeleted, bool updateSlotInformation)
-        {
-            Debug.Assert(slot >= DisplayData.FirstScrollingSlot &&
-                         slot <= DisplayData.LastScrollingSlot);
-
-            RemoveDisplayedElement(DisplayData.GetDisplayedElement(slot), slot, wasDeleted, updateSlotInformation);
-        }
-
-        // Removes an element from display either because it was deleted or it was scrolled out of view.
-        // If the element was provided, it will be the element removed; otherwise, the element will be
-        // retrieved from the slot information
-        private void RemoveDisplayedElement(Control element, int slot, bool wasDeleted, bool updateSlotInformation)
-        {
-            if (element is DataGridRow dataGridRow)
-            {
-                if (IsRowRecyclable(dataGridRow))
-                {
-                    UnloadRow(dataGridRow);
-                }
-                else
-                {
-                    dataGridRow.Clip = new RectangleGeometry();
-                }
-            }
-            else if (element is DataGridRowGroupHeader groupHeader)
-            {
-                OnUnloadingRowGroup(new DataGridRowGroupHeaderEventArgs(groupHeader));
-                DisplayData.AddRecylableRowGroupHeader(groupHeader);
-            }
-            else if (_rowsPresenter != null)
-            {
-                _rowsPresenter.Children.Remove(element);
-            }
-
-            // Update DisplayData
-            if (wasDeleted)
-            {
-                DisplayData.CorrectSlotsAfterDeletion(slot, wasCollapsed: false);
-            }
-            else
-            {
-                DisplayData.UnloadScrollingElement(slot, updateSlotInformation, wasDeleted: false);
-            }
-        }
-
-        /// <summary>
-        /// Removes all of the editing elements for the row that is just leaving editing mode.
-        /// </summary>
-        private void RemoveEditingElements()
-        {
-            if (EditingRow != null && EditingRow.Cells != null)
-            {
-                Debug.Assert(EditingRow.Cells.Count == ColumnsItemsInternal.Count);
-                foreach (DataGridColumn column in Columns)
-                {
-                    column.RemoveEditingElement();
-                }
-            }
-        }
-
-        private void RemoveElementAt(int slot, object item, bool isRow)
-        {
-            Debug.Assert(slot >= 0 && slot < SlotCount);
-
-            OnRemovingElement(slot);
-
-            CorrectSlotsAfterDeletion(slot, isRow);
-
-            OnRemovedElement(slot, item);
-            
-            // Synchronize CurrentCellCoordinates, CurrentColumn, CurrentColumnIndex, CurrentItem
-            // and CurrentSlot with the currently edited cell, since OnRemovingElement called
-            // SetCurrentCellCore(-1, -1) to temporarily reset the current cell.
-            if (_temporarilyResetCurrentCell &&
-                _editingColumnIndex != -1 &&
-                _previousCurrentItem != null &&
-                EditingRow != null &&
-                EditingRow.Slot != -1)
-            {
-                ProcessSelectionAndCurrency(
-                    columnIndex: _editingColumnIndex,
-                    item: _previousCurrentItem,
-                    backupSlot: this.EditingRow.Slot,
-                    action: DataGridSelectionAction.None,
-                    scrollIntoView: false);
-            }
-        }
-
-        private void RemoveNonDisplayedRows(int newFirstDisplayedSlot, int newLastDisplayedSlot)
-        {
-            while (DisplayData.FirstScrollingSlot < newFirstDisplayedSlot)
-            {
-                // Need to add rows above the lastDisplayedScrollingRow
-                RemoveDisplayedElement(DisplayData.FirstScrollingSlot, false /*wasDeleted*/, true /*updateSlotInformation*/);
-            }
-            while (DisplayData.LastScrollingSlot > newLastDisplayedSlot)
-            {
-                // Need to remove rows below the lastDisplayedScrollingRow
-                RemoveDisplayedElement(DisplayData.LastScrollingSlot, false /*wasDeleted*/, true /*updateSlotInformation*/);
-            }
-        }
-
-        private void ResetDisplayedRows()
-        {
-            if (UnloadingRow != null || UnloadingRowGroup != null)
-            {
-                foreach (Control element in DisplayData.GetScrollingElements())
-                {
-                    // Raise Unloading Row for all the rows we're displaying
-                    if (element is DataGridRow row)
-                    {
-                        if (IsRowRecyclable(row))
-                        {
-                            OnUnloadingRow(new DataGridRowEventArgs(row));
-                        }
-                    }
-                    // Raise Unloading Row for all the RowGroupHeaders we're displaying
-                    else if (element is DataGridRowGroupHeader groupHeader)
-                    {
-                        OnUnloadingRowGroup(new DataGridRowGroupHeaderEventArgs(groupHeader));
-                    }
-                }
-            }
-
-            DisplayData.ClearElements(recycle: true);
-            AvailableSlotElementRoom = CellsEstimatedHeight;
-        }
-
-        /// <summary>
-        /// Determines whether the row at the provided index must be displayed or not.
-        /// </summary>
-        private bool SlotIsDisplayed(int slot)
-        {
-            Debug.Assert(slot >= 0);
-
-            if (slot >= DisplayData.FirstScrollingSlot &&
-                slot <= DisplayData.LastScrollingSlot)
-            {
-                // Additional row takes the spot of a displayed row - it is necessarily displayed
-                return true;
-            }
-            else if (DisplayData.FirstScrollingSlot == -1 &&
-                     CellsEstimatedHeight > 0 &&
-                     CellsWidth > 0)
-            {
-                return true;
-            }
-            else if (slot == GetNextVisibleSlot(DisplayData.LastScrollingSlot))
-            {
-                if (AvailableSlotElementRoom > 0)
-                {
-                    // There is room for this additional row
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        // Updates display information and displayed rows after scrolling the given number of pixels
-        private void ScrollSlotsByHeight(double height)
-        {
-            Debug.Assert(DisplayData.FirstScrollingSlot >= 0);
-            Debug.Assert(!MathUtilities.IsZero(height));
-
-            _scrollingByHeight = true;
-            try
-            {
-                double deltaY = 0;
-                int newFirstScrollingSlot = DisplayData.FirstScrollingSlot;
-                double newVerticalOffset = _verticalOffset + height;
-                if (height > 0)
-                {
-                    // Scrolling Down
-                    int lastVisibleSlot = GetPreviousVisibleSlot(SlotCount);
-                    if (_vScrollBar != null && MathUtilities.AreClose(_vScrollBar.Maximum, newVerticalOffset))
-                    {
-                        // We've scrolled to the bottom of the ScrollBar, automatically place the user at the very bottom
-                        // of the DataGrid.  If this produces very odd behavior, evaluate the coping strategy used by
-                        // OnRowMeasure(Size).  For most data, this should be unnoticeable.
-                        ResetDisplayedRows();
-                        UpdateDisplayedRowsFromBottom(lastVisibleSlot);
-                        newFirstScrollingSlot = DisplayData.FirstScrollingSlot;
-                    }
-                    else
-                    {
-                        deltaY = GetSlotElementHeight(newFirstScrollingSlot) - NegVerticalOffset;
-                        if (MathUtilities.LessThan(height, deltaY))
-                        {
-                            // We've merely covered up more of the same row we're on
-                            NegVerticalOffset += height;
-                        }
-                        else
-                        {
-                            // Figure out what row we've scrolled down to and update the value for NegVerticalOffset
-                            NegVerticalOffset = 0;
-                            //
-                            if (height > 2 * CellsEstimatedHeight &&
-                                (RowDetailsVisibilityMode != DataGridRowDetailsVisibilityMode.VisibleWhenSelected || RowDetailsTemplate == null))
-                            {
-                                // Very large scroll occurred. Instead of determining the exact number of scrolled off rows,
-                                // let's estimate the number based on RowHeight.
-                                ResetDisplayedRows();
-                                double singleRowHeightEstimate = RowHeightEstimate + (RowDetailsVisibilityMode == DataGridRowDetailsVisibilityMode.Visible ? RowDetailsHeightEstimate : 0);
-                                int scrolledToSlot = newFirstScrollingSlot + (int)(height / singleRowHeightEstimate);
-                                scrolledToSlot += _collapsedSlotsTable.GetIndexCount(newFirstScrollingSlot, newFirstScrollingSlot + scrolledToSlot);
-                                newFirstScrollingSlot = Math.Min(GetNextVisibleSlot(scrolledToSlot), lastVisibleSlot);
-                            }
-                            else
-                            {
-                                while (MathUtilities.LessThanOrClose(deltaY, height))
-                                {
-                                    if (newFirstScrollingSlot < lastVisibleSlot)
-                                    {
-                                        if (IsSlotVisible(newFirstScrollingSlot))
-                                        {
-                                            // Make the top row available for reuse
-                                            RemoveDisplayedElement(newFirstScrollingSlot, false /*wasDeleted*/, true /*updateSlotInformation*/);
-                                        }
-                                        newFirstScrollingSlot = GetNextVisibleSlot(newFirstScrollingSlot);
-                                    }
-                                    else
-                                    {
-                                        // We're being told to scroll beyond the last row, ignore the extra
-                                        NegVerticalOffset = 0;
-                                        break;
-                                    }
-
-                                    double rowHeight = GetExactSlotElementHeight(newFirstScrollingSlot);
-                                    double remainingHeight = height - deltaY;
-                                    if (MathUtilities.LessThanOrClose(rowHeight, remainingHeight))
-                                    {
-                                        deltaY += rowHeight;
-                                    }
-                                    else
-                                    {
-                                        NegVerticalOffset = remainingHeight;
-                                        break;
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-                else
-                {
-                    // Scrolling Up
-                    if (MathUtilities.GreaterThanOrClose(height + NegVerticalOffset, 0))
-                    {
-                        // We've merely exposing more of the row we're on
-                        NegVerticalOffset += height;
-                    }
-                    else
-                    {
-                        // Figure out what row we've scrolled up to and update the value for NegVerticalOffset
-                        deltaY = -NegVerticalOffset;
-                        NegVerticalOffset = 0;
-                        //
-
-                        if (height < -2 * CellsEstimatedHeight &&
-                            (RowDetailsVisibilityMode != DataGridRowDetailsVisibilityMode.VisibleWhenSelected || RowDetailsTemplate == null))
-                        {
-                            // Very large scroll occurred. Instead of determining the exact number of scrolled off rows,
-                            // let's estimate the number based on RowHeight.
-                            if (newVerticalOffset == 0)
-                            {
-                                newFirstScrollingSlot = 0;
-                            }
-                            else
-                            {
-                                double singleRowHeightEstimate = RowHeightEstimate + (RowDetailsVisibilityMode == DataGridRowDetailsVisibilityMode.Visible ? RowDetailsHeightEstimate : 0);
-                                int scrolledToSlot = newFirstScrollingSlot + (int)(height / singleRowHeightEstimate);
-                                scrolledToSlot -= _collapsedSlotsTable.GetIndexCount(scrolledToSlot, newFirstScrollingSlot);
-
-                                newFirstScrollingSlot = Math.Max(0, GetPreviousVisibleSlot(scrolledToSlot + 1));
-                            }
-                            ResetDisplayedRows();
-                        }
-                        else
-                        {
-                            int lastScrollingSlot = DisplayData.LastScrollingSlot;
-                            while (MathUtilities.GreaterThan(deltaY, height))
-                            {
-                                if (newFirstScrollingSlot > 0)
-                                {
-                                    if (IsSlotVisible(lastScrollingSlot))
-                                    {
-                                        // Make the bottom row available for reuse
-                                        RemoveDisplayedElement(lastScrollingSlot, wasDeleted: false, updateSlotInformation: true);
-                                        lastScrollingSlot = GetPreviousVisibleSlot(lastScrollingSlot);
-                                    }
-                                    newFirstScrollingSlot = GetPreviousVisibleSlot(newFirstScrollingSlot);
-                                }
-                                else
-                                {
-                                    NegVerticalOffset = 0;
-                                    break;
-                                }
-                                double rowHeight = GetExactSlotElementHeight(newFirstScrollingSlot);
-                                double remainingHeight = height - deltaY;
-                                if (MathUtilities.LessThanOrClose(rowHeight + remainingHeight, 0))
-                                {
-                                    deltaY -= rowHeight;
-                                }
-                                else
-                                {
-                                    NegVerticalOffset = rowHeight + remainingHeight;
-                                    break;
-                                }
-                            }
-                        }
-                    }
-                    if (MathUtilities.GreaterThanOrClose(0, newVerticalOffset) && newFirstScrollingSlot != 0)
-                    {
-                        // We've scrolled to the top of the ScrollBar, automatically place the user at the very top
-                        // of the DataGrid.  If this produces very odd behavior, evaluate the RowHeight estimate.
-                        // strategy. For most data, this should be unnoticeable.
-                        ResetDisplayedRows();
-                        NegVerticalOffset = 0;
-                        UpdateDisplayedRows(0, CellsEstimatedHeight);
-                        newFirstScrollingSlot = 0;
-                    }
-                }
-
-                double firstRowHeight = GetExactSlotElementHeight(newFirstScrollingSlot);
-                if (MathUtilities.LessThan(firstRowHeight, NegVerticalOffset))
-                {
-                    // We've scrolled off more of the first row than what's possible.  This can happen
-                    // if the first row got shorter (Ex: Collapsing RowDetails) or if the user has a recycling
-                    // cleanup issue.  In this case, simply try to display the next row as the first row instead
-                    if (newFirstScrollingSlot < SlotCount - 1)
-                    {
-                        newFirstScrollingSlot = GetNextVisibleSlot(newFirstScrollingSlot);
-                        Debug.Assert(newFirstScrollingSlot != -1);
-                    }
-                    NegVerticalOffset = 0;
-                }
-
-                UpdateDisplayedRows(newFirstScrollingSlot, CellsEstimatedHeight);
-
-                double firstElementHeight = GetExactSlotElementHeight(DisplayData.FirstScrollingSlot);
-                if (MathUtilities.GreaterThan(NegVerticalOffset, firstElementHeight))
-                {
-                    int firstElementSlot = DisplayData.FirstScrollingSlot;
-                    // We filled in some rows at the top and now we have a NegVerticalOffset that's greater than the first element
-                    while (newFirstScrollingSlot > 0 && MathUtilities.GreaterThan(NegVerticalOffset, firstElementHeight))
-                    {
-                        int previousSlot = GetPreviousVisibleSlot(firstElementSlot);
-                        if (previousSlot == -1)
-                        {
-                            NegVerticalOffset = 0;
-                            _verticalOffset = 0;
-                        }
-                        else
-                        {
-                            NegVerticalOffset -= firstElementHeight;
-                            _verticalOffset = Math.Max(0, _verticalOffset - firstElementHeight);
-                            firstElementSlot = previousSlot;
-                            firstElementHeight = GetExactSlotElementHeight(firstElementSlot);
-                        }
-                    }
-                    // We could be smarter about this, but it's not common so we wouldn't gain much from optimizing here
-                    if (firstElementSlot != DisplayData.FirstScrollingSlot)
-                    {
-                        UpdateDisplayedRows(firstElementSlot, CellsEstimatedHeight);
-                    }
-                }
-
-                Debug.Assert(DisplayData.FirstScrollingSlot >= 0);
-                Debug.Assert(GetExactSlotElementHeight(DisplayData.FirstScrollingSlot) > NegVerticalOffset);
-
-                if (DisplayData.FirstScrollingSlot == 0)
-                {
-                    _verticalOffset = NegVerticalOffset;
-                }
-                else if (MathUtilities.GreaterThan(NegVerticalOffset, newVerticalOffset))
-                {
-                    // The scrolled-in row was larger than anticipated. Adjust the DataGrid so the ScrollBar thumb
-                    // can stay in the same place
-                    NegVerticalOffset = newVerticalOffset;
-                    _verticalOffset = newVerticalOffset;
-                }
-                else
-                {
-                    _verticalOffset = newVerticalOffset;
-                }
-
-                Debug.Assert(!(_verticalOffset == 0 && NegVerticalOffset == 0 && DisplayData.FirstScrollingSlot > 0));
-
-                SetVerticalOffset(_verticalOffset);
-
-                DisplayData.FullyRecycleElements();
-
-                Debug.Assert(MathUtilities.GreaterThanOrClose(NegVerticalOffset, 0));
-                Debug.Assert(MathUtilities.GreaterThanOrClose(_verticalOffset, NegVerticalOffset));
-            }
-            finally
-            {
-                _scrollingByHeight = false;
-            }
-        }
-
-        private void SelectDisplayedElement(int slot)
-        {
-            Debug.Assert(IsSlotVisible(slot));
-            Control element = DisplayData.GetDisplayedElement(slot);
-            if (element is DataGridRow row)
-            {
-                row.ApplyState();
-                EnsureRowDetailsVisibility(row, raiseNotification: true, animate: true);
-            }
-            else
-            {
-                // Assume it's a RowGroupHeader
-                DataGridRowGroupHeader groupHeader = element as DataGridRowGroupHeader;
-                groupHeader.UpdatePseudoClasses();
-            }
-        }
-
-        private void SelectSlot(int slot, bool isSelected)
-        {
-            _selectedItems.SelectSlot(slot, isSelected);
-            if (IsSlotVisible(slot))
-            {
-                SelectDisplayedElement(slot);
-            }
-        }
-
-        private void SelectSlots(int startSlot, int endSlot, bool isSelected)
-        {
-            _selectedItems.SelectSlots(startSlot, endSlot, isSelected);
-
-            // Apply the correct row state for display rows and also expand or collapse detail accordingly
-            int firstSlot = Math.Max(DisplayData.FirstScrollingSlot, startSlot);
-            int lastSlot = Math.Min(DisplayData.LastScrollingSlot, endSlot);
-
-            for (int slot = firstSlot; slot <= lastSlot; slot++)
-            {
-                if (IsSlotVisible(slot))
-                {
-                    SelectDisplayedElement(slot);
-                }
-            }
-        }
-
-        private void UnloadElements(bool recycle)
-        {
-            // Since we're unloading all the elements, we can't be in editing mode anymore,
-            // so commit if we can, otherwise force cancel.
-            if (!CommitEdit())
-            {
-                CancelEdit(DataGridEditingUnit.Row, false);
-            }
-            ResetEditingRow();
-
-            // Make sure to clear the focused row (because it's no longer relevant).
-            if (_focusedRow != null)
-            {
-                ResetFocusedRow();
-                Focus();
-            }
-
-            if (_rowsPresenter != null)
-            {
-                foreach (Control element in _rowsPresenter.Children)
-                {
-                    if (element is DataGridRow row)
-                    {
-                        // Raise UnloadingRow for any row that was visible
-                        if (IsSlotVisible(row.Slot))
-                        {
-                            OnUnloadingRow(new DataGridRowEventArgs(row));
-                        }
-                        row.DetachFromDataGrid(recycle && row.IsRecyclable /*recycle*/);
-                    }
-                    else if (element is DataGridRowGroupHeader groupHeader)
-                    {
-                        if (IsSlotVisible(groupHeader.RowGroupInfo.Slot))
-                        {
-                            OnUnloadingRowGroup(new DataGridRowGroupHeaderEventArgs(groupHeader));
-                        }
-                    }
-                }
-
-                if (!recycle)
-                {
-                    _rowsPresenter.Children.Clear();
-                }
-            }
-            DisplayData.ClearElements(recycle);
-
-            // Update the AvailableRowRoom since we're displaying 0 rows now
-            AvailableSlotElementRoom = CellsEstimatedHeight;
-            VisibleSlotCount = 0;
-        }
-
-        private void UnloadRow(DataGridRow dataGridRow)
-        {
-            Debug.Assert(dataGridRow != null);
-            Debug.Assert(_rowsPresenter != null);
-            Debug.Assert(_rowsPresenter.Children.Contains(dataGridRow));
-
-            if (_loadedRows.Contains(dataGridRow))
-            {
-                return; // The row is still referenced, we can't release it.
-            }
-
-            // Raise UnloadingRow regardless of whether the row will be recycled
-            OnUnloadingRow(new DataGridRowEventArgs(dataGridRow));
-            bool recycleRow = CurrentSlot != dataGridRow.Index;
-
-            if (recycleRow)
-            {
-                DisplayData.AddRecyclableRow(dataGridRow);
-            }
-            else
-            {
-                //
-                _rowsPresenter.Children.Remove(dataGridRow);
-                dataGridRow.DetachFromDataGrid(false);
-            }
-        }
-
-        private void UpdateDisplayedRows(int newFirstDisplayedSlot, double displayHeight)
-        {
-            Debug.Assert(!_collapsedSlotsTable.Contains(newFirstDisplayedSlot));
-            int firstDisplayedScrollingSlot = newFirstDisplayedSlot;
-            int lastDisplayedScrollingSlot = -1;
-            double deltaY = -NegVerticalOffset;
-            int visibleScrollingRows = 0;
-
-            if (MathUtilities.LessThanOrClose(displayHeight, 0) || SlotCount == 0 || ColumnsItemsInternal.Count == 0)
-            {
-                return;
-            }
-
-            if (firstDisplayedScrollingSlot == -1)
-            {
-                // 0 is fine because the element in the first slot cannot be collapsed
-                firstDisplayedScrollingSlot = 0;
-            }
-
-            int slot = firstDisplayedScrollingSlot;
-            while (slot < SlotCount && !MathUtilities.GreaterThanOrClose(deltaY, displayHeight))
-            {
-                deltaY += GetExactSlotElementHeight(slot);
-                visibleScrollingRows++;
-                lastDisplayedScrollingSlot = slot;
-                slot = GetNextVisibleSlot(slot);
-            }
-
-            while (MathUtilities.LessThan(deltaY, displayHeight) && slot >= 0)
-            {
-                slot = GetPreviousVisibleSlot(firstDisplayedScrollingSlot);
-                if (slot >= 0)
-                {
-                    deltaY += GetExactSlotElementHeight(slot);
-                    firstDisplayedScrollingSlot = slot;
-                    visibleScrollingRows++;
-                }
-            }
-            // If we're up to the first row, and we still have room left, uncover as much of the first row as we can
-            if (firstDisplayedScrollingSlot == 0 && MathUtilities.LessThan(deltaY, displayHeight))
-            {
-                double newNegVerticalOffset = Math.Max(0, NegVerticalOffset - displayHeight + deltaY);
-                deltaY += NegVerticalOffset - newNegVerticalOffset;
-                NegVerticalOffset = newNegVerticalOffset;
-            }
-
-            if (MathUtilities.GreaterThan(deltaY, displayHeight) || (MathUtilities.AreClose(deltaY, displayHeight) && MathUtilities.GreaterThan(NegVerticalOffset, 0)))
-            {
-                DisplayData.NumTotallyDisplayedScrollingElements = visibleScrollingRows - 1;
-            }
-            else
-            {
-                DisplayData.NumTotallyDisplayedScrollingElements = visibleScrollingRows;
-            }
-            if (visibleScrollingRows == 0)
-            {
-                firstDisplayedScrollingSlot = -1;
-                Debug.Assert(lastDisplayedScrollingSlot == -1);
-            }
-
-            Debug.Assert(lastDisplayedScrollingSlot < SlotCount, "lastDisplayedScrollingRow larger than number of rows");
-
-            RemoveNonDisplayedRows(firstDisplayedScrollingSlot, lastDisplayedScrollingSlot);
-
-            Debug.Assert(DisplayData.NumDisplayedScrollingElements >= 0, "the number of visible scrolling rows can't be negative");
-            Debug.Assert(DisplayData.NumTotallyDisplayedScrollingElements >= 0, "the number of totally visible scrolling rows can't be negative");
-            Debug.Assert(DisplayData.FirstScrollingSlot < SlotCount, "firstDisplayedScrollingRow larger than number of rows");
-            Debug.Assert(DisplayData.FirstScrollingSlot == firstDisplayedScrollingSlot);
-            Debug.Assert(DisplayData.LastScrollingSlot == lastDisplayedScrollingSlot);
-        }
-
-        // Similar to UpdateDisplayedRows except that it starts with the LastDisplayedScrollingRow
-        // and computes the FirstDisplayScrollingRow instead of doing it the other way around.  We use this
-        // when scrolling down to a full row
-        private void UpdateDisplayedRowsFromBottom(int newLastDisplayedScrollingRow)
-        {
-            //Debug.Assert(!_collapsedSlotsTable.Contains(newLastDisplayedScrollingRow));
-
-            int lastDisplayedScrollingRow = newLastDisplayedScrollingRow;
-            int firstDisplayedScrollingRow = -1;
-            double displayHeight = CellsEstimatedHeight;
-            double deltaY = 0;
-            int visibleScrollingRows = 0;
-
-            if (MathUtilities.LessThanOrClose(displayHeight, 0) || SlotCount == 0 || ColumnsItemsInternal.Count == 0)
-            {
-                ResetDisplayedRows();
-                return;
-            }
-
-            if (lastDisplayedScrollingRow == -1)
-            {
-                lastDisplayedScrollingRow = 0;
-            }
-
-            int slot = lastDisplayedScrollingRow;
-            while (MathUtilities.LessThan(deltaY, displayHeight) && slot >= 0)
-            {
-                deltaY += GetExactSlotElementHeight(slot);
-                visibleScrollingRows++;
-                firstDisplayedScrollingRow = slot;
-                slot = GetPreviousVisibleSlot(slot);
-            }
-
-            DisplayData.NumTotallyDisplayedScrollingElements = deltaY > displayHeight ? visibleScrollingRows - 1 : visibleScrollingRows;
-
-            Debug.Assert(DisplayData.NumTotallyDisplayedScrollingElements >= 0);
-            Debug.Assert(lastDisplayedScrollingRow < SlotCount, "lastDisplayedScrollingRow larger than number of rows");
-
-            NegVerticalOffset = Math.Max(0, deltaY - displayHeight);
-
-            RemoveNonDisplayedRows(firstDisplayedScrollingRow, lastDisplayedScrollingRow);
-
-            Debug.Assert(DisplayData.NumDisplayedScrollingElements >= 0, "the number of visible scrolling rows can't be negative");
-            Debug.Assert(DisplayData.NumTotallyDisplayedScrollingElements >= 0, "the number of totally visible scrolling rows can't be negative");
-            Debug.Assert(DisplayData.FirstScrollingSlot < SlotCount, "firstDisplayedScrollingRow larger than number of rows");
-        }
-
-        private void UpdateTablesForRemoval(int slotDeleted, object itemDeleted)
-        {
-            if (RowGroupHeadersTable.Contains(slotDeleted))
-            {
-                // A RowGroupHeader was removed
-                RowGroupHeadersTable.RemoveIndexAndValue(slotDeleted);
-                _collapsedSlotsTable.RemoveIndexAndValue(slotDeleted);
-                _selectedItems.DeleteSlot(slotDeleted);
-            }
-            else
-            {
-                // Update the ranges of selected rows
-                if (_selectedItems.ContainsSlot(slotDeleted))
-                {
-                    SelectionHasChanged = true;
-                }
-                _selectedItems.Delete(slotDeleted, itemDeleted);
-                RowGroupHeadersTable.RemoveIndex(slotDeleted);
-                _collapsedSlotsTable.RemoveIndex(slotDeleted);
-            }
-        }
-
-        private void CollectionViewGroup_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
-        {
-            // If we receive this event when the number of GroupDescriptions is different than what we have already
-            // accounted for, that means the ICollectionView is still in the process of updating its groups.  It will
-            // send a reset notification when it's done, at which point we can update our visuals.
-
-            if (_rowGroupHeightsByLevel != null &&
-                DataConnection.CollectionView != null &&
-                DataConnection.CollectionView.IsGrouping &&
-                DataConnection.CollectionView.GroupingDepth == _rowGroupHeightsByLevel.Length)
-            {
-                switch (e.Action)
-                {
-                    case NotifyCollectionChangedAction.Add:
-                        CollectionViewGroup_CollectionChanged_Add(sender, e);
-                        break;
-                    case NotifyCollectionChangedAction.Remove:
-                        CollectionViewGroup_CollectionChanged_Remove(sender, e);
-                        break;
-                }
-            }
-        }
-
-        private void CollectionViewGroup_CollectionChanged_Add(object sender, NotifyCollectionChangedEventArgs e)
-        {
-            if (e.NewItems != null && e.NewItems.Count > 0)
-            {
-                // We need to figure out the CollectionViewGroup that the sender belongs to.  We could cache
-                // it by tagging the collections ahead of time, but I think the extra storage might not be worth
-                // it since this lookup should be performant enough
-                int insertSlot = -1;
-                DataGridRowGroupInfo parentGroupInfo = GetParentGroupInfo(sender);
-                DataGridCollectionViewGroup group = e.NewItems[0] as DataGridCollectionViewGroup;
-
-                if (parentGroupInfo != null)
-                {
-                    if (group != null || parentGroupInfo.Level == -1)
-                    {
-                        insertSlot = parentGroupInfo.Slot + 1;
-                        // For groups, we need to skip over subgroups to find the correct slot
-                        DataGridRowGroupInfo groupInfo;
-                        for (int i = 0; i < e.NewStartingIndex; i++)
-                        {
-                            do
-                            {
-                                insertSlot = RowGroupHeadersTable.GetNextIndex(insertSlot);
-                                groupInfo = RowGroupHeadersTable.GetValueAt(insertSlot);
-                            }
-                            while (groupInfo != null && groupInfo.Level > parentGroupInfo.Level + 1);
-                            if (groupInfo == null)
-                            {
-                                // We couldn't find the subchild so this should go at the end
-                                insertSlot = SlotCount;
-                            }
-                        }
-
-                    }
-                    else
-                    {
-                        // For items the slot is a simple calculation
-                        insertSlot = parentGroupInfo.Slot + e.NewStartingIndex + 1;
-                    }
-                }
-
-                // This could not be found when new GroupDescriptions are added to the PagedCollectionView
-                if (insertSlot != -1)
-                {
-                    bool isCollapsed = (parentGroupInfo != null) && (!parentGroupInfo.IsVisible || _collapsedSlotsTable.Contains(parentGroupInfo.Slot));
-                    if (group != null)
-                    {
-                        if (group.Items != null)
-                        {
-                            group.Items.CollectionChanged += CollectionViewGroup_CollectionChanged;
-                        }
-                        var newGroupInfo = new DataGridRowGroupInfo(group, true, parentGroupInfo.Level + 1, insertSlot, insertSlot);
-                        InsertElementAt(insertSlot,
-                            rowIndex: -1,
-                            item: null,
-                            groupInfo: newGroupInfo,
-                            isCollapsed: isCollapsed);
-                        RowGroupHeadersTable.AddValue(insertSlot, newGroupInfo);
-                    }
-                    else
-                    {
-                        // Assume we're adding a new row
-                        int rowIndex = DataConnection.IndexOf(e.NewItems[0]);
-                        Debug.Assert(rowIndex != -1);
-                        if (SlotCount == 0 && DataConnection.ShouldAutoGenerateColumns)
-                        {
-                            AutoGenerateColumnsPrivate();
-                        }
-                        InsertElementAt(insertSlot, rowIndex,
-                            item: e.NewItems[0],
-                            groupInfo: null,
-                            isCollapsed: isCollapsed);
-                    }
-
-                    CorrectLastSubItemSlotsAfterInsertion(parentGroupInfo);
-                    if (parentGroupInfo.LastSubItemSlot - parentGroupInfo.Slot == 1)
-                    {
-                        // We just added the first item to a RowGroup so the header should transition from Empty to either Expanded or Collapsed
-                        EnsureAncestorsExpanderButtonChecked(parentGroupInfo);
-                    }
-                }
-            }
-        }
-
-        private void CollectionViewGroup_CollectionChanged_Remove(object sender, NotifyCollectionChangedEventArgs e)
-        {
-            Debug.Assert(e.OldItems.Count == 1);
-            if (e.OldItems != null && e.OldItems.Count > 0)
-            {
-                if (e.OldItems[0] is DataGridCollectionViewGroup removedGroup)
-                {
-                    if (removedGroup.Items != null)
-                    {
-                        removedGroup.Items.CollectionChanged -= CollectionViewGroup_CollectionChanged;
-                    }
-                    DataGridRowGroupInfo groupInfo = RowGroupInfoFromCollectionViewGroup(removedGroup);
-                    Debug.Assert(groupInfo != null);
-                    if ((groupInfo.Level == _rowGroupHeightsByLevel.Length - 1) && (removedGroup.Items != null) && (removedGroup.Items.Count > 0))
-                    {
-                        Debug.Assert((groupInfo.LastSubItemSlot - groupInfo.Slot) == removedGroup.Items.Count);
-                        // If we're removing a leaf Group then remove all of its items before removing the Group
-                        for (int i = 0; i < removedGroup.Items.Count; i++)
-                        {
-                            RemoveElementAt(groupInfo.Slot + 1, item: removedGroup.Items[i], isRow: true);
-                        }
-                    }
-                    RemoveElementAt(groupInfo.Slot, item: null, isRow: false);
-                }
-                else
-                {
-                    // A single item was removed from a leaf group
-                    DataGridRowGroupInfo parentGroupInfo = GetParentGroupInfo(sender);
-                    if (parentGroupInfo != null)
-                    {
-                        int slot;
-                        if (parentGroupInfo.CollectionViewGroup == null && RowGroupHeadersTable.IndexCount > 0)
-                        {
-                            // In this case, we're removing from the root group.  If there are other groups, then this must
-                            // be the new item row that doesn't belong to any group because if there are other groups then
-                            // this item cannot be a child of the root group.
-                            slot = SlotCount - 1;
-                        }
-                        else
-                        {
-                            slot = parentGroupInfo.Slot + e.OldStartingIndex + 1;
-                        }
-                        RemoveElementAt(slot, e.OldItems[0], isRow: true);
-                    }
-                }
-            }
-        }
-
-        private void ClearRowGroupHeadersTable()
-        {
-            // Detach existing handlers on CollectionViewGroup.Items.CollectionChanged
-            foreach (int slot in RowGroupHeadersTable.GetIndexes())
-            {
-                DataGridRowGroupInfo groupInfo = RowGroupHeadersTable.GetValueAt(slot);
-                if (groupInfo.CollectionViewGroup.Items != null)
-                {
-                    groupInfo.CollectionViewGroup.Items.CollectionChanged -= CollectionViewGroup_CollectionChanged;
-                }
-            }
-            if (_topLevelGroup != null)
-            {
-                // The PagedCollectionView reuses the top level group so we need to detach any existing or else we'll get duplicate handers here
-                _topLevelGroup.CollectionChanged -= CollectionViewGroup_CollectionChanged;
-                _topLevelGroup = null;
-            }
-
-            RowGroupHeadersTable.Clear();
-            // Unfortunately PagedCollectionView does not allow us to preserve expanded or collapsed states for RowGroups since
-            // the CollectionViewGroups are recreated when a Reset happens.  This is true in both SL and WPF
-            _collapsedSlotsTable.Clear();
-
-            _rowGroupHeightsByLevel = null;
-            RowGroupSublevelIndents = null;
-        }
-
-        private void CollectionViewGroup_PropertyChanged(object sender, PropertyChangedEventArgs e)
-        {
-            if (e.PropertyName == "ItemCount")
-            {
-                DataGridRowGroupInfo rowGroupInfo = RowGroupInfoFromCollectionViewGroup(sender as DataGridCollectionViewGroup);
-                if (rowGroupInfo != null && IsSlotVisible(rowGroupInfo.Slot))
-                {
-                    if (DisplayData.GetDisplayedElement(rowGroupInfo.Slot) is DataGridRowGroupHeader rowGroupHeader)
-                    {
-                        rowGroupHeader.UpdateTitleElements();
-                    }
-                }
-            }
-        }
-
-        // This method is necessary for incrementing the LastSubItemSlot property of the group ancestors
-        // because CorrectSlotsAfterInsertion only increments those that come after the specified group
-        private void CorrectLastSubItemSlotsAfterInsertion(DataGridRowGroupInfo subGroupInfo)
-        {
-            int subGroupSlot;
-            int subGroupLevel;
-            while (subGroupInfo != null)
-            {
-                subGroupLevel = subGroupInfo.Level;
-                subGroupInfo.LastSubItemSlot++;
-
-                while (subGroupInfo != null && subGroupInfo.Level >= subGroupLevel)
-                {
-                    subGroupSlot = RowGroupHeadersTable.GetPreviousIndex(subGroupInfo.Slot);
-                    subGroupInfo = RowGroupHeadersTable.GetValueAt(subGroupSlot);
-                }
-            }
-        }
-
-        private int CountAndPopulateGroupHeaders(object group, int rootSlot, int level)
-        {
-            int treeCount = 1;
-
-            if (group is DataGridCollectionViewGroup collectionViewGroup)
-            {
-                if (collectionViewGroup.Items != null && collectionViewGroup.Items.Count > 0)
-                {
-                    collectionViewGroup.Items.CollectionChanged += CollectionViewGroup_CollectionChanged;
-                    if (collectionViewGroup.Items[0] is DataGridCollectionViewGroup)
-                    {
-                        foreach (object subGroup in collectionViewGroup.Items)
-                        {
-                            treeCount += CountAndPopulateGroupHeaders(subGroup, rootSlot + treeCount, level + 1);
-                        }
-                    }
-                    else
-                    {
-                        // Optimization: don't walk to the bottom level nodes
-                        treeCount += collectionViewGroup.Items.Count;
-                    }
-                }
-                RowGroupHeadersTable.AddValue(rootSlot, new DataGridRowGroupInfo(collectionViewGroup, true, level, rootSlot, rootSlot + treeCount - 1));
-            }
-            return treeCount;
-        }
-
-        private void EnsureAncestorsExpanderButtonChecked(DataGridRowGroupInfo parentGroupInfo)
-        {
-            if (IsSlotVisible(parentGroupInfo.Slot))
-            {
-                DataGridRowGroupHeader ancestorGroupHeader = DisplayData.GetDisplayedElement(parentGroupInfo.Slot) as DataGridRowGroupHeader;
-                while (ancestorGroupHeader != null)
-                {
-                    ancestorGroupHeader.EnsureExpanderButtonIsChecked();
-                    if (ancestorGroupHeader.Level > 0)
-                    {
-                        int slot = RowGroupHeadersTable.GetPreviousIndex(ancestorGroupHeader.RowGroupInfo.Slot);
-                        if (IsSlotVisible(slot))
-                        {
-                            ancestorGroupHeader = DisplayData.GetDisplayedElement(slot) as DataGridRowGroupHeader;
-                            continue;
-                        }
-                    }
-                    break;
-                }
-            }
-        }
-
-        private void PopulateRowGroupHeadersTable()
-        {
-            if (DataConnection.CollectionView != null
-                && DataConnection.CollectionView.CanGroup
-                && DataConnection.CollectionView.Groups != null)
-            {
-                int totalSlots = 0;
-                _topLevelGroup = (INotifyCollectionChanged)DataConnection.CollectionView.Groups;
-                _topLevelGroup.CollectionChanged += CollectionViewGroup_CollectionChanged;
-                foreach (object group in DataConnection.CollectionView.Groups)
-                {
-                    totalSlots += CountAndPopulateGroupHeaders(group, totalSlots, 0);
-                }
-            }
-            SlotCount = DataConnection.Count + RowGroupHeadersTable.IndexCount;
-            VisibleSlotCount = SlotCount;
-        }
-
-        private void RefreshRowGroupHeaders()
-        {
-            if (DataConnection.CollectionView != null
-                && DataConnection.CollectionView.CanGroup
-                && DataConnection.CollectionView.Groups != null
-                && DataConnection.CollectionView.IsGrouping
-                && DataConnection.CollectionView.GroupingDepth > 0)
-            {
-                // Initialize our array for the height of the RowGroupHeaders by Level.
-                // If the Length is the same, we can reuse the old array
-                int groupLevelCount = DataConnection.CollectionView.GroupingDepth;
-                if (_rowGroupHeightsByLevel == null || _rowGroupHeightsByLevel.Length != groupLevelCount)
-                {
-                    _rowGroupHeightsByLevel = new double[groupLevelCount];
-                    for (int i = 0; i < groupLevelCount; i++)
-                    {
-                        // Default height for now, the actual heights are updated as the RowGroupHeaders
-                        // are added and measured
-                        _rowGroupHeightsByLevel[i] = DATAGRID_defaultRowHeight;
-                    }
-                }
-                if (RowGroupSublevelIndents == null || RowGroupSublevelIndents.Length != groupLevelCount)
-                {
-                    RowGroupSublevelIndents = new double[groupLevelCount];
-                    double indent;
-                    for (int i = 0; i < groupLevelCount; i++)
-                    {
-                        indent = DATAGRID_defaultRowGroupSublevelIndent;
-                        RowGroupSublevelIndents[i] = indent;
-                        if (i > 0)
-                        {
-                            RowGroupSublevelIndents[i] += RowGroupSublevelIndents[i - 1];
-                        }
-                    }
-                }
-                EnsureRowGroupSpacerColumnWidth(groupLevelCount);
-            }
-        }
-
-        private void EnsureRowGroupSpacerColumn()
-        {
-            bool spacerColumnChanged = ColumnsInternal.EnsureRowGrouping(!RowGroupHeadersTable.IsEmpty);
-            if (spacerColumnChanged)
-            {
-                if (ColumnsInternal.RowGroupSpacerColumn.IsRepresented && CurrentColumnIndex == 0)
-                {
-                    CurrentColumn = ColumnsInternal.FirstVisibleNonFillerColumn;
-                }
-
-                ProcessFrozenColumnCount();
-            }
-        }
-
-        private void EnsureRowGroupSpacerColumnWidth(int groupLevelCount)
-        {
-            if (groupLevelCount == 0)
-            {
-                ColumnsInternal.RowGroupSpacerColumn.Width = new DataGridLength(0);
-            }
-            else
-            {
-                ColumnsInternal.RowGroupSpacerColumn.Width = new DataGridLength(RowGroupSublevelIndents[groupLevelCount - 1]);
-            }
-        }
-
-        private void EnsureRowGroupVisibility(DataGridRowGroupInfo rowGroupInfo, bool isVisible, bool setCurrent)
-        {
-            if (rowGroupInfo == null)
-            {
-                return;
-            }
-            if (rowGroupInfo.IsVisible != isVisible)
-            {
-                if (IsSlotVisible(rowGroupInfo.Slot))
-                {
-                    DataGridRowGroupHeader rowGroupHeader = DisplayData.GetDisplayedElement(rowGroupInfo.Slot) as DataGridRowGroupHeader;
-                    Debug.Assert(rowGroupHeader != null);
-                    rowGroupHeader.ToggleExpandCollapse(isVisible, setCurrent);
-                }
-                else
-                {
-                    if (_collapsedSlotsTable.Contains(rowGroupInfo.Slot))
-                    {
-                        // Somewhere up the parent chain, there's a collapsed header so all the slots remain the same and
-                        // we just need to mark this header with the new visibility
-                        rowGroupInfo.IsVisible = isVisible;
-                    }
-                    else
-                    {
-                        if (rowGroupInfo.Slot < DisplayData.FirstScrollingSlot)
-                        {
-                            double heightChange = UpdateRowGroupVisibility(rowGroupInfo, isVisible, isDisplayed: false);
-                            // Use epsilon instead of 0 here so that in the off chance that our estimates put the vertical offset negative
-                            // the user can still scroll to the top since the offset is non-zero
-                            SetVerticalOffset(Math.Max(MathUtilities.DoubleEpsilon, _verticalOffset + heightChange));
-                        }
-                        else
-                        {
-                            UpdateRowGroupVisibility(rowGroupInfo, isVisible, isDisplayed: false);
-                        }
-                        UpdateVerticalScrollBar();
-                    }
-                }
-            }
-        }
-
-        // Returns the inclusive count of expanded RowGroupHeaders from startSlot to endSlot
-        private int GetRowGroupHeaderCount(int startSlot, int endSlot, bool? isVisible, out double headersHeight)
-        {
-            int count = 0;
-            headersHeight = 0;
-            foreach (int slot in RowGroupHeadersTable.GetIndexes(startSlot))
-            {
-                if (slot > endSlot)
-                {
-                    return count;
-                }
-                DataGridRowGroupInfo rowGroupInfo = RowGroupHeadersTable.GetValueAt(slot);
-                if (!isVisible.HasValue ||
-                    (isVisible.Value && !_collapsedSlotsTable.Contains(slot)) ||
-                    (!isVisible.Value && _collapsedSlotsTable.Contains(slot)))
-                {
-                    count++;
-                    headersHeight += _rowGroupHeightsByLevel[rowGroupInfo.Level];
-                }
-            }
-            return count;
-        }
-
-        // This method does not check the state of the parent RowGroupHeaders, it assumes they're ready for this newVisibility to
-        // be applied this header
-        // Returns the number of pixels that were expanded or (collapsed); however, if we're expanding displayed rows, we only expand up
-        // to what we can display
-        private double UpdateRowGroupVisibility(DataGridRowGroupInfo targetRowGroupInfo, bool newIsVisible, bool isDisplayed)
-        {
-            double heightChange = 0;
-            int slotsExpanded = 0;
-            int startSlot = targetRowGroupInfo.Slot + 1;
-            int endSlot;
-
-            targetRowGroupInfo.IsVisible = newIsVisible;
-            if (newIsVisible)
-            {
-                // Expand
-                foreach (int slot in RowGroupHeadersTable.GetIndexes(targetRowGroupInfo.Slot + 1))
-                {
-                    if (slot >= startSlot)
-                    {
-                        DataGridRowGroupInfo rowGroupInfo = RowGroupHeadersTable.GetValueAt(slot);
-                        if (rowGroupInfo.Level <= targetRowGroupInfo.Level)
-                        {
-                            break;
-                        }
-                        if (!rowGroupInfo.IsVisible)
-                        {
-                            // Skip over the items in collapsed subgroups
-                            endSlot = rowGroupInfo.Slot;
-                            ExpandSlots(startSlot, endSlot, isDisplayed, ref slotsExpanded, ref heightChange);
-                            startSlot = rowGroupInfo.LastSubItemSlot + 1;
-                        }
-                    }
-                }
-                if (targetRowGroupInfo.LastSubItemSlot >= startSlot)
-                {
-                    ExpandSlots(startSlot, targetRowGroupInfo.LastSubItemSlot, isDisplayed, ref slotsExpanded, ref heightChange);
-                }
-                if (isDisplayed)
-                {
-                    UpdateDisplayedRows(DisplayData.FirstScrollingSlot, CellsEstimatedHeight);
-                }
-            }
-            else
-            {
-                // Collapse
-                endSlot = SlotCount - 1;
-                foreach (int slot in RowGroupHeadersTable.GetIndexes(targetRowGroupInfo.Slot + 1))
-                {
-                    DataGridRowGroupInfo rowGroupInfo = RowGroupHeadersTable.GetValueAt(slot);
-                    if (rowGroupInfo.Level <= targetRowGroupInfo.Level)
-                    {
-                        endSlot = slot - 1;
-                        break;
-                    }
-                }
-
-                int oldLastDisplayedSlot = DisplayData.LastScrollingSlot;
-                int endDisplayedSlot = Math.Min(endSlot, DisplayData.LastScrollingSlot);
-                if (isDisplayed)
-                {
-                    // We need to remove all the displayed slots that aren't already collapsed
-                    int elementsToRemove = endDisplayedSlot - startSlot + 1 - _collapsedSlotsTable.GetIndexCount(startSlot, endDisplayedSlot);
-
-                    if (_focusedRow != null && _focusedRow.Slot >= startSlot && _focusedRow.Slot <= endSlot)
-                    {
-                        Debug.Assert(EditingRow == null);
-                        // Don't call ResetFocusedRow here because we're already cleaning it up below, and we don't want to FullyRecycle yet
-                        _focusedRow = null;
-                    }
-
-                    for (int i = 0; i < elementsToRemove; i++)
-                    {
-                        RemoveDisplayedElement(startSlot, wasDeleted: false , updateSlotInformation: false);
-                    }
-                }
-
-                double heightChangeBelowLastDisplayedSlot = 0;
-                if (DisplayData.FirstScrollingSlot >= startSlot && DisplayData.FirstScrollingSlot <= endSlot)
-                {
-                    // Our first visible slot was collapsed, find the replacement
-                    int collapsedSlotsAbove = DisplayData.FirstScrollingSlot - startSlot - _collapsedSlotsTable.GetIndexCount(startSlot, DisplayData.FirstScrollingSlot);
-                    Debug.Assert(collapsedSlotsAbove > 0);
-                    int newFirstScrollingSlot = GetNextVisibleSlot(DisplayData.FirstScrollingSlot);
-                    while (collapsedSlotsAbove > 1 && newFirstScrollingSlot < SlotCount)
-                    {
-                        collapsedSlotsAbove--;
-                        newFirstScrollingSlot = GetNextVisibleSlot(newFirstScrollingSlot);
-                    }
-                    heightChange += CollapseSlotsInTable(startSlot, endSlot, ref slotsExpanded, oldLastDisplayedSlot, ref heightChangeBelowLastDisplayedSlot);
-                    if (isDisplayed)
-                    {
-                        if (newFirstScrollingSlot >= SlotCount)
-                        {
-                            // No visible slots below, look up
-                            UpdateDisplayedRowsFromBottom(targetRowGroupInfo.Slot);
-                        }
-                        else
-                        {
-                            UpdateDisplayedRows(newFirstScrollingSlot, CellsEstimatedHeight);
-                        }
-                    }
-                }
-                else
-                {
-                    heightChange += CollapseSlotsInTable(startSlot, endSlot, ref slotsExpanded, oldLastDisplayedSlot, ref heightChangeBelowLastDisplayedSlot);
-                }
-
-                if (DisplayData.LastScrollingSlot >= startSlot && DisplayData.LastScrollingSlot <= endSlot)
-                {
-                    // Collapsed the last scrolling row, we need to update it
-                    DisplayData.LastScrollingSlot = GetPreviousVisibleSlot(DisplayData.LastScrollingSlot);
-                }
-
-                // Collapsing could cause the vertical offset to move up if we collapsed a lot of slots
-                // near the bottom of the DataGrid.  To do this, we compare the height we collapsed to
-                // the distance to the last visible row and adjust the scrollbar if we collapsed more
-                if (isDisplayed && _verticalOffset > 0)
-                {
-                    int lastVisibleSlot = GetPreviousVisibleSlot(SlotCount);
-                    int slot = GetNextVisibleSlot(oldLastDisplayedSlot);
-                    // AvailableSlotElementRoom ends up being the amount of the last slot that is partially scrolled off
-                    // as a negative value, heightChangeBelowLastDisplayed slot is also a negative value since we're collapsing
-                    double heightToLastVisibleSlot = AvailableSlotElementRoom + heightChangeBelowLastDisplayedSlot;
-                    while ((heightToLastVisibleSlot > heightChange) && (slot < lastVisibleSlot))
-                    {
-                        heightToLastVisibleSlot -= GetSlotElementHeight(slot);
-                        slot = GetNextVisibleSlot(slot);
-                    }
-                    if (heightToLastVisibleSlot > heightChange)
-                    {
-                        double newVerticalOffset = _verticalOffset + heightChange - heightToLastVisibleSlot;
-                        if (newVerticalOffset > 0)
-                        {
-                            SetVerticalOffset(newVerticalOffset);
-                        }
-                        else
-                        {
-                            // Collapsing causes the vertical offset to go to 0 so we should go back to the first row.
-                            ResetDisplayedRows();
-                            NegVerticalOffset = 0;
-                            SetVerticalOffset(0);
-                            int firstDisplayedRow = GetNextVisibleSlot(-1);
-                            UpdateDisplayedRows(firstDisplayedRow, CellsEstimatedHeight);
-                        }
-                    }
-                }
-            }
-
-            // Update VisibleSlotCount
-            VisibleSlotCount += slotsExpanded;
-
-            return heightChange;
-        }
-
-        private DataGridRowGroupHeader GenerateRowGroupHeader(int slot, DataGridRowGroupInfo rowGroupInfo)
-        {
-            Debug.Assert(slot > -1);
-            Debug.Assert(rowGroupInfo != null);
-
-            DataGridRowGroupHeader groupHeader = DisplayData.GetUsedGroupHeader() ?? new DataGridRowGroupHeader();
-            groupHeader.OwningGrid = this;
-            groupHeader.RowGroupInfo = rowGroupInfo;
-            groupHeader.DataContext = rowGroupInfo.CollectionViewGroup;
-            groupHeader.Level = rowGroupInfo.Level;
-            if (RowGroupTheme is {} rowGroupTheme)
-            {
-                groupHeader.SetValue(ThemeProperty, rowGroupTheme, BindingPriority.Template);
-            }
-
-            // Set the RowGroupHeader's PropertyName. Unfortunately, CollectionViewGroup doesn't have this
-            // so we have to set it manually
-            Debug.Assert(DataConnection.CollectionView != null && groupHeader.Level < DataConnection.CollectionView.GroupingDepth);
-            string propertyName = DataConnection.CollectionView.GetGroupingPropertyNameAtDepth(groupHeader.Level);
-
-            if(string.IsNullOrWhiteSpace(propertyName))
-            {
-                groupHeader.PropertyName = null;
-            }
-            else
-            {
-                groupHeader.PropertyName = DataConnection.DataType?.GetDisplayName(propertyName) ?? propertyName;
-            }
-
-            if (rowGroupInfo.CollectionViewGroup is INotifyPropertyChanged inpc)
-            {
-                inpc.PropertyChanged -= new PropertyChangedEventHandler(CollectionViewGroup_PropertyChanged);
-                inpc.PropertyChanged += new PropertyChangedEventHandler(CollectionViewGroup_PropertyChanged);
-            }
-            groupHeader.UpdateTitleElements();
-
-            OnLoadingRowGroup(new DataGridRowGroupHeaderEventArgs(groupHeader));
-
-            return groupHeader;
-        }
-
-        private DataGridRowGroupInfo GetParentGroupInfo(object collection)
-        {
-            if (collection == DataConnection.CollectionView.Groups)
-            {
-                // If the new item is a root level element, it has no parent group, so create an empty RowGroupInfo
-                return new DataGridRowGroupInfo(null, true, -1, -1, -1);
-            }
-            else
-            {
-                foreach (int slot in RowGroupHeadersTable.GetIndexes())
-                {
-                    DataGridRowGroupInfo groupInfo = RowGroupHeadersTable.GetValueAt(slot);
-                    if (groupInfo.CollectionViewGroup.Items == collection)
-                    {
-                        return groupInfo;
-                    }
-                }
-            }
-            return null;
-        }
-
-        internal void OnRowGroupHeaderToggled(DataGridRowGroupHeader groupHeader, bool newIsVisible, bool setCurrent)
-        {
-            Debug.Assert(groupHeader.RowGroupInfo.CollectionViewGroup.ItemCount > 0);
-
-            if (WaitForLostFocus(delegate { OnRowGroupHeaderToggled(groupHeader, newIsVisible, setCurrent); }) || !CommitEdit())
-            {
-                return;
-            }
-
-            if (setCurrent && CurrentSlot != groupHeader.RowGroupInfo.Slot)
-            {
-                // Most of the time this is set by the MouseLeftButtonDown handler but validation could cause that code path to fail
-                UpdateSelectionAndCurrency(CurrentColumnIndex, groupHeader.RowGroupInfo.Slot, DataGridSelectionAction.SelectCurrent, scrollIntoView: false);
-            }
-
-            UpdateRowGroupVisibility(groupHeader.RowGroupInfo, newIsVisible, isDisplayed: true);
-
-            ComputeScrollBarsLayout();
-            // We need force arrange since our Scrollings Rows could update without automatically triggering layout
-            InvalidateRowsArrange();
-        }
-
-        internal void OnSublevelIndentUpdated(DataGridRowGroupHeader groupHeader, double newValue)
-        {
-            Debug.Assert(DataConnection.CollectionView != null);
-            Debug.Assert(RowGroupSublevelIndents != null);
-
-            int groupLevelCount = DataConnection.CollectionView.GroupingDepth;
-            Debug.Assert(groupHeader.Level >= 0 && groupHeader.Level < groupLevelCount);
-
-            double oldValue = RowGroupSublevelIndents[groupHeader.Level];
-            if (groupHeader.Level > 0)
-            {
-                oldValue -= RowGroupSublevelIndents[groupHeader.Level - 1];
-            }
-            // Update the affected values in our table by the amount affected
-            double change = newValue - oldValue;
-            for (int i = groupHeader.Level; i < groupLevelCount; i++)
-            {
-                RowGroupSublevelIndents[i] += change;
-                Debug.Assert(RowGroupSublevelIndents[i] >= 0);
-            }
-
-            EnsureRowGroupSpacerColumnWidth(groupLevelCount);
-        }
-
-        internal DataGridRowGroupInfo RowGroupInfoFromCollectionViewGroup(DataGridCollectionViewGroup collectionViewGroup)
-        {
-            foreach (int slot in RowGroupHeadersTable.GetIndexes())
-            {
-                DataGridRowGroupInfo rowGroupInfo = RowGroupHeadersTable.GetValueAt(slot);
-                if (rowGroupInfo.CollectionViewGroup == collectionViewGroup)
-                {
-                    return rowGroupInfo;
-                }
-            }
-            return null;
-        }
-
-        /// <summary>
-        /// Collapses the DataGridRowGroupHeader that represents a given CollectionViewGroup
-        /// </summary>
-        /// <param name="collectionViewGroup">CollectionViewGroup</param>
-        /// <param name="collapseAllSubgroups">Set to true to collapse all Subgroups</param>
-        public void CollapseRowGroup(DataGridCollectionViewGroup collectionViewGroup, bool collapseAllSubgroups)
-        {
-            if (WaitForLostFocus(delegate { CollapseRowGroup(collectionViewGroup, collapseAllSubgroups); }) ||
-                collectionViewGroup == null || !CommitEdit())
-            {
-                return;
-            }
-
-            EnsureRowGroupVisibility(RowGroupInfoFromCollectionViewGroup(collectionViewGroup), false, true);
-
-            if (collapseAllSubgroups)
-            {
-                foreach (object groupObj in collectionViewGroup.Items)
-                {
-                    if (groupObj is DataGridCollectionViewGroup subGroup)
-                    {
-                        CollapseRowGroup(subGroup, collapseAllSubgroups);
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// Expands the DataGridRowGroupHeader that represents a given CollectionViewGroup
-        /// </summary>
-        /// <param name="collectionViewGroup">CollectionViewGroup</param>
-        /// <param name="expandAllSubgroups">Set to true to expand all Subgroups</param>
-        public void ExpandRowGroup(DataGridCollectionViewGroup collectionViewGroup, bool expandAllSubgroups)
-        {
-            if (WaitForLostFocus(delegate { ExpandRowGroup(collectionViewGroup, expandAllSubgroups); }) ||
-                collectionViewGroup == null || !CommitEdit())
-                if (collectionViewGroup == null || !CommitEdit())
-                {
-                    return;
-                }
-
-            EnsureRowGroupVisibility(RowGroupInfoFromCollectionViewGroup(collectionViewGroup), true, true);
-
-            if (expandAllSubgroups)
-            {
-                foreach (object groupObj in collectionViewGroup.Items)
-                {
-                    if (groupObj is DataGridCollectionViewGroup subGroup)
-                    {
-                        ExpandRowGroup(subGroup, expandAllSubgroups);
-                    }
-                }
-            }
-        }
-
-        // Returns the number of rows with details visible between lowerBound and upperBound exclusive.
-        // As of now, the caller needs to account for Collapsed slots.  This method assumes everything
-        // is visible
-        private int GetDetailsCountInclusive(int lowerBound, int upperBound)
-        {
-            int indexCount = upperBound - lowerBound + 1;
-            if (indexCount <= 0)
-            {
-                return 0;
-            }
-            if (RowDetailsVisibilityMode == DataGridRowDetailsVisibilityMode.Visible)
-            {
-                // Total rows minus ones which explicity turned details off minus the RowGroupHeaders
-                return indexCount - _showDetailsTable.GetIndexCount(lowerBound, upperBound, false) - RowGroupHeadersTable.GetIndexCount(lowerBound, upperBound);
-            }
-            else if (RowDetailsVisibilityMode == DataGridRowDetailsVisibilityMode.Collapsed)
-            {
-                // Total rows with details explicitly turned on
-                return _showDetailsTable.GetIndexCount(lowerBound, upperBound, true);
-            }
-            else if (RowDetailsVisibilityMode == DataGridRowDetailsVisibilityMode.VisibleWhenSelected)
-            {
-                // Total number of remaining rows that are selected
-                return _selectedItems.GetIndexCount(lowerBound, upperBound);
-            }
-            Debug.Assert(false); // Shouldn't ever happen
-            return 0;
-        }
-
-        private void EnsureRowDetailsVisibility(DataGridRow row, bool raiseNotification, bool animate)
-        {
-            // Show or hide RowDetails based on DataGrid settings
-            row.SetDetailsVisibilityInternal(GetRowDetailsVisibility(row.Index), raiseNotification, animate);
-        }
-
-        private void UpdateRowDetailsHeightEstimate()
-        {
-            if (_rowsPresenter != null && _measured && RowDetailsTemplate != null)
-            {
-                object dataItem = null;
-                if(VisibleSlotCount > 0)
-                    dataItem = DataConnection.GetDataItem(0);
-                var detailsContent = RowDetailsTemplate.Build(dataItem);
-                if (detailsContent != null)
-                {
-                    detailsContent.DataContext = dataItem;
-                    _rowsPresenter.Children.Add(detailsContent);
-                    detailsContent.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
-                    RowDetailsHeightEstimate = detailsContent.DesiredSize.Height;
-                    _rowsPresenter.Children.Remove(detailsContent);
-                }
-            }
-        }
-
-        // detailsElement is the FrameworkElement created by the DetailsTemplate
-        internal void OnUnloadingRowDetails(DataGridRow row, Control detailsElement)
-        {
-            OnUnloadingRowDetails(new DataGridRowDetailsEventArgs(row, detailsElement));
-        }
-
-        // detailsElement is the FrameworkElement created by the DetailsTemplate
-        internal void OnLoadingRowDetails(DataGridRow row, Control detailsElement)
-        {
-            OnLoadingRowDetails(new DataGridRowDetailsEventArgs(row, detailsElement));
-        }
-
-        internal void OnRowDetailsVisibilityPropertyChanged(int rowIndex, bool isVisible)
-        {
-            Debug.Assert(rowIndex >= 0 && rowIndex < SlotCount);
-
-            _showDetailsTable.AddValue(rowIndex, isVisible);
-        }
-
-        internal bool GetRowDetailsVisibility(int rowIndex)
-        {
-            return GetRowDetailsVisibility(rowIndex, RowDetailsVisibilityMode);
-        }
-
-        internal bool GetRowDetailsVisibility(int rowIndex, DataGridRowDetailsVisibilityMode gridLevelRowDetailsVisibility)
-        {
-            Debug.Assert(rowIndex != -1);
-            if (_showDetailsTable.Contains(rowIndex))
-            {
-                // The user explicity set DetailsVisibility on a row so we should respect that
-                return _showDetailsTable.GetValueAt(rowIndex);
-            }
-            else
-            {
-                return
-                    gridLevelRowDetailsVisibility == DataGridRowDetailsVisibilityMode.Visible ||
-                    (gridLevelRowDetailsVisibility == DataGridRowDetailsVisibilityMode.VisibleWhenSelected &&
-                     _selectedItems.ContainsSlot(SlotFromRowIndex(rowIndex)));
-            }
-        }
-
-        /// <summary>
-        /// Raises the <see cref="E:Avalonia.Controls.DataGrid.RowDetailsVisibilityChanged" /> event.
-        /// </summary>
-        /// <param name="e">The event data.</param>
-        protected internal virtual void OnRowDetailsVisibilityChanged(DataGridRowDetailsEventArgs e)
-        {
-            RowDetailsVisibilityChanged?.Invoke(this, e);
-        }
-
-#if DEBUG
-        internal void PrintRowGroupInfo()
-        {
-            Debug.WriteLine("-----------------------------------------------RowGroupHeaders");
-            foreach (int slot in RowGroupHeadersTable.GetIndexes())
-            {
-                DataGridRowGroupInfo info = RowGroupHeadersTable.GetValueAt(slot);
-                Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} {1} Slot:{2} Last:{3} Level:{4}", info.CollectionViewGroup.Key, info.IsVisible.ToString(), slot, info.LastSubItemSlot, info.Level));
-            }
-            Debug.WriteLine("-----------------------------------------------CollapsedSlots");
-            _collapsedSlotsTable.PrintIndexes();
-        }
-#endif
-    }
-}

+ 0 - 470
src/Avalonia.Controls.DataGrid/DataGridSelectedItemsCollection.cs

@@ -1,470 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System;
-using System.Diagnostics;
-using System.Collections.Generic;
-using System.Collections;
-
-namespace Avalonia.Controls
-{
-    internal class DataGridSelectedItemsCollection : IList
-    {
-        private List<object> _oldSelectedItemsCache;
-        private IndexToValueTable<bool> _oldSelectedSlotsTable;
-        private List<object> _selectedItemsCache;
-        private IndexToValueTable<bool> _selectedSlotsTable;
-
-        public DataGridSelectedItemsCollection(DataGrid owningGrid)
-        {
-            OwningGrid = owningGrid;
-            _oldSelectedItemsCache = new List<object>();
-            _oldSelectedSlotsTable = new IndexToValueTable<bool>();
-            _selectedItemsCache = new List<object>();
-            _selectedSlotsTable = new IndexToValueTable<bool>();
-        }
-
-        public object this[int index]
-        {
-            get
-            {
-                if (index < 0 || index >= _selectedSlotsTable.IndexCount)
-                {
-                    throw DataGridError.DataGrid.ValueMustBeBetween("index", "Index", 0, true, _selectedSlotsTable.IndexCount, false);
-                }
-                int slot = _selectedSlotsTable.GetNthIndex(index);
-                Debug.Assert(slot > -1);
-                return OwningGrid.DataConnection.GetDataItem(OwningGrid.RowIndexFromSlot(slot));
-            }
-            set
-            {
-                throw new NotSupportedException();
-            }
-        }
-
-        public bool IsFixedSize
-        {
-            get
-            {
-                return false;
-            }
-        }
-
-        public bool IsReadOnly
-        {
-            get
-            {
-                return false;
-            }
-        }
-
-        public int Add(object dataItem)
-        {
-            if (OwningGrid.SelectionMode == DataGridSelectionMode.Single)
-            {
-                throw DataGridError.DataGridSelectedItemsCollection.CannotChangeSelectedItemsCollectionInSingleMode();
-            }
-
-            int itemIndex = OwningGrid.DataConnection.IndexOf(dataItem);
-            if (itemIndex == -1)
-            {
-                throw DataGridError.DataGrid.ItemIsNotContainedInTheItemsSource("dataItem");
-            }
-            Debug.Assert(itemIndex >= 0);
-
-            int slot = OwningGrid.SlotFromRowIndex(itemIndex);
-            if (_selectedSlotsTable.RangeCount == 0)
-            {
-                OwningGrid.SelectedItem = dataItem;
-            }
-            else
-            {
-                OwningGrid.SetRowSelection(slot, true /*isSelected*/, false /*setAnchorSlot*/);
-            }
-            return _selectedSlotsTable.IndexOf(slot);
-        }
-
-        public void Clear()
-        {
-            if (OwningGrid.SelectionMode == DataGridSelectionMode.Single)
-            {
-                throw DataGridError.DataGridSelectedItemsCollection.CannotChangeSelectedItemsCollectionInSingleMode();
-            }
-
-            if (_selectedSlotsTable.RangeCount > 0)
-            {
-                // Clearing the selection does not reset the potential current cell.
-                if (!OwningGrid.CommitEdit(DataGridEditingUnit.Row, true /*exitEditing*/))
-                {
-                    // Edited value couldn't be committed or aborted
-                    return;
-                }
-                OwningGrid.ClearRowSelection(true /*resetAnchorSlot*/);
-            }
-        }
-
-        public bool Contains(object dataItem)
-        {
-            int itemIndex = OwningGrid.DataConnection.IndexOf(dataItem);
-            if (itemIndex == -1)
-            {
-                return false;
-            }
-            Debug.Assert(itemIndex >= 0);
-
-            return ContainsSlot(OwningGrid.SlotFromRowIndex(itemIndex));
-        }
-
-        public int IndexOf(object dataItem)
-        {
-            int itemIndex = OwningGrid.DataConnection.IndexOf(dataItem);
-            if (itemIndex == -1)
-            {
-                return -1;
-            }
-            Debug.Assert(itemIndex >= 0);
-            int slot = OwningGrid.SlotFromRowIndex(itemIndex);
-            return _selectedSlotsTable.IndexOf(slot);
-        }
-
-        public void Insert(int index, object dataItem)
-        {
-            throw new NotSupportedException();
-        }
-
-        public void Remove(object dataItem)
-        {
-            if (OwningGrid.SelectionMode == DataGridSelectionMode.Single)
-            {
-                throw DataGridError.DataGridSelectedItemsCollection.CannotChangeSelectedItemsCollectionInSingleMode();
-            }
-
-            int itemIndex = OwningGrid.DataConnection.IndexOf(dataItem);
-            if (itemIndex == -1)
-            {
-                return;
-            }
-            Debug.Assert(itemIndex >= 0);
-
-            if (itemIndex == OwningGrid.CurrentSlot &&
-                !OwningGrid.CommitEdit(DataGridEditingUnit.Row, true /*exitEditing*/))
-            {
-                // Edited value couldn't be committed or aborted
-                return;
-            }
-
-            OwningGrid.SetRowSelection(OwningGrid.SlotFromRowIndex(itemIndex), false /*isSelected*/, false /*setAnchorSlot*/);
-        }
-
-        public void RemoveAt(int index)
-        {
-            if (OwningGrid.SelectionMode == DataGridSelectionMode.Single)
-            {
-                throw DataGridError.DataGridSelectedItemsCollection.CannotChangeSelectedItemsCollectionInSingleMode();
-            }
-
-            if (index < 0 || index >= _selectedSlotsTable.IndexCount)
-            {
-                throw DataGridError.DataGrid.ValueMustBeBetween("index", "Index", 0, true, _selectedSlotsTable.IndexCount, false);
-            }
-            int rowIndex = _selectedSlotsTable.GetNthIndex(index);
-            Debug.Assert(rowIndex > -1);
-
-            if (rowIndex == OwningGrid.CurrentSlot &&
-                !OwningGrid.CommitEdit(DataGridEditingUnit.Row, true /*exitEditing*/))
-            {
-                // Edited value couldn't be committed or aborted
-                return;
-            }
-
-            OwningGrid.SetRowSelection(rowIndex, false /*isSelected*/, false /*setAnchorSlot*/);
-        }
-
-        public int Count
-        {
-            get
-            {
-                return _selectedSlotsTable.IndexCount;
-            }
-        }
-
-        public bool IsSynchronized
-        {
-            get
-            {
-                return false;
-            }
-        }
-
-        public object SyncRoot
-        {
-            get
-            {
-                return this;
-            }
-        }
-
-        public void CopyTo(System.Array array, int index)
-        {
-            throw new NotImplementedException();
-        }
-
-        public IEnumerator GetEnumerator()
-        {
-            Debug.Assert(OwningGrid != null);
-            Debug.Assert(OwningGrid.DataConnection != null);
-            Debug.Assert(_selectedSlotsTable != null);
-
-            foreach (int slot in _selectedSlotsTable.GetIndexes())
-            {
-                int rowIndex = OwningGrid.RowIndexFromSlot(slot);
-                Debug.Assert(rowIndex > -1);
-                yield return OwningGrid.DataConnection.GetDataItem(rowIndex);
-            }
-        }
-
-        internal DataGrid OwningGrid
-        {
-            get;
-            private set;
-        }
-
-        internal List<object> SelectedItemsCache
-        {
-            get
-            {
-                return _selectedItemsCache;
-            }
-            set
-            {
-                _selectedItemsCache = value;
-                UpdateIndexes();
-            }
-        }
-
-        internal void ClearRows()
-        {
-            _selectedSlotsTable.Clear();
-            _selectedItemsCache.Clear();
-        }
-
-        internal bool ContainsSlot(int slot)
-        {
-            return _selectedSlotsTable.Contains(slot);
-        }
-
-        internal bool ContainsAll(int startSlot, int endSlot)
-        {
-            int itemSlot = OwningGrid.RowGroupHeadersTable.GetNextGap(startSlot - 1);
-            while (itemSlot <= endSlot)
-            {
-                // Skip over the RowGroupHeaderSlots
-                int nextRowGroupHeaderSlot = OwningGrid.RowGroupHeadersTable.GetNextIndex(itemSlot);
-                int lastItemSlot = nextRowGroupHeaderSlot == -1 ? endSlot : Math.Min(endSlot, nextRowGroupHeaderSlot - 1);
-                if (!_selectedSlotsTable.ContainsAll(itemSlot, lastItemSlot))
-                {
-                    return false;
-                }
-                itemSlot = OwningGrid.RowGroupHeadersTable.GetNextGap(lastItemSlot);
-            }
-            return true;
-        }
-
-        // Called when an item is deleted from the ItemsSource as opposed to just being unselected
-        internal void Delete(int slot, object item)
-        {
-            if (_oldSelectedSlotsTable.Contains(slot))
-            {
-                OwningGrid.SelectionHasChanged = true;
-            }
-            DeleteSlot(slot);
-            _selectedItemsCache.Remove(item);
-        }
-
-        internal void DeleteSlot(int slot)
-        {
-            _selectedSlotsTable.RemoveIndex(slot);
-            _oldSelectedSlotsTable.RemoveIndex(slot);
-        }
-
-        // Returns the inclusive index count between lowerBound and upperBound of all indexes with the given value
-        internal int GetIndexCount(int lowerBound, int upperBound)
-        {
-            return _selectedSlotsTable.GetIndexCount(lowerBound, upperBound, true);
-        }
-
-        internal IEnumerable<int> GetIndexes()
-        {
-            return _selectedSlotsTable.GetIndexes();
-        }
-
-        internal IEnumerable<int> GetSlots(int startSlot)
-        {
-            return _selectedSlotsTable.GetIndexes(startSlot);
-        }
-
-        internal SelectionChangedEventArgs GetSelectionChangedEventArgs()
-        {
-            List<object> addedSelectedItems = new List<object>();
-            List<object> removedSelectedItems = new List<object>();
-
-            // Compare the old selected indexes with the current selection to determine which items
-            // have been added and removed since the last time this method was called
-            foreach (int newSlot in _selectedSlotsTable.GetIndexes())
-            {
-                object newItem = OwningGrid.DataConnection.GetDataItem(OwningGrid.RowIndexFromSlot(newSlot));
-                if (_oldSelectedSlotsTable.Contains(newSlot))
-                {
-                    _oldSelectedSlotsTable.RemoveValue(newSlot);
-                    _oldSelectedItemsCache.Remove(newItem);
-                }
-                else
-                {
-                    addedSelectedItems.Add(newItem);
-                }
-            }
-            foreach (object oldItem in _oldSelectedItemsCache)
-            {
-                removedSelectedItems.Add(oldItem);
-            }
-
-            // The current selection becomes the old selection
-            _oldSelectedSlotsTable = _selectedSlotsTable.Copy();
-            _oldSelectedItemsCache = new List<object>(_selectedItemsCache);
-
-            return
-                new SelectionChangedEventArgs(DataGrid.SelectionChangedEvent, removedSelectedItems, addedSelectedItems)
-                {
-                    Source = OwningGrid
-                };
-        }
-
-        internal void InsertIndex(int slot)
-        {
-            _selectedSlotsTable.InsertIndex(slot);
-            _oldSelectedSlotsTable.InsertIndex(slot);
-
-            // It's possible that we're inserting an item that was just removed.  If that's the case,
-            // and the re-inserted item used to be selected, we want to update the _oldSelectedSlotsTable
-            // to include the item's new index within the collection.
-            int rowIndex = OwningGrid.RowIndexFromSlot(slot);
-            if (rowIndex != -1)
-            {
-                object insertedItem = OwningGrid.DataConnection.GetDataItem(rowIndex);
-                if (insertedItem != null && _oldSelectedItemsCache.Contains(insertedItem))
-                {
-                    _oldSelectedSlotsTable.AddValue(slot, true);
-                }
-            }
-        }
-
-        internal void SelectSlot(int slot, bool select)
-        {
-            if (OwningGrid.RowGroupHeadersTable.Contains(slot))
-            {
-                return;
-            }
-            if (select)
-            {
-                if (!_selectedSlotsTable.Contains(slot))
-                {
-                    _selectedItemsCache.Add(OwningGrid.DataConnection.GetDataItem(OwningGrid.RowIndexFromSlot(slot)));
-                }
-                _selectedSlotsTable.AddValue(slot, true);
-            }
-            else
-            {
-                if (_selectedSlotsTable.Contains(slot))
-                {
-                    _selectedItemsCache.Remove(OwningGrid.DataConnection.GetDataItem(OwningGrid.RowIndexFromSlot(slot)));
-                }
-                _selectedSlotsTable.RemoveValue(slot);
-            }
-        }
-
-        internal void SelectSlots(int startSlot, int endSlot, bool select)
-        {
-            int itemSlot = OwningGrid.RowGroupHeadersTable.GetNextGap(startSlot - 1);
-            int endItemSlot = OwningGrid.RowGroupHeadersTable.GetPreviousGap(endSlot + 1);
-            if (select)
-            {
-                while (itemSlot <= endItemSlot)
-                {
-                    // Add the newly selected item slots by skipping over the RowGroupHeaderSlots
-                    int nextRowGroupHeaderSlot = OwningGrid.RowGroupHeadersTable.GetNextIndex(itemSlot);
-                    int lastItemSlot = nextRowGroupHeaderSlot == -1 ? endItemSlot : Math.Min(endItemSlot, nextRowGroupHeaderSlot - 1);
-                    for (int slot = itemSlot; slot <= lastItemSlot; slot++)
-                    {
-                        if (!_selectedSlotsTable.Contains(slot))
-                        {
-                            _selectedItemsCache.Add(OwningGrid.DataConnection.GetDataItem(OwningGrid.RowIndexFromSlot(slot)));
-                        }
-                    }
-                    _selectedSlotsTable.AddValues(itemSlot, lastItemSlot - itemSlot + 1, true);
-                    itemSlot = OwningGrid.RowGroupHeadersTable.GetNextGap(lastItemSlot);
-                }
-            }
-            else
-            {
-                while (itemSlot <= endItemSlot)
-                {
-                    // Remove the unselected item slots by skipping over the RowGroupHeaderSlots
-                    int nextRowGroupHeaderSlot = OwningGrid.RowGroupHeadersTable.GetNextIndex(itemSlot);
-                    int lastItemSlot = nextRowGroupHeaderSlot == -1 ? endItemSlot : Math.Min(endItemSlot, nextRowGroupHeaderSlot - 1);
-                    for (int slot = itemSlot; slot <= lastItemSlot; slot++)
-                    {
-                        if (_selectedSlotsTable.Contains(slot))
-                        {
-                            _selectedItemsCache.Remove(OwningGrid.DataConnection.GetDataItem(OwningGrid.RowIndexFromSlot(slot)));
-                        }
-                    }
-                    _selectedSlotsTable.RemoveValues(itemSlot, lastItemSlot - itemSlot + 1);
-                    itemSlot = OwningGrid.RowGroupHeadersTable.GetNextGap(lastItemSlot);
-                }
-            }
-        }
-
-        internal void UpdateIndexes()
-        {
-            _oldSelectedSlotsTable.Clear();
-            _selectedSlotsTable.Clear();
-
-            if (OwningGrid.DataConnection.DataSource == null)
-            {
-                if (SelectedItemsCache.Count > 0)
-                {
-                    OwningGrid.SelectionHasChanged = true;
-                    SelectedItemsCache.Clear();
-                }
-            }
-            else
-            {
-                List<object> tempSelectedItemsCache = new List<object>();
-                foreach (object item in _selectedItemsCache)
-                {
-                    int index = OwningGrid.DataConnection.IndexOf(item);
-                    if (index != -1)
-                    {
-                        tempSelectedItemsCache.Add(item);
-                        _selectedSlotsTable.AddValue(OwningGrid.SlotFromRowIndex(index), true);
-                    }
-                }
-                foreach (object item in _oldSelectedItemsCache)
-                {
-                    int index = OwningGrid.DataConnection.IndexOf(item);
-                    if (index == -1)
-                    {
-                        OwningGrid.SelectionHasChanged = true;
-                    }
-                    else
-                    {
-                        _oldSelectedSlotsTable.AddValue(OwningGrid.SlotFromRowIndex(index), true);
-                    }
-                }
-                _selectedItemsCache = tempSelectedItemsCache;
-            }
-        }
-    }
-}

+ 0 - 147
src/Avalonia.Controls.DataGrid/DataGridTemplateColumn.cs

@@ -1,147 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Controls.Templates;
-using Avalonia.Controls.Utils;
-using Avalonia.Input;
-using Avalonia.Interactivity;
-using Avalonia.Media;
-using Avalonia.Metadata;
-using Avalonia.Utilities;
-
-namespace Avalonia.Controls
-{
-    public class DataGridTemplateColumn : DataGridColumn
-    {
-        private IDataTemplate _cellTemplate;
-
-        public static readonly DirectProperty<DataGridTemplateColumn, IDataTemplate> CellTemplateProperty =
-            AvaloniaProperty.RegisterDirect<DataGridTemplateColumn, IDataTemplate>(
-                nameof(CellTemplate),
-                o => o.CellTemplate,
-                (o, v) => o.CellTemplate = v);
-
-        [Content]
-        [InheritDataTypeFromItems(nameof(DataGrid.ItemsSource), AncestorType = typeof(DataGrid))]
-        public IDataTemplate CellTemplate
-        {
-            get { return _cellTemplate; }
-            set { SetAndRaise(CellTemplateProperty, ref _cellTemplate, value); }
-        }
-
-        private IDataTemplate _cellEditingCellTemplate;
-
-        /// <summary>
-        /// Defines the <see cref="CellEditingTemplate"/> property.
-        /// </summary>
-        public static readonly DirectProperty<DataGridTemplateColumn, IDataTemplate> CellEditingTemplateProperty =
-                AvaloniaProperty.RegisterDirect<DataGridTemplateColumn, IDataTemplate>(
-                    nameof(CellEditingTemplate),
-                    o => o.CellEditingTemplate,
-                    (o, v) => o.CellEditingTemplate = v);
-
-        /// <summary>
-        /// Gets or sets the <see cref="IDataTemplate"/> which is used for the editing mode of the current <see cref="DataGridCell"/>
-        /// </summary>
-        /// <value>
-        /// An <see cref="IDataTemplate"/> for the editing mode of the current <see cref="DataGridCell"/>
-        /// </value>
-        /// <remarks>
-        /// If this property is <see langword="null"/> the column is read-only.
-        /// </remarks>
-        [InheritDataTypeFromItems(nameof(DataGrid.ItemsSource), AncestorType = typeof(DataGrid))]
-        public IDataTemplate CellEditingTemplate
-        {
-            get => _cellEditingCellTemplate;
-            set => SetAndRaise(CellEditingTemplateProperty, ref _cellEditingCellTemplate, value);
-        }
-
-        private bool _forceGenerateCellFromTemplate;
-
-        protected override void EndCellEdit()
-        {
-            //the next call to generate element should not resuse the current content as we need to exit edit mode
-            _forceGenerateCellFromTemplate = true;
-            base.EndCellEdit();
-        }
-
-        protected override Control GenerateElement(DataGridCell cell, object dataItem)
-        {
-            if (CellTemplate != null)
-            {
-                if (_forceGenerateCellFromTemplate)
-                {
-                    _forceGenerateCellFromTemplate = false;
-                    return CellTemplate.Build(dataItem);
-                }
-                return (CellTemplate is IRecyclingDataTemplate recyclingDataTemplate)
-                    ? recyclingDataTemplate.Build(dataItem, cell.Content as Control)
-                    : CellTemplate.Build(dataItem);
-            }
-            if (Design.IsDesignMode)
-            {
-                return null;
-            }
-            else
-            {
-                throw DataGridError.DataGridTemplateColumn.MissingTemplateForType(typeof(DataGridTemplateColumn));
-            }
-        }
-
-        protected override Control GenerateEditingElement(DataGridCell cell, object dataItem, out ICellEditBinding binding)
-        {
-            binding = null;
-            if(CellEditingTemplate != null)
-            {
-                return CellEditingTemplate.Build(dataItem);
-            }
-            else if (CellTemplate != null)
-            {
-                return CellTemplate.Build(dataItem);
-            }
-            if (Design.IsDesignMode)
-            {
-                return null;
-            }
-            else
-            {
-                throw DataGridError.DataGridTemplateColumn.MissingTemplateForType(typeof(DataGridTemplateColumn));
-            }
-        }
-
-        protected override object PrepareCellForEdit(Control editingElement, RoutedEventArgs editingEventArgs)
-        {
-            return null;
-        }
-
-        protected internal override void RefreshCellContent(Control element, string propertyName)
-        {
-            var cell = element.Parent as DataGridCell;
-            if(propertyName == nameof(CellTemplate) && cell is not null)
-            {
-                cell.Content = GenerateElement(cell, cell.DataContext);
-            }
-
-            base.RefreshCellContent(element, propertyName);
-        }
-        
-        public override bool IsReadOnly
-        {
-            get
-            {
-                if (CellEditingTemplate is null)
-                {
-                    return true;
-                }
-
-                return base.IsReadOnly;
-            }
-            set
-            {
-                base.IsReadOnly = value;
-            }
-        }
-    }
-}

+ 0 - 287
src/Avalonia.Controls.DataGrid/DataGridTextColumn.cs

@@ -1,287 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Input;
-using Avalonia.Interactivity;
-using Avalonia.Media;
-using System;
-using System.ComponentModel;
-using Avalonia.Layout;
-using Avalonia.Markup.Xaml.MarkupExtensions;
-using Avalonia.Controls.Documents;
-using Avalonia.Styling;
-
-namespace Avalonia.Controls
-{
-    /// <summary>
-    /// Represents a <see cref="T:Avalonia.Controls.DataGrid" /> column that hosts textual content in its cells.
-    /// </summary>
-    public class DataGridTextColumn : DataGridBoundColumn
-    {
-        private readonly Lazy<ControlTheme> _cellTextBoxTheme;
-        private readonly Lazy<ControlTheme> _cellTextBlockTheme;
-        
-        /// <summary>
-        /// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridTextColumn" /> class.
-        /// </summary>
-        public DataGridTextColumn()
-        {
-            BindingTarget = TextBox.TextProperty;
-
-            _cellTextBoxTheme = new Lazy<ControlTheme>(() =>
-                OwningGrid.TryFindResource("DataGridCellTextBoxTheme", out var theme) ? (ControlTheme)theme : null);
-            _cellTextBlockTheme = new Lazy<ControlTheme>(() =>
-                OwningGrid.TryFindResource("DataGridCellTextBlockTheme", out var theme) ? (ControlTheme)theme : null);
-        }
-
-        /// <summary>
-        /// Identifies the FontFamily dependency property.
-        /// </summary>
-        public static readonly AttachedProperty<FontFamily> FontFamilyProperty =
-            TextElement.FontFamilyProperty.AddOwner<DataGridTextColumn>();
-
-        /// <summary>
-        /// Gets or sets the font name.
-        /// </summary>
-        public FontFamily FontFamily
-        {
-            get => GetValue(FontFamilyProperty);
-            set => SetValue(FontFamilyProperty, value);
-        }
-
-        /// <summary>
-        /// Identifies the FontSize dependency property.
-        /// </summary>
-        public static readonly AttachedProperty<double> FontSizeProperty =
-            TextElement.FontSizeProperty.AddOwner<DataGridTextColumn>();
-
-        /// <summary>
-        /// Gets or sets the font size.
-        /// </summary>
-        // Use DefaultValue here so undo in the Designer will set this to NaN
-        [DefaultValue(double.NaN)]
-        public double FontSize
-        {
-            get => GetValue(FontSizeProperty);
-            set => SetValue(FontSizeProperty, value);
-        }
-
-        /// <summary>
-        /// Identifies the FontStyle dependency property.
-        /// </summary>
-        public static readonly AttachedProperty<FontStyle> FontStyleProperty =
-            TextElement.FontStyleProperty.AddOwner<DataGridTextColumn>();
-
-        /// <summary>
-        /// Gets or sets the font style.
-        /// </summary>
-        public FontStyle FontStyle
-        {
-            get => GetValue(FontStyleProperty);
-            set => SetValue(FontStyleProperty, value);
-        }
-
-        /// <summary>
-        /// Identifies the FontWeight dependency property.
-        /// </summary>
-        public static readonly AttachedProperty<FontWeight> FontWeightProperty =
-            TextElement.FontWeightProperty.AddOwner<DataGridTextColumn>();
-
-        /// <summary>
-        /// Gets or sets the font weight or thickness.
-        /// </summary>
-        public FontWeight FontWeight
-        {
-            get => GetValue(FontWeightProperty);
-            set => SetValue(FontWeightProperty, value);
-        }
-
-        /// <summary>
-        /// Identifies the FontStretch dependency property.
-        /// </summary>
-        public static readonly AttachedProperty<FontStretch> FontStretchProperty =
-            TextElement.FontStretchProperty.AddOwner<DataGridTextColumn>();
-
-        /// <summary>
-        /// Gets or sets the font weight or thickness.
-        /// </summary>
-        public FontStretch FontStretch
-        {
-            get => GetValue(FontStretchProperty);
-            set => SetValue(FontStretchProperty, value);
-        }
-
-        /// <summary>
-        /// Identifies the Foreground dependency property.
-        /// </summary>
-        public static readonly AttachedProperty<IBrush> ForegroundProperty =
-            TextElement.ForegroundProperty.AddOwner<DataGridTextColumn>();
-
-        /// <summary>
-        /// Gets or sets a brush that describes the foreground of the column cells.
-        /// </summary>
-        public IBrush Foreground
-        {
-            get => GetValue(ForegroundProperty);
-            set => SetValue(ForegroundProperty, value);
-        }
-
-        protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
-        {
-            base.OnPropertyChanged(change);
-
-            if (change.Property == FontFamilyProperty
-                || change.Property == FontSizeProperty
-                || change.Property == FontStyleProperty
-                || change.Property == FontWeightProperty
-                || change.Property == ForegroundProperty)
-            {
-                NotifyPropertyChanged(change.Property.Name);
-            }
-        }
-
-        /// <summary>
-        /// Causes the column cell being edited to revert to the specified value.
-        /// </summary>
-        /// <param name="editingElement">The element that the column displays for a cell in editing mode.</param>
-        /// <param name="uneditedValue">The previous, unedited value in the cell being edited.</param>
-        protected override void CancelCellEdit(Control editingElement, object uneditedValue)
-        {
-            if (editingElement is TextBox textBox)
-            {
-                string uneditedString = uneditedValue as string;
-                textBox.Text = uneditedString ?? string.Empty;
-            }
-        }
-
-        /// <summary>
-        /// Gets a <see cref="T:Avalonia.Controls.TextBox" /> control that is bound to the column's <see cref="P:Avalonia.Controls.DataGridBoundColumn.Binding" /> property value.
-        /// </summary>
-        /// <param name="cell">The cell that will contain the generated element.</param>
-        /// <param name="dataItem">The data item represented by the row that contains the intended cell.</param>
-        /// <returns>A new <see cref="T:Avalonia.Controls.TextBox" /> control that is bound to the column's <see cref="P:Avalonia.Controls.DataGridBoundColumn.Binding" /> property value.</returns>
-        protected override Control GenerateEditingElementDirect(DataGridCell cell, object dataItem)
-        {
-            var textBox = new TextBox
-            {                
-                Name = "CellTextBox"
-            };
-            if (_cellTextBoxTheme.Value is { } theme)
-            {
-                textBox.Theme = theme;
-            }
-
-            SyncProperties(textBox);
-
-            return textBox;
-        }
-        
-        /// <summary>
-        /// Gets a read-only <see cref="T:Avalonia.Controls.TextBlock" /> element that is bound to the column's <see cref="P:Avalonia.Controls.DataGridBoundColumn.Binding" /> property value.
-        /// </summary>
-        /// <param name="cell">The cell that will contain the generated element.</param>
-        /// <param name="dataItem">The data item represented by the row that contains the intended cell.</param>
-        /// <returns>A new, read-only <see cref="T:Avalonia.Controls.TextBlock" /> element that is bound to the column's <see cref="P:Avalonia.Controls.DataGridBoundColumn.Binding" /> property value.</returns>
-        protected override Control GenerateElement(DataGridCell cell, object dataItem)
-        {
-            var textBlockElement = new TextBlock
-            {
-                Name = "CellTextBlock"
-            };
-            if (_cellTextBlockTheme.Value is { } theme)
-            {
-                textBlockElement.Theme = theme;
-            }
-
-            SyncProperties(textBlockElement);
-
-            if (Binding != null)
-            {
-                textBlockElement.Bind(TextBlock.TextProperty, Binding);
-            }
-            return textBlockElement;
-        }
-
-        /// <summary>
-        /// Called when the cell in the column enters editing mode.
-        /// </summary>
-        /// <param name="editingElement">The element that the column displays for a cell in editing mode.</param>
-        /// <param name="editingEventArgs">Information about the user gesture that is causing a cell to enter editing mode.</param>
-        /// <returns>The unedited value. </returns>
-        protected override object PrepareCellForEdit(Control editingElement, RoutedEventArgs editingEventArgs)
-        {
-            if (editingElement is TextBox textBox)
-            {
-                string uneditedText = textBox.Text ?? String.Empty;
-                int len = uneditedText.Length;
-                if (editingEventArgs is KeyEventArgs keyEventArgs && keyEventArgs.Key == Key.F2)
-                {
-                    // Put caret at the end of the text
-                    textBox.SelectionStart = len;
-                    textBox.SelectionEnd = len;
-                }
-                else
-                {
-                    // Select all text
-                    textBox.SelectionStart = 0;
-                    textBox.SelectionEnd = len;
-                    textBox.CaretIndex = len;
-                }
-
-                return uneditedText;
-            }
-            return string.Empty;
-        }
-
-        /// <summary>
-        /// Called by the DataGrid control when this column asks for its elements to be
-        /// updated, because a property changed.
-        /// </summary>
-        protected internal override void RefreshCellContent(Control element, string propertyName)
-        {
-            if (element == null)
-            {
-                throw new ArgumentNullException(nameof(element));
-            }
-
-            if (element is AvaloniaObject content)
-            {
-                if (propertyName == nameof(FontFamily))
-                {
-                    DataGridHelper.SyncColumnProperty(this, content, FontFamilyProperty);
-                }
-                else if (propertyName == nameof(FontSize))
-                {
-                    DataGridHelper.SyncColumnProperty(this, content, FontSizeProperty);
-                }
-                else if (propertyName == nameof(FontStyle))
-                {
-                    DataGridHelper.SyncColumnProperty(this, content, FontStyleProperty);
-                }
-                else if (propertyName == nameof(FontWeight))
-                {
-                    DataGridHelper.SyncColumnProperty(this, content, FontWeightProperty);
-                }
-                else if (propertyName == nameof(Foreground))
-                {
-                    DataGridHelper.SyncColumnProperty(this, content, ForegroundProperty);
-                }
-            }
-            else
-            {
-                throw DataGridError.DataGrid.ValueIsNotAnInstanceOf("element", typeof(AvaloniaObject));
-            }
-        }
-
-        private void SyncProperties(AvaloniaObject content)
-        {
-            DataGridHelper.SyncColumnProperty(this, content, FontFamilyProperty);
-            DataGridHelper.SyncColumnProperty(this, content, FontSizeProperty);
-            DataGridHelper.SyncColumnProperty(this, content, FontStyleProperty);
-            DataGridHelper.SyncColumnProperty(this, content, FontWeightProperty);
-            DataGridHelper.SyncColumnProperty(this, content, ForegroundProperty);
-        }
-    }
-}

+ 0 - 44
src/Avalonia.Controls.DataGrid/DataGridValueConverter.cs

@@ -1,44 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Controls.Utils;
-using Avalonia.Data.Converters;
-using Avalonia.Utilities;
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Text;
-
-namespace Avalonia.Controls
-{
-    internal class DataGridValueConverter : IValueConverter
-    {
-        public static DataGridValueConverter Instance = new DataGridValueConverter();
-
-        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            return DefaultValueConverter.Instance.Convert(value, targetType, parameter, culture);
-        }
-
-
-        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
-        {
-            if (targetType != null && targetType.IsNullableType())
-            {
-                var strValue = value as string;
-
-                // This suppresses a warning saying that we should use String.IsNullOrEmpty instead of a string
-                // comparison, but in this case we want to explicitly check for Empty and not Null.
-#pragma warning disable CA1820
-                if (strValue == string.Empty)
-#pragma warning restore CA1820
-                {
-                    return null;
-                }
-            }
-            return DefaultValueConverter.Instance.ConvertBack(value, targetType, parameter, culture);
-        }
-    }
-}

+ 0 - 569
src/Avalonia.Controls.DataGrid/EventArgs.cs

@@ -1,569 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Input;
-using Avalonia.Interactivity;
-using System;
-using System.ComponentModel;
-using System.Diagnostics;
-
-namespace Avalonia.Controls
-{
-    /// <summary>
-    /// Provides data for the <see cref="E:Avalonia.Controls.DataGrid.AutoGeneratingColumn" /> event. 
-    /// </summary>
-    public class DataGridAutoGeneratingColumnEventArgs : CancelEventArgs
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridAutoGeneratingColumnEventArgs" /> class.
-        /// </summary>
-        /// <param name="propertyName">
-        /// The name of the property bound to the generated column.
-        /// </param>
-        /// <param name="propertyType">
-        /// The <see cref="T:System.Type" /> of the property bound to the generated column.
-        /// </param>
-        /// <param name="column">
-        /// The generated column.
-        /// </param>
-        public DataGridAutoGeneratingColumnEventArgs(string propertyName, Type propertyType, DataGridColumn column)
-        {
-            Column = column;
-            PropertyName = propertyName;
-            PropertyType = propertyType;
-        }
-
-        /// <summary>
-        /// Gets the generated column.
-        /// </summary>
-        public DataGridColumn Column
-        {
-            get;
-            set;
-        }
-
-        /// <summary>
-        /// Gets the name of the property bound to the generated column.
-        /// </summary>
-        public string PropertyName
-        {
-            get;
-            private set;
-        }
-
-        /// <summary>
-        /// Gets the <see cref="T:System.Type" /> of the property bound to the generated column.
-        /// </summary>
-        public Type PropertyType
-        {
-            get;
-            private set;
-        }
-    }
-
-    /// <summary>
-    /// Provides data for the <see cref="E:Avalonia.Controls.DataGrid.BeginningEdit" /> event.
-    /// </summary>
-    public class DataGridBeginningEditEventArgs : CancelEventArgs
-    {
-        /// <summary>
-        /// Initializes a new instance of the 
-        /// <see cref="T:Avalonia.Controls.DataGridBeginningEditEventArgs" /> class.
-        /// </summary>
-        /// <param name="column">
-        /// The column that contains the cell to be edited.
-        /// </param>
-        /// <param name="row">
-        /// The row that contains the cell to be edited.
-        /// </param>
-        /// <param name="editingEventArgs">
-        /// Information about the user gesture that caused the cell to enter edit mode.
-        /// </param>
-        public DataGridBeginningEditEventArgs(DataGridColumn column,
-                                              DataGridRow row,
-                                              RoutedEventArgs editingEventArgs)
-        {
-            this.Column = column;
-            this.Row = row;
-            this.EditingEventArgs = editingEventArgs;
-        }
-
-        /// <summary>
-        /// Gets the column that contains the cell to be edited.
-        /// </summary>
-        public DataGridColumn Column
-        {
-            get;
-            private set;
-        }
-
-        /// <summary>
-        /// Gets information about the user gesture that caused the cell to enter edit mode.
-        /// </summary>
-        public RoutedEventArgs EditingEventArgs
-        {
-            get;
-            private set;
-        }
-
-        /// <summary>
-        /// Gets the row that contains the cell to be edited.
-        /// </summary>
-        public DataGridRow Row
-        {
-            get;
-            private set;
-        }
-
-    }
-
-    /// <summary>
-    /// Provides information just after a cell has exited editing mode.
-    /// </summary>
-    public class DataGridCellEditEndedEventArgs : EventArgs
-    {
-        /// <summary>
-        /// Instantiates a new instance of this class.
-        /// </summary>
-        /// <param name="column">The column of the cell that has just exited edit mode.</param>
-        /// <param name="row">The row container of the cell container that has just exited edit mode.</param>
-        /// <param name="editAction">The editing action that has been taken.</param>
-        public DataGridCellEditEndedEventArgs(DataGridColumn column, DataGridRow row, DataGridEditAction editAction)
-        {
-            Column = column;
-            Row = row;
-            EditAction = editAction;
-        }
-
-        /// <summary>
-        /// The column of the cell that has just exited edit mode.
-        /// </summary>
-        public DataGridColumn Column
-        {
-            get;
-            private set;
-        }
-
-        /// <summary>
-        /// The edit action taken when leaving edit mode.
-        /// </summary>
-        public DataGridEditAction EditAction
-        {
-            get;
-            private set;
-        }
-
-        /// <summary>
-        /// The row container of the cell container that has just exited edit mode.
-        /// </summary>
-        public DataGridRow Row
-        {
-            get;
-            private set;
-        }
-
-    }
-
-    /// <summary>
-    /// Provides information after the cell has been pressed.
-    /// </summary>
-    public class DataGridCellPointerPressedEventArgs : EventArgs
-    {
-        /// <summary>
-        /// Instantiates a new instance of this class.
-        /// </summary>
-        /// <param name="cell">The cell that has been pressed.</param>
-        /// <param name="row">The row container of the cell that has been pressed.</param>
-        /// <param name="column">The column of the cell that has been pressed.</param>
-        /// <param name="e">The pointer action that has been taken.</param>
-        public DataGridCellPointerPressedEventArgs(DataGridCell cell, 
-                                                   DataGridRow row,
-                                                   DataGridColumn column,
-                                                   PointerPressedEventArgs e)
-        {
-            Cell = cell;
-            Row = row;
-            Column = column;
-            PointerPressedEventArgs = e;
-        }
-
-        /// <summary>
-        /// The cell that has been pressed.
-        /// </summary> 
-        public DataGridCell Cell { get; }
-
-        /// <summary>
-        /// The row container of the cell that has been pressed.
-        /// </summary> 
-        public DataGridRow Row { get; }
-
-        /// <summary>
-        /// The column of the cell that has been pressed.
-        /// </summary> 
-        public DataGridColumn Column { get; }
-
-        /// <summary>
-        /// The pointer action that has been taken.
-        /// </summary> 
-        public PointerPressedEventArgs PointerPressedEventArgs { get; }
-    }
-
-    /// <summary>
-    /// Provides information just before a cell exits editing mode.
-    /// </summary>
-    public class DataGridCellEditEndingEventArgs : CancelEventArgs
-    {
-        /// <summary>
-        /// Instantiates a new instance of this class.
-        /// </summary>
-        /// <param name="column">The column of the cell that is about to exit edit mode.</param>
-        /// <param name="row">The row container of the cell container that is about to exit edit mode.</param>
-        /// <param name="editingElement">The editing element within the cell.</param>
-        /// <param name="editAction">The editing action that will be taken.</param>
-        public DataGridCellEditEndingEventArgs(DataGridColumn column,
-                                               DataGridRow row,
-                                               Control editingElement,
-                                               DataGridEditAction editAction)
-        {
-            Column = column;
-            Row = row;
-            EditingElement = editingElement;
-            EditAction = editAction;
-        }
-
-        /// <summary>
-        /// The column of the cell that is about to exit edit mode.
-        /// </summary>
-        public DataGridColumn Column
-        {
-            get;
-            private set;
-        }
-
-        /// <summary>
-        /// The edit action to take when leaving edit mode.
-        /// </summary>
-        public DataGridEditAction EditAction
-        {
-            get;
-            private set;
-        }
-
-        /// <summary>
-        /// The editing element within the cell. 
-        /// </summary>
-        public Control EditingElement
-        {
-            get;
-            private set;
-        }
-
-        /// <summary>
-        /// The row container of the cell container that is about to exit edit mode.
-        /// </summary>
-        public DataGridRow Row
-        {
-            get;
-            private set;
-        }
-
-    }
-
-    internal class DataGridCellEventArgs : EventArgs
-    {
-        internal DataGridCellEventArgs(DataGridCell dataGridCell)
-        {
-            Debug.Assert(dataGridCell != null);
-            this.Cell = dataGridCell;
-        }
-
-        internal DataGridCell Cell
-        {
-            get;
-            private set;
-        }
-    }
-
-    /// <summary>
-    /// Provides data for <see cref="T:Avalonia.Controls.DataGrid" /> column-related events.
-    /// </summary>
-    public class DataGridColumnEventArgs : HandledEventArgs
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridColumnEventArgs" /> class.
-        /// </summary>
-        /// <param name="column">The column that the event occurs for.</param>
-        public DataGridColumnEventArgs(DataGridColumn column)
-        {
-            Column = column ?? throw new ArgumentNullException(nameof(column));
-        }
-
-        /// <summary>
-        /// Gets the column that the event occurs for.
-        /// </summary>
-        public DataGridColumn Column
-        {
-            get;
-            private set;
-        }
-    }
-
-    /// <summary>
-    /// Provides data for the <see cref="E:Avalonia.Controls.DataGrid.ColumnReordering" /> event.
-    /// </summary>
-    public class DataGridColumnReorderingEventArgs : CancelEventArgs
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridColumnReorderingEventArgs" /> class.
-        /// </summary>
-        /// <param name="dataGridColumn"></param>
-        public DataGridColumnReorderingEventArgs(DataGridColumn dataGridColumn)
-        {
-            this.Column = dataGridColumn;
-        }
-
-        /// <summary>
-        /// The column being moved.
-        /// </summary>
-        public DataGridColumn Column
-        {
-            get;
-            private set;
-        }
-
-        /// <summary>
-        /// The popup indicator displayed while dragging.  If null and Handled = true, then do not display a tooltip.
-        /// </summary>
-        public Control DragIndicator
-        {
-            get;
-            set;
-        }
-
-        /// <summary>
-        /// UIElement to display at the insertion position.  If null and Handled = true, then do not display an insertion indicator.
-        /// </summary>
-        public Control DropLocationIndicator
-        {
-            get;
-            set;
-        }
-    }
-
-    /// <summary>
-    /// Provides data for <see cref="T:Avalonia.Controls.DataGrid" /> row-related events.
-    /// </summary>
-    public class DataGridRowEventArgs : EventArgs
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridRowEventArgs" /> class.
-        /// </summary>
-        /// <param name="dataGridRow">The row that the event occurs for.</param>
-        public DataGridRowEventArgs(DataGridRow dataGridRow)
-        {
-            this.Row = dataGridRow;
-        }
-
-        /// <summary>
-        /// Gets the row that the event occurs for.
-        /// </summary>
-        public DataGridRow Row
-        {
-            get;
-            private set;
-        }
-    }
-
-    /// <summary>
-    /// Provides information just before a row exits editing mode.
-    /// </summary>
-    public class DataGridRowEditEndingEventArgs : CancelEventArgs
-    {
-        /// <summary>
-        /// Instantiates a new instance of this class.
-        /// </summary>
-        /// <param name="row">The row container of the cell container that is about to exit edit mode.</param>
-        /// <param name="editAction">The editing action that will be taken.</param>
-        public DataGridRowEditEndingEventArgs(DataGridRow row, DataGridEditAction editAction)
-        {
-            this.Row = row;
-            this.EditAction = editAction;
-        }
-
-        /// <summary>
-        /// The editing action that will be taken.
-        /// </summary>
-        public DataGridEditAction EditAction
-        {
-            get;
-            private set;
-        }
-
-        /// <summary>
-        /// The row container of the cell container that is about to exit edit mode.
-        /// </summary>
-        public DataGridRow Row
-        {
-            get;
-            private set;
-        }
-    }
-
-    /// <summary>
-    /// Provides information just after a row has exited edit mode.
-    /// </summary>
-    public class DataGridRowEditEndedEventArgs : EventArgs
-    {
-        /// <summary>
-        /// Instantiates a new instance of this class.
-        /// </summary>
-        /// <param name="row">The row container of the cell container that has just exited edit mode.</param>
-        /// <param name="editAction">The editing action that has been taken.</param>
-        public DataGridRowEditEndedEventArgs(DataGridRow row, DataGridEditAction editAction)
-        {
-            this.Row = row;
-            this.EditAction = editAction;
-        }
-
-        /// <summary>
-        /// The editing action that has been taken.
-        /// </summary>
-        public DataGridEditAction EditAction
-        {
-            get;
-            private set;
-        }
-
-        /// <summary>
-        /// The row container of the cell container that has just exited edit mode.
-        /// </summary>
-        public DataGridRow Row
-        {
-            get;
-            private set;
-        }
-    }
-
-    /// <summary>
-    /// Provides data for the <see cref="E:Avalonia.Controls.DataGrid.PreparingCellForEdit" /> event.
-    /// </summary>
-    public class DataGridPreparingCellForEditEventArgs : EventArgs
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridPreparingCellForEditEventArgs" /> class.
-        /// </summary>
-        /// <param name="column">The column that contains the cell to be edited.</param>
-        /// <param name="row">The row that contains the cell to be edited.</param>
-        /// <param name="editingEventArgs">Information about the user gesture that caused the cell to enter edit mode.</param>
-        /// <param name="editingElement">The element that the column displays for a cell in editing mode.</param>
-        public DataGridPreparingCellForEditEventArgs(DataGridColumn column,
-                                                     DataGridRow row,
-                                                     RoutedEventArgs editingEventArgs,
-                                                     Control editingElement)
-        {
-            Column = column;
-            Row = row;
-            EditingEventArgs = editingEventArgs;
-            EditingElement = editingElement;
-        }
-
-        /// <summary>
-        /// Gets the column that contains the cell to be edited.
-        /// </summary>
-        public DataGridColumn Column
-        {
-            get;
-            private set;
-        }
-
-        /// <summary>
-        /// Gets the element that the column displays for a cell in editing mode.
-        /// </summary>
-        public Control EditingElement
-        {
-            get;
-            private set;
-        }
-
-        /// <summary>
-        /// Gets information about the user gesture that caused the cell to enter edit mode.
-        /// </summary>
-        public RoutedEventArgs EditingEventArgs
-        {
-            get;
-            private set;
-        }
-
-        /// <summary>
-        /// Gets the row that contains the cell to be edited.
-        /// </summary>
-        public DataGridRow Row
-        {
-            get;
-            private set;
-        }
-
-    }
-
-    /// <summary>
-    /// EventArgs used for the DataGrid's LoadingRowGroup and UnloadingRowGroup events
-    /// </summary>
-    public class DataGridRowGroupHeaderEventArgs : EventArgs
-    {
-        /// <summary>
-        /// Constructs a DataGridRowGroupHeaderEventArgs instance
-        /// </summary>
-        /// <param name="rowGroupHeader"></param>
-        public DataGridRowGroupHeaderEventArgs(DataGridRowGroupHeader rowGroupHeader)
-        {
-            RowGroupHeader = rowGroupHeader;
-        }
-
-        /// <summary>
-        /// DataGridRowGroupHeader associated with this instance
-        /// </summary>
-        public DataGridRowGroupHeader RowGroupHeader
-        {
-            get;
-            private set;
-        }
-    }
-
-    /// <summary>
-    /// Provides data for the <see cref="E:Avalonia.Controls.DataGrid.LoadingRowDetails" />, <see cref="E:Avalonia.Controls.DataGrid.UnloadingRowDetails" />, 
-    /// and <see cref="E:Avalonia.Controls.DataGrid.RowDetailsVisibilityChanged" /> events.
-    /// </summary>
-    public class DataGridRowDetailsEventArgs : EventArgs
-    {
-        /// <summary>
-        /// Initializes a new instance of the <see cref="T:Avalonia.Controls.DataGridRowDetailsEventArgs" /> class. 
-        /// </summary>
-        /// <param name="row">The row that the event occurs for.</param>
-        /// <param name="detailsElement">The row details section as a framework element.</param>
-        public DataGridRowDetailsEventArgs(DataGridRow row, Control detailsElement)
-        {
-            Row = row;
-            DetailsElement = detailsElement;
-        }
-
-        /// <summary>
-        /// Gets the row details section as a framework element.
-        /// </summary>
-        public Control DetailsElement
-        {
-            get;
-            private set;
-        }
-
-        /// <summary>
-        /// Gets the row that the event occurs for.
-        /// </summary>
-        public DataGridRow Row
-        {
-            get;
-            private set;
-        }
-    }
-}

+ 0 - 25
src/Avalonia.Controls.DataGrid/Extensions.cs

@@ -1,25 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Avalonia.Controls
-{
-    internal static class Extensions
-    {
-        internal static Point Translate(this Visual fromElement, Visual toElement, Point fromPoint)
-        {
-            if (fromElement == toElement)
-            {
-                return fromPoint;
-            }
-            else
-            {
-                var transform = fromElement.TransformToVisual(toElement);
-                if (transform.HasValue)
-                    return fromPoint.Transform(transform.Value);
-                else
-                    return fromPoint;
-            }
-        }
-    }
-}

+ 0 - 850
src/Avalonia.Controls.DataGrid/IndexToValueTable.cs

@@ -1,850 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System.Collections.Generic;
-using System.Diagnostics;
-using System;
-using System.Text;
-
-namespace Avalonia.Controls
-{
-    internal class IndexToValueTable<T> : IEnumerable<Range<T>>
-    {
-        private List<Range<T>> _list;
-
-        public IndexToValueTable()
-        {
-            _list = new List<Range<T>>();
-        }
-
-        /// <summary>
-        /// Total number of indices represented in the table
-        /// </summary>
-        public int IndexCount
-        {
-            get
-            {
-                int indexCount = 0;
-                foreach (Range<T> range in _list)
-                {
-                    indexCount += range.Count;
-                }
-                return indexCount;
-            }
-        }
-
-        /// <summary>
-        /// Returns true if the table is empty
-        /// </summary>
-        public bool IsEmpty
-        {
-            get
-            {
-                return _list.Count == 0;
-            }
-        }
-
-        /// <summary>
-        /// Returns the number of index ranges in the table
-        /// </summary>
-        public int RangeCount
-        {
-            get
-            {
-                return _list.Count;
-            }
-        }
-
-        /// <summary>
-        /// Add a value with an associated index to the table
-        /// </summary>
-        /// <param name="index">Index where the value is to be added or updated</param>
-        /// <param name="value">Value to add</param>
-        public void AddValue(int index, T value)
-        {
-            AddValues(index, 1, value);
-        }
-
-        /// <summary>
-        /// Add multiples values with an associated start index to the table 
-        /// </summary>
-        /// <param name="startIndex">index where first value is added</param>
-        /// <param name="count">Total number of values to add (must be greater than 0)</param>
-        /// <param name="value">Value to add</param>
-        public void AddValues(int startIndex, int count, T value)
-        {
-            Debug.Assert(count > 0);
-            AddValuesPrivate(startIndex, count, value, null);
-        }
-
-        /// <summary>
-        /// Clears the index table
-        /// </summary>
-        public void Clear()
-        {
-            _list.Clear();
-        }
-
-        /// <summary>
-        /// Returns true if the given index is contained in the table
-        /// </summary>
-        /// <param name="index">index to search for</param>
-        /// <returns>True if the index is contained in the table</returns>
-        public bool Contains(int index)
-        {
-            return IsCorrectRangeIndex(this.FindRangeIndex(index), index);
-        }
-
-        /// <summary>
-        /// Returns true if the entire given index range is contained in the table
-        /// </summary>
-        /// <param name="startIndex">beginning of the range</param>
-        /// <param name="endIndex">end of the range</param>
-        /// <returns>True if the entire index range is present in the table</returns>
-        public bool ContainsAll(int startIndex, int endIndex)
-        {
-            int start = -1;
-            int end = -1;
-
-            foreach (Range<T> range in _list)
-            {
-                if (start == -1 && range.UpperBound >= startIndex)
-                {
-                    if (startIndex < range.LowerBound)
-                    {
-                        return false;
-                    }
-                    start = startIndex;
-                    end = range.UpperBound;
-                    if (end >= endIndex)
-                    {
-                        return true;
-                    }
-                }
-                else if (start != -1)
-                {
-                    if (range.LowerBound > end + 1)
-                    {
-                        return false;
-                    }
-                    end = range.UpperBound;
-                    if (end >= endIndex)
-                    {
-                        return true;
-                    }
-                }
-            }
-            return false;
-        }
-
-        /// <summary>
-        /// Returns true if the given index is contained in the table with the given value
-        /// </summary>
-        /// <param name="index">index to search for</param>
-        /// <param name="value">value expected</param>
-        /// <returns>true if the given index is contained in the table with the given value</returns>
-        public bool ContainsIndexAndValue(int index, T value)
-        {
-            int lowerRangeIndex = this.FindRangeIndex(index);
-            return ((IsCorrectRangeIndex(lowerRangeIndex, index)) && (_list[lowerRangeIndex].ContainsValue(value)));
-        }
-
-        /// <summary>
-        /// Returns a copy of this IndexToValueTable
-        /// </summary>
-        /// <returns>copy of this IndexToValueTable</returns>
-        public IndexToValueTable<T> Copy()
-        {
-            IndexToValueTable<T> copy = new IndexToValueTable<T>();
-            foreach (Range<T> range in this._list)
-            {
-                copy._list.Add(range.Copy());
-            }
-            return copy;
-        }
-
-        public int GetNextGap(int index)
-        {
-            int targetIndex = index + 1;
-            int rangeIndex = FindRangeIndex(targetIndex);
-            if (IsCorrectRangeIndex(rangeIndex, targetIndex))
-            {
-                while (rangeIndex < _list.Count - 1 && _list[rangeIndex].UpperBound == _list[rangeIndex + 1].LowerBound - 1)
-                {
-                    rangeIndex++;
-                }
-                return _list[rangeIndex].UpperBound + 1;
-            }
-            else
-            {
-                return targetIndex;
-            }
-        }
-
-        public int GetNextIndex(int index)
-        {
-            int targetIndex = index + 1;
-            int rangeIndex = FindRangeIndex(targetIndex);
-            if (IsCorrectRangeIndex(rangeIndex, targetIndex))
-            {
-                return targetIndex;
-            }
-            else
-            {
-                rangeIndex++;
-                return rangeIndex < _list.Count ? _list[rangeIndex].LowerBound : -1;
-            }
-        }
-
-        public int GetPreviousGap(int index)
-        {
-            int targetIndex = index - 1;
-            int rangeIndex = FindRangeIndex(targetIndex);
-            if (IsCorrectRangeIndex(rangeIndex, targetIndex))
-            {
-                while (rangeIndex > 0 && _list[rangeIndex].LowerBound == _list[rangeIndex - 1].UpperBound + 1)
-                {
-                    rangeIndex--;
-                }
-                return _list[rangeIndex].LowerBound - 1;
-            }
-            else
-            {
-                return targetIndex;
-            }
-        }
-
-        public int GetPreviousIndex(int index)
-        {
-            int targetIndex = index - 1;
-            int rangeIndex = FindRangeIndex(targetIndex);
-            if (IsCorrectRangeIndex(rangeIndex, targetIndex))
-            {
-                return targetIndex;
-            }
-            else
-            {
-                return rangeIndex >= 0 && rangeIndex < _list.Count ? _list[rangeIndex].UpperBound : -1;
-            }
-        }
-
-        /// <summary>
-        /// Returns the inclusive index count between lowerBound and upperBound of all indexes with the given value
-        /// </summary>
-        /// <param name="lowerBound">lowerBound criteria</param>
-        /// <param name="upperBound">upperBound criteria</param>
-        /// <param name="value">value to look for</param>
-        /// <returns>Number of indexes contained in the table between lowerBound and upperBound (inclusive)</returns>
-        public int GetIndexCount(int lowerBound, int upperBound, T value)
-        {
-            Debug.Assert(upperBound >= lowerBound);
-            if (_list.Count == 0)
-            {
-                return 0;
-            }
-            int count = 0;
-            int index = FindRangeIndex(lowerBound);
-            if (IsCorrectRangeIndex(index, lowerBound) && _list[index].ContainsValue(value))
-            {
-                count += _list[index].UpperBound - lowerBound + 1;
-            }
-            index++;
-            while (index < _list.Count && _list[index].UpperBound <= upperBound)
-            {
-                if (_list[index].ContainsValue(value))
-                {
-                    count += _list[index].Count;
-                }
-                index++;
-            }
-            if (index < _list.Count && IsCorrectRangeIndex(index, upperBound) && _list[index].ContainsValue(value))
-            {
-                count += upperBound - _list[index].LowerBound;
-            }
-            return count;
-        }
-
-        /// <summary>
-        /// Returns the inclusive index count between lowerBound and upperBound
-        /// </summary>
-        /// <param name="lowerBound">lowerBound criteria</param>
-        /// <param name="upperBound">upperBound criteria</param>
-        /// <returns>Number of indexes contained in the table between lowerBound and upperBound (inclusive)</returns>
-        public int GetIndexCount(int lowerBound, int upperBound)
-        {
-            if (upperBound < lowerBound || _list.Count == 0)
-            {
-                return 0;
-            }
-            int count = 0;
-            int index = this.FindRangeIndex(lowerBound);
-            if (IsCorrectRangeIndex(index, lowerBound))
-            {
-                count += _list[index].UpperBound - lowerBound + 1;
-            }
-            index++;
-            while (index < _list.Count && _list[index].UpperBound <= upperBound)
-            {
-                count += _list[index].Count;
-                index++;
-            }
-            if (index < _list.Count && IsCorrectRangeIndex(index, upperBound))
-            {
-                count += upperBound - _list[index].LowerBound;
-            }
-            return count;
-        }
-
-        /// <summary>
-        /// Returns the number indexes in this table after a given startingIndex but before
-        /// reaching a gap of indexes of a given size
-        /// </summary>
-        /// <param name="startingIndex">Index to start at</param>
-        /// <param name="gapSize">Size of index gap</param>
-        /// <returns></returns>
-        public int GetIndexCountBeforeGap(int startingIndex, int gapSize)
-        {
-            if (_list.Count == 0)
-            {
-                return 0;
-            }
-
-            int count = 0;
-            int currentIndex = startingIndex;
-            int rangeIndex = 0;
-            int gap = 0;
-            while (gap <= gapSize && rangeIndex < _list.Count)
-            {
-                gap += _list[rangeIndex].LowerBound - currentIndex;
-                if (gap <= gapSize)
-                {
-                    count += _list[rangeIndex].UpperBound - _list[rangeIndex].LowerBound + 1;
-                    currentIndex = _list[rangeIndex].UpperBound + 1;
-                    rangeIndex++;
-                }
-            }
-            return count;
-        }
-
-        /// <summary>
-        /// Returns an enumerator that goes through the indexes present in the table
-        /// </summary>
-        /// <returns>an enumerator that enumerates the indexes present in the table</returns>
-        public IEnumerable<int> GetIndexes()
-        {
-            Debug.Assert(_list != null);
-
-            foreach (Range<T> range in _list)
-            {
-                for (int i = range.LowerBound; i <= range.UpperBound; i++)
-                {
-                    yield return i;
-                }
-            }
-        }
-
-        /// <summary>
-        /// Returns all the indexes on or after a starting index
-        /// </summary>
-        /// <param name="startIndex">start index</param>
-        /// <returns></returns>
-        public IEnumerable<int> GetIndexes(int startIndex)
-        {
-            Debug.Assert(_list != null);
-
-            int rangeIndex = FindRangeIndex(startIndex);
-            if (rangeIndex == -1)
-            {
-                rangeIndex++;
-            }
-
-            while (rangeIndex < _list.Count)
-            {
-                for (int i = _list[rangeIndex].LowerBound; i <= _list[rangeIndex].UpperBound; i++)
-                {
-                    if (i >= startIndex)
-                    {
-                        yield return i;
-                    }
-                }
-                rangeIndex++;
-            }
-        }
-
-        /// <summary>
-        /// Return the index of the Nth element in the table.
-        /// </summary>
-        /// <param name="n">n</param>
-        public int GetNthIndex(int n)
-        {
-            Debug.Assert(n >= 0 && n < this.IndexCount);
-            int cumulatedEntries = 0;
-            foreach (Range<T> range in _list)
-            {
-                if (cumulatedEntries + range.Count > n)
-                {
-                    return range.LowerBound + n - cumulatedEntries;
-                }
-                else
-                {
-                    cumulatedEntries += range.Count;
-                }
-            }
-            return -1;
-        }
-
-        /// <summary>
-        /// Returns the value at a given index or the default value if the index is not in the table
-        /// </summary>
-        /// <param name="index">index to search for</param>
-        /// <returns>the value at the given index or the default value if index is not in the table</returns>
-        public T GetValueAt(int index)
-        {
-            return GetValueAt(index, out bool found);
-        }
-
-        /// <summary>
-        /// Returns the value at a given index or the default value if the index is not in the table
-        /// </summary>
-        /// <param name="index">index to search for</param>
-        /// <param name="found">set to true by the method if the index was found; otherwise, false</param>
-        /// <returns>the value at the given index or the default value if index is not in the table</returns>
-        public T GetValueAt(int index, out bool found)
-        {
-            int rangeIndex = this.FindRangeIndex(index);
-            if (this.IsCorrectRangeIndex(rangeIndex, index))
-            {
-                found = true;
-                return _list[rangeIndex].Value;
-            }
-            else
-            {
-                found = false;
-                return default;
-            }
-        }
-
-        /// <summary>
-        /// Returns an index's index within this table
-        /// </summary>
-        /// <param name="index"></param>
-        /// <returns></returns>
-        public int IndexOf(int index)
-        {
-            int cumulatedIndexes = 0;
-            foreach (Range<T> range in _list)
-            {
-                if (range.UpperBound >= index)
-                {
-                    cumulatedIndexes += index - range.LowerBound;
-                    break;
-                }
-                else
-                {
-                    cumulatedIndexes += range.Count;
-                }
-            }
-            return cumulatedIndexes;
-        }
-
-        /// <summary>
-        /// Inserts an index at the given location.  This does not alter values in the table
-        /// </summary>
-        /// <param name="index">index location to insert an index</param>
-        public void InsertIndex(int index)
-        {
-            InsertIndexes(index, 1);
-        }
-
-        /// <summary>
-        /// Inserts an index into the table with the given value 
-        /// </summary>
-        /// <param name="index">index to insert</param>
-        /// <param name="value">value for the index</param>
-        public void InsertIndexAndValue(int index, T value)
-        {
-            InsertIndexesAndValues(index, 1, value);
-        }
-
-        /// <summary>
-        /// Inserts multiple indexes into the table.  This does not alter Values in the table
-        /// </summary>
-        /// <param name="startIndex">first index to insert</param>
-        /// <param name="count">total number of indexes to insert</param>
-        public void InsertIndexes(int startIndex, int count)
-        {
-            Debug.Assert(count > 0);
-            InsertIndexesPrivate(startIndex, count, this.FindRangeIndex(startIndex));
-        }
-
-        /// <summary>
-        /// Inserts multiple indexes into the table with the given value 
-        /// </summary>
-        /// <param name="startIndex">Index to insert first value</param>
-        /// <param name="count">Total number of values to insert (must be greater than 0)</param>
-        /// <param name="value">Value to insert</param>
-        public void InsertIndexesAndValues(int startIndex, int count, T value)
-        {
-            Debug.Assert(count > 0);
-            int lowerRangeIndex = this.FindRangeIndex(startIndex);
-            InsertIndexesPrivate(startIndex, count, lowerRangeIndex);
-            if ((lowerRangeIndex >= 0) && (_list[lowerRangeIndex].LowerBound > startIndex))
-            {
-                // Because of the insert, the original range no longer contains the startIndex
-                lowerRangeIndex--;
-            }
-            AddValuesPrivate(startIndex, count, value, lowerRangeIndex);
-        }
-
-        /// <summary>
-        /// Removes an index from the table.  This does not alter Values in the table
-        /// </summary>
-        /// <param name="index">index to remove</param>
-        public void RemoveIndex(int index)
-        {
-            RemoveIndexes(index, 1);
-        }
-
-        /// <summary>
-        /// Removes a value and its index from the table
-        /// </summary>
-        /// <param name="index">index to remove</param>
-        public void RemoveIndexAndValue(int index)
-        {
-            RemoveIndexesAndValues(index, 1);
-        }
-
-        /// <summary>
-        /// Removes multiple indexes from the table.  This does not alter Values in the table
-        /// </summary>
-        /// <param name="startIndex">first index to remove</param>
-        /// <param name="count">total number of indexes to remove</param>
-        public void RemoveIndexes(int startIndex, int count)
-        {
-            int lowerRangeIndex = this.FindRangeIndex(startIndex);
-            if (lowerRangeIndex < 0)
-            {
-                lowerRangeIndex = 0;
-            }
-            int i = lowerRangeIndex;
-            while (i < _list.Count)
-            {
-                Range<T> range = _list[i];
-                if (range.UpperBound >= startIndex)
-                {
-                    if (range.LowerBound >= startIndex + count)
-                    {
-                        // Both bounds will remain after the removal
-                        range.LowerBound -= count;
-                        range.UpperBound -= count;
-                    }
-                    else
-                    {
-                        int currentIndex = i;
-                        if (range.LowerBound <= startIndex)
-                        {
-                            // Range gets split up
-                            if (range.UpperBound >= startIndex + count)
-                            {
-                                i++;
-                                _list.Insert(i, new Range<T>(startIndex, range.UpperBound - count, range.Value));
-                            }
-                            range.UpperBound = startIndex - 1;
-                        }
-                        else
-                        {
-                            range.LowerBound = startIndex;
-                            range.UpperBound -= count;
-                        }
-                        if (RemoveRangeIfInvalid(range, currentIndex))
-                        {
-                            i--;
-                        }
-                    }
-                }
-                i++;
-            }
-            if (!this.Merge(lowerRangeIndex))
-            {
-                this.Merge(lowerRangeIndex + 1);
-            }
-        }
-
-        /// <summary>
-        /// Removes multiple values and their indexes from the table
-        /// </summary>
-        /// <param name="startIndex">first index to remove</param>
-        /// <param name="count">total number of indexes to remove</param>
-        public void RemoveIndexesAndValues(int startIndex, int count)
-        {
-            RemoveValues(startIndex, count);
-            RemoveIndexes(startIndex, count);
-        }
-
-        /// <summary>
-        /// Removes a value from the table at the given index.  This does not alter other indexes in the table.
-        /// </summary>
-        /// <param name="index">index where value should be removed</param>
-        public void RemoveValue(int index)
-        {
-            RemoveValues(index, 1);
-        }
-
-        /// <summary>
-        /// Removes multiple values from the table.  This does not alter other indexes in the table.
-        /// </summary>
-        /// <param name="startIndex">first index where values should be removed </param>
-        /// <param name="count">total number of values to remove</param>
-        public void RemoveValues(int startIndex, int count)
-        {
-            Debug.Assert(count > 0);
-
-            int lowerRangeIndex = this.FindRangeIndex(startIndex);
-            if (lowerRangeIndex < 0)
-            {
-                lowerRangeIndex = 0;
-            }
-            while ((lowerRangeIndex < _list.Count) && (_list[lowerRangeIndex].UpperBound < startIndex))
-            {
-                lowerRangeIndex++;
-            }
-            if (lowerRangeIndex >= _list.Count || _list[lowerRangeIndex].LowerBound > startIndex + count - 1)
-            {
-                // If all the values are above our below our values, we have nothing to remove
-                return;
-            }
-            if (_list[lowerRangeIndex].LowerBound < startIndex)
-            {
-                // Need to split this up
-                _list.Insert(lowerRangeIndex, new Range<T>(_list[lowerRangeIndex].LowerBound, startIndex - 1, _list[lowerRangeIndex].Value));
-                lowerRangeIndex++;
-            }
-            _list[lowerRangeIndex].LowerBound = startIndex + count;
-            if (!RemoveRangeIfInvalid(_list[lowerRangeIndex], lowerRangeIndex))
-            {
-                lowerRangeIndex++;
-            }
-            while ((lowerRangeIndex < _list.Count) && (_list[lowerRangeIndex].UpperBound < startIndex + count))
-            {
-                _list.RemoveAt(lowerRangeIndex);
-            }
-            if ((lowerRangeIndex < _list.Count) && (_list[lowerRangeIndex].UpperBound >= startIndex + count) &&
-                (_list[lowerRangeIndex].LowerBound < startIndex + count))
-            {
-                // Chop off the start of the remaining Range if it contains values that we're removing
-                _list[lowerRangeIndex].LowerBound = startIndex + count;
-                RemoveRangeIfInvalid(_list[lowerRangeIndex], lowerRangeIndex);
-            }
-        }
-
-        private void AddValuesPrivate(int startIndex, int count, T value, int? startRangeIndex)
-        {
-            Debug.Assert(count > 0);
-
-            int endIndex = startIndex + count - 1;
-            Range<T> newRange = new Range<T>(startIndex, endIndex, value);
-            if (_list.Count == 0)
-            {
-                _list.Add(newRange);
-            }
-            else
-            {
-                int lowerRangeIndex = startRangeIndex ?? FindRangeIndex(startIndex);
-                Range<T> lowerRange = (lowerRangeIndex < 0) ? null : _list[lowerRangeIndex];
-                if (lowerRange == null)
-                {
-                    if (lowerRangeIndex < 0)
-                    {
-                        lowerRangeIndex = 0;
-                    }
-                    _list.Insert(lowerRangeIndex, newRange);
-                }
-                else
-                {
-                    if (!lowerRange.Value.Equals(value) && (lowerRange.UpperBound >= startIndex))
-                    {
-                        // Split up the range
-                        if (lowerRange.UpperBound > endIndex)
-                        {
-                            _list.Insert(lowerRangeIndex + 1, new Range<T>(endIndex + 1, lowerRange.UpperBound, lowerRange.Value));
-                        }
-                        lowerRange.UpperBound = startIndex - 1;
-                        if (!RemoveRangeIfInvalid(lowerRange, lowerRangeIndex))
-                        {
-                            lowerRangeIndex++;
-                        }
-                        _list.Insert(lowerRangeIndex, newRange);
-                    }
-                    else
-                    {
-                        _list.Insert(lowerRangeIndex + 1, newRange);
-                        if (!Merge(lowerRangeIndex))
-                        {
-                            lowerRangeIndex++;
-                        }
-                    }
-                }
-
-                // At this point the newRange has been inserted in the correct place, now we need to remove
-                // any subsequent ranges that no longer make sense and possibly update the one at newRange.UpperBound
-                int upperRangeIndex = lowerRangeIndex + 1;
-                while ((upperRangeIndex < _list.Count) && (_list[upperRangeIndex].UpperBound < endIndex))
-                {
-                    _list.RemoveAt(upperRangeIndex);
-                }
-                if (upperRangeIndex < _list.Count)
-                {
-                    Range<T> upperRange = _list[upperRangeIndex];
-                    if (upperRange.LowerBound <= endIndex)
-                    {
-                        // Update the range
-                        upperRange.LowerBound = endIndex + 1;
-                        RemoveRangeIfInvalid(upperRange, upperRangeIndex);
-                    }
-                    Merge(lowerRangeIndex);
-                }
-            }
-        }
-
-        // Returns the index of the range that contains the input or the range before if the input is not found
-        private int FindRangeIndex(int index)
-        {
-            if (_list.Count == 0)
-            {
-                return -1;
-            }
-
-            // Do a binary search for the index
-            int front = 0;
-            int end = _list.Count - 1;
-            Range<T> range = null;
-            while (end > front)
-            {
-                int median = (front + end) / 2;
-                range = _list[median];
-                if (range.UpperBound < index)
-                {
-                    front = median + 1;
-                }
-                else if (range.LowerBound > index)
-                {
-                    end = median - 1;
-                }
-                else
-                {
-                    // we found it
-                    return median;
-                }
-            }
-
-            if (front == end)
-            {
-                range = _list[front];
-                if (range.ContainsIndex(index) || (range.UpperBound < index))
-                {
-                    // we found it or the index isn't there and we're one range before
-                    return front;
-                }
-                else
-                {
-                    // not found and we're one range after
-                    return front - 1;
-                }
-            }
-            else
-            {
-                // end is one index before front in this case so it's the range before
-                return end;
-            }
-        }
-
-        private bool Merge(int lowerRangeIndex)
-        {
-            int upperRangeIndex = lowerRangeIndex + 1;
-            if ((lowerRangeIndex >= 0) && (upperRangeIndex < _list.Count))
-            {
-                Range<T> lowerRange = _list[lowerRangeIndex];
-                Range<T> upperRange = _list[upperRangeIndex];
-                if ((lowerRange.UpperBound + 1 >= upperRange.LowerBound) && (lowerRange.Value.Equals(upperRange.Value)))
-                {
-                    lowerRange.UpperBound = Math.Max(lowerRange.UpperBound, upperRange.UpperBound);
-                    _list.RemoveAt(upperRangeIndex);
-                    return true;
-                }
-            }
-            return false;
-        }
-
-        private void InsertIndexesPrivate(int startIndex, int count, int lowerRangeIndex)
-        {
-            Debug.Assert(count > 0);
-
-            // Same as AddRange after we fix the indicies affected by the insertion
-            int startRangeIndex = (lowerRangeIndex >= 0) ? lowerRangeIndex : 0;
-            for (int i = startRangeIndex; i < _list.Count; i++)
-            {
-                Range<T> range = _list[i];
-                if (range.LowerBound >= startIndex)
-                {
-                    range.LowerBound += count;
-                }
-                else
-                {
-                    if (range.UpperBound >= startIndex)
-                    {
-                        // Split up this range
-                        i++;
-                        _list.Insert(i, new Range<T>(startIndex, range.UpperBound + count, range.Value));
-                        range.UpperBound = startIndex - 1;
-                        continue;
-                    }
-                }
-
-                if (range.UpperBound >= startIndex)
-                {
-                    range.UpperBound += count;
-                }
-            }
-        }
-
-        private bool IsCorrectRangeIndex(int rangeIndex, int index)
-        {
-            return (-1 != rangeIndex) && (_list[rangeIndex].ContainsIndex(index));
-        }
-
-        private bool RemoveRangeIfInvalid(Range<T> range, int rangeIndex)
-        {
-            if (range.UpperBound < range.LowerBound)
-            {
-                _list.RemoveAt(rangeIndex);
-                return true;
-            }
-            return false;
-        }
-
-        public IEnumerator<Range<T>> GetEnumerator()
-        {
-            return _list.GetEnumerator();
-        }
-
-        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
-        {
-            return _list.GetEnumerator();
-        }
-
-#if DEBUG
-
-        public void PrintIndexes()
-        {
-            Debug.WriteLine(this.IndexCount + " indexes");
-            foreach (Range<T> range in _list)
-            {
-                Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "{0} - {1}", range.LowerBound, range.UpperBound));
-            }
-        }
-
-#endif
-    }
-}

+ 0 - 358
src/Avalonia.Controls.DataGrid/Primitives/DataGridCellsPresenter.cs

@@ -1,358 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.LogicalTree;
-using Avalonia.Media;
-using Avalonia.Utilities;
-using System;
-using System.Collections.Generic;
-using System.Collections.Specialized;
-using System.Diagnostics;
-
-namespace Avalonia.Controls.Primitives
-{
-    /// <summary>
-    /// Used within the template of a <see cref="T:Avalonia.Controls.DataGrid" />
-    /// to specify the location in the control's visual tree where the cells are to be added. 
-    /// </summary>
-    public sealed class DataGridCellsPresenter : Panel, IChildIndexProvider
-    {
-        private double _fillerLeftEdge;
-        private EventHandler<ChildIndexChangedEventArgs> _childIndexChanged;
-
-        // The desired height needs to be cached due to column virtualization; otherwise, the cells
-        // would grow and shrink as the DataGrid scrolls horizontally
-        private double DesiredHeight
-        {
-            get;
-            set;
-        }
-
-        private DataGrid OwningGrid
-        {
-            get
-            {
-                return OwningRow?.OwningGrid;
-            }
-        }
-
-        internal DataGridRow OwningRow
-        {
-            get;
-            set;
-        }
-
-        event EventHandler<ChildIndexChangedEventArgs> IChildIndexProvider.ChildIndexChanged
-        {
-            add => _childIndexChanged += value;
-            remove => _childIndexChanged -= value;
-        }
-
-        int IChildIndexProvider.GetChildIndex(ILogical child)
-        {
-            return child is DataGridCell cell
-                ? cell.OwningColumn?.DisplayIndex ?? -1
-                : throw new InvalidOperationException("Invalid cell type");
-        }
-
-        bool IChildIndexProvider.TryGetTotalCount(out int count)
-        {
-            count = Children.Count - 1; // Adjust for filler column
-            return true;
-        }
-
-        /// <summary>
-        /// Arranges the content of the <see cref="T:Avalonia.Controls.Primitives.DataGridCellsPresenter" />.
-        /// </summary>
-        /// <returns>
-        /// The actual size used by the <see cref="T:Avalonia.Controls.Primitives.DataGridCellsPresenter" />.
-        /// </returns>
-        /// <param name="finalSize">
-        /// The final area within the parent that this element should use to arrange itself and its children.
-        /// </param>
-        protected override Size ArrangeOverride(Size finalSize)
-        {
-            if (OwningGrid == null)
-            {
-                return base.ArrangeOverride(finalSize);
-            }
-
-            if (OwningGrid.AutoSizingColumns)
-            {
-                // When we initially load an auto-column, we have to wait for all the rows to be measured
-                // before we know its final desired size.  We need to trigger a new round of measures now
-                // that the final sizes have been calculated.
-                OwningGrid.AutoSizingColumns = false;
-                return base.ArrangeOverride(finalSize);
-            }
-
-            double frozenLeftEdge = 0;
-            double scrollingLeftEdge = -OwningGrid.HorizontalOffset;
-
-            double cellLeftEdge;
-            foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetVisibleColumns())
-            {
-                DataGridCell cell = OwningRow.Cells[column.Index];
-                Debug.Assert(cell.OwningColumn == column);
-                Debug.Assert(column.IsVisible);
-
-                if (column.IsFrozen)
-                {
-                    cellLeftEdge = frozenLeftEdge;
-                    // This can happen before or after clipping because frozen cells aren't clipped
-                    frozenLeftEdge += column.ActualWidth;
-                }
-                else
-                {
-                    cellLeftEdge = scrollingLeftEdge;
-                }
-                if (cell.IsVisible)
-                {
-                    cell.Arrange(new Rect(cellLeftEdge, 0, column.LayoutRoundedWidth, finalSize.Height));
-                    EnsureCellClip(cell, column.ActualWidth, finalSize.Height, frozenLeftEdge, scrollingLeftEdge);
-                }
-                scrollingLeftEdge += column.ActualWidth;
-                column.IsInitialDesiredWidthDetermined = true;
-            }
-
-            _fillerLeftEdge = scrollingLeftEdge;
-
-            OwningRow.FillerCell.Arrange(new Rect(_fillerLeftEdge, 0, OwningGrid.ColumnsInternal.FillerColumn.FillerWidth, finalSize.Height));
-
-            return finalSize;
-        }
-
-        private static void EnsureCellClip(DataGridCell cell, double width, double height, double frozenLeftEdge, double cellLeftEdge)
-        {
-            // Clip the cell only if it's scrolled under frozen columns.  Unfortunately, we need to clip in this case
-            // because cells could be transparent
-            if (!cell.OwningColumn.IsFrozen && frozenLeftEdge > cellLeftEdge)
-            {
-                RectangleGeometry rg = new RectangleGeometry();
-                double xClip = Math.Round(Math.Min(width, frozenLeftEdge - cellLeftEdge));
-                rg.Rect = new Rect(xClip, 0, Math.Max(0, width - xClip), height);
-                cell.Clip = rg;
-            }
-            else
-            {
-                cell.Clip = null;
-            }
-        }
-
-        protected override void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
-        {
-            base.ChildrenChanged(sender, e);
-
-            InvalidateChildIndex();
-        }
-
-        private static void EnsureCellDisplay(DataGridCell cell, bool displayColumn)
-        {
-            if (cell.IsCurrent)
-            {
-                if (displayColumn)
-                {
-                    cell.IsVisible = true;
-                    cell.Clip = null;
-                }
-                else
-                {
-                    // Clip
-                    RectangleGeometry rg = new RectangleGeometry();
-                    rg.Rect = default;
-                    cell.Clip = rg;
-                }
-            }
-            else
-            {
-                cell.IsVisible = displayColumn;
-            }
-        }
-
-        internal void EnsureFillerVisibility()
-        {
-            DataGridFillerColumn fillerColumn = OwningGrid.ColumnsInternal.FillerColumn;
-            bool newVisibility = fillerColumn.IsActive;
-            if (OwningRow.FillerCell.IsVisible != newVisibility)
-            {
-                OwningRow.FillerCell.IsVisible = newVisibility;
-                if (newVisibility)
-                {
-                    OwningRow.FillerCell.Arrange(new Rect(_fillerLeftEdge, 0, fillerColumn.FillerWidth, Bounds.Height));
-                }
-            }
-
-            // This must be done after the Filler visibility is determined.  This also must be done
-            // regardless of whether or not the filler visibility actually changed values because
-            // we could scroll in a cell that didn't have EnsureGridLine called yet
-            DataGridColumn lastVisibleColumn = OwningGrid.ColumnsInternal.LastVisibleColumn;
-            if (lastVisibleColumn != null)
-            {
-                DataGridCell cell = OwningRow.Cells[lastVisibleColumn.Index];
-                cell.EnsureGridLine(lastVisibleColumn);
-            }
-        }
-
-        /// <summary>
-        /// Measures the children of a <see cref="T:Avalonia.Controls.Primitives.DataGridCellsPresenter" /> to 
-        /// prepare for arranging them during the <see cref="M:System.Windows.FrameworkElement.ArrangeOverride(System.Windows.Size)" /> pass.
-        /// </summary>
-        /// <param name="availableSize">
-        /// The available size that this element can give to child elements. Indicates an upper limit that child elements should not exceed.
-        /// </param>
-        /// <returns>
-        /// The size that the <see cref="T:Avalonia.Controls.Primitives.DataGridCellsPresenter" /> determines it needs during layout, based on its calculations of child object allocated sizes.
-        /// </returns>
-        protected override Size MeasureOverride(Size availableSize)
-        {
-            if (OwningGrid == null)
-            {
-                return base.MeasureOverride(availableSize);
-            }
-
-            bool autoSizeHeight;
-            double measureHeight;
-            if (double.IsNaN(OwningGrid.RowHeight))
-            {
-                // No explicit height values were set so we can autosize
-                autoSizeHeight = true;
-                // We need to invalidate desired height in order to grow or shrink as needed
-                InvalidateDesiredHeight();
-                measureHeight = double.PositiveInfinity;
-            }
-            else
-            {
-                DesiredHeight = OwningGrid.RowHeight;
-                measureHeight = DesiredHeight;
-                autoSizeHeight = false;
-            }
-
-            double frozenLeftEdge = 0;
-            double totalDisplayWidth = 0;
-            double scrollingLeftEdge = -OwningGrid.HorizontalOffset;
-            OwningGrid.ColumnsInternal.EnsureVisibleEdgedColumnsWidth();
-            DataGridColumn lastVisibleColumn = OwningGrid.ColumnsInternal.LastVisibleColumn;
-            foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetVisibleColumns())
-            {
-                DataGridCell cell = OwningRow.Cells[column.Index];
-                // Measure the entire first row to make the horizontal scrollbar more accurate
-                bool shouldDisplayCell = ShouldDisplayCell(column, frozenLeftEdge, scrollingLeftEdge) || OwningRow.Index == 0;
-                EnsureCellDisplay(cell, shouldDisplayCell);
-                if (shouldDisplayCell)
-                {
-                    DataGridLength columnWidth = column.Width;
-                    bool autoGrowWidth = columnWidth.IsSizeToCells || columnWidth.IsAuto;
-                    if (column != lastVisibleColumn)
-                    {
-                        cell.EnsureGridLine(lastVisibleColumn);
-                    }
-
-                    // If we're not using star sizing or the current column can't be resized,
-                    // then just set the display width according to the column's desired width
-                    if (!OwningGrid.UsesStarSizing || (!column.ActualCanUserResize && !column.Width.IsStar))
-                    {
-                        // In the edge-case where we're given infinite width and we have star columns, the 
-                        // star columns grow to their predefined limit of 10,000 (or their MaxWidth)
-                        double newDisplayWidth = column.Width.IsStar ?
-                            Math.Min(column.ActualMaxWidth, DataGrid.DATAGRID_maximumStarColumnWidth) :
-                            Math.Max(column.ActualMinWidth, Math.Min(column.ActualMaxWidth, column.Width.DesiredValue));
-                        column.SetWidthDisplayValue(newDisplayWidth);
-                    }
-
-                    // If we're auto-growing the column based on the cell content, we want to measure it at its maximum value
-                    if (autoGrowWidth)
-                    {
-                        cell.Measure(new Size(column.ActualMaxWidth, measureHeight));
-                        OwningGrid.AutoSizeColumn(column, cell.DesiredSize.Width);
-                        column.ComputeLayoutRoundedWidth(totalDisplayWidth);
-                    }
-                    else if (!OwningGrid.UsesStarSizing)
-                    {
-                        column.ComputeLayoutRoundedWidth(scrollingLeftEdge);
-                        cell.Measure(new Size(column.LayoutRoundedWidth, measureHeight));
-                    }
-
-                    // We need to track the largest height in order to auto-size
-                    if (autoSizeHeight)
-                    {
-                        DesiredHeight = Math.Max(DesiredHeight, cell.DesiredSize.Height);
-                    }
-                }
-
-                if (column.IsFrozen)
-                {
-                    frozenLeftEdge += column.ActualWidth;
-                }
-                scrollingLeftEdge += column.ActualWidth;
-                totalDisplayWidth += column.ActualWidth;
-            }
-
-            // If we're using star sizing (and we're not waiting for an auto-column to finish growing)
-            // then we will resize all the columns to fit the available space.
-            if (OwningGrid.UsesStarSizing && !OwningGrid.AutoSizingColumns)
-            {
-                double adjustment = OwningGrid.CellsWidth - totalDisplayWidth;
-                totalDisplayWidth += adjustment - OwningGrid.AdjustColumnWidths(0, adjustment, false);
-
-                // Since we didn't know the final widths of the columns until we resized,
-                // we waited until now to measure each cell
-                double leftEdge = 0;
-                if (autoSizeHeight)
-                    DesiredHeight = 0;
-
-                foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetVisibleColumns())
-                {
-                    DataGridCell cell = OwningRow.Cells[column.Index];
-                    column.ComputeLayoutRoundedWidth(leftEdge);
-                    cell.Measure(new Size(column.LayoutRoundedWidth, measureHeight));
-                    if (autoSizeHeight)
-                    {
-                        DesiredHeight = Math.Max(DesiredHeight, cell.DesiredSize.Height);
-                    }
-                    leftEdge += column.ActualWidth;
-                }
-            }
-
-            // Measure FillerCell, we're doing it unconditionally here because we don't know if we'll need the filler
-            // column and we don't want to cause another Measure if we do
-            OwningRow.FillerCell.Measure(new Size(double.PositiveInfinity, DesiredHeight));
-
-            OwningGrid.ColumnsInternal.EnsureVisibleEdgedColumnsWidth();
-            return new Size(OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth, DesiredHeight);
-        }
-
-        internal void Recycle()
-        {
-            // Clear out the cached desired height so it is not reused for other rows
-            DesiredHeight = 0;
-        }
-
-        internal void InvalidateDesiredHeight()
-        {
-            DesiredHeight = 0;
-        }
-
-        internal void InvalidateChildIndex()
-        {
-            _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.ChildIndexesReset);
-        }
-
-        private bool ShouldDisplayCell(DataGridColumn column, double frozenLeftEdge, double scrollingLeftEdge)
-        {
-            if (!column.IsVisible)
-            {
-                return false;
-            }
-
-            scrollingLeftEdge += OwningGrid.HorizontalAdjustment;
-            double leftEdge = column.IsFrozen ? frozenLeftEdge : scrollingLeftEdge;
-            double rightEdge = leftEdge + column.ActualWidth;
-            return 
-                MathUtilities.GreaterThan(rightEdge, 0) &&
-                MathUtilities.LessThanOrClose(leftEdge, OwningGrid.CellsWidth) &&
-                MathUtilities.GreaterThan(rightEdge, frozenLeftEdge); // scrolling column covered up by frozen column(s)
-        }
-    }
-}

+ 0 - 436
src/Avalonia.Controls.DataGrid/Primitives/DataGridColumnHeadersPresenter.cs

@@ -1,436 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.LogicalTree;
-using Avalonia.Media;
-using System;
-using System.Collections.Specialized;
-using System.Diagnostics;
-using Avalonia.Automation.Peers;
-using Avalonia.Controls.Automation.Peers;
-
-namespace Avalonia.Controls.Primitives
-{
-    /// <summary>
-    /// Used within the template of a <see cref="T:Avalonia.Controls.DataGrid" /> to specify the 
-    /// location in the control's visual tree where the column headers are to be added.
-    /// </summary>
-    public sealed class DataGridColumnHeadersPresenter : Panel, IChildIndexProvider
-    {
-        private Control _dragIndicator;
-        private Control _dropLocationIndicator;
-        private EventHandler<ChildIndexChangedEventArgs> _childIndexChanged;
-
-        /// <summary>
-        /// Tracks which column is currently being dragged.
-        /// </summary>
-        internal DataGridColumn DragColumn
-        {
-            get;
-            set;
-        }
-
-        /// <summary>
-        /// The current drag indicator control.  This value is null if no column is being dragged.
-        /// </summary>
-        internal Control DragIndicator
-        {
-            get
-            {
-                return _dragIndicator;
-            }
-            set
-            {
-                if (value != _dragIndicator)
-                {
-                    if (Children.Contains(_dragIndicator))
-                    {
-                        Children.Remove(_dragIndicator);
-                    }
-                    _dragIndicator = value;
-                    if (_dragIndicator != null)
-                    {
-                        Children.Add(_dragIndicator);
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// The distance, in pixels, that the DragIndicator should be positioned away from the corresponding DragColumn.
-        /// </summary>
-        internal Double DragIndicatorOffset
-        {
-            get;
-            set;
-        }
-
-        /// <summary>
-        /// The drop location indicator control.  This value is null if no column is being dragged.
-        /// </summary>
-        internal Control DropLocationIndicator
-        {
-            get
-            {
-                return _dropLocationIndicator;
-            }
-            set
-            {
-                if (value != _dropLocationIndicator)
-                {
-                    if (Children.Contains(_dropLocationIndicator))
-                    {
-                        Children.Remove(_dropLocationIndicator);
-                    }
-                    _dropLocationIndicator = value;
-                    if (_dropLocationIndicator != null)
-                    {
-                        Children.Add(_dropLocationIndicator);
-                    }
-                }
-            }
-        }
-
-        /// <summary>
-        /// The distance, in pixels, that the drop location indicator should be positioned away from the left edge
-        /// of the ColumnsHeaderPresenter.
-        /// </summary>
-        internal double DropLocationIndicatorOffset
-        {
-            get;
-            set;
-        }
-
-        internal DataGrid OwningGrid
-        {
-            get;
-            set;
-        }
-
-        event EventHandler<ChildIndexChangedEventArgs> IChildIndexProvider.ChildIndexChanged
-        {
-            add => _childIndexChanged += value;
-            remove => _childIndexChanged -= value;
-        }
-
-        int IChildIndexProvider.GetChildIndex(ILogical child)
-        {
-            return child is DataGridColumnHeader header
-                ? header.OwningColumn?.DisplayIndex ?? -1
-                : throw new InvalidOperationException("Invalid cell type");
-        }
-
-        bool IChildIndexProvider.TryGetTotalCount(out int count)
-        {
-            count = Children.Count - 1; // Adjust for filler column
-            return true;
-        }
-
-        protected override AutomationPeer OnCreateAutomationPeer()
-        {
-            return new DataGridColumnHeadersPresenterAutomationPeer(this);
-        }
-
-        /// <summary>
-        /// Arranges the content of the <see cref="T:Avalonia.Controls.Primitives.DataGridColumnHeadersPresenter" />.
-        /// </summary>
-        /// <returns>
-        /// The actual size used by the <see cref="T:Avalonia.Controls.Primitives.DataGridColumnHeadersPresenter" />.
-        /// </returns>
-        /// <param name="finalSize">
-        /// The final area within the parent that this element should use to arrange itself and its children.
-        /// </param>
-        protected override Size ArrangeOverride(Size finalSize)
-        {
-            if (OwningGrid == null)
-            {
-                return base.ArrangeOverride(finalSize);
-            }
-
-            if (OwningGrid.AutoSizingColumns)
-            {
-                // When we initially load an auto-column, we have to wait for all the rows to be measured
-                // before we know its final desired size.  We need to trigger a new round of measures now
-                // that the final sizes have been calculated.
-                OwningGrid.AutoSizingColumns = false;
-                return base.ArrangeOverride(finalSize);
-            }
-
-            double dragIndicatorLeftEdge = 0;
-            double frozenLeftEdge = 0;
-            double scrollingLeftEdge = -OwningGrid.HorizontalOffset;
-            foreach (DataGridColumn dataGridColumn in OwningGrid.ColumnsInternal.GetVisibleColumns())
-            {
-                DataGridColumnHeader columnHeader = dataGridColumn.HeaderCell;
-                Debug.Assert(columnHeader.OwningColumn == dataGridColumn);
-
-                if (dataGridColumn.IsFrozen)
-                {
-                    columnHeader.Arrange(new Rect(frozenLeftEdge, 0, dataGridColumn.LayoutRoundedWidth, finalSize.Height));
-                    columnHeader.Clip = null; // The layout system could have clipped this because it's not aware of our render transform
-                    if (DragColumn == dataGridColumn && DragIndicator != null)
-                    {
-                        dragIndicatorLeftEdge = frozenLeftEdge + DragIndicatorOffset;
-                    }
-                    frozenLeftEdge += dataGridColumn.ActualWidth;
-                }
-                else
-                {
-                    columnHeader.Arrange(new Rect(scrollingLeftEdge, 0, dataGridColumn.LayoutRoundedWidth, finalSize.Height));
-                    EnsureColumnHeaderClip(columnHeader, dataGridColumn.ActualWidth, finalSize.Height, frozenLeftEdge, scrollingLeftEdge);
-                    if (DragColumn == dataGridColumn && DragIndicator != null)
-                    {
-                        dragIndicatorLeftEdge = scrollingLeftEdge + DragIndicatorOffset;
-                    }
-                }
-                scrollingLeftEdge += dataGridColumn.ActualWidth;
-            }
-            if (DragColumn != null)
-            {
-                if (DragIndicator != null)
-                {
-                    EnsureColumnReorderingClip(DragIndicator, finalSize.Height, frozenLeftEdge, dragIndicatorLeftEdge);
-
-                    var height = DragIndicator.Bounds.Height;
-                    if (height <= 0)
-                        height = DragIndicator.DesiredSize.Height;
-
-                    DragIndicator.Arrange(new Rect(dragIndicatorLeftEdge, 0, DragIndicator.Bounds.Width, height));
-                }
-                if (DropLocationIndicator != null)
-                {
-                    if (DropLocationIndicator is Control element)
-                    {
-                        EnsureColumnReorderingClip(element, finalSize.Height, frozenLeftEdge, DropLocationIndicatorOffset);
-                    }
-
-                    DropLocationIndicator.Arrange(new Rect(DropLocationIndicatorOffset, 0, DropLocationIndicator.Bounds.Width, DropLocationIndicator.Bounds.Height));
-                }
-            }
-
-            // Arrange filler
-            OwningGrid.OnFillerColumnWidthNeeded(finalSize.Width);
-            DataGridFillerColumn fillerColumn = OwningGrid.ColumnsInternal.FillerColumn;
-            if (fillerColumn.FillerWidth > 0)
-            {
-                fillerColumn.HeaderCell.IsVisible = true;
-                fillerColumn.HeaderCell.Arrange(new Rect(scrollingLeftEdge, 0, fillerColumn.FillerWidth, finalSize.Height));
-            }
-            else
-            {
-                fillerColumn.HeaderCell.IsVisible = false;
-            }
-
-            // This needs to be updated after the filler column is configured
-            DataGridColumn lastVisibleColumn = OwningGrid.ColumnsInternal.LastVisibleColumn;
-            if (lastVisibleColumn != null)
-            {
-                lastVisibleColumn.HeaderCell.UpdateSeparatorVisibility(lastVisibleColumn);
-            }
-            return finalSize;
-        }
-
-        private static void EnsureColumnHeaderClip(DataGridColumnHeader columnHeader, double width, double height, double frozenLeftEdge, double columnHeaderLeftEdge)
-        {
-            // Clip the cell only if it's scrolled under frozen columns.  Unfortunately, we need to clip in this case
-            // because cells could be transparent
-            if (frozenLeftEdge > columnHeaderLeftEdge)
-            {
-                RectangleGeometry rg = new RectangleGeometry();
-                double xClip = Math.Min(width, frozenLeftEdge - columnHeaderLeftEdge);
-                rg.Rect = new Rect(xClip, 0, width - xClip, height);
-                columnHeader.Clip = rg;
-            }
-            else
-            {
-                columnHeader.Clip = null;
-            }
-        }
-
-        /// <summary>
-        /// Clips the DragIndicator and DropLocationIndicator controls according to current ColumnHeaderPresenter constraints.
-        /// </summary>
-        /// <param name="control">The DragIndicator or DropLocationIndicator</param>
-        /// <param name="height">The available height</param>
-        /// <param name="frozenColumnsWidth">The width of the frozen column region</param>
-        /// <param name="controlLeftEdge">The left edge of the control to clip</param>
-        private void EnsureColumnReorderingClip(Control control, double height, double frozenColumnsWidth, double controlLeftEdge)
-        {
-            double leftEdge = 0;
-            double rightEdge = OwningGrid.CellsWidth;
-            double width = control.Bounds.Width;
-            if (DragColumn.IsFrozen)
-            {
-                // If we're dragging a frozen column, we want to clip the corresponding DragIndicator control when it goes
-                // into the scrolling columns region, but not the DropLocationIndicator.
-                if (control == DragIndicator)
-                {
-                    rightEdge = Math.Min(rightEdge, frozenColumnsWidth);
-                }
-            }
-            else if (OwningGrid.FrozenColumnCount > 0)
-            {
-                // If we're dragging a scrolling column, we want to clip both the DragIndicator and the DropLocationIndicator
-                // controls when they go into the frozen column range.
-                leftEdge = frozenColumnsWidth;
-            }
-            RectangleGeometry rg = null;
-            if (leftEdge > controlLeftEdge)
-            {
-                rg = new RectangleGeometry();
-                double xClip = Math.Min(width, leftEdge - controlLeftEdge);
-                rg.Rect = new Rect(xClip, 0, width - xClip, height);
-            }
-            if (controlLeftEdge + width >= rightEdge)
-            {
-                if (rg == null)
-                {
-                    rg = new RectangleGeometry();
-                }
-                rg.Rect = new Rect(rg.Rect.X, rg.Rect.Y, Math.Max(0, rightEdge - controlLeftEdge - rg.Rect.X), height);
-            }
-            control.Clip = rg;
-        }
-
-        /// <summary>
-        /// Measures the children of a <see cref="T:Avalonia.Controls.Primitives.DataGridColumnHeadersPresenter" /> to 
-        /// prepare for arranging them during the <see cref="M:System.Windows.FrameworkElement.ArrangeOverride(System.Windows.Size)" /> pass.
-        /// </summary>
-        /// <param name="availableSize">
-        /// The available size that this element can give to child elements. Indicates an upper limit that child elements should not exceed.
-        /// </param>
-        /// <returns>
-        /// The size that the <see cref="T:Avalonia.Controls.Primitives.DataGridColumnHeadersPresenter" /> determines it needs during layout, based on its calculations of child object allocated sizes.
-        /// </returns>
-        protected override Size MeasureOverride(Size availableSize)
-        {
-            if (OwningGrid == null)
-            {
-                return base.MeasureOverride(availableSize);
-            }
-            if (!OwningGrid.AreColumnHeadersVisible)
-            {
-                return default;
-            }
-            double height = OwningGrid.ColumnHeaderHeight;
-            bool autoSizeHeight;
-            if (double.IsNaN(height))
-            {
-                // No explicit height values were set so we can autosize
-                height = 0;
-                autoSizeHeight = true;
-            }
-            else
-            {
-                autoSizeHeight = false;
-            }
-
-            double totalDisplayWidth = 0;
-            OwningGrid.ColumnsInternal.EnsureVisibleEdgedColumnsWidth();
-            DataGridColumn lastVisibleColumn = OwningGrid.ColumnsInternal.LastVisibleColumn;
-            foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetVisibleColumns())
-            {
-                // Measure each column header
-                bool autoGrowWidth = column.Width.IsAuto || column.Width.IsSizeToHeader;
-                DataGridColumnHeader columnHeader = column.HeaderCell;
-                if (column != lastVisibleColumn)
-                {
-                    columnHeader.UpdateSeparatorVisibility(lastVisibleColumn);
-                }
-
-                // If we're not using star sizing or the current column can't be resized,
-                // then just set the display width according to the column's desired width
-                if (!OwningGrid.UsesStarSizing || (!column.ActualCanUserResize && !column.Width.IsStar))
-                {
-                    // In the edge-case where we're given infinite width and we have star columns, the 
-                    // star columns grow to their predefined limit of 10,000 (or their MaxWidth)
-                    double newDisplayWidth = column.Width.IsStar ?
-                        Math.Min(column.ActualMaxWidth, DataGrid.DATAGRID_maximumStarColumnWidth) :
-                        Math.Max(column.ActualMinWidth, Math.Min(column.ActualMaxWidth, column.Width.DesiredValue));
-                    column.SetWidthDisplayValue(newDisplayWidth);
-                }
-
-                // If we're auto-growing the column based on the header content, we want to measure it at its maximum value
-                if (autoGrowWidth)
-                {
-                    columnHeader.Measure(new Size(column.ActualMaxWidth, double.PositiveInfinity));
-                    OwningGrid.AutoSizeColumn(column, columnHeader.DesiredSize.Width);
-                    column.ComputeLayoutRoundedWidth(totalDisplayWidth);
-                }
-                else if (!OwningGrid.UsesStarSizing)
-                {
-                    column.ComputeLayoutRoundedWidth(totalDisplayWidth);
-                    columnHeader.Measure(new Size(column.LayoutRoundedWidth, double.PositiveInfinity));
-                }
-
-                // We need to track the largest height in order to auto-size
-                if (autoSizeHeight)
-                {
-                    height = Math.Max(height, columnHeader.DesiredSize.Height);
-                }
-                totalDisplayWidth += column.ActualWidth;
-            }
-
-            // If we're using star sizing (and we're not waiting for an auto-column to finish growing)
-            // then we will resize all the columns to fit the available space.
-            if (OwningGrid.UsesStarSizing && !OwningGrid.AutoSizingColumns)
-            {
-                double adjustment = Double.IsPositiveInfinity(availableSize.Width) ? OwningGrid.CellsWidth : availableSize.Width - totalDisplayWidth;
-                totalDisplayWidth += adjustment - OwningGrid.AdjustColumnWidths(0, adjustment, false);
-
-                // Since we didn't know the final widths of the columns until we resized,
-                // we waited until now to measure each header
-                double leftEdge = 0;
-                foreach (DataGridColumn column in OwningGrid.ColumnsInternal.GetVisibleColumns())
-                {
-                    column.ComputeLayoutRoundedWidth(leftEdge);
-                    column.HeaderCell.Measure(new Size(column.LayoutRoundedWidth, double.PositiveInfinity));
-                    if (autoSizeHeight)
-                    {
-                        height = Math.Max(height, column.HeaderCell.DesiredSize.Height);
-                    }
-                    leftEdge += column.ActualWidth;
-                }
-            }
-
-            // Add the filler column if it's not represented.  We won't know whether we need it or not until Arrange
-            DataGridFillerColumn fillerColumn = OwningGrid.ColumnsInternal.FillerColumn;
-            if (!fillerColumn.IsRepresented)
-            {
-                Debug.Assert(!Children.Contains(fillerColumn.HeaderCell));
-                fillerColumn.HeaderCell.AreSeparatorsVisible = false;
-                Children.Insert(OwningGrid.ColumnsInternal.Count, fillerColumn.HeaderCell);
-                fillerColumn.IsRepresented = true;
-                // Optimize for the case where we don't need the filler cell 
-                fillerColumn.HeaderCell.IsVisible = false;
-            }
-            fillerColumn.HeaderCell.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
-
-            if (DragIndicator != null)
-            {
-                DragIndicator.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
-            }
-            if (DropLocationIndicator != null)
-            {
-                DropLocationIndicator.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
-            }
-
-            OwningGrid.ColumnsInternal.EnsureVisibleEdgedColumnsWidth();
-            return new Size(OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth, height);
-        }
-
-        protected override void ChildrenChanged(object sender, NotifyCollectionChangedEventArgs e)
-        {
-            base.ChildrenChanged(sender, e);
-
-            InvalidateChildIndex();
-        }
-
-        internal void InvalidateChildIndex()
-        {
-            _childIndexChanged?.Invoke(this, ChildIndexChangedEventArgs.ChildIndexesReset);
-        }
-    }
-}

+ 0 - 141
src/Avalonia.Controls.DataGrid/Primitives/DataGridDetailsPresenter.cs

@@ -1,141 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-using Avalonia.Automation.Peers;
-using Avalonia.Controls.Automation.Peers;
-using Avalonia.Media;
-
-namespace Avalonia.Controls.Primitives
-{
-    /// <summary>
-    /// Used within the template of a <see cref="T:Avalonia.Controls.DataGrid" /> to specify the location in the control's visual tree 
-    /// where the row details are to be added.
-    /// </summary>
-    public sealed class DataGridDetailsPresenter : Panel
-    {
-        public static readonly StyledProperty<double> ContentHeightProperty =
-            AvaloniaProperty.Register<DataGridDetailsPresenter, double>(nameof(ContentHeight));
-
-        /// <summary>
-        /// Gets or sets the height of the content.
-        /// </summary>
-        /// <returns>
-        /// The height of the content.
-        /// </returns>
-        public double ContentHeight
-        {
-            get { return GetValue(ContentHeightProperty); }
-            set { SetValue(ContentHeightProperty, value); }
-        }
-
-        internal DataGridRow OwningRow
-        {
-            get;
-            set;
-        }
-        private DataGrid OwningGrid => OwningRow?.OwningGrid;
-
-        public DataGridDetailsPresenter()
-        {
-            AffectsMeasure<DataGridDetailsPresenter>(ContentHeightProperty);
-        }
-
-        protected override AutomationPeer OnCreateAutomationPeer()
-        {
-            return new DataGridDetailsPresenterAutomationPeer(this);
-        }
-
-        /// <summary>
-        /// Arranges the content of the <see cref="T:Avalonia.Controls.Primitives.DataGridDetailsPresenter" />.
-        /// </summary>
-        /// <returns>
-        /// The actual size used by the <see cref="T:Avalonia.Controls.Primitives.DataGridDetailsPresenter" />.
-        /// </returns>
-        /// <param name="finalSize">
-        /// The final area within the parent that this element should use to arrange itself and its children.
-        /// </param>
-        protected override Size ArrangeOverride(Size finalSize)
-        {
-            if (OwningGrid == null)
-            {
-                return base.ArrangeOverride(finalSize);
-            }
-            double rowGroupSpacerWidth = OwningGrid.ColumnsInternal.RowGroupSpacerColumn.Width.Value;
-            double leftEdge = rowGroupSpacerWidth;
-            double xClip = OwningGrid.AreRowGroupHeadersFrozen ? rowGroupSpacerWidth : 0;
-            double width;
-            if (OwningGrid.AreRowDetailsFrozen)
-            {
-                leftEdge += OwningGrid.HorizontalOffset;
-                width = OwningGrid.CellsWidth;
-            }
-            else
-            {
-                xClip += OwningGrid.HorizontalOffset;
-                width = Math.Max(OwningGrid.CellsWidth, OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth);
-            }
-            // Details should not extend through the indented area
-            width -= rowGroupSpacerWidth;
-            double height = Math.Max(0, double.IsNaN(ContentHeight) ? 0 : ContentHeight);
-
-            foreach (Control child in Children)
-            {
-                child.Arrange(new Rect(leftEdge, 0, width, height));
-            }
-
-            if (OwningGrid.AreRowDetailsFrozen)
-            {
-                // Frozen Details should not be clipped, similar to frozen cells
-                Clip = null;
-            }
-            else
-            {
-                // Clip so Details doesn't obstruct elements to the left (the RowHeader by default) as we scroll to the right
-                Clip = new RectangleGeometry
-                {
-                    Rect = new Rect(xClip, 0, Math.Max(0, width - xClip + rowGroupSpacerWidth), height)
-                };
-            }
-
-            return finalSize;
-        }
-
-        /// <summary>
-        /// Measures the children of a <see cref="T:Avalonia.Controls.Primitives.DataGridDetailsPresenter" /> to 
-        /// prepare for arranging them during the <see cref="M:System.Windows.FrameworkElement.ArrangeOverride(System.Windows.Size)" /> pass.
-        /// </summary>
-        /// <param name="availableSize">
-        /// The available size that this element can give to child elements. Indicates an upper limit that child elements should not exceed.
-        /// </param>
-        /// <returns>
-        /// The size that the <see cref="T:Avalonia.Controls.Primitives.DataGridDetailsPresenter" /> determines it needs during layout, based on its calculations of child object allocated sizes.
-        /// </returns>
-        protected override Size MeasureOverride(Size availableSize)
-        {
-            if (OwningGrid == null || Children.Count == 0)
-            {
-                return default;
-            }
-
-            double desiredWidth = OwningGrid.AreRowDetailsFrozen ?
-                OwningGrid.CellsWidth :
-                Math.Max(OwningGrid.CellsWidth, OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth);
-
-            desiredWidth -= OwningGrid.ColumnsInternal.RowGroupSpacerColumn.Width.Value;
-
-            foreach (Control child in Children)
-            {
-                child.Measure(new Size(desiredWidth, double.PositiveInfinity));
-            }
-
-            double desiredHeight = Math.Max(0, double.IsNaN(ContentHeight) ? 0 : ContentHeight);
-
-            return new Size(desiredWidth, desiredHeight);
-        }
-    }
-}

+ 0 - 43
src/Avalonia.Controls.DataGrid/Primitives/DataGridFrozenGrid.cs

@@ -1,43 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Avalonia.Controls.Primitives
-{
-    /// <summary>
-    /// Represents a non-scrollable grid that contains <see cref="T:Avalonia.Controls.DataGrid" /> row headers.
-    /// </summary>
-    public class DataGridFrozenGrid : Grid
-    {
-        public static readonly StyledProperty<bool> IsFrozenProperty =
-            AvaloniaProperty.RegisterAttached<DataGridFrozenGrid, Control, bool>("IsFrozen");
-
-        /// <summary>
-        /// Gets a value that indicates whether the grid is frozen.
-        /// </summary>
-        /// <param name="element">
-        /// The object to get the <see cref="P:Avalonia.Controls.Primitives.DataGridFrozenGrid.IsFrozen" /> value from.
-        /// </param>
-        /// <returns>true if the grid is frozen; otherwise, false. The default is true.</returns>
-        public static bool GetIsFrozen(Control element)
-        {
-            return element.GetValue(IsFrozenProperty);
-        }
-
-        /// <summary>
-        /// Sets a value that indicates whether the grid is frozen.
-        /// </summary>
-        /// <param name="element">The object to set the <see cref="P:Avalonia.Controls.Primitives.DataGridFrozenGrid.IsFrozen" /> value on.</param>
-        /// <param name="value">true if <paramref name="element" /> is frozen; otherwise, false.</param>
-        /// <exception cref="T:System.ArgumentNullException"><paramref name="element" /> is null.</exception>
-        public static void SetIsFrozen(Control element, bool value)
-        {
-            element.SetValue(IsFrozenProperty, value);
-        }
-    }
-}

+ 0 - 214
src/Avalonia.Controls.DataGrid/Primitives/DataGridRowsPresenter.cs

@@ -1,214 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System;
-using System.Diagnostics;
-
-using Avalonia.Input;
-using Avalonia.Layout;
-using Avalonia.LogicalTree;
-using Avalonia.Media;
-
-namespace Avalonia.Controls.Primitives
-{
-    /// <summary>
-    /// Used within the template of a <see cref="T:Avalonia.Controls.DataGrid" /> to specify the
-    /// location in the control's visual tree where the rows are to be added.
-    /// </summary>
-    public sealed class DataGridRowsPresenter : Panel, IChildIndexProvider
-    {
-        private EventHandler<ChildIndexChangedEventArgs> _childIndexChanged;
-
-        public DataGridRowsPresenter()
-        {
-            AddHandler(Gestures.ScrollGestureEvent, OnScrollGesture);
-        }
-
-        internal DataGrid OwningGrid
-        {
-            get;
-            set;
-        }
-
-        event EventHandler<ChildIndexChangedEventArgs> IChildIndexProvider.ChildIndexChanged
-        {
-            add => _childIndexChanged += value;
-            remove => _childIndexChanged -= value;
-        }
-
-        int IChildIndexProvider.GetChildIndex(ILogical child)
-        {
-            return child is DataGridRow row
-                ? row.Index
-                : throw new InvalidOperationException("Invalid DataGrid child");
-        }
-
-        bool IChildIndexProvider.TryGetTotalCount(out int count)
-        {
-            return OwningGrid.DataConnection.TryGetCount(false, true, out count);
-        }
-
-        internal void InvalidateChildIndex(DataGridRow row)
-        {
-            _childIndexChanged?.Invoke(this, new ChildIndexChangedEventArgs(row, row.Index));
-        }
-
-        /// <summary>
-        /// Arranges the content of the <see cref="T:Avalonia.Controls.Primitives.DataGridRowsPresenter" />.
-        /// </summary>
-        /// <returns>
-        /// The actual size used by the <see cref="T:Avalonia.Controls.Primitives.DataGridRowsPresenter" />.
-        /// </returns>
-        /// <param name="finalSize">
-        /// The final area within the parent that this element should use to arrange itself and its children.
-        /// </param>
-        protected override Size ArrangeOverride(Size finalSize)
-        {
-            if (finalSize.Height == 0 || OwningGrid == null)
-            {
-                return base.ArrangeOverride(finalSize);
-            }
-
-            if (OwningGrid.RowsPresenterAvailableSize.HasValue)
-            {
-                var availableHeight = OwningGrid.RowsPresenterAvailableSize.Value.Height;
-            }
-
-            OwningGrid.OnFillerColumnWidthNeeded(finalSize.Width);
-
-            double rowDesiredWidth = OwningGrid.RowHeadersDesiredWidth + OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth + OwningGrid.ColumnsInternal.FillerColumn.FillerWidth;
-            double topEdge = -OwningGrid.NegVerticalOffset;
-            foreach (Control element in OwningGrid.DisplayData.GetScrollingElements())
-            {
-                if (element is DataGridRow row)
-                {
-                    Debug.Assert(row.Index != -1); // A displayed row should always have its index
-
-                    // Visibility for all filler cells needs to be set in one place.  Setting it individually in
-                    // each CellsPresenter causes an NxN layout cycle (see DevDiv Bugs 211557)
-                    row.EnsureFillerVisibility();
-                    row.Arrange(new Rect(-OwningGrid.HorizontalOffset, topEdge, rowDesiredWidth, element.DesiredSize.Height));
-                }
-                else if (element is DataGridRowGroupHeader groupHeader)
-                {
-                    double leftEdge = (OwningGrid.AreRowGroupHeadersFrozen) ? 0 : -OwningGrid.HorizontalOffset;
-                    groupHeader.Arrange(new Rect(leftEdge, topEdge, rowDesiredWidth - leftEdge, element.DesiredSize.Height));
-                }
-
-                topEdge += element.DesiredSize.Height;
-            }
-
-            double finalHeight = Math.Max(topEdge + OwningGrid.NegVerticalOffset, finalSize.Height);
-
-            // Clip the RowsPresenter so rows cannot overlap other elements in certain styling scenarios
-            var rg = new RectangleGeometry
-            {
-                Rect = new Rect(0, 0, finalSize.Width, finalHeight)
-            };
-            Clip = rg;
-
-            return new Size(finalSize.Width, finalHeight);
-        }
-
-        /// <summary>
-        /// Measures the children of a <see cref="T:Avalonia.Controls.Primitives.DataGridRowsPresenter" /> to 
-        /// prepare for arranging them during the <see cref="M:System.Windows.FrameworkElement.ArrangeOverride(System.Windows.Size)" /> pass.
-        /// </summary>
-        /// <param name="availableSize">
-        /// The available size that this element can give to child elements. Indicates an upper limit that child elements should not exceed.
-        /// </param>
-        /// <returns>
-        /// The size that the <see cref="T:Avalonia.Controls.Primitives.DataGridRowsPresenter" /> determines it needs during layout, based on its calculations of child object allocated sizes.
-        /// </returns>
-        protected override Size MeasureOverride(Size availableSize)
-        {
-            if (double.IsInfinity(availableSize.Height))
-            {
-                if (VisualRoot is TopLevel topLevel)
-                {
-                    double maxHeight = topLevel.IsArrangeValid ?
-                                        topLevel.Bounds.Height :
-                                        LayoutHelper.ApplyLayoutConstraints(topLevel, availableSize).Height;
-
-                    availableSize = availableSize.WithHeight(maxHeight);
-                }
-            }
-
-            if (availableSize.Height == 0 || OwningGrid == null)
-            {
-                return base.MeasureOverride(availableSize);
-            }
-
-            // If the Width of our RowsPresenter changed then we need to invalidate our rows
-            bool invalidateRows = (!OwningGrid.RowsPresenterAvailableSize.HasValue || availableSize.Width != OwningGrid.RowsPresenterAvailableSize.Value.Width)
-                                  && !double.IsInfinity(availableSize.Width);
-
-            // The DataGrid uses the RowsPresenter available size in order to autogrow
-            // and calculate the scrollbars
-            OwningGrid.RowsPresenterAvailableSize = availableSize;
-
-            OwningGrid.OnRowsMeasure();
-
-            double totalHeight = -OwningGrid.NegVerticalOffset;
-            double totalCellsWidth = OwningGrid.ColumnsInternal.VisibleEdgedColumnsWidth;
-
-            double headerWidth = 0;
-            foreach (Control element in OwningGrid.DisplayData.GetScrollingElements())
-            {
-                DataGridRow row = element as DataGridRow;
-                if (row != null)
-                {
-                    if (invalidateRows)
-                    {
-                        row.InvalidateMeasure();
-                    }
-                }
-
-                element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
-
-                if (row != null && row.HeaderCell != null)
-                {
-                    headerWidth = Math.Max(headerWidth, row.HeaderCell.DesiredSize.Width);
-                }
-                else if (element is DataGridRowGroupHeader groupHeader && groupHeader.HeaderCell != null)
-                {
-                    headerWidth = Math.Max(headerWidth, groupHeader.HeaderCell.DesiredSize.Width);
-                }
-
-                totalHeight += element.DesiredSize.Height;
-            }
-
-            OwningGrid.RowHeadersDesiredWidth = headerWidth;
-            // Could be positive infinity depending on the DataGrid's bounds
-            OwningGrid.AvailableSlotElementRoom = availableSize.Height - totalHeight;
-
-            totalHeight = Math.Max(0, totalHeight);
-
-            return new Size(totalCellsWidth + headerWidth, totalHeight);
-        }
-
-        private void OnScrollGesture(object sender, ScrollGestureEventArgs e)
-        {
-            e.Handled = e.Handled || OwningGrid.UpdateScroll(-e.Delta);
-        }
-
-#if DEBUG
-        internal void PrintChildren()
-        {
-            foreach (Control element in Children)
-            {
-                if (element is DataGridRow row)
-                {
-                    Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Slot: {0} Row: {1} Visibility: {2} ", row.Slot, row.Index, row.IsVisible));
-                }
-                else if (element is DataGridRowGroupHeader groupHeader)
-                {
-                    Debug.WriteLine(String.Format(System.Globalization.CultureInfo.InvariantCulture, "Slot: {0} GroupHeader: {1} Visibility: {2}", groupHeader.RowGroupInfo.Slot, groupHeader.RowGroupInfo.CollectionViewGroup.Key, groupHeader.IsVisible));
-                }
-            }
-        }
-#endif
-    }
-}

+ 0 - 5
src/Avalonia.Controls.DataGrid/Properties/AssemblyInfo.cs

@@ -1,5 +0,0 @@
-using Avalonia.Metadata;
-
-[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls")]
-[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Collections")]
-[assembly: XmlnsDefinition("https://github.com/avaloniaui", "Avalonia.Controls.Primitives")]

+ 0 - 69
src/Avalonia.Controls.DataGrid/Range.cs

@@ -1,69 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System;
-using System.Collections.Generic;
-using System.Text;
-
-namespace Avalonia.Controls
-{
-    internal class Range<T>
-    {
-        public Range(int lowerBound, int upperBound, T value)
-        {
-            LowerBound = lowerBound;
-            UpperBound = upperBound;
-            Value = value;
-        }
-
-        public int Count
-        {
-            get
-            {
-                return UpperBound - LowerBound + 1;
-            }
-        }
-
-        public int LowerBound
-        {
-            get;
-            set;
-        }
-
-        public int UpperBound
-        {
-            get;
-            set;
-        }
-
-        public T Value
-        {
-            get;
-            set;
-        }
-
-        public bool ContainsIndex(int index)
-        {
-            return (LowerBound <= index) && (UpperBound >= index);
-        }
-
-        public bool ContainsValue(object value)
-        {
-            if (Value == null)
-            {
-                return value == null;
-            }
-            else
-            {
-                return Value.Equals(value);
-            }
-        }
-
-        public Range<T> Copy()
-        {
-            return new Range<T>(LowerBound, UpperBound, Value);
-        }
-    }
-}

+ 0 - 598
src/Avalonia.Controls.DataGrid/Themes/Fluent.xaml

@@ -1,598 +0,0 @@
-<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-        xmlns:collections="using:Avalonia.Collections">
-  <Styles.Resources>
-   <ResourceDictionary>
-    <ResourceDictionary.ThemeDictionaries>
-        <ResourceDictionary x:Key="Dark">
-          <SolidColorBrush x:Key="DataGridColumnHeaderForegroundBrush" Color="{DynamicResource SystemBaseMediumColor}" />
-          <SolidColorBrush x:Key="DataGridColumnHeaderBackgroundBrush" Color="{DynamicResource SystemAltHighColor}" />
-          <SolidColorBrush x:Key="DataGridColumnHeaderHoveredBackgroundBrush" Color="{DynamicResource SystemListLowColor}" />
-          <SolidColorBrush x:Key="DataGridColumnHeaderPressedBackgroundBrush" Color="{DynamicResource SystemListMediumColor}" />
-          <SolidColorBrush x:Key="DataGridColumnHeaderDraggedBackgroundBrush" Color="{DynamicResource SystemChromeMediumLowColor}" />
-          <SolidColorBrush x:Key="DataGridRowGroupHeaderBackgroundBrush" Color="{DynamicResource SystemChromeMediumColor}" />
-          <SolidColorBrush x:Key="DataGridRowGroupHeaderPressedBackgroundBrush" Color="{DynamicResource SystemListMediumColor}" />
-          <SolidColorBrush x:Key="DataGridRowGroupHeaderForegroundBrush" Color="{DynamicResource SystemBaseHighColor}" />
-          <SolidColorBrush x:Key="DataGridRowGroupHeaderHoveredBackgroundBrush" Color="{DynamicResource SystemListLowColor}" />
-          <SolidColorBrush x:Key="DataGridRowHoveredBackgroundColor" Color="{DynamicResource SystemListLowColor}" />
-          <SolidColorBrush x:Key="DataGridRowInvalidBrush" Color="{DynamicResource SystemErrorTextColor}" />
-          <SolidColorBrush x:Key="DataGridRowSelectedBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
-          <SolidColorBrush x:Key="DataGridRowSelectedHoveredBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
-          <SolidColorBrush x:Key="DataGridRowSelectedUnfocusedBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
-          <SolidColorBrush x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
-          <SolidColorBrush x:Key="DataGridCellFocusVisualPrimaryBrush" Color="{DynamicResource SystemBaseHighColor}" />
-          <SolidColorBrush x:Key="DataGridCellFocusVisualSecondaryBrush" Color="{DynamicResource SystemAltMediumColor}" />
-          <SolidColorBrush x:Key="DataGridCellInvalidBrush" Color="{DynamicResource SystemErrorTextColor}" />
-          <SolidColorBrush x:Key="DataGridGridLinesBrush" Opacity="0.4" Color="{DynamicResource SystemBaseMediumLowColor}" />
-          <SolidColorBrush x:Key="DataGridDetailsPresenterBackgroundBrush" Color="{DynamicResource SystemChromeMediumLowColor}" />
-
-        </ResourceDictionary>
-        <ResourceDictionary x:Key="Default">
-          <SolidColorBrush x:Key="DataGridColumnHeaderForegroundBrush" Color="{DynamicResource SystemBaseMediumColor}" />
-          <SolidColorBrush x:Key="DataGridColumnHeaderBackgroundBrush" Color="{DynamicResource SystemAltHighColor}" />
-          <SolidColorBrush x:Key="DataGridColumnHeaderHoveredBackgroundBrush" Color="{DynamicResource SystemListLowColor}" />
-          <SolidColorBrush x:Key="DataGridColumnHeaderPressedBackgroundBrush" Color="{DynamicResource SystemListMediumColor}" />
-          <SolidColorBrush x:Key="DataGridColumnHeaderDraggedBackgroundBrush" Color="{DynamicResource SystemChromeMediumLowColor}" />
-          <SolidColorBrush x:Key="DataGridRowGroupHeaderBackgroundBrush" Color="{DynamicResource SystemChromeMediumColor}" />
-          <SolidColorBrush x:Key="DataGridRowGroupHeaderPressedBackgroundBrush" Color="{DynamicResource SystemListMediumColor}" />
-          <SolidColorBrush x:Key="DataGridRowGroupHeaderForegroundBrush" Color="{DynamicResource SystemBaseHighColor}" />
-          <SolidColorBrush x:Key="DataGridRowGroupHeaderHoveredBackgroundBrush" Color="{DynamicResource SystemListLowColor}" />
-          <SolidColorBrush x:Key="DataGridRowHoveredBackgroundColor" Color="{DynamicResource SystemListLowColor}" />
-          <SolidColorBrush x:Key="DataGridRowInvalidBrush" Color="{DynamicResource SystemErrorTextColor}" />
-          <SolidColorBrush x:Key="DataGridRowSelectedBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
-          <SolidColorBrush x:Key="DataGridRowSelectedHoveredBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
-          <SolidColorBrush x:Key="DataGridRowSelectedUnfocusedBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
-          <SolidColorBrush x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundBrush" Color="{DynamicResource SystemAccentColor}" />
-          <SolidColorBrush x:Key="DataGridCellFocusVisualPrimaryBrush" Color="{DynamicResource SystemBaseHighColor}" />
-          <SolidColorBrush x:Key="DataGridCellFocusVisualSecondaryBrush" Color="{DynamicResource SystemAltMediumColor}" />
-          <SolidColorBrush x:Key="DataGridCellInvalidBrush" Color="{DynamicResource SystemErrorTextColor}" />
-          <SolidColorBrush x:Key="DataGridGridLinesBrush" Opacity="0.4" Color="{DynamicResource SystemBaseMediumLowColor}" />
-          <SolidColorBrush x:Key="DataGridDetailsPresenterBackgroundBrush" Color="{DynamicResource SystemChromeMediumLowColor}" />
-
-        </ResourceDictionary>
-    </ResourceDictionary.ThemeDictionaries>
-
-    <x:Double x:Key="ListAccentLowOpacity">0.6</x:Double>
-    <x:Double x:Key="ListAccentMediumOpacity">0.8</x:Double>
-    <x:Double x:Key="DataGridSortIconMinWidth">32</x:Double>
-
-    <StreamGeometry x:Key="DataGridSortIconDescendingPath">M1875 1011l-787 787v-1798h-128v1798l-787 -787l-90 90l941 941l941 -941z</StreamGeometry>
-    <StreamGeometry x:Key="DataGridSortIconAscendingPath">M1965 947l-941 -941l-941 941l90 90l787 -787v1798h128v-1798l787 787z</StreamGeometry>
-    <StreamGeometry x:Key="DataGridRowGroupHeaderIconClosedPath">M515 93l930 931l-930 931l90 90l1022 -1021l-1022 -1021z</StreamGeometry>
-    <StreamGeometry x:Key="DataGridRowGroupHeaderIconOpenedPath">M109 486 19 576 1024 1581 2029 576 1939 486 1024 1401z</StreamGeometry>
-
-    <StaticResource x:Key="DataGridRowBackgroundBrush" ResourceKey="SystemControlTransparentBrush" />
-    <StaticResource x:Key="DataGridRowSelectedBackgroundOpacity" ResourceKey="ListAccentLowOpacity" />
-    <StaticResource x:Key="DataGridRowSelectedHoveredBackgroundOpacity" ResourceKey="ListAccentMediumOpacity" />
-    <StaticResource x:Key="DataGridRowSelectedUnfocusedBackgroundOpacity" ResourceKey="ListAccentLowOpacity" />
-    <StaticResource x:Key="DataGridRowSelectedHoveredUnfocusedBackgroundOpacity" ResourceKey="ListAccentMediumOpacity" />
-    <StaticResource x:Key="DataGridCellBackgroundBrush" ResourceKey="SystemControlTransparentBrush" />
-    <StaticResource x:Key="DataGridCurrencyVisualPrimaryBrush" ResourceKey="SystemControlTransparentBrush" />
-    <StaticResource x:Key="DataGridFillerColumnGridLinesBrush" ResourceKey="SystemControlTransparentBrush" />
-
-    <ControlTheme x:Key="DataGridCellTextBlockTheme" TargetType="TextBlock">
-      <Setter Property="Margin" Value="12,0,12,0" />
-      <Setter Property="VerticalAlignment" Value="Center" />
-    </ControlTheme>
-    <ControlTheme x:Key="DataGridCellTextBoxTheme" TargetType="TextBox" BasedOn="{StaticResource {x:Type TextBox}}">
-      <Setter Property="VerticalAlignment" Value="Stretch" />
-      <Setter Property="Background" Value="Transparent" />
-      <Style Selector="^ /template/ DataValidationErrors">
-        <Setter Property="Theme" Value="{StaticResource TooltipDataValidationErrors}" />
-      </Style>
-    </ControlTheme>
-
-    <ControlTheme x:Key="{x:Type DataGridCell}" TargetType="DataGridCell">
-      <Setter Property="Background" Value="{DynamicResource DataGridCellBackgroundBrush}" />
-      <Setter Property="HorizontalContentAlignment" Value="Stretch" />
-      <Setter Property="VerticalContentAlignment" Value="Stretch" />
-      <Setter Property="FontSize" Value="15" />
-      <Setter Property="MinHeight" Value="32" />
-      <Setter Property="Template">
-        <ControlTemplate>
-          <Border x:Name="CellBorder"
-                  Background="{TemplateBinding Background}"
-                  BorderBrush="{TemplateBinding BorderBrush}"
-                  BorderThickness="{TemplateBinding BorderThickness}"
-                  CornerRadius="{TemplateBinding CornerRadius}">
-            <Grid x:Name="PART_CellRoot" ColumnDefinitions="*,Auto">
-
-              <Rectangle x:Name="CurrencyVisual"
-                         IsVisible="False"
-                         HorizontalAlignment="Stretch"
-                         VerticalAlignment="Stretch"
-                         Fill="Transparent"
-                         IsHitTestVisible="False"
-                         Stroke="{DynamicResource DataGridCurrencyVisualPrimaryBrush}"
-                         StrokeThickness="1" />
-              <Grid Grid.Column="0" x:Name="FocusVisual" IsHitTestVisible="False"
-                    IsVisible="False">
-                <Rectangle HorizontalAlignment="Stretch"
-                           VerticalAlignment="Stretch"
-                           Fill="Transparent"
-                           IsHitTestVisible="False"
-                           Stroke="{DynamicResource DataGridCellFocusVisualPrimaryBrush}"
-                           StrokeThickness="2" />
-                <Rectangle Margin="2"
-                           HorizontalAlignment="Stretch"
-                           VerticalAlignment="Stretch"
-                           Fill="Transparent"
-                           IsHitTestVisible="False"
-                           Stroke="{DynamicResource DataGridCellFocusVisualSecondaryBrush}"
-                           StrokeThickness="1" />
-              </Grid>
-
-              <ContentPresenter Grid.Column="0" Margin="{TemplateBinding Padding}"
-                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
-                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
-                                Content="{TemplateBinding Content}"
-                                ContentTemplate="{TemplateBinding ContentTemplate}"
-                                Foreground="{TemplateBinding Foreground}" />
-
-              <Rectangle Grid.Column="0" x:Name="InvalidVisualElement"
-                         IsVisible="False"
-                         HorizontalAlignment="Stretch"
-                         VerticalAlignment="Stretch"
-                         IsHitTestVisible="False"
-                         Stroke="{DynamicResource DataGridCellInvalidBrush}"
-                         StrokeThickness="1" />
-
-              <Rectangle Name="PART_RightGridLine"
-                         Grid.Column="1"
-                         Width="1"
-                         VerticalAlignment="Stretch"
-                         Fill="{DynamicResource DataGridFillerColumnGridLinesBrush}" />
-            </Grid>
-          </Border>
-        </ControlTemplate>
-      </Setter>
-      <Style Selector="^:current /template/ Rectangle#CurrencyVisual">
-        <Setter Property="IsVisible" Value="True" />
-      </Style>
-      <Style Selector="^:focus /template/ Grid#FocusVisual">
-        <Setter Property="IsVisible" Value="True" />
-      </Style>
-      <Style Selector="^:invalid /template/ Rectangle#InvalidVisualElement">
-        <Setter Property="IsVisible" Value="True" />
-      </Style>
-    </ControlTheme>
-
-    <ControlTheme x:Key="{x:Type DataGridColumnHeader}" TargetType="DataGridColumnHeader">
-      <Setter Property="Foreground" Value="{DynamicResource DataGridColumnHeaderForegroundBrush}" />
-      <Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderBackgroundBrush}" />
-      <Setter Property="HorizontalContentAlignment" Value="Stretch" />
-      <Setter Property="VerticalContentAlignment" Value="Center" />
-      <Setter Property="SeparatorBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
-      <Setter Property="Padding" Value="12,0,0,0" />
-      <Setter Property="FontSize" Value="12" />
-      <Setter Property="MinHeight" Value="32" />
-      <Setter Property="Template">
-        <ControlTemplate>
-          <Border x:Name="HeaderBorder"
-                  Background="{TemplateBinding Background}"
-                  BorderBrush="{TemplateBinding BorderBrush}"
-                  BorderThickness="{TemplateBinding BorderThickness}"
-                  CornerRadius="{TemplateBinding CornerRadius}">
-            <Grid Name="PART_ColumnHeaderRoot" ColumnDefinitions="*,Auto">
-
-              <Panel Margin="{TemplateBinding Padding}"
-                    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
-                    VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
-                <Grid>
-                  <Grid.ColumnDefinitions>
-                    <ColumnDefinition Width="*" />
-                    <ColumnDefinition Width="Auto" MinWidth="{DynamicResource DataGridSortIconMinWidth}" />
-                  </Grid.ColumnDefinitions>
-                
-                  <ContentPresenter x:Name="PART_ContentPresenter"
-                                    Content="{TemplateBinding Content}"
-                                    ContentTemplate="{TemplateBinding ContentTemplate}" />
-                
-                  <Path Name="SortIcon"
-                        IsVisible="False"
-                        Grid.Column="1"
-                        Height="12"
-                        HorizontalAlignment="Center"
-                        VerticalAlignment="Center"
-                        Fill="{TemplateBinding Foreground}"
-                        Stretch="Uniform" />
-                </Grid>
-              </Panel>
-
-              <Rectangle Name="VerticalSeparator"
-                         Grid.Column="1"
-                         Width="1"
-                         VerticalAlignment="Stretch"
-                         Fill="{TemplateBinding SeparatorBrush}"
-                         IsVisible="{TemplateBinding AreSeparatorsVisible}" />
-
-              <Grid x:Name="FocusVisual" IsHitTestVisible="False"
-                    IsVisible="False">
-                <Rectangle x:Name="FocusVisualPrimary"
-                           HorizontalAlignment="Stretch"
-                           VerticalAlignment="Stretch"
-                           Fill="Transparent"
-                           IsHitTestVisible="False"
-                           Stroke="{DynamicResource DataGridCellFocusVisualPrimaryBrush}"
-                           StrokeThickness="2" />
-                <Rectangle x:Name="FocusVisualSecondary"
-                           Margin="2"
-                           HorizontalAlignment="Stretch"
-                           VerticalAlignment="Stretch"
-                           Fill="Transparent"
-                           IsHitTestVisible="False"
-                           Stroke="{DynamicResource DataGridCellFocusVisualSecondaryBrush}"
-                           StrokeThickness="1" />
-              </Grid>
-            </Grid>
-          </Border>
-        </ControlTemplate>
-      </Setter>
-
-      <Style Selector="^:focus-visible /template/ Grid#FocusVisual">
-        <Setter Property="IsVisible" Value="True" />
-      </Style>
-
-      <Style Selector="^:pointerover /template/ Grid#PART_ColumnHeaderRoot">
-        <Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderHoveredBackgroundBrush}" />
-      </Style>
-
-      <Style Selector="^:pressed /template/ Grid#PART_ColumnHeaderRoot">
-        <Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderPressedBackgroundBrush}" />
-      </Style>
-
-      <Style Selector="^:dragIndicator">
-        <Setter Property="Opacity" Value="0.5" />
-      </Style>
-
-      <Style Selector="^:sortascending /template/ Path#SortIcon">
-        <Setter Property="IsVisible" Value="True" />
-        <Setter Property="Data" Value="{StaticResource DataGridSortIconAscendingPath}" />
-      </Style>
-
-      <Style Selector="^:sortdescending /template/ Path#SortIcon">
-        <Setter Property="IsVisible" Value="True" />
-        <Setter Property="Data" Value="{StaticResource DataGridSortIconDescendingPath}" />
-      </Style>
-    </ControlTheme>
-
-    <ControlTheme x:Key="DataGridTopLeftColumnHeader" TargetType="DataGridColumnHeader" BasedOn="{StaticResource {x:Type DataGridColumnHeader}}">
-      <Setter Property="Template">
-        <ControlTemplate>
-          <Grid x:Name="TopLeftHeaderRoot"
-                RowDefinitions="*,*,Auto">
-            <Border Grid.RowSpan="2"
-                    BorderThickness="0,0,1,0"
-                    BorderBrush="{DynamicResource DataGridGridLinesBrush}" />
-            <Rectangle Grid.Row="0" Grid.RowSpan="2"
-                       VerticalAlignment="Bottom"
-                       StrokeThickness="1"
-                       Height="1"
-                       Fill="{DynamicResource DataGridGridLinesBrush}" />
-          </Grid>
-        </ControlTemplate>
-      </Setter>
-    </ControlTheme>
-
-    <ControlTheme x:Key="{x:Type DataGridRowHeader}" TargetType="DataGridRowHeader">
-      <Setter Property="SeparatorBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
-      <Setter Property="AreSeparatorsVisible" Value="False" />
-      <Setter Property="Template">
-        <ControlTemplate>
-          <Grid x:Name="PART_Root"
-                RowDefinitions="*,*,Auto"
-                ColumnDefinitions="Auto,*">
-            <Border Grid.RowSpan="3"
-                    Grid.ColumnSpan="2"
-                    BorderBrush="{TemplateBinding SeparatorBrush}"
-                    BorderThickness="0,0,1,0">
-              <Grid Background="{TemplateBinding Background}">
-                <Rectangle x:Name="RowInvalidVisualElement"
-                           Opacity="0"
-                           Fill="{DynamicResource DataGridRowInvalidBrush}"
-                           Stretch="Fill" />
-                <Rectangle x:Name="BackgroundRectangle"
-                           Fill="{DynamicResource DataGridRowBackgroundBrush}"
-                           Stretch="Fill" />
-              </Grid>
-            </Border>
-            <Rectangle x:Name="HorizontalSeparator"
-                       Grid.Row="2"
-                       Grid.ColumnSpan="2"
-                       Height="1"
-                       Margin="1,0,1,0"
-                       HorizontalAlignment="Stretch"
-                       Fill="{TemplateBinding SeparatorBrush}"
-                       IsVisible="{TemplateBinding AreSeparatorsVisible}" />
-
-            <ContentPresenter Grid.RowSpan="2"
-                              Grid.Column="1"
-                              HorizontalAlignment="Center"
-                              VerticalAlignment="Center"
-                              Content="{TemplateBinding Content}" />
-          </Grid>
-        </ControlTemplate>
-      </Setter>
-    </ControlTheme>
-
-    <ControlTheme x:Key="{x:Type DataGridRow}" TargetType="DataGridRow">
-      <Setter Property="Background" Value="{Binding $parent[DataGrid].RowBackground}" />
-      <Setter Property="Template">
-        <ControlTemplate>
-          <Border x:Name="RowBorder"
-                  Background="{TemplateBinding Background}"
-                  BorderBrush="{TemplateBinding BorderBrush}"
-                  BorderThickness="{TemplateBinding BorderThickness}"
-                  CornerRadius="{TemplateBinding CornerRadius}">
-            <DataGridFrozenGrid Name="PART_Root"
-                                ColumnDefinitions="Auto,*"
-                                RowDefinitions="*,Auto,Auto">
-
-              <Rectangle Name="BackgroundRectangle"
-                         Fill="{DynamicResource DataGridRowBackgroundBrush}"
-                         Grid.RowSpan="2"
-                         Grid.ColumnSpan="2" />
-              <Rectangle x:Name="InvalidVisualElement"
-                         Opacity="0"
-                         Grid.ColumnSpan="2"
-                         Fill="{DynamicResource DataGridRowInvalidBrush}" />
-
-              <DataGridRowHeader Name="PART_RowHeader"
-                                 Grid.RowSpan="3"
-                                 DataGridFrozenGrid.IsFrozen="True" />
-              <DataGridCellsPresenter Name="PART_CellsPresenter"
-                                      Grid.Column="1"
-                                      DataGridFrozenGrid.IsFrozen="True" />
-              <DataGridDetailsPresenter Name="PART_DetailsPresenter"
-                                        Grid.Row="1"
-                                        Grid.Column="1"
-                                        Background="{DynamicResource DataGridDetailsPresenterBackgroundBrush}" />
-              <Rectangle Name="PART_BottomGridLine"
-                         Grid.Row="2"
-                         Grid.Column="1"
-                         Height="1"
-                         HorizontalAlignment="Stretch" />
-
-            </DataGridFrozenGrid>
-          </Border>
-        </ControlTemplate>
-      </Setter>
-
-      <Style Selector="^:invalid">
-        <Style Selector="^ /template/ Rectangle#InvalidVisualElement">
-          <Setter Property="Opacity" Value="0.4" />
-        </Style>
-        <Style Selector="^ /template/ Rectangle#BackgroundRectangle">
-          <Setter Property="Opacity" Value="0" />
-        </Style>
-      </Style>
-
-      <Style Selector="^:pointerover /template/ Rectangle#BackgroundRectangle">
-        <Setter Property="Fill" Value="{DynamicResource DataGridRowHoveredBackgroundColor}" />
-      </Style>
-
-      <Style Selector="^:selected">
-        <Style Selector="^ /template/ Rectangle#BackgroundRectangle">
-          <Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundBrush}" />
-          <Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedUnfocusedBackgroundOpacity}" />
-        </Style>
-        <Style Selector="^:pointerover /template/ Rectangle#BackgroundRectangle">
-          <Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundBrush}" />
-          <Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredUnfocusedBackgroundOpacity}" />
-        </Style>
-        <Style Selector="^:focus /template/ Rectangle#BackgroundRectangle">
-          <Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedBackgroundBrush}" />
-          <Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedBackgroundOpacity}" />
-        </Style>
-        <Style Selector="^:pointerover:focus /template/ Rectangle#BackgroundRectangle">
-          <Setter Property="Fill" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundBrush}" />
-          <Setter Property="Opacity" Value="{DynamicResource DataGridRowSelectedHoveredBackgroundOpacity}" />
-        </Style>
-      </Style>
-    </ControlTheme>
-
-    <ControlTheme x:Key="FluentDataGridRowGroupExpanderButtonTheme" TargetType="ToggleButton">
-      <Setter Property="Template">
-        <ControlTemplate>
-          <Border Width="12"
-                  Height="12"
-                  Background="Transparent"
-                  HorizontalAlignment="Center"
-                  VerticalAlignment="Center">
-            <Path Fill="{TemplateBinding Foreground}"
-                  Data="{StaticResource DataGridRowGroupHeaderIconClosedPath}"
-                  HorizontalAlignment="Right"
-                  VerticalAlignment="Center"
-                  Stretch="Uniform" />
-          </Border>
-        </ControlTemplate>
-      </Setter>
-      <Style Selector="^:checked /template/ Path">
-        <Setter Property="Data" Value="{StaticResource DataGridRowGroupHeaderIconOpenedPath}" />
-      </Style>
-    </ControlTheme>
-
-    <ControlTheme x:Key="{x:Type DataGridRowGroupHeader}" TargetType="DataGridRowGroupHeader">
-      <Setter Property="Foreground" Value="{DynamicResource DataGridRowGroupHeaderForegroundBrush}" />
-      <Setter Property="Background" Value="{DynamicResource DataGridRowGroupHeaderBackgroundBrush}" />
-      <Setter Property="FontSize" Value="15" />
-      <Setter Property="MinHeight" Value="32" />
-      <Setter Property="Template">
-        <ControlTemplate x:DataType="collections:DataGridCollectionViewGroup">
-          <DataGridFrozenGrid Name="PART_Root"
-                              Background="{TemplateBinding Background}"
-                              MinHeight="{TemplateBinding MinHeight}"
-                              ColumnDefinitions="Auto,Auto,Auto,Auto,*"
-                              RowDefinitions="*,Auto">
-
-            <Rectangle Name="PART_IndentSpacer"
-                       Grid.Column="1" />
-            <ToggleButton Name="PART_ExpanderButton"
-                          Grid.Column="2"
-                          Width="12"
-                          Height="12"
-                          Margin="12,0,0,0"
-                          Theme="{StaticResource FluentDataGridRowGroupExpanderButtonTheme}"
-                          BorderBrush="{TemplateBinding BorderBrush}"
-                          BorderThickness="{TemplateBinding BorderThickness}"
-                          Background="{TemplateBinding Background}"
-                          CornerRadius="{TemplateBinding CornerRadius}"
-                          IsTabStop="False"
-                          Foreground="{TemplateBinding Foreground}" />
-
-            <StackPanel Grid.Column="3"
-                        Orientation="Horizontal"
-                        VerticalAlignment="Center"
-                        Margin="12,0,0,0">
-              <TextBlock Name="PART_PropertyNameElement"
-                         Margin="4,0,0,0"
-                         IsVisible="{TemplateBinding IsPropertyNameVisible}"
-                         Foreground="{TemplateBinding Foreground}" />
-              <TextBlock Margin="4,0,0,0"
-                         Text="{Binding Key}"
-                         Foreground="{TemplateBinding Foreground}" />
-              <TextBlock Name="PART_ItemCountElement"
-                         Margin="4,0,0,0"
-                         IsVisible="{TemplateBinding IsItemCountVisible}"
-                         Foreground="{TemplateBinding Foreground}" />
-            </StackPanel>
-
-            <Rectangle x:Name="CurrencyVisual"
-                       Grid.ColumnSpan="5"
-                       IsVisible="False"
-                       HorizontalAlignment="Stretch"
-                       VerticalAlignment="Stretch"
-                       Fill="Transparent"
-                       IsHitTestVisible="False"
-                       Stroke="{DynamicResource DataGridCurrencyVisualPrimaryBrush}"
-                       StrokeThickness="1" />
-            <Grid x:Name="FocusVisual"
-                  Grid.ColumnSpan="5"
-                  IsVisible="False"
-                  IsHitTestVisible="False">
-              <Rectangle HorizontalAlignment="Stretch"
-                         VerticalAlignment="Stretch"
-                         Fill="Transparent"
-                         IsHitTestVisible="False"
-                         Stroke="{DynamicResource DataGridCellFocusVisualPrimaryBrush}"
-                         StrokeThickness="2" />
-              <Rectangle Margin="2"
-                         HorizontalAlignment="Stretch"
-                         VerticalAlignment="Stretch"
-                         Fill="Transparent"
-                         IsHitTestVisible="False"
-                         Stroke="{DynamicResource DataGridCellFocusVisualSecondaryBrush}"
-                         StrokeThickness="1" />
-            </Grid>
-
-            <DataGridRowHeader Name="PART_RowHeader"
-                               Grid.RowSpan="2"
-                               DataGridFrozenGrid.IsFrozen="True" />
-
-            <Rectangle x:Name="PART_BottomGridLine"
-                       Grid.Row="1"
-                       Grid.ColumnSpan="5"
-                       Height="1" />
-          </DataGridFrozenGrid>
-        </ControlTemplate>
-      </Setter>
-    </ControlTheme>
-
-    <ControlTheme x:Key="{x:Type DataGrid}" TargetType="DataGrid">
-      <Setter Property="RowBackground" Value="Transparent" />
-      <Setter Property="HeadersVisibility" Value="Column" />
-      <Setter Property="HorizontalScrollBarVisibility" Value="Auto" />
-      <Setter Property="VerticalScrollBarVisibility" Value="Auto" />
-      <Setter Property="SelectionMode" Value="Extended" />
-      <Setter Property="GridLinesVisibility" Value="None" />
-      <Setter Property="HorizontalGridLinesBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
-      <Setter Property="VerticalGridLinesBrush" Value="{DynamicResource DataGridGridLinesBrush}" />
-      <Setter Property="FocusAdorner" Value="{x:Null}" />
-      <Setter Property="DropLocationIndicatorTemplate">
-        <Template>
-          <Rectangle Fill="{DynamicResource DataGridDropLocationIndicatorBackground}"
-                     Width="2" />
-        </Template>
-      </Setter>
-      <Setter Property="Template">
-        <ControlTemplate>
-          <Border x:Name="DataGridBorder"
-                  Background="{TemplateBinding Background}"
-                  BorderBrush="{TemplateBinding BorderBrush}"
-                  BorderThickness="{TemplateBinding BorderThickness}"
-                  CornerRadius="{TemplateBinding CornerRadius}">
-            <Grid ColumnDefinitions="Auto,*,Auto"
-                  RowDefinitions="Auto,*,Auto,Auto"
-                  ClipToBounds="True">
-              <DataGridColumnHeader Name="PART_TopLeftCornerHeader"
-                                    Theme="{StaticResource DataGridTopLeftColumnHeader}" />
-              <DataGridColumnHeadersPresenter Name="PART_ColumnHeadersPresenter"
-                                              Grid.Column="1"
-                                              Grid.Row="0" Grid.ColumnSpan="2" />
-              <Rectangle Name="PART_ColumnHeadersAndRowsSeparator"
-                         Grid.Row="0" Grid.ColumnSpan="3" Grid.Column="0"
-                         VerticalAlignment="Bottom"
-                         Height="1"
-                         Fill="{DynamicResource DataGridGridLinesBrush}" />
-
-              <DataGridRowsPresenter Name="PART_RowsPresenter"
-                                     Grid.Row="1"
-                                     Grid.Column="0"
-                                     ScrollViewer.IsScrollInertiaEnabled="{TemplateBinding IsScrollInertiaEnabled}">
-                <DataGridRowsPresenter.GestureRecognizers>
-                  <ScrollGestureRecognizer CanHorizontallyScroll="True"
-                                           CanVerticallyScroll="True"
-                                           IsScrollInertiaEnabled="{Binding (ScrollViewer.IsScrollInertiaEnabled), ElementName=PART_RowsPresenter}" />
-                </DataGridRowsPresenter.GestureRecognizers>
-              </DataGridRowsPresenter>
-              <Rectangle Name="PART_BottomRightCorner"
-                         Fill="{DynamicResource DataGridScrollBarsSeparatorBackground}"
-                         Grid.Column="2"
-                         Grid.Row="2" />
-              <ScrollBar Name="PART_VerticalScrollbar"
-                         Orientation="Vertical"
-                         Grid.Column="2"
-                         Grid.Row="1"
-                         Width="{DynamicResource ScrollBarSize}" />
-
-              <Grid Grid.Column="1"
-                    Grid.Row="2"
-                    ColumnDefinitions="Auto,*">
-                <Rectangle Name="PART_FrozenColumnScrollBarSpacer" />
-                <ScrollBar Name="PART_HorizontalScrollbar"
-                           Grid.Column="1"
-                           Orientation="Horizontal"
-                           Height="{DynamicResource ScrollBarSize}" />
-              </Grid>
-              <Border x:Name="PART_DisabledVisualElement"
-                      Grid.ColumnSpan="3" Grid.Column="0"
-                      Grid.Row="0" Grid.RowSpan="4"
-                      IsHitTestVisible="False"
-                      HorizontalAlignment="Stretch"
-                      VerticalAlignment="Stretch"
-                      CornerRadius="2"
-                      Background="{DynamicResource DataGridDisabledVisualElementBackground}"
-                      IsVisible="{Binding !$parent[DataGrid].IsEnabled}" />
-            </Grid>
-          </Border>
-        </ControlTemplate>
-      </Setter>
-
-      <Style Selector="^:empty-columns">
-        <Style Selector="^ /template/ DataGridColumnHeader#PART_TopLeftCornerHeader">
-          <Setter Property="IsVisible" Value="False" />
-        </Style>
-        <Style Selector="^ /template/ DataGridColumnHeadersPresenter#PART_ColumnHeadersPresenter">
-          <Setter Property="IsVisible" Value="False" />
-        </Style>
-        <Style Selector="^ /template/ Rectangle#PART_ColumnHeadersAndRowsSeparator">
-          <Setter Property="IsVisible" Value="False" />
-        </Style>
-      </Style>
-      <Style Selector="^ /template/ DataGridRowsPresenter#PART_RowsPresenter">
-        <Setter Property="Grid.RowSpan" Value="2" />
-        <Setter Property="Grid.ColumnSpan" Value="3" />
-      </Style>
-    </ControlTheme>
-   </ResourceDictionary>
-  </Styles.Resources>
-</Styles>

+ 0 - 376
src/Avalonia.Controls.DataGrid/Themes/Simple.xaml

@@ -1,376 +0,0 @@
-<Styles xmlns="https://github.com/avaloniaui"
-        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
-        xmlns:collections="using:Avalonia.Collections">
-  <Styles.Resources>
-    <Thickness x:Key="DataGridTextColumnCellTextBlockMargin">4</Thickness>
-    <ControlTheme x:Key="DataGridCellTextBlockTheme"
-                  TargetType="TextBlock">
-      <Setter Property="Margin" Value="{DynamicResource DataGridTextColumnCellTextBlockMargin}" />
-      <Setter Property="VerticalAlignment" Value="Center" />
-    </ControlTheme>
-    <ControlTheme x:Key="DataGridCellTextBoxTheme"
-                  BasedOn="{StaticResource {x:Type TextBox}}"
-                  TargetType="TextBox">
-      <Setter Property="VerticalAlignment" Value="Stretch" />
-      <Setter Property="Background" Value="Transparent" />
-    </ControlTheme>
-
-    <ControlTheme x:Key="{x:Type DataGridCell}"
-                  TargetType="DataGridCell">
-      <Setter Property="Background" Value="Transparent" />
-      <Setter Property="HorizontalContentAlignment" Value="Stretch" />
-      <Setter Property="VerticalContentAlignment" Value="Stretch" />
-      <Setter Property="Template">
-        <ControlTemplate>
-          <Border x:Name="CellBorder"
-                  Background="{TemplateBinding Background}"
-                  BorderBrush="{TemplateBinding BorderBrush}"
-                  BorderThickness="{TemplateBinding BorderThickness}"
-                  CornerRadius="{TemplateBinding CornerRadius}">
-            <Grid ColumnDefinitions="*,Auto">
-              <ContentPresenter Margin="{TemplateBinding Padding}"
-                                HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
-                                VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
-                                Content="{TemplateBinding Content}"
-                                ContentTemplate="{TemplateBinding ContentTemplate}"
-                                Foreground="{TemplateBinding Foreground}" />
-
-              <Rectangle Name="PART_RightGridLine"
-                         Grid.Column="1"
-                         Width="1"
-                         VerticalAlignment="Stretch" />
-            </Grid>
-          </Border>
-        </ControlTemplate>
-      </Setter>
-    </ControlTheme>
-
-    <ControlTheme x:Key="{x:Type DataGridColumnHeader}"
-                  TargetType="DataGridColumnHeader">
-      <Setter Property="Foreground" Value="{DynamicResource ThemeForegroundBrush}" />
-      <Setter Property="HorizontalContentAlignment" Value="Left" />
-      <Setter Property="VerticalContentAlignment" Value="Center" />
-      <Setter Property="SeparatorBrush" Value="{DynamicResource ThemeControlLowColor}" />
-      <Setter Property="Padding" Value="4" />
-      <Setter Property="Background" Value="{DynamicResource ThemeControlMidBrush}" />
-      <Setter Property="Template">
-        <ControlTemplate>
-          <Border x:Name="HeaderBorder"
-                  Background="{TemplateBinding Background}"
-                  BorderBrush="{TemplateBinding BorderBrush}"
-                  BorderThickness="{TemplateBinding BorderThickness}"
-                  CornerRadius="{TemplateBinding CornerRadius}">
-            <Grid ColumnDefinitions="*,Auto">
-
-              <Grid Margin="{TemplateBinding Padding}"
-                    HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
-                    VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
-                    ColumnDefinitions="*,Auto">
-                <ContentPresenter x:Name="PART_ContentPresenter"
-                                  Content="{TemplateBinding Content}"
-                                  ContentTemplate="{TemplateBinding ContentTemplate}" />
-
-                <Path Name="SortIcon"
-                      Grid.Column="1"
-                      Width="8"
-                      Margin="4,0,0,0"
-                      HorizontalAlignment="Left"
-                      VerticalAlignment="Center"
-                      Data="F1 M -5.215,6.099L 5.215,6.099L 0,0L -5.215,6.099 Z "
-                      Fill="{TemplateBinding Foreground}"
-                      IsVisible="False"
-                      Stretch="Uniform" />
-
-              </Grid>
-
-              <Rectangle Name="VerticalSeparator"
-                         Grid.Column="1"
-                         Width="1"
-                         VerticalAlignment="Stretch"
-                         Fill="{TemplateBinding SeparatorBrush}"
-                         IsVisible="{TemplateBinding AreSeparatorsVisible}" />
-
-            </Grid>
-          </Border>
-        </ControlTemplate>
-      </Setter>
-
-      <Style Selector="^:focus-visible /template/ Grid#FocusVisual">
-        <Setter Property="IsVisible" Value="True" />
-      </Style>
-
-      <Style Selector="^:pointerover /template/ Grid#PART_ColumnHeaderRoot">
-        <Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderHoveredBackgroundBrush}" />
-      </Style>
-
-      <Style Selector="^:pressed /template/ Grid#PART_ColumnHeaderRoot">
-        <Setter Property="Background" Value="{DynamicResource DataGridColumnHeaderPressedBackgroundBrush}" />
-      </Style>
-
-      <Style Selector="^:dragIndicator">
-        <Setter Property="Opacity" Value="0.5" />
-      </Style>
-
-      <Style Selector="^:sortascending /template/ Path#SortIcon">
-        <Setter Property="IsVisible" Value="True" />
-      </Style>
-
-      <Style Selector="^:sortdescending /template/ Path#SortIcon">
-        <Setter Property="IsVisible" Value="True" />
-        <Setter Property="RenderTransform">
-          <Setter.Value>
-            <ScaleTransform ScaleX="1" ScaleY="-1" />
-          </Setter.Value>
-        </Setter>
-      </Style>
-    </ControlTheme>
-
-    <ControlTheme x:Key="{x:Type DataGridRowHeader}"
-                  TargetType="DataGridRowHeader">
-      <Setter Property="Template">
-        <ControlTemplate>
-          <Grid x:Name="PART_Root"
-                ColumnDefinitions="Auto,*"
-                RowDefinitions="*,*,Auto">
-            <Border Grid.RowSpan="3"
-                    Grid.ColumnSpan="2"
-                    BorderBrush="{TemplateBinding SeparatorBrush}"
-                    BorderThickness="0,0,1,0">
-              <Grid Background="{TemplateBinding Background}">
-                <Rectangle x:Name="RowInvalidVisualElement"
-                           Stretch="Fill" />
-                <Rectangle x:Name="BackgroundRectangle"
-                           Stretch="Fill" />
-              </Grid>
-            </Border>
-            <Rectangle x:Name="HorizontalSeparator"
-                       Grid.Row="2"
-                       Grid.ColumnSpan="2"
-                       Height="1"
-                       Margin="1,0,1,0"
-                       HorizontalAlignment="Stretch"
-                       Fill="{TemplateBinding SeparatorBrush}"
-                       IsVisible="{TemplateBinding AreSeparatorsVisible}" />
-
-            <ContentPresenter Grid.RowSpan="2"
-                              Grid.Column="1"
-                              HorizontalAlignment="Center"
-                              VerticalAlignment="Center"
-                              Content="{TemplateBinding Content}" />
-          </Grid>
-        </ControlTemplate>
-      </Setter>
-    </ControlTheme>
-
-    <ControlTheme x:Key="{x:Type DataGridRow}"
-                  TargetType="DataGridRow">
-      <Setter Property="Background" Value="{Binding $parent[DataGrid].RowBackground}" />
-      <Setter Property="Template">
-        <ControlTemplate>
-          <Border x:Name="RowBorder"
-                  Background="{TemplateBinding Background}"
-                  BorderBrush="{TemplateBinding BorderBrush}"
-                  BorderThickness="{TemplateBinding BorderThickness}"
-                  CornerRadius="{TemplateBinding CornerRadius}">
-            <DataGridFrozenGrid Name="PART_Root"
-                                ColumnDefinitions="Auto,*"
-                                RowDefinitions="*,Auto,Auto">
-
-              <Rectangle Name="BackgroundRectangle"
-                         Grid.RowSpan="2"
-                         Grid.ColumnSpan="2" />
-
-              <DataGridRowHeader Name="PART_RowHeader"
-                                 Grid.RowSpan="3"
-                                 DataGridFrozenGrid.IsFrozen="True" />
-              <DataGridCellsPresenter Name="PART_CellsPresenter"
-                                      Grid.Column="1"
-                                      DataGridFrozenGrid.IsFrozen="True" />
-              <DataGridDetailsPresenter Name="PART_DetailsPresenter"
-                                        Grid.Row="1"
-                                        Grid.Column="1" />
-              <Rectangle Name="PART_BottomGridLine"
-                         Grid.Row="2"
-                         Grid.Column="1"
-                         Height="1"
-                         HorizontalAlignment="Stretch" />
-
-            </DataGridFrozenGrid>
-          </Border>
-        </ControlTemplate>
-      </Setter>
-
-      <Style Selector="^ /template/ Rectangle#BackgroundRectangle">
-        <Setter Property="IsVisible" Value="False" />
-        <Setter Property="Fill" Value="{DynamicResource HighlightBrush2}" />
-      </Style>
-
-      <Style Selector="^:pointerover /template/ Rectangle#BackgroundRectangle">
-        <Setter Property="IsVisible" Value="True" />
-        <Setter Property="Opacity" Value="0.5" />
-      </Style>
-
-      <Style Selector="^:selected /template/ Rectangle#BackgroundRectangle">
-        <Setter Property="IsVisible" Value="True" />
-        <Setter Property="Opacity" Value="1" />
-      </Style>
-
-      <Style Selector="^:selected">
-        <Setter Property="Foreground" Value="{DynamicResource HighlightForegroundBrush}" />
-      </Style>
-    </ControlTheme>
-
-    <ControlTheme x:Key="SimpleDataGridRowGroupExpanderButtonTheme"
-                  TargetType="ToggleButton">
-      <Setter Property="Template">
-        <ControlTemplate>
-          <Border Grid.Column="0"
-                  Width="20"
-                  Height="20"
-                  HorizontalAlignment="Center"
-                  VerticalAlignment="Center"
-                  Background="Transparent">
-            <Path HorizontalAlignment="Center"
-                  VerticalAlignment="Center"
-                  Data="M 0 2 L 4 6 L 0 10 Z"
-                  Fill="{TemplateBinding Foreground}" />
-          </Border>
-        </ControlTemplate>
-      </Setter>
-      <Style Selector="^:checked /template/ Path">
-        <Setter Property="RenderTransform">
-          <RotateTransform Angle="90" />
-        </Setter>
-      </Style>
-    </ControlTheme>
-
-
-    <ControlTheme x:Key="{x:Type DataGridRowGroupHeader}"
-                  TargetType="DataGridRowGroupHeader">
-      <Setter Property="Background" Value="{DynamicResource ThemeControlMidHighBrush}" />
-      <Setter Property="Height" Value="20" />
-      <Setter Property="Template">
-        <ControlTemplate x:DataType="collections:DataGridCollectionViewGroup">
-          <DataGridFrozenGrid Name="Root"
-                              ColumnDefinitions="Auto,Auto,Auto,Auto"
-                              RowDefinitions="Auto,*,Auto">
-
-            <Rectangle Name="PART_IndentSpacer"
-                       Grid.Row="1"
-                       Grid.Column="1" />
-            <ToggleButton Name="PART_ExpanderButton"
-                          Grid.Row="1"
-                          Grid.Column="2"
-                          Margin="2,0,0,0"
-                          Background="{TemplateBinding Background}"
-                          BorderBrush="{TemplateBinding BorderBrush}"
-                          BorderThickness="{TemplateBinding BorderThickness}"
-                          CornerRadius="{TemplateBinding CornerRadius}"
-                          Foreground="{TemplateBinding Foreground}"
-                          Theme="{StaticResource SimpleDataGridRowGroupExpanderButtonTheme}" />
-
-            <StackPanel Grid.Row="1"
-                        Grid.Column="3"
-                        Margin="0,1,0,1"
-                        VerticalAlignment="Center"
-                        Orientation="Horizontal">
-              <TextBlock Name="PART_PropertyNameElement"
-                         Margin="4,0,0,0"
-                         IsVisible="{TemplateBinding IsPropertyNameVisible}" />
-              <TextBlock Margin="4,0,0,0"
-                         Text="{Binding Key}" />
-              <TextBlock Name="PART_ItemCountElement"
-                         Margin="4,0,0,0"
-                         IsVisible="{TemplateBinding IsItemCountVisible}" />
-            </StackPanel>
-
-            <DataGridRowHeader Name="RowHeader"
-                               Grid.RowSpan="3"
-                               DataGridFrozenGrid.IsFrozen="True" />
-
-          </DataGridFrozenGrid>
-        </ControlTemplate>
-      </Setter>
-    </ControlTheme>
-
-    <ControlTheme x:Key="{x:Type DataGrid}"
-                  TargetType="DataGrid">
-      <Setter Property="RowBackground" Value="{DynamicResource ThemeAccentBrush4}" />
-      <Setter Property="Background" Value="{DynamicResource ThemeBackgroundBrush}" />
-      <Setter Property="HeadersVisibility" Value="Column" />
-      <Setter Property="HorizontalScrollBarVisibility" Value="Auto" />
-      <Setter Property="VerticalScrollBarVisibility" Value="Auto" />
-      <Setter Property="SelectionMode" Value="Extended" />
-      <Setter Property="HorizontalGridLinesBrush" Value="{DynamicResource ThemeBorderHighColor}" />
-      <Setter Property="VerticalGridLinesBrush" Value="{DynamicResource ThemeBorderHighColor}" />
-      <Setter Property="BorderBrush" Value="{DynamicResource ThemeBorderLowColor}" />
-      <Setter Property="BorderThickness" Value="{DynamicResource ThemeBorderThickness}" />
-      <Setter Property="DropLocationIndicatorTemplate">
-        <Template>
-          <Rectangle Width="2"
-                     Fill="{DynamicResource ThemeBorderHighColor}" />
-        </Template>
-      </Setter>
-      <Setter Property="Template">
-        <ControlTemplate>
-          <Border x:Name="DataGridBorder"
-                  Background="{TemplateBinding Background}"
-                  BorderBrush="{TemplateBinding BorderBrush}"
-                  BorderThickness="{TemplateBinding BorderThickness}"
-                  CornerRadius="{TemplateBinding CornerRadius}">
-            <Grid ColumnDefinitions="Auto,*,Auto"
-                  RowDefinitions="Auto,*,Auto,Auto"
-                  ClipToBounds="True">
-              <DataGridColumnHeader Name="PART_TopLeftCornerHeader"
-                                    Width="22" />
-              <DataGridColumnHeadersPresenter Name="PART_ColumnHeadersPresenter"
-                                              Grid.Column="1" />
-              <DataGridColumnHeader Name="PART_TopRightCornerHeader"
-                                    Grid.Column="2" />
-              <Rectangle Name="PART_ColumnHeadersAndRowsSeparator"
-                         Grid.ColumnSpan="3"
-                         Height="1"
-                         VerticalAlignment="Bottom"
-                         Fill="{DynamicResource ThemeControlMidHighBrush}"
-                         StrokeThickness="1" />
-
-              <DataGridRowsPresenter Name="PART_RowsPresenter"
-                                     Grid.Row="1"
-                                     Grid.ColumnSpan="2"
-                                     ScrollViewer.IsScrollInertiaEnabled="{TemplateBinding IsScrollInertiaEnabled}">
-                <DataGridRowsPresenter.GestureRecognizers>
-                  <ScrollGestureRecognizer CanHorizontallyScroll="True"
-                                           CanVerticallyScroll="True"
-                                           IsScrollInertiaEnabled="{Binding (ScrollViewer.IsScrollInertiaEnabled), ElementName=PART_RowsPresenter}" />
-                </DataGridRowsPresenter.GestureRecognizers>
-              </DataGridRowsPresenter>
-              <Rectangle Name="PART_BottomRightCorner"
-                         Grid.Row="2"
-                         Grid.Column="2"
-                         Fill="{DynamicResource ThemeControlMidHighBrush}" />
-              <Rectangle Name="BottomLeftCorner"
-                         Grid.Row="2"
-                         Grid.ColumnSpan="2"
-                         Fill="{DynamicResource ThemeControlMidHighBrush}" />
-              <ScrollBar Name="PART_VerticalScrollbar"
-                         Grid.Row="1"
-                         Grid.Column="2"
-                         Width="{DynamicResource ScrollBarThickness}"
-                         Orientation="Vertical" />
-
-              <Grid Grid.Row="2"
-                    Grid.Column="1"
-                    ColumnDefinitions="Auto,*">
-                <Rectangle Name="PART_FrozenColumnScrollBarSpacer" />
-                <ScrollBar Name="PART_HorizontalScrollbar"
-                           Grid.Column="1"
-                           Height="{DynamicResource ScrollBarThickness}"
-                           Orientation="Horizontal" />
-              </Grid>
-            </Grid>
-          </Border>
-        </ControlTemplate>
-      </Setter>
-    </ControlTheme>
-  </Styles.Resources>
-</Styles>

+ 0 - 160
src/Avalonia.Controls.DataGrid/Utils/CellEditBinding.cs

@@ -1,160 +0,0 @@
-using Avalonia.Data;
-using Avalonia.Reactive;
-using System;
-using System.Collections.Generic;
-
-namespace Avalonia.Controls.Utils
-{
-    public interface ICellEditBinding
-    {
-        bool IsValid { get; }
-        IEnumerable<Exception> ValidationErrors { get; }
-        IObservable<bool> ValidationChanged { get; }
-        bool CommitEdit();
-    }
-
-    internal class CellEditBinding : ICellEditBinding
-    {
-        private readonly LightweightSubject<bool> _changedSubject = new();
-        private readonly List<Exception> _validationErrors = new List<Exception>();
-        private readonly SubjectWrapper _inner;
-
-        public bool IsValid => _validationErrors.Count <= 0;
-        public IEnumerable<Exception> ValidationErrors => _validationErrors;
-        public IObservable<bool> ValidationChanged => _changedSubject;
-        public IAvaloniaSubject<object> InternalSubject => _inner;
-
-        public CellEditBinding(IAvaloniaSubject<object> bindingSourceSubject)
-        {
-            _inner = new SubjectWrapper(bindingSourceSubject, this);
-        }
-
-        private void AlterValidationErrors(Action<List<Exception>> action)
-        {
-            var wasValid = IsValid;
-            action(_validationErrors);
-            var isValid = IsValid;
-
-            if (!isValid || !wasValid)
-            {
-                _changedSubject.OnNext(isValid);
-            }
-        }
-
-        public bool CommitEdit()
-        {
-            _inner.CommitEdit();
-            return IsValid;
-        }
-
-        class SubjectWrapper : LightweightObservableBase<object>, IAvaloniaSubject<object>, IDisposable
-        {
-            private readonly IAvaloniaSubject<object> _sourceSubject;
-            private readonly CellEditBinding _editBinding;
-            private IDisposable _subscription;
-            private object _controlValue;
-            private bool _isControlValueSet = false;
-            private bool _settingSourceValue = false;
-
-            public SubjectWrapper(IAvaloniaSubject<object> bindingSourceSubject, CellEditBinding editBinding)
-            {
-                _sourceSubject = bindingSourceSubject;
-                _editBinding = editBinding;
-            }
-
-            private void SetSourceValue(object value)
-            {
-                if (!_settingSourceValue)
-                {
-                    _settingSourceValue = true;
-
-                    _sourceSubject.OnNext(value);
-
-                    _settingSourceValue = false;
-                }
-            }
-            private void SetControlValue(object value)
-            {
-                PublishNext(value);
-            }
-
-            private void OnValidationError(BindingNotification notification)
-            {
-                if (notification.Error != null)
-                {
-                    _editBinding.AlterValidationErrors(errors =>
-                    {
-                        errors.Clear();
-                        var unpackedErrors = ValidationUtil.UnpackException(notification.Error);
-                        if (unpackedErrors != null)
-                            errors.AddRange(unpackedErrors);
-                    });
-                }
-            }
-            private void OnControlValueUpdated(object value)
-            {
-                _controlValue = value;
-                _isControlValueSet = true;
-
-                if (!_editBinding.IsValid)
-                {
-                    SetSourceValue(value);
-                }
-            }
-            private void OnSourceValueUpdated(object value)
-            {
-                void OnValidValue(object val)
-                {
-                    SetControlValue(val);
-                    _editBinding.AlterValidationErrors(errors => errors.Clear());
-                }
-
-                if (value is BindingNotification notification)
-                {
-                    if (notification.ErrorType != BindingErrorType.None)
-                        OnValidationError(notification);
-                    else
-                        OnValidValue(value);
-                }
-                else
-                {
-                    OnValidValue(value);
-                }
-            }
-
-            protected override void Deinitialize()
-            {
-                _subscription?.Dispose();
-                _subscription = null;
-            }
-            protected override void Initialize()
-            {
-                _subscription = _sourceSubject.Subscribe(OnSourceValueUpdated);
-            }
-
-            void IObserver<object>.OnCompleted()
-            {
-                throw new NotImplementedException();
-            }
-            void IObserver<object>.OnError(Exception error)
-            {
-                throw new NotImplementedException();
-            }
-            void IObserver<object>.OnNext(object value)
-            {
-                OnControlValueUpdated(value);
-            }
-
-            public void Dispose()
-            {
-                _subscription?.Dispose();
-                _subscription = null;
-            }
-            public void CommitEdit()
-            {
-                if (_isControlValueSet)
-                    SetSourceValue(_controlValue);
-            }
-        }
-    }
-}

+ 0 - 22
src/Avalonia.Controls.DataGrid/Utils/DataGridHelper.cs

@@ -1,22 +0,0 @@
-namespace Avalonia.Controls
-{
-    internal static class DataGridHelper
-    {
-        internal static void SyncColumnProperty<T>(AvaloniaObject column, AvaloniaObject content, AvaloniaProperty<T> property)
-        {
-            SyncColumnProperty(column, content, property, property);
-        }
-
-        internal static void SyncColumnProperty<T>(AvaloniaObject column, AvaloniaObject content, AvaloniaProperty<T> contentProperty, AvaloniaProperty<T> columnProperty)
-        {
-            if (!column.IsSet(columnProperty))
-            {
-                content.ClearValue(contentProperty);
-            }
-            else
-            {
-                content.SetValue(contentProperty, column.GetValue(columnProperty));
-            }
-        }
-    }
-}

+ 0 - 32
src/Avalonia.Controls.DataGrid/Utils/KeyboardHelper.cs

@@ -1,32 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Input;
-using Avalonia.Input.Platform;
-
-namespace Avalonia.Controls.Utils
-{
-    internal static class KeyboardHelper
-    {
-        public static void GetMetaKeyState(Control target, KeyModifiers modifiers, out bool ctrlOrCmd, out bool shift)
-        {
-            ctrlOrCmd = modifiers.HasFlag(GetPlatformCtrlOrCmdKeyModifier(target));
-            shift = modifiers.HasFlag(KeyModifiers.Shift);
-        }
-
-        public static void GetMetaKeyState(Control target, KeyModifiers modifiers, out bool ctrlOrCmd, out bool shift, out bool alt)
-        {
-            ctrlOrCmd = modifiers.HasFlag(GetPlatformCtrlOrCmdKeyModifier(target));
-            shift = modifiers.HasFlag(KeyModifiers.Shift);
-            alt = modifiers.HasFlag(KeyModifiers.Alt);
-        }
-
-        public static KeyModifiers GetPlatformCtrlOrCmdKeyModifier(Control target)
-        {
-            var keymap = TopLevel.GetTopLevel(target)!.PlatformSettings!.HotkeyConfiguration;
-            return keymap.CommandModifiers;
-        }
-    }
-}

+ 0 - 585
src/Avalonia.Controls.DataGrid/Utils/ReflectionHelper.cs

@@ -1,585 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.ComponentModel.DataAnnotations;
-using System.Diagnostics;
-using System.Globalization;
-using System.Reflection;
-
-namespace Avalonia.Controls.Utils
-{
-    internal static class TypeHelper
-    {
-        internal const char LeftIndexerToken = '[';
-        internal const char PropertyNameSeparator = '.';
-        internal const char RightIndexerToken = ']';
-        internal const char LeftParenthesisToken  = '(';
-        internal const char RightParenthesisToken = ')';
-
-        private static Type FindGenericType(Type definition, Type type)
-        {
-            while ((type != null) && (type != typeof(object)))
-            {
-                if (type.IsGenericType && (type.GetGenericTypeDefinition() == definition))
-                {
-                    return type;
-                }
-                if (definition.IsInterface)
-                {
-                    foreach (Type type2 in type.GetInterfaces())
-                    {
-                        Type type3 = FindGenericType(definition, type2);
-                        if (type3 != null)
-                        {
-                            return type3;
-                        }
-                    }
-                }
-                type = type.BaseType;
-            }
-            return null;
-        }
-
-        /// <summary>
-        /// Finds an int or string indexer in the specified collection of members, where int indexers take priority
-        /// over string indexers.  If found, this method will return the associated PropertyInfo and set the out index
-        /// argument to its appropriate value.  If not found, the return value will be null, as will the index.
-        /// </summary>
-        /// <param name="members">Collection of members to search through for an indexer.</param>
-        /// <param name="stringIndex">String value of indexer argument.</param>
-        /// <param name="index">Resultant index value.</param>
-        /// <returns>Indexer PropertyInfo if found, null otherwise.</returns>
-        private static PropertyInfo FindIndexerInMembers(MemberInfo[] members, string stringIndex, out object[] index)
-        {
-            index = null;
-            ParameterInfo[] parameters;
-            PropertyInfo stringIndexer = null;
-
-            foreach (PropertyInfo pi in members)
-            {
-                if (pi == null)
-                {
-                    continue;
-                }
-
-                // Only a single parameter is supported and it must be a string or Int32 value.
-                parameters = pi.GetIndexParameters();
-                if (parameters.Length > 1)
-                {
-                    continue;
-                }
-
-                if (parameters[0].ParameterType == typeof(int))
-                {
-                    int intIndex = -1;
-                    if (Int32.TryParse(stringIndex.Trim(), NumberStyles.None, CultureInfo.InvariantCulture, out intIndex))
-                    {
-                        index = new object[] { intIndex };
-                        return pi;
-                    }
-                }
-
-                // If string indexer is found save it, in case there is an int indexer.
-                if (parameters[0].ParameterType == typeof(string))
-                {
-                    index = new object[] { stringIndex };
-                    stringIndexer = pi;
-                }
-            }
-
-            return stringIndexer;
-        }
-
-        /// <summary>
-        /// Gets the default member name that is used for an indexer (e.g. "Item").
-        /// </summary>
-        /// <param name="type">Type to check.</param>
-        /// <returns>Default member name.</returns>
-        private static string GetDefaultMemberName(this Type type)
-        {
-            object[] attributes = type.GetCustomAttributes(typeof(DefaultMemberAttribute), true);
-            if (attributes != null && attributes.Length == 1)
-            {
-                DefaultMemberAttribute defaultMemberAttribute = attributes[0] as DefaultMemberAttribute;
-                return defaultMemberAttribute.MemberName;
-            }
-            else
-            {
-                return null;
-            }
-        }
-
-        /// <summary>
-        /// Finds the PropertyInfo for the specified property path within this Type, and returns
-        /// the value of GetShortName on its DisplayAttribute, if one exists. GetShortName will return
-        /// the value of Name if there is no ShortName specified.
-        /// </summary>
-        /// <param name="type">Type to search</param>
-        /// <param name="propertyPath">property path</param>
-        /// <returns>DisplayAttribute.ShortName if it exists, null otherwise</returns>
-        internal static string GetDisplayName(this Type type, string propertyPath)
-        {
-            PropertyInfo propertyInfo = type.GetNestedProperty(propertyPath);
-            if (propertyInfo != null)
-            {
-                object[] attributes = propertyInfo.GetCustomAttributes(typeof(DisplayAttribute), true);
-                if (attributes != null && attributes.Length > 0)
-                {
-                    Debug.Assert(attributes.Length == 1);
-                    if (attributes[0] is DisplayAttribute displayAttribute)
-                    {
-                        return displayAttribute.GetShortName();
-                    }
-                }
-            }
-            return null;
-        }
-
-        internal static Type GetEnumerableItemType(this Type enumerableType)
-        {
-            Type type = FindGenericType(typeof(IEnumerable<>), enumerableType);
-            if (type != null)
-            {
-                return type.GetGenericArguments()[0];
-            }
-            return enumerableType;
-        }
-
-        /// <summary>
-        /// Retrieves the value and type of a property. That property can be nested and its path
-        /// can include indexers. Each element of the path needs to be a public instance property.
-        /// </summary>
-        /// <param name="parentType">The parent Type</param>
-        /// <param name="propertyPath">Property path</param>
-        /// <param name="exception">Potential exception</param>
-        /// <param name="item">Parent item which will be set to the property value if non-null.</param>
-        /// <returns></returns>
-        private static PropertyInfo GetNestedProperty(this Type parentType, string propertyPath, out Exception exception, ref object item)
-        {
-            exception = null;
-            if (parentType == null || String.IsNullOrEmpty(propertyPath))
-            {
-                item = null;
-                return null;
-            }
-
-            Type type = parentType;
-            PropertyInfo propertyInfo = null;
-            List<string> propertyNames = SplitPropertyPath(propertyPath);
-            for (int i = 0; i < propertyNames.Count; i++)
-            {
-                // if we can't find the property or it is not of the correct type,
-                // treat it as a null value
-                propertyInfo = type.GetPropertyOrIndexer(propertyNames[i], out object[] index);
-                if (propertyInfo == null)
-                {
-                    item = null;
-                    return null;
-                }
-
-                if (!propertyInfo.CanRead)
-                {
-                    exception =
-                        new InvalidOperationException(
-                            $"The property named '{propertyNames[i]}' on type '{type.GetTypeName()}' cannot be read.");
-                    item = null;
-                    return null;
-                }
-
-                if (item != null)
-                {
-                    item = propertyInfo.GetValue(item, index);
-                }
-                type = propertyInfo.PropertyType.GetNonNullableType();
-            }
-
-            return propertyInfo;
-        }
-
-        /// <summary>
-        /// Finds the leaf PropertyInfo for the specified property path, and returns its value
-        /// if the item is non-null.
-        /// </summary>
-        /// <param name="parentType">Type to search.</param>
-        /// <param name="propertyPath">Property path.</param>
-        /// <param name="item">Parent item which will be set to the property value if non-null.</param>
-        /// <returns>The PropertyInfo.</returns>
-        internal static PropertyInfo GetNestedProperty(this Type parentType, string propertyPath, ref object item)
-        {
-            return parentType.GetNestedProperty(propertyPath, out Exception ex, ref item);
-        }
-
-        internal static PropertyInfo GetNestedProperty(this Type parentType, string propertyPath)
-        {
-            if (parentType != null)
-            {
-                object item = null;
-                return parentType.GetNestedProperty(propertyPath, ref item);
-            }
-            return null;
-        }
-
-        /// <summary>
-        /// Returns the friendly name for a type
-        /// </summary>
-        /// <param name="type">The type to get the name from</param>
-        /// <returns>Textual representation of the input type</returns>
-        internal static string GetTypeName(this Type type)
-        {
-            Type baseType = type.GetNonNullableType();
-            string s = baseType.Name;
-            if (type != baseType)
-            {
-                s += '?';
-            }
-            return s;
-        }
-
-        internal static Type GetNestedPropertyType(this Type parentType, string propertyPath)
-        {
-            if (parentType == null || String.IsNullOrEmpty(propertyPath))
-            {
-                return parentType;
-            }
-
-            PropertyInfo propertyInfo = parentType.GetNestedProperty(propertyPath);
-            if (propertyInfo != null)
-            {
-                return propertyInfo.PropertyType;
-            }
-            return null;
-        }
-
-        /// <summary>
-        /// Retrieves the value of a property. That property can be nested and its path can
-        /// include indexers. Each element of the path needs to be a public instance property.
-        /// The return value will either be of type propertyType or it will be null.
-        /// </summary>
-        /// <param name="item">Object that exposes the property</param>
-        /// <param name="propertyPath">Property path</param>
-        /// <param name="propertyType">Property type</param>
-        /// <param name="exception">Potential exception</param>
-        /// <returns>Property value</returns>
-        internal static object GetNestedPropertyValue(object item, string propertyPath, Type propertyType, out Exception exception)
-        {
-            exception = null;
-
-            // if the item is null, treat the property value as null
-            if (item == null)
-            {
-                return null;
-            }
-
-            // if the propertyPath is null or empty, return the item
-            if (String.IsNullOrEmpty(propertyPath))
-            {
-                return item;
-            }
-
-            object propertyValue = item;
-            Type itemType = item.GetType();
-            if (itemType != null)
-            {
-                PropertyInfo propertyInfo = itemType.GetNestedProperty(propertyPath, out exception, ref propertyValue);
-                if (propertyInfo != null && propertyInfo.PropertyType != propertyType)
-                {
-                    return null;
-                }
-            }
-            return propertyValue;
-        }
-
-        /// <summary>
-        /// Gets the value of a given property path on a particular data item.
-        /// </summary>
-        /// <param name="item">Parent data item.</param>
-        /// <param name="propertyPath">Property path.</param>
-        /// <returns>Value.</returns>
-        internal static object GetNestedPropertyValue(object item, string propertyPath)
-        {
-            if (item != null)
-            {
-                Type parentType = item.GetType();
-                if (String.IsNullOrEmpty(propertyPath))
-                {
-                    return item;
-                }
-                else if (parentType != null)
-                {
-                    object nestedValue = item;
-                    parentType.GetNestedProperty(propertyPath, ref nestedValue);
-                    return nestedValue;
-                }
-            }
-            return null;
-        }
-
-        internal static Type GetNonNullableType(this Type type)
-        {
-            if (IsNullableType(type))
-            {
-                return type.GetGenericArguments()[0];
-            }
-            return type;
-        }
-
-        /// <summary>
-        /// Returns the PropertyInfo for the specified property path.  If the property path
-        /// refers to an indexer (e.g. "[abc]"), then the index out parameter will be set to the value
-        /// specified in the property path.  This method only supports indexers with a single parameter
-        /// that is either an int or a string.  Int parameters take priority over string parameters.
-        /// </summary>
-        /// <param name="type">Type to search.</param>
-        /// <param name="propertyPath">Property path.</param>
-        /// <param name="index">Set to the index if return value is an indexer, otherwise null.</param>
-        /// <returns>PropertyInfo for either a property or an indexer.</returns>
-        internal static PropertyInfo GetPropertyOrIndexer(this Type type, string propertyPath, out object[] index)
-        {
-            index = null;
-            // Return the default value of GetProperty if the first character is not an indexer token.
-            if (string.IsNullOrEmpty(propertyPath) || propertyPath[0] != LeftIndexerToken)
-            {
-                var property = type.GetProperty(propertyPath);
-                if (property != null)
-                {
-                    return property;
-                }
-
-                // GetProperty does not return inherited interface properties,
-                // so we need to enumerate them manually.
-                if (type.IsInterface)
-                {
-                    foreach (var typeInterface in type.GetInterfaces())
-                    {
-                        property = type.GetProperty(propertyPath);
-                        if (property != null)
-                        {
-                            return property;
-                        }
-                    }
-                }
-
-                return null;
-            }
-
-            if (propertyPath.Length < 2 || propertyPath[propertyPath.Length - 1] != RightIndexerToken)
-            {
-                // Return null if the indexer does not meet the standard format (i.e. "[x]").
-                return null;
-            }
-
-            string stringIndex = propertyPath.Substring(1, propertyPath.Length - 2);
-            var indexer = FindIndexerInMembers(type.GetDefaultMembers(), stringIndex, out index);
-            if (indexer != null)
-            {
-                // We found the indexer, so return it.
-                return indexer;
-            }
-
-            var elementType = type.GetElementType();
-            if (elementType == null)
-            {
-                var genericArguments = type.GetGenericArguments();
-                if (genericArguments.Length == 1)
-                {
-                    elementType = genericArguments[0];
-                }
-            }
-
-            if (elementType != null)
-            {
-                // If the object is of type IList, try to use its default indexer.
-                if (typeof(IList<>).MakeGenericType(elementType) is Type genericList
-                    && genericList.IsAssignableFrom(type))
-                {
-                    indexer = FindIndexerInMembers(genericList.GetDefaultMembers(), stringIndex, out index);
-                }
-                if (typeof(IReadOnlyList<>).MakeGenericType(elementType) is Type genericReadOnlyList
-                   && genericReadOnlyList.IsAssignableFrom(type))
-                {
-                    indexer = FindIndexerInMembers(genericReadOnlyList.GetDefaultMembers(), stringIndex, out index);
-                }
-            }
-
-            return indexer;
-        }
-
-        internal static bool IsEnumerableType(this Type enumerableType)
-        {
-            return (FindGenericType(typeof(IEnumerable<>), enumerableType) != null);
-        }
-
-        internal static bool IsNullableType(this Type type)
-        {
-            return (((type != null) && type.IsGenericType) && (type.GetGenericTypeDefinition() == typeof(Nullable<>)));
-        }
-
-        internal static bool IsNullableEnum(this Type type)
-        {
-            return type.IsNullableType() &&
-                 type.GetGenericArguments().Length == 1 &&
-                 type.GetGenericArguments()[0].IsEnum;
-        }
-
-        /// <summary>
-        /// If the specified property is an indexer, this method will prepend the object's
-        /// default member name to it (e.g. "[foo]" returns "Item[foo]").
-        /// </summary>
-        /// <param name="item">Declaring data item.</param>
-        /// <param name="property">Property name.</param>
-        /// <returns>Property with default member name prepended, or property if unchanged.</returns>
-        internal static string PrependDefaultMemberName(object item, string property)
-        {
-            if (item != null && !string.IsNullOrEmpty(property) && property[0] == TypeHelper.LeftIndexerToken)
-            {
-                // The leaf property name is an indexer, so add the default member name.
-                Type declaringType = item.GetType();
-                if (declaringType != null)
-                {
-                    string defaultMemberName = declaringType.GetNonNullableType().GetDefaultMemberName();
-                    if (!string.IsNullOrEmpty(defaultMemberName))
-                    {
-                        return defaultMemberName + property;
-                    }
-                }
-            }
-            return property;
-        }
-
-        /// <summary>
-        /// If the specified property is an indexer, this method will remove the object's
-        /// default member name from it (e.g. "Item[foo]" returns "[foo]").
-        /// </summary>
-        /// <param name="property">Property name.</param>
-        /// <returns>Property with default member name removed, or property if unchanged.</returns>
-        internal static string RemoveDefaultMemberName(string property)
-        {
-            if (!string.IsNullOrEmpty(property) && property[property.Length - 1] == TypeHelper.RightIndexerToken)
-            {
-                // The property is an indexer, so remove the default member name.
-                int leftIndexerToken = property.IndexOf(TypeHelper.LeftIndexerToken);
-                if (leftIndexerToken >= 0)
-                {
-                    return property.Substring(leftIndexerToken);
-                }
-            }
-            return property;
-        }
-
-        /// <summary>
-        /// Returns a list of substrings where each one represents a single property within a nested
-        /// property path which may include indexers.  For example, the string "abc.d[efg][h].ijk"
-        /// would return the substrings: "abc", "d", "[efg]", "[h]", and "ijk".
-        /// </summary>
-        /// <param name="propertyPath">Path to split.</param>
-        /// <returns>List of property substrings.</returns>
-        internal static List<string> SplitPropertyPath(string propertyPath)
-        {
-            List<string> propertyPaths = new List<string>();
-            if (!string.IsNullOrEmpty(propertyPath))
-            {
-                bool parenthesisOn = false;
-                int startIndex = 0;
-                for (int index = 0; index < propertyPath.Length; index++)
-                {
-                    if (parenthesisOn)
-                    {
-                        if (propertyPath[index] == RightParenthesisToken)
-                        {
-                            parenthesisOn = false;
-                            startIndex = index + 1;
-                        }
-                        continue;
-                    }
-
-                    if (propertyPath[index] == LeftParenthesisToken)
-                    {
-                        parenthesisOn = true;
-                        if (startIndex != index)
-                        {
-                            propertyPaths.Add(propertyPath.Substring(startIndex, index - startIndex));
-                            startIndex = index + 1;
-                        }
-                    }
-                    else if (propertyPath[index] == PropertyNameSeparator)
-                    {
-                        if (startIndex != index)
-                        {
-                            propertyPaths.Add(propertyPath.Substring(startIndex, index - startIndex));
-                        }
-                        startIndex = index + 1;
-                    }
-                    else if (startIndex != index && propertyPath[index] == LeftIndexerToken)
-                    {
-                        propertyPaths.Add(propertyPath.Substring(startIndex, index - startIndex));
-                        startIndex = index;
-                    }
-                    else if (index == propertyPath.Length - 1)
-                    {
-                        propertyPaths.Add(propertyPath.Substring(startIndex));
-                    }
-                }
-            }
-            return propertyPaths;
-        }
-
-        /// <summary>
-        /// Checks a MemberInfo object (e.g. a Type or PropertyInfo) for the ReadOnly attribute
-        /// and returns the value of IsReadOnly if it exists.
-        /// </summary>
-        /// <param name="memberInfo">MemberInfo to check</param>
-        /// <returns>true if MemberInfo is read-only, false otherwise</returns>
-        internal static bool GetIsReadOnly(this MemberInfo memberInfo)
-        {
-            if (memberInfo != null)
-            {
-                // Check if ReadOnlyAttribute is defined on the member
-                object[] attributes = memberInfo.GetCustomAttributes(typeof(ReadOnlyAttribute), true);
-                if (attributes != null && attributes.Length > 0)
-                {
-                    ReadOnlyAttribute readOnlyAttribute = attributes[0] as ReadOnlyAttribute;
-                    Debug.Assert(readOnlyAttribute != null);
-                    return readOnlyAttribute.IsReadOnly;
-                }
-            }
-            return false;
-        }
-
-        internal static Type GetItemType(this IEnumerable list)
-        {
-            Type listType = list.GetType();
-            Type itemType = null;
-
-            // if it's a generic enumerable, we get the generic type
-
-            // Unfortunately, if data source is fed from a bare IEnumerable, TypeHelper will report an element type of object,
-            // which is not particularly interesting.  We deal with it further on.
-            if (listType.IsEnumerableType())
-            {
-                itemType = listType.GetEnumerableItemType();
-            }
-
-            // Bare IEnumerables mean that result type will be object.  In that case, we try to get something more interesting
-            if (itemType == null || itemType == typeof(object))
-            {
-                // We haven't located a type yet.. try a different approach.
-                // Does the list have anything in it?
-
-                IEnumerator en = list.GetEnumerator();
-                if (en.MoveNext() && en.Current != null)
-                {
-                    return en.Current.GetType();
-                }
-            }
-
-            // if we're null at this point, give up
-            return itemType;
-        }
-    }
-}

+ 0 - 60
src/Avalonia.Controls.DataGrid/Utils/TreeHelper.cs

@@ -1,60 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using Avalonia.Input;
-using Avalonia.VisualTree;
-using Avalonia.Controls;
-
-namespace Avalonia.Controls.Utils
-{
-    internal static class TreeHelper
-    {
-        /// <summary>
-        /// Walks the visual tree to determine if a particular child is contained within a parent Visual.
-        /// </summary>
-        /// <param name="element">Parent Visual</param>
-        /// <param name="child">Child Visual</param>
-        /// <returns>True if the parent element contains the child</returns>
-        internal static bool ContainsChild(this Visual element, Visual child)
-        {
-            if (element != null)
-            {
-                while (child != null)
-                {
-                    if (child == element)
-                    {
-                        return true;
-                    }
-
-                    // Walk up the visual tree.  If we hit the root, try using the framework element's
-                    // parent.  We do this because Popups behave differently with respect to the visual tree,
-                    // and it could have a parent even if the VisualTreeHelper doesn't find it.
-                    Visual parent = child.GetVisualParent();
-                    if (parent == null)
-                    {
-                        if (child is Control childElement)
-                        {
-                            parent = childElement.VisualParent;
-                        }
-                    }
-                    child = parent;
-                }
-            }
-            return false;
-        }
-
-        /// <summary>
-        /// Walks the visual tree to determine if the currently focused element is contained within
-        /// a parent AvaloniaObject.  The FocusManager's Current property is used to determine
-        /// the currently focused element, which is updated synchronously.
-        /// </summary>
-        /// <param name="element">Parent Visual</param>
-        /// <returns>True if the currently focused element is within the visual tree of the parent</returns>
-        internal static bool ContainsFocusedElement(this Visual element)
-        {
-            return element is InputElement { IsKeyboardFocusWithin: true };
-        }
-    }
-}

+ 0 - 172
src/Avalonia.Controls.DataGrid/Utils/ValidationUtil.cs

@@ -1,172 +0,0 @@
-// (c) Copyright Microsoft Corporation.
-// This source is subject to the Microsoft Public License (Ms-PL).
-// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
-// All other rights reserved.
-
-using System;
-using System.Collections.Generic;
-using System.ComponentModel.DataAnnotations;
-using System.Text;
-using System.Threading;
-using System.Linq;
-using Avalonia.Data;
-
-namespace Avalonia.Controls.Utils
-{
-    internal static class ValidationUtil
-    {
-        /// <summary>
-        /// Searches a ValidationResult for the specified target member name.  If the target is null
-        /// or empty, this method will return true if there are no member names at all.
-        /// </summary>
-        /// <param name="validationResult">ValidationResult to search.</param>
-        /// <param name="target">Member name to search for.</param>
-        /// <returns>True if found.</returns>
-        public static bool ContainsMemberName(this ValidationResult validationResult, string target)
-        {
-            int memberNameCount = 0;
-            foreach (string memberName in validationResult.MemberNames)
-            {
-                if (string.Equals(target, memberName))
-                {
-                    return true;
-                }
-                memberNameCount++;
-            }
-            return (memberNameCount == 0 && string.IsNullOrEmpty(target));
-        }
-
-        /// <summary>
-        /// Finds an equivalent ValidationResult if one exists.
-        /// </summary>
-        /// <param name="collection">ValidationResults to search through.</param>
-        /// <param name="target">ValidationResult to find.</param>
-        /// <returns>Equal ValidationResult if found, null otherwise.</returns>
-        public static ValidationResult FindEqualValidationResult(this ICollection<ValidationResult> collection, ValidationResult target)
-        {
-            foreach (ValidationResult oldValidationResult in collection)
-            {
-                if (oldValidationResult.ErrorMessage == target.ErrorMessage)
-                {
-                    bool movedOld = true;
-                    bool movedTarget = true;
-                    IEnumerator<string> oldEnumerator = oldValidationResult.MemberNames.GetEnumerator();
-                    IEnumerator<string> targetEnumerator = target.MemberNames.GetEnumerator();
-                    while (movedOld && movedTarget)
-                    {
-                        movedOld = oldEnumerator.MoveNext();
-                        movedTarget = targetEnumerator.MoveNext();
-
-                        if (!movedOld && !movedTarget)
-                        {
-                            return oldValidationResult;
-                        }
-                        if (movedOld != movedTarget || oldEnumerator.Current != targetEnumerator.Current)
-                        {
-                            break;
-                        }
-                    }
-                }
-            }
-            return null;
-        }
-
-        public static bool IsValid(this ValidationResult result)
-        {
-            return result == null || result == ValidationResult.Success;
-        }
-
-        public static IEnumerable<Exception> UnpackException(Exception exception)
-        {
-            if (exception != null)
-            {
-                var exceptions = exception is AggregateException aggregate ?
-                    aggregate.InnerExceptions :
-                    (IEnumerable<Exception>)new[] { exception };
-
-                return exceptions.Where(x => !(x is BindingChainException)).ToList();
-            }
-
-            return Array.Empty<Exception>();
-        }
-
-        public static object UnpackDataValidationException(Exception exception)
-        {
-            if (exception is DataValidationException dataValidationException)
-            {
-                return dataValidationException.ErrorData;
-            }
-
-            return exception;
-        }
-
-        /// <summary>
-        /// Determines whether the collection contains an equivalent ValidationResult
-        /// </summary>
-        /// <param name="collection">ValidationResults to search through</param>
-        /// <param name="target">ValidationResult to search for</param>
-        /// <returns></returns>
-        public static bool ContainsEqualValidationResult(this ICollection<ValidationResult> collection, ValidationResult target)
-        {
-            return (collection.FindEqualValidationResult(target) != null);
-        }
-
-        /// <summary>
-        /// Adds a new ValidationResult to the collection if an equivalent does not exist.
-        /// </summary>
-        /// <param name="collection">ValidationResults to search through</param>
-        /// <param name="value">ValidationResult to add</param>
-        public static void AddIfNew(this ICollection<ValidationResult> collection, ValidationResult value)
-        {
-            if (!collection.ContainsEqualValidationResult(value))
-            {
-                collection.Add(value);
-            }
-        }
-
-        private static bool ExceptionsMatch(Exception e1, Exception e2)
-        {
-            return e1.Message == e2.Message;
-        }
-        public static void AddExceptionIfNew(this ICollection<Exception> collection, Exception value)
-        {
-            if(!collection.Any(e => ExceptionsMatch(e, value)))
-            {
-                collection.Add(value);
-            }
-        }
-
-        /// <summary>
-        /// Performs an action and catches any non-critical exceptions.
-        /// </summary>
-        /// <param name="action">Action to perform</param>
-        public static void CatchNonCriticalExceptions(Action action)
-        {
-            try
-            {
-                action();
-            }
-            catch (Exception exception)
-            {
-                if (IsCriticalException(exception))
-                {
-                    throw;
-                }
-                // Catch any non-critical exceptions
-            }
-        }
-
-        /// <summary>
-        /// Determines if the specified exception is un-recoverable.
-        /// </summary>
-        /// <param name="exception">The exception.</param>
-        /// <returns>True if the process cannot be recovered from the exception.</returns>
-        public static bool IsCriticalException(Exception exception)
-        {
-            return (exception is OutOfMemoryException) ||
-                (exception is StackOverflowException) ||
-                (exception is AccessViolationException) ||
-                (exception is ThreadAbortException);
-        }
-    }
-}

+ 12 - 4
src/Avalonia.Diagnostics/Avalonia.Diagnostics.csproj

@@ -1,24 +1,32 @@
 <Project Sdk="Microsoft.NET.Sdk">
+
   <PropertyGroup>
     <TargetFrameworks>$(AvsCurrentTargetFramework);$(AvsLegacyTargetFrameworks);netstandard2.0</TargetFrameworks>
     <RootNamespace>Avalonia</RootNamespace>
+    <DefineConstants>$(DefineConstants);DATAGRID_INTERNAL</DefineConstants>
   </PropertyGroup>
+
   <ItemGroup>
-    <Compile Update="**\*.xaml.cs">
-      <DependentUpon>%(Filename)</DependentUpon>
-    </Compile>
+    <Compile Update="**/*.xaml.cs"
+             DependentUpon="%(Filename)" />
+    <Compile Include="../Avalonia.Controls.DataGrid/src/Avalonia.Controls.DataGrid/**/*.cs"
+             LinkBase="Diagnostics/Controls/DataGrid" />
+    <AvaloniaXaml Include="../Avalonia.Controls.DataGrid/src/Avalonia.Controls.DataGrid/Themes/Simple.xaml"
+                  Link="Diagnostics/Controls/DataGrid/Themes/Simple.xaml" />
   </ItemGroup>
+
   <ItemGroup>
     <ProjectReference Include="..\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj" />
-    <ProjectReference Include="..\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
     <ProjectReference Include="..\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
     <ProjectReference Include="..\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
     <ProjectReference Include="..\Avalonia.Base\Avalonia.Base.csproj" />
     <ProjectReference Include="..\Avalonia.Controls\Avalonia.Controls.csproj" />
     <ProjectReference Include="..\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
   </ItemGroup>
+
   <Import Project="..\..\build\EmbedXaml.props" />
   <Import Project="..\..\build\BuildTargets.targets" />
   <Import Project="..\..\build\NullableEnable.props" />
   <Import Project="..\..\build\DevAnalyzers.props" />
+
 </Project>

+ 1 - 1
src/Avalonia.Diagnostics/Diagnostics/Views/MainWindow.xaml

@@ -12,7 +12,7 @@
 
   <Window.Styles>
     <SimpleTheme />
-    <StyleInclude Source="avares://Avalonia.Controls.DataGrid/Themes/Simple.xaml"/>
+    <StyleInclude Source="avares://Avalonia.Diagnostics/Diagnostics/Controls/DataGrid/Themes/Simple.xaml"/>
     <StyleInclude Source="avares://Avalonia.Diagnostics/Diagnostics/Controls/ThicknessEditor.axaml" />
     <StyleInclude Source="avares://Avalonia.Diagnostics/Diagnostics/Controls/FilterTextBox.axaml" />
     <StyleInclude Source="avares://Avalonia.Diagnostics/Diagnostics/Controls/BrushEditor.axaml" />

+ 0 - 22
tests/Avalonia.Controls.DataGrid.UnitTests/Avalonia.Controls.DataGrid.UnitTests.csproj

@@ -1,22 +0,0 @@
-<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
-  <PropertyGroup>
-    <TargetFramework>$(AvsCurrentTargetFramework)</TargetFramework>
-    <OutputType>Library</OutputType>
-    <IsTestProject>true</IsTestProject>
-    <RootNamespace>Avalonia.Controls.DataGridTests</RootNamespace>
-  </PropertyGroup>
-  <Import Project="..\..\build\UnitTests.NetCore.targets" />
-  <Import Project="..\..\build\UnitTests.NetFX.props" />
-  <Import Project="..\..\build\Moq.props" />
-  <Import Project="..\..\build\XUnit.props" />
-  <Import Project="..\..\build\Rx.props" />
-  <Import Project="..\..\build\Microsoft.Reactive.Testing.props" />
-  <Import Project="..\..\build\SharedVersion.props" />
-  <ItemGroup>
-    <ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
-    <ProjectReference Include="..\Avalonia.UnitTests\Avalonia.UnitTests.csproj" />
-  </ItemGroup>
-  <ItemGroup>
-    <Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
-  </ItemGroup>
-</Project>

+ 0 - 226
tests/Avalonia.Controls.DataGrid.UnitTests/Collections/ComparerTests.cs

@@ -1,226 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.ComponentModel;
-using System.Linq;
-using Avalonia.Collections;
-using Avalonia.Logging;
-using Xunit;
-
-namespace Avalonia.Controls.DataGridTests.Collections;
-
-public class NoStringTypeComparerTests
-{
-    public static IEnumerable<object[]> GetComparerForNotStringTypeParameters()
-    {
-        yield return
-        [
-            nameof(Item.IntProp1),
-            (object item, object value) =>
-            {
-                (item as Item)!.IntProp1 = (int)value;
-            },
-            (object item) => (object)(item as Item)!.IntProp1,
-            new object[] { 2, 3, 1 },
-            new object[] { 1, 2, 3 }
-        ];
-        yield return
-        [
-            nameof(Item.IntProp2),
-            (object item, object value) =>
-            {
-                (item as Item)!.IntProp2 = (int?)value;
-            },
-            (object item) => (object)(item as Item)!.IntProp2,
-            new object[] { 2, 3, null, 1 },
-            new object[] { null, 1, 2, 3 }
-        ];
-        yield return
-        [
-            nameof(Item.DoubleProp1),
-            (object item, object value) =>
-            {
-                (item as Item)!.DoubleProp1 = (double)value;
-            },
-            (object item) => (object)(item as Item)!.DoubleProp1,
-            new object[] { 2.1, 3.1, 1.1 },
-            new object[] { 1.1, 2.1, 3.1 }
-        ];
-        yield return
-        [
-            nameof(Item.DoubleProp2),
-            (object item, object value) =>
-            {
-                (item as Item)!.DoubleProp2 = (double?)value;
-            },
-            (object item) => (object)(item as Item)!.DoubleProp2,
-            new object[] { 2.1, 3.1, null, 1.1 },
-            new object[] { null, 1.1, 2.1, 3.1 }
-        ];
-        yield return
-        [
-            nameof(Item.DecimalProp1),
-            (object item, object value) =>
-            {
-                (item as Item)!.DecimalProp1 = (decimal)value;
-            },
-            (object item) => (object)(item as Item)!.DecimalProp1,
-            new object[] { 2.1M, 3.1M, 1.1M },
-            new object[] { 1.1M, 2.1M, 3.1M }
-        ];
-        yield return
-        [
-            nameof(Item.DecimalProp2),
-            (object item, object value) =>
-            {
-                (item as Item)!.DecimalProp2 = (decimal?)value;
-            },
-            (object item) => (object)(item as Item)!.DecimalProp2,
-            new object[] { 2.1M, 3.1M, null, 1.1M },
-            new object[] { null, 1.1M, 2.1M, 3.1M }
-        ];
-        yield return
-        [
-            nameof(Item.EnumProp1),
-            (object item, object value) =>
-            {
-                (item as Item)!.EnumProp1 = (LogEventLevel)value;
-            },
-            (object item) => (object)(item as Item)!.EnumProp1,
-            new object[] { LogEventLevel.Information, LogEventLevel.Debug, LogEventLevel.Error },
-            new object[] { LogEventLevel.Debug, LogEventLevel.Information, LogEventLevel.Error }
-        ];
-        yield return
-        [
-            nameof(Item.EnumProp2),
-            (object item, object value) =>
-            {
-                (item as Item)!.EnumProp2 = (LogEventLevel?)value;
-            },
-            (object item) => (object)(item as Item)!.EnumProp2,
-            new object[]
-            {
-                LogEventLevel.Information,
-                LogEventLevel.Debug,
-                null,
-                LogEventLevel.Error
-            },
-            new object[]
-            {
-                null,
-                LogEventLevel.Debug,
-                LogEventLevel.Information,
-                LogEventLevel.Error
-            }
-        ];
-        yield return
-        [
-            nameof(Item.CustomProp2),
-            (object item, object value) =>
-            {
-                (item as Item)!.CustomProp2 = (CustomType?)value;
-            },
-            (object item) => (object)(item as Item)!.CustomProp2,
-            new object[]
-            {
-                new CustomType() { Prop = 2 },
-                new CustomType() { Prop = 3 },
-                null,
-                new CustomType() { Prop = 1 }
-            },
-            new object[]
-            {
-                null,
-                new CustomType() { Prop = 1 },
-                new CustomType() { Prop = 2 },
-                new CustomType() { Prop = 3 }
-            }
-        ];
-    }
-
-    [Theory]
-    [MemberData(nameof(GetComparerForNotStringTypeParameters))]
-    public void GetComparerForNotStringType_Correctly_WhenSorting(
-        string pathName,
-        Action<object, object> setAction,
-        Func<object, object> getAction,
-        object[] orignal,
-        object[] ordered
-    )
-    {
-        List<Item> items = new();
-        for (int i = 0; i < orignal.Length; i++)
-        {
-            var item = new Item();
-            setAction(item, orignal[i]);
-            items.Add(item);
-        }
-
-        //Ascending
-        var sortDescription = DataGridSortDescription.FromPath(
-            pathName,
-            ListSortDirection.Ascending
-        );
-        sortDescription.Initialize(typeof(Item));
-        var result = sortDescription.OrderBy(items).ToList();
-
-        for (int i = 0; i < ordered.Length; i++)
-        {
-            Assert.Equal(ordered[i], getAction(result[i]));
-        }
-
-        //Descending
-        sortDescription = DataGridSortDescription.FromPath(pathName, ListSortDirection.Descending);
-        sortDescription.Initialize(typeof(Item));
-        result = sortDescription.OrderBy(items).ToList();
-
-        ordered = ordered.Reverse().ToArray();
-        for (int i = 0; i < ordered.Length; i++)
-        {
-            Assert.Equal(ordered[i], getAction(result[i]));
-        }
-    }
-
-    private class Item
-    {
-        public int IntProp1 { get; set; }
-        public int? IntProp2 { get; set; }
-        public double DoubleProp1 { get; set; }
-        public double? DoubleProp2 { get; set; }
-
-        public decimal DecimalProp1 { get; set; }
-        public decimal? DecimalProp2 { get; set; }
-
-        public LogEventLevel EnumProp1 { get; set; }
-        public LogEventLevel? EnumProp2 { get; set; }
-        public CustomType? CustomProp2 { get; set; }
-    }
-
-    public struct CustomType : IComparable
-    {
-        public int Prop { get; set; }
-
-        public int CompareTo(object obj)
-        {
-            if (obj is CustomType other)
-            {
-                return Prop.CompareTo(other.Prop);
-            }
-            else
-            {
-                return 1;
-            }
-        }
-
-        public override bool Equals(object obj)
-        {
-            if (obj is CustomType other)
-            {
-                return Prop == other.Prop;
-            }
-            return false;
-        }
-
-        public override int GetHashCode()
-            => Prop;
-    }
-}

+ 0 - 133
tests/Avalonia.Controls.DataGrid.UnitTests/Collections/DataGridSortDescriptionTests.cs

@@ -1,133 +0,0 @@
-using System;
-using System.ComponentModel;
-using System.Linq;
-using Avalonia.Collections;
-using Xunit;
-
-namespace Avalonia.Controls.DataGridTests.Collections
-{
-
-    public class DataGridSortDescriptionTests
-    {
-        [Fact]
-        public void OrderBy_Orders_Correctly_When_Ascending()
-        {
-            var items = new[]
-            {
-                new Item("b", "b"),
-                new Item("a", "a"),
-                new Item("c", "c"),
-            };
-            var expectedResult = items.OrderBy(i => i.Prop1).ToList();
-            var sortDescription = DataGridSortDescription.FromPath(nameof(Item.Prop1), ListSortDirection.Ascending);
-            
-            sortDescription.Initialize(typeof(Item));
-            var result = sortDescription.OrderBy(items).ToList();
-            
-            Assert.Equal(expectedResult, result);
-        }
-        
-        [Fact]
-        public void OrderBy_Orders_Correctly_When_Descending()
-        {
-            var items = new[]
-            {
-                new Item("b", "b"),
-                new Item("a", "a"),
-                new Item("c", "c"),
-            };
-            var expectedResult = items.OrderByDescending(i => i.Prop1).ToList();
-            var sortDescription = DataGridSortDescription.FromPath(nameof(Item.Prop1), ListSortDirection.Descending);
-            
-            sortDescription.Initialize(typeof(Item));
-            var result = sortDescription.OrderBy(items).ToList();
-            
-            Assert.Equal(expectedResult, result);
-        }
-
-        [Fact]
-        public void ThenBy_Orders_Correctly_When_Ascending()
-        {
-            // Casting nonsense below because IOrderedEnumerable<T> isn't covariant in full framework and we need an
-            // object of type IOrderedEnumerable<object> for DataGridSortDescription.ThenBy
-            var items = new[]
-            {
-                (object)new Item("a", "b"),
-                        new Item("a", "a"),
-                        new Item("a", "c"), 
-            }.OrderBy(i => ((Item)i).Prop1);
-            var expectedResult = new[]
-            {
-                new Item("a", "a"),
-                new Item("a", "b"),
-                new Item("a", "c"),
-            };
-            var sortDescription = DataGridSortDescription.FromPath(nameof(Item.Prop2), ListSortDirection.Ascending);
-            
-            sortDescription.Initialize(typeof(Item));
-            var result = sortDescription.ThenBy(items).ToList();
-            
-            Assert.Equal(expectedResult, result);
-        }
-        
-        [Fact]
-        public void ThenBy_Orders_Correctly_When_Descending()
-        {
-            // Casting nonsense below because IOrderedEnumerable<T> isn't covariant in full framework and we need an
-            // object of type IOrderedEnumerable<object> for DataGridSortDescription.ThenBy
-            var items = new[]
-            {
-                (object)new Item("a", "b"),
-                        new Item("a", "a"),
-                        new Item("a", "c"), 
-            }.OrderBy(i => ((Item)i).Prop1);
-            var expectedResult = new[]
-            {
-                new Item("a", "c"),
-                new Item("a", "b"),
-                new Item("a", "a"),
-            };
-            var sortDescription = DataGridSortDescription.FromPath(nameof(Item.Prop2), ListSortDirection.Descending);
-            
-            sortDescription.Initialize(typeof(Item));
-            var result = sortDescription.ThenBy(items).ToList();
-            
-            Assert.Equal(expectedResult, result);
-        }
-
-        private class Item : IEquatable<Item>
-        {
-            public Item(string prop1, string prop2)
-            {
-                Prop1 = prop1;
-                Prop2 = prop2;
-            }
-
-            public string Prop1 { get; }
-            public string Prop2 { get; }
-
-            public bool Equals(Item other)
-            {
-                if (ReferenceEquals(null, other)) return false;
-                if (ReferenceEquals(this, other)) return true;
-                return Prop1 == other.Prop1 && Prop2 == other.Prop2;
-            }
-
-            public override bool Equals(object obj)
-            {
-                if (ReferenceEquals(null, obj)) return false;
-                if (ReferenceEquals(this, obj)) return true;
-                if (obj.GetType() != this.GetType()) return false;
-                return Equals((Item) obj);
-            }
-
-            public override int GetHashCode()
-            {
-                unchecked
-                {
-                    return ((Prop1 != null ? Prop1.GetHashCode() : 0) * 397) ^ (Prop2 != null ? Prop2.GetHashCode() : 0);
-                }
-            }
-        }
-    }
-}

+ 0 - 177
tests/Avalonia.Controls.DataGrid.UnitTests/DataGridRowTests.cs

@@ -1,177 +0,0 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using Avalonia.Data;
-using Avalonia.Markup.Xaml.Styling;
-using Avalonia.Styling;
-using Avalonia.UnitTests;
-using Avalonia.VisualTree;
-using Xunit;
-
-#nullable enable
-
-namespace Avalonia.Controls.DataGridTests;
-
-public class DataGridRowTests
-{
-    [Fact]
-    public void IsSelected_Binding_Works_For_Initial_Rows()
-    {
-        using var app = Start();
-        var items = Enumerable.Range(0, 100).Select(x => new Model($"Item {x}")).ToList();
-        items[2].IsSelected = true;
-        
-        var target = CreateTarget(items, [IsSelectedBinding()]);
-        var rows = GetRows(target);
-
-        Assert.Equal(0, GetFirstRealizedRowIndex(target));
-        Assert.Equal(4, GetLastRealizedRowIndex(target));
-        Assert.All(rows, x => Assert.Equal(x.Index == 2, x.IsSelected));
-    }
-
-    [Fact]
-    public void IsSelected_Binding_Works_For_Rows_Scrolled_Into_View()
-    {
-        using var app = Start();
-        var items = Enumerable.Range(0, 100).Select(x => new Model($"Item {x}")).ToList();
-        items[10].IsSelected = true;
-
-        var target = CreateTarget(items, [IsSelectedBinding()]);
-        var rows = GetRows(target);
-
-        Assert.Equal(0, GetFirstRealizedRowIndex(target));
-        Assert.Equal(4, GetLastRealizedRowIndex(target));
-
-        target.ScrollIntoView(items[10], target.Columns[0]);
-        target.UpdateLayout();
-
-        Assert.Equal(6, GetFirstRealizedRowIndex(target));
-        Assert.Equal(10, GetLastRealizedRowIndex(target));
-
-        Assert.All(rows, x => Assert.Equal(x.Index == 10, x.IsSelected));
-    }
-
-    [Fact]
-    public void Can_Toggle_IsSelected_Via_Binding()
-    {
-        using var app = Start();
-        var items = Enumerable.Range(0, 100).Select(x => new Model($"Item {x}")).ToList();
-        items[2].IsSelected = true;
-
-        var target = CreateTarget(items, [IsSelectedBinding()]);
-        var rows = GetRows(target);
-
-        Assert.Equal(0, GetFirstRealizedRowIndex(target));
-        Assert.Equal(4, GetLastRealizedRowIndex(target));
-        Assert.All(rows, x => Assert.Equal(x.Index == 2, x.IsSelected));
-
-        items[2].IsSelected = false;
-
-        Assert.All(rows, x => Assert.False(x.IsSelected));
-    }
-
-    [Fact]
-    public void Can_Toggle_IsSelected_Via_DataGrid()
-    {
-        using var app = Start();
-        var items = Enumerable.Range(0, 100).Select(x => new Model($"Item {x}")).ToList();
-        items[2].IsSelected = true;
-
-        var target = CreateTarget(items, [IsSelectedBinding()]);
-        var rows = GetRows(target);
-
-        Assert.Equal(0, GetFirstRealizedRowIndex(target));
-        Assert.Equal(4, GetLastRealizedRowIndex(target));
-        Assert.All(rows, x => Assert.Equal(x.Index == 2, x.IsSelected));
-
-        target.SelectedItems.Remove(items[2]);
-
-        Assert.All(rows, x => Assert.False(x.IsSelected));
-        Assert.False(items[2].IsSelected);
-    }
-
-    private static IDisposable Start()
-    {
-        return UnitTestApplication.Start(TestServices.StyledWindow);
-    }
-
-    private static DataGrid CreateTarget(
-        IList items,
-        IEnumerable<Style>? styles = null)
-    {
-        var root = new TestRoot
-        {
-            ClientSize = new(100, 100),
-            Styles =
-            {
-                new StyleInclude((Uri?)null)
-                {
-                    Source = new Uri("avares://Avalonia.Controls.DataGrid/Themes/Simple.xaml")
-                },
-            }
-        };
-
-        var target = new DataGrid 
-        { 
-            Columns =
-            {
-                new DataGridTextColumn { Header = "Name", Binding = new Binding("Name") }
-            },
-            ItemsSource = items
-        };
-
-        if (styles is not null)
-        {
-            foreach (var style in styles)
-                target.Styles.Add(style);
-        }
-
-        root.Child = target;
-        root.ExecuteInitialLayoutPass();
-        return target;
-    }
-
-    private static int GetFirstRealizedRowIndex(DataGrid target)
-    {
-        return target.GetSelfAndVisualDescendants().OfType<DataGridRow>().Select(x => x.Index).Min();
-    }
-
-    private static int GetLastRealizedRowIndex(DataGrid target)
-    {
-        return target.GetSelfAndVisualDescendants().OfType<DataGridRow>().Select(x => x.Index).Max();
-    }
-
-    private static IReadOnlyList<DataGridRow> GetRows(DataGrid target)
-    {
-        return target.GetSelfAndVisualDescendants().OfType<DataGridRow>().ToList();
-    }
-
-    private static Style IsSelectedBinding()
-    {
-        return new Style(x => x.OfType<DataGridRow>())
-        {
-            Setters = { new Setter(DataGridRow.IsSelectedProperty, new Binding("IsSelected", BindingMode.TwoWay)) }
-        };
-    }
-
-    private class Model : NotifyingBase
-    {
-        private bool _isSelected;
-        private string _name;
-
-        public Model(string name) => _name = name;
-
-        public bool IsSelected 
-        {
-            get => _isSelected;
-            set => SetField(ref _isSelected, value);
-        }
-
-        public string Name 
-        { 
-            get => _name;
-            set => SetField(ref _name, value);
-        }
-    }
-}

+ 0 - 20
tests/Avalonia.Controls.DataGrid.UnitTests/Utils/ReflectionHelperTests.cs

@@ -1,20 +0,0 @@
-
-using Avalonia.Controls.Utils;
-using Xunit;
-
-namespace Avalonia.Controls.DataGridTests.Utils
-{
-    public class ReflectionHelperTests
-    {
-        [Fact]
-        public void SplitPropertyPath_Splits_PropertyPath_With_Cast()
-        {
-            var path = "(Type).Property";
-            var expected = new [] { "Property" };
-
-            var result = TypeHelper.SplitPropertyPath(path);
-            
-            Assert.Equal(expected, result);
-        }
-    }
-}

+ 0 - 1
tests/Avalonia.LeakTests/Avalonia.LeakTests.csproj

@@ -10,7 +10,6 @@
   <Import Project="..\..\build\SharedVersion.props" />
   <ItemGroup>
     <ProjectReference Include="..\..\src\Avalonia.Controls.ColorPicker\Avalonia.Controls.ColorPicker.csproj" />
-    <ProjectReference Include="..\..\src\Avalonia.Controls.DataGrid\Avalonia.Controls.DataGrid.csproj" />
     <ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj" />
     <ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj" />
     <ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj" />

+ 0 - 41
tests/Avalonia.LeakTests/ControlTests.cs

@@ -39,47 +39,6 @@ namespace Avalonia.LeakTests
             DotMemoryUnitTestOutput.SetOutputMethod(atr.WriteLine);
         }
 
- 
-        [Fact]
-        public void DataGrid_Is_Freed()
-        {
-            using (Start())
-            {
-                // When attached to INotifyCollectionChanged, DataGrid will subscribe to it's events, potentially causing leak
-                Func<Window> run = () =>
-                {
-                    var window = new Window
-                    {
-                        Content = new DataGrid
-                        {
-                            ItemsSource = _observableCollection
-                        }
-                    };
-
-                    window.Show();
-
-                    // Do a layout and make sure that DataGrid gets added to visual tree.
-                    window.LayoutManager.ExecuteInitialLayoutPass();
-                    Assert.IsType<DataGrid>(window.Presenter.Child);
-
-                    // Clear the content and ensure the DataGrid is removed.
-                    window.Content = null;
-                    window.LayoutManager.ExecuteLayoutPass();
-                    Assert.Null(window.Presenter.Child);
-
-                    return window;
-                };
-
-                var result = run();
-
-                // Process all Loaded events to free control reference(s)
-                Dispatcher.UIThread.RunJobs(DispatcherPriority.Loaded);
-
-                dotMemory.Check(memory =>
-                    Assert.Equal(0, memory.GetObjects(where => where.Type.Is<DataGrid>()).ObjectsCount));
-            }
-        }
-
         [Fact]
         public void Canvas_Is_Freed()
         {

この差分においてかなりの量のファイルが変更されているため、一部のファイルを表示していません