├── .editorconfig ├── .gitattributes ├── .github ├── FUNDING.yml ├── stale.yml └── workflows │ └── build.yml ├── .gitignore ├── LICENSE.TXT ├── NuGet.Config ├── README.md ├── TreeDataGridEx.sln ├── TreeDataGridEx ├── Properties │ └── AssemblyInfo.cs ├── TreeDataGridCheckBoxColumn.cs ├── TreeDataGridColumn.cs ├── TreeDataGridColumnBase.cs ├── TreeDataGridColumnsTemplate.cs ├── TreeDataGridEx.axaml ├── TreeDataGridEx.axaml.cs ├── TreeDataGridEx.csproj ├── TreeDataGridHierarchicalExpanderColumn.cs ├── TreeDataGridTemplateColumn.cs └── TreeDataGridTextColumn.cs ├── TreeDataGridExDemo ├── App.axaml ├── App.axaml.cs ├── Assets │ └── avalonia-logo.ico ├── Models │ ├── Countries.cs │ ├── Country.cs │ └── DragDropItem.cs ├── Program.cs ├── TreeDataGridExDemo.csproj ├── ViewLocator.cs ├── ViewModels │ ├── MainWindowViewModel.cs │ └── ViewModelBase.cs ├── Views │ ├── MainWindow.axaml │ └── MainWindow.axaml.cs └── app.manifest └── global.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Default settings: 7 | # A newline ending every file 8 | # Use 4 spaces as indentation 9 | [*] 10 | insert_final_newline = true 11 | indent_style = space 12 | indent_size = 4 13 | 14 | # C# files 15 | [*.cs] 16 | # New line preferences 17 | csharp_new_line_before_open_brace = all 18 | csharp_new_line_before_else = true 19 | csharp_new_line_before_catch = true 20 | csharp_new_line_before_finally = true 21 | csharp_new_line_before_members_in_object_initializers = true 22 | csharp_new_line_before_members_in_anonymous_types = true 23 | csharp_new_line_between_query_expression_clauses = true 24 | 25 | # Indentation preferences 26 | csharp_indent_block_contents = true 27 | csharp_indent_braces = false 28 | csharp_indent_case_contents = true 29 | csharp_indent_switch_labels = true 30 | csharp_indent_labels = one_less_than_current 31 | 32 | # avoid this. unless absolutely necessary 33 | dotnet_style_qualification_for_field = false:suggestion 34 | dotnet_style_qualification_for_property = false:suggestion 35 | dotnet_style_qualification_for_method = false:suggestion 36 | dotnet_style_qualification_for_event = false:suggestion 37 | 38 | # prefer var 39 | csharp_style_var_for_built_in_types = true 40 | csharp_style_var_when_type_is_apparent = true 41 | csharp_style_var_elsewhere = true:suggestion 42 | 43 | # use language keywords instead of BCL types 44 | dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion 45 | dotnet_style_predefined_type_for_member_access = true:suggestion 46 | 47 | # name all constant fields using PascalCase 48 | dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion 49 | dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields 50 | dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style 51 | 52 | dotnet_naming_symbols.constant_fields.applicable_kinds = field 53 | dotnet_naming_symbols.constant_fields.required_modifiers = const 54 | 55 | dotnet_naming_style.pascal_case_style.capitalization = pascal_case 56 | 57 | # static fields should have s_ prefix 58 | dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion 59 | dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields 60 | dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style 61 | 62 | dotnet_naming_symbols.static_fields.applicable_kinds = field 63 | dotnet_naming_symbols.static_fields.required_modifiers = static 64 | 65 | dotnet_naming_style.static_prefix_style.required_prefix = s_ 66 | dotnet_naming_style.static_prefix_style.capitalization = camel_case 67 | 68 | # internal and private fields should be _camelCase 69 | dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion 70 | dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields 71 | dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style 72 | 73 | dotnet_naming_symbols.private_internal_fields.applicable_kinds = field 74 | dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal 75 | 76 | dotnet_naming_style.camel_case_underscore_style.required_prefix = _ 77 | dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case 78 | 79 | # use accessibility modifiers 80 | dotnet_style_require_accessibility_modifiers = for_non_interface_members:suggestion 81 | 82 | # Code style defaults 83 | dotnet_sort_system_directives_first = true 84 | csharp_preserve_single_line_blocks = true 85 | csharp_preserve_single_line_statements = false 86 | 87 | # Expression-level preferences 88 | dotnet_style_object_initializer = true:suggestion 89 | dotnet_style_collection_initializer = true:suggestion 90 | dotnet_style_explicit_tuple_names = true:suggestion 91 | dotnet_style_coalesce_expression = true:suggestion 92 | dotnet_style_null_propagation = true:suggestion 93 | 94 | # Expression-bodied members 95 | csharp_style_expression_bodied_methods = false:none 96 | csharp_style_expression_bodied_constructors = false:none 97 | csharp_style_expression_bodied_operators = false:none 98 | csharp_style_expression_bodied_properties = true:none 99 | csharp_style_expression_bodied_indexers = true:none 100 | csharp_style_expression_bodied_accessors = true:none 101 | 102 | # Pattern matching 103 | csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion 104 | csharp_style_pattern_matching_over_as_with_null_check = true:suggestion 105 | csharp_style_inlined_variable_declaration = true:suggestion 106 | 107 | # Null checking preferences 108 | csharp_style_throw_expression = true:suggestion 109 | csharp_style_conditional_delegate_call = true:suggestion 110 | 111 | # Space preferences 112 | csharp_space_after_cast = false 113 | csharp_space_after_colon_in_inheritance_clause = true 114 | csharp_space_after_comma = true 115 | csharp_space_after_dot = false 116 | csharp_space_after_keywords_in_control_flow_statements = true 117 | csharp_space_after_semicolon_in_for_statement = true 118 | csharp_space_around_binary_operators = before_and_after 119 | csharp_space_around_declaration_statements = do_not_ignore 120 | csharp_space_before_colon_in_inheritance_clause = true 121 | csharp_space_before_comma = false 122 | csharp_space_before_dot = false 123 | csharp_space_before_open_square_brackets = false 124 | csharp_space_before_semicolon_in_for_statement = false 125 | csharp_space_between_empty_square_brackets = false 126 | csharp_space_between_method_call_empty_parameter_list_parentheses = false 127 | csharp_space_between_method_call_name_and_opening_parenthesis = false 128 | csharp_space_between_method_call_parameter_list_parentheses = false 129 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false 130 | csharp_space_between_method_declaration_name_and_open_parenthesis = false 131 | csharp_space_between_method_declaration_parameter_list_parentheses = false 132 | csharp_space_between_parentheses = false 133 | csharp_space_between_square_brackets = false 134 | 135 | # Xaml files 136 | [*.{xaml,axaml}] 137 | indent_style = space 138 | indent_size = 2 139 | 140 | # Xml project files 141 | [*.{csproj,vcxproj,vcxproj.filters,proj,nativeproj,locproj}] 142 | indent_size = 2 143 | 144 | # Xml build files 145 | [*.builds] 146 | indent_size = 2 147 | 148 | # Xml files 149 | [*.{xml,stylecop,resx,ruleset}] 150 | indent_size = 2 151 | 152 | # Xml config files 153 | [*.{props,targets,config,nuspec}] 154 | indent_size = 2 155 | 156 | # Shell scripts 157 | [*.sh] 158 | end_of_line = lf 159 | [*.{cmd, bat}] 160 | end_of_line = crlf -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [wieslawsoltes] 2 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # https://probot.github.io/apps/stale/ 2 | 3 | # Number of days of inactivity before an issue becomes stale 4 | daysUntilStale: 60 5 | 6 | # Number of days of inactivity before a stale issue is closed 7 | daysUntilClose: 7 8 | 9 | # Issues with these labels will never be considered stale 10 | exemptLabels: 11 | - pinned 12 | - security 13 | 14 | # Label to use when marking an issue as stale 15 | staleLabel: stale 16 | 17 | # Comment to post when marking an issue as stale. Set to `false` to disable 18 | markComment: > 19 | This issue has been automatically marked as stale because it has not had 20 | recent activity. It will be closed if no further activity occurs. Thank you 21 | for your contributions. 22 | 23 | # Comment to post when closing a stale issue. Set to `false` to disable 24 | closeComment: true 25 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - release/* 8 | pull_request: 9 | branches: 10 | - main 11 | 12 | jobs: 13 | build: 14 | strategy: 15 | matrix: 16 | os: [ubuntu-latest, windows-latest, macos-latest] 17 | name: Build ${{ matrix.os }} 18 | runs-on: ${{ matrix.os }} 19 | 20 | steps: 21 | - uses: actions/checkout@v1 22 | - name: Setup .NET Core 23 | uses: actions/setup-dotnet@v1 24 | - name: Build Release 25 | run: dotnet build --configuration Release 26 | - name: Test Release 27 | run: dotnet test --configuration Release 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | # .NET Core 46 | project.lock.json 47 | project.fragment.lock.json 48 | artifacts/ 49 | **/Properties/launchSettings.json 50 | 51 | *_i.c 52 | *_p.c 53 | *_i.h 54 | *.ilk 55 | *.meta 56 | *.obj 57 | *.pch 58 | *.pdb 59 | *.pgc 60 | *.pgd 61 | *.rsp 62 | *.sbr 63 | *.tlb 64 | *.tli 65 | *.tlh 66 | *.tmp 67 | *.tmp_proj 68 | *.log 69 | *.vspscc 70 | *.vssscc 71 | .builds 72 | *.pidb 73 | *.svclog 74 | *.scc 75 | 76 | # Chutzpah Test files 77 | _Chutzpah* 78 | 79 | # Visual C++ cache files 80 | ipch/ 81 | *.aps 82 | *.ncb 83 | *.opendb 84 | *.opensdf 85 | *.sdf 86 | *.cachefile 87 | *.VC.db 88 | *.VC.VC.opendb 89 | 90 | # Visual Studio profiler 91 | *.psess 92 | *.vsp 93 | *.vspx 94 | *.sap 95 | 96 | # TFS 2012 Local Workspace 97 | $tf/ 98 | 99 | # Guidance Automation Toolkit 100 | *.gpState 101 | 102 | # ReSharper is a .NET coding add-in 103 | _ReSharper*/ 104 | *.[Rr]e[Ss]harper 105 | *.DotSettings.user 106 | 107 | # JustCode is a .NET coding add-in 108 | .JustCode 109 | 110 | # TeamCity is a build add-in 111 | _TeamCity* 112 | 113 | # DotCover is a Code Coverage Tool 114 | *.dotCover 115 | 116 | # Visual Studio code coverage results 117 | *.coverage 118 | *.coveragexml 119 | 120 | # NCrunch 121 | _NCrunch_* 122 | .*crunch*.local.xml 123 | nCrunchTemp_* 124 | 125 | # MightyMoose 126 | *.mm.* 127 | AutoTest.Net/ 128 | 129 | # Web workbench (sass) 130 | .sass-cache/ 131 | 132 | # Installshield output folder 133 | [Ee]xpress/ 134 | 135 | # DocProject is a documentation generator add-in 136 | DocProject/buildhelp/ 137 | DocProject/Help/*.HxT 138 | DocProject/Help/*.HxC 139 | DocProject/Help/*.hhc 140 | DocProject/Help/*.hhk 141 | DocProject/Help/*.hhp 142 | DocProject/Help/Html2 143 | DocProject/Help/html 144 | 145 | # Click-Once directory 146 | publish/ 147 | 148 | # Publish Web Output 149 | *.[Pp]ublish.xml 150 | *.azurePubxml 151 | # TODO: Comment the next line if you want to checkin your web deploy settings 152 | # but database connection strings (with potential passwords) will be unencrypted 153 | *.pubxml 154 | *.publishproj 155 | 156 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 157 | # checkin your Azure Web App publish settings, but sensitive information contained 158 | # in these scripts will be unencrypted 159 | PublishScripts/ 160 | 161 | # NuGet Packages 162 | *.nupkg 163 | # The packages folder can be ignored because of Package Restore 164 | **/packages/* 165 | # except build/, which is used as an MSBuild target. 166 | !**/packages/build/ 167 | # Uncomment if necessary however generally it will be regenerated when needed 168 | #!**/packages/repositories.config 169 | # NuGet v3's project.json files produces more ignorable files 170 | *.nuget.props 171 | *.nuget.targets 172 | 173 | # Microsoft Azure Build Output 174 | csx/ 175 | *.build.csdef 176 | 177 | # Microsoft Azure Emulator 178 | ecf/ 179 | rcf/ 180 | 181 | # Windows Store app package directories and files 182 | AppPackages/ 183 | BundleArtifacts/ 184 | Package.StoreAssociation.xml 185 | _pkginfo.txt 186 | 187 | # Visual Studio cache files 188 | # files ending in .cache can be ignored 189 | *.[Cc]ache 190 | # but keep track of directories ending in .cache 191 | !*.[Cc]ache/ 192 | 193 | # Others 194 | ClientBin/ 195 | ~$* 196 | *~ 197 | *.dbmdl 198 | *.dbproj.schemaview 199 | *.jfm 200 | *.pfx 201 | *.publishsettings 202 | orleans.codegen.cs 203 | 204 | # Since there are multiple workflows, uncomment next line to ignore bower_components 205 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 206 | #bower_components/ 207 | 208 | # RIA/Silverlight projects 209 | Generated_Code/ 210 | 211 | # Backup & report files from converting an old project file 212 | # to a newer Visual Studio version. Backup files are not needed, 213 | # because we have git ;-) 214 | _UpgradeReport_Files/ 215 | Backup*/ 216 | UpgradeLog*.XML 217 | UpgradeLog*.htm 218 | 219 | # SQL Server files 220 | *.mdf 221 | *.ldf 222 | *.ndf 223 | 224 | # Business Intelligence projects 225 | *.rdl.data 226 | *.bim.layout 227 | *.bim_*.settings 228 | 229 | # Microsoft Fakes 230 | FakesAssemblies/ 231 | 232 | # GhostDoc plugin setting file 233 | *.GhostDoc.xml 234 | 235 | # Node.js Tools for Visual Studio 236 | .ntvs_analysis.dat 237 | node_modules/ 238 | 239 | # Typescript v1 declaration files 240 | typings/ 241 | 242 | # Visual Studio 6 build log 243 | *.plg 244 | 245 | # Visual Studio 6 workspace options file 246 | *.opt 247 | 248 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 249 | *.vbw 250 | 251 | # Visual Studio LightSwitch build output 252 | **/*.HTMLClient/GeneratedArtifacts 253 | **/*.DesktopClient/GeneratedArtifacts 254 | **/*.DesktopClient/ModelManifest.xml 255 | **/*.Server/GeneratedArtifacts 256 | **/*.Server/ModelManifest.xml 257 | _Pvt_Extensions 258 | 259 | # Paket dependency manager 260 | .paket/paket.exe 261 | paket-files/ 262 | 263 | # FAKE - F# Make 264 | .fake/ 265 | 266 | # JetBrains Rider 267 | .idea/ 268 | *.sln.iml 269 | 270 | # CodeRush 271 | .cr/ 272 | 273 | # Python Tools for Visual Studio (PTVS) 274 | __pycache__/ 275 | *.pyc 276 | 277 | # Cake - Uncomment if you are using it 278 | tools/** 279 | # !tools/packages.config 280 | 281 | # Telerik's JustMock configuration file 282 | *.jmconfig 283 | 284 | # BizTalk build output 285 | *.btp.cs 286 | *.btm.cs 287 | *.odx.cs 288 | *.xsd.cs 289 | 290 | # macOS 291 | .DS_Store 292 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Wiesław Šoltés 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /NuGet.Config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TreeDataGridEx 2 | 3 | [![CI](https://github.com/wieslawsoltes/TreeDataGridEx/actions/workflows/build.yml/badge.svg)](https://github.com/wieslawsoltes/TreeDataGridEx/actions/workflows/build.yml) 4 | 5 | [![NuGet](https://img.shields.io/nuget/v/TreeDataGridEx.svg)](https://www.nuget.org/packages/TreeDataGridEx) 6 | [![NuGet](https://img.shields.io/nuget/dt/TreeDataGridEx.svg)](https://www.nuget.org/packages/TreeDataGridEx) 7 | 8 | **TreeDataGridEx** is an experimental version of [TreeDataGrid](https://github.com/AvaloniaUI/Avalonia.Controls.TreeDataGrid) for [Avalonia](https://github.com/AvaloniaUI/Avalonia) with added XAML syntax. 9 | 10 | ![image](https://github.com/wieslawsoltes/TreeDataGridEx/assets/2297442/cd6d9484-3707-40a7-b012-e0d58966c406) 11 | 12 | ## Usage 13 | 14 | _**Warning:** Please note that the TreeDataGridEx control uses reflection so it might cause issues when using AOT compilation or trimming._ 15 | 16 | ### NuGet 17 | 18 | TreeDataGridEx is delivered as a NuGet package. 19 | 20 | You can find the packages here [NuGet](https://www.nuget.org/packages/TreeDataGridEx/) and install the package like this: 21 | 22 | `Install-Package TreeDataGridEx` 23 | 24 | ### Styles 25 | 26 | Add styles to `App.axaml`: 27 | 28 | ```xaml 29 | 30 | 31 | 32 | 33 | 34 | ``` 35 | 36 | ### Columns 37 | 38 | Available column types: 39 | - TreeDataGridTemplateColumn 40 | - TreeDataGridTextColumn 41 | - TreeDataGridCheckBoxColumn 42 | - TreeDataGridHierarchicalExpanderColumn 43 | 44 | ### Example: Countries 45 | 46 | ```xaml 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | ``` 69 | 70 | ### Example: Drag/Drop 71 | 72 | ```xaml 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | ``` 83 | 84 | ### Example: Setting Columns from Style 85 | 86 | To set `Columns` property from Style use `TreeDataGridColumnsTemplate` as `Setter.Value`. 87 | 88 | ```xaml 89 | 90 | 91 | 92 | 106 | 107 | 108 | 109 | 110 | 111 | ``` 112 | 113 | ## Resources 114 | 115 | * [GitHub source code repository.](https://github.com/wieslawsoltes/TreeDataGridEx) 116 | 117 | ## License 118 | 119 | TreeDataGridEx is licensed under the [MIT license](LICENSE.TXT). 120 | -------------------------------------------------------------------------------- /TreeDataGridEx.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TreeDataGridEx", "TreeDataGridEx\TreeDataGridEx.csproj", "{122C6D32-0E37-4EC1-9109-CC2475DF558E}" 4 | EndProject 5 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{E8C54A26-9629-4EFB-B414-A955D99386EC}" 6 | ProjectSection(SolutionItems) = preProject 7 | global.json = global.json 8 | NuGet.Config = NuGet.Config 9 | EndProjectSection 10 | EndProject 11 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TreeDataGridExDemo", "TreeDataGridExDemo\TreeDataGridExDemo.csproj", "{6275CD13-658A-48EE-969B-007594B982B5}" 12 | EndProject 13 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{575FCB8F-C920-4D20-91A5-D27B61D56DC8}" 14 | ProjectSection(SolutionItems) = preProject 15 | README.md = README.md 16 | LICENSE.TXT = LICENSE.TXT 17 | EndProjectSection 18 | EndProject 19 | Global 20 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 21 | Debug|Any CPU = Debug|Any CPU 22 | Release|Any CPU = Release|Any CPU 23 | EndGlobalSection 24 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 25 | {122C6D32-0E37-4EC1-9109-CC2475DF558E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 26 | {122C6D32-0E37-4EC1-9109-CC2475DF558E}.Debug|Any CPU.Build.0 = Debug|Any CPU 27 | {122C6D32-0E37-4EC1-9109-CC2475DF558E}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {122C6D32-0E37-4EC1-9109-CC2475DF558E}.Release|Any CPU.Build.0 = Release|Any CPU 29 | {6275CD13-658A-48EE-969B-007594B982B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 30 | {6275CD13-658A-48EE-969B-007594B982B5}.Debug|Any CPU.Build.0 = Debug|Any CPU 31 | {6275CD13-658A-48EE-969B-007594B982B5}.Release|Any CPU.ActiveCfg = Release|Any CPU 32 | {6275CD13-658A-48EE-969B-007594B982B5}.Release|Any CPU.Build.0 = Release|Any CPU 33 | EndGlobalSection 34 | EndGlobal 35 | -------------------------------------------------------------------------------- /TreeDataGridEx/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using Avalonia.Metadata; 3 | 4 | [assembly: XmlnsDefinition("https://github.com/avaloniaui", "TreeDataGridEx")] 5 | -------------------------------------------------------------------------------- /TreeDataGridEx/TreeDataGridCheckBoxColumn.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Avalonia; 4 | using Avalonia.Controls.Models.TreeDataGrid; 5 | using Avalonia.Data; 6 | using Avalonia.Markup.Xaml.MarkupExtensions; 7 | using Avalonia.Metadata; 8 | 9 | namespace TreeDataGridEx; 10 | 11 | public class TreeDataGridCheckBoxColumn : TreeDataGridColumnBase 12 | { 13 | public static readonly DirectProperty BindingProperty = 14 | AvaloniaProperty.RegisterDirect( 15 | nameof(Binding), 16 | o => o.Binding, 17 | (o, v) => o.Binding = v); 18 | 19 | private IBinding? _binding; 20 | 21 | [Content] 22 | [InheritDataTypeFromItems(nameof(TreeDataGridEx.ItemsSource), AncestorType = typeof(TreeDataGridEx))] 23 | [AssignBinding] 24 | public IBinding? Binding 25 | { 26 | get { return _binding; } 27 | set { SetAndRaise(BindingProperty, ref _binding, value); } 28 | } 29 | 30 | [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(CheckBoxColumn<>))] 31 | [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ColumnOptions<>))] 32 | [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(CheckBoxColumnOptions<>))] 33 | internal override IColumn? Create( 34 | [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] 35 | Type modelType) 36 | { 37 | var header = Header; 38 | var binding = Binding; 39 | var width = Width; 40 | 41 | if (binding is null) 42 | { 43 | return null; 44 | } 45 | 46 | var path = (binding as Binding)?.Path ?? (binding as CompiledBindingExtension)?.Path.ToString(); 47 | if (path is null) 48 | { 49 | return null; 50 | } 51 | 52 | var property = modelType.GetProperty(path); 53 | if (property is null) 54 | { 55 | return null; 56 | } 57 | 58 | var getter = CreateGetterLambdaExpression(modelType, property); 59 | var type = typeof(CheckBoxColumn<>).MakeGenericType(modelType); 60 | 61 | var optionsType = typeof(CheckBoxColumnOptions<>).MakeGenericType(modelType); 62 | var options = Activator.CreateInstance(optionsType); 63 | 64 | // ColumnOptions 65 | optionsType.GetProperty("CanUserResizeColumn")?.SetValue(options, CanUserResizeColumn); 66 | optionsType.GetProperty("CanUserSortColumn")?.SetValue(options, CanUserSortColumn); 67 | optionsType.GetProperty("MinWidth")?.SetValue(options, MinWidth); 68 | optionsType.GetProperty("MaxWidth")?.SetValue(options, MaxWidth); 69 | // TODO: CompareAscending 70 | // TODO: CompareDescending 71 | optionsType.GetProperty("BeginEditGestures")?.SetValue(options, BeginEditGestures); 72 | 73 | // CheckBoxColumnOptions (none) 74 | 75 | if (!property.CanWrite || (property.SetMethod is not null && !property.SetMethod.IsPublic)) 76 | { 77 | return (IColumn?) Activator.CreateInstance(type, header, getter, width, options); 78 | } 79 | 80 | var setter = CreateSetterLambdaExpression(modelType, property).Compile(); 81 | 82 | return (IColumn?) Activator.CreateInstance(type, header, getter, setter, width, options); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /TreeDataGridEx/TreeDataGridColumn.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Avalonia; 4 | using Avalonia.Controls.Models.TreeDataGrid; 5 | using Avalonia.Metadata; 6 | 7 | namespace TreeDataGridEx; 8 | 9 | public abstract class TreeDataGridColumn : AvaloniaObject 10 | { 11 | public static readonly DirectProperty DataTypeProperty = 12 | AvaloniaProperty.RegisterDirect( 13 | nameof(DataType), 14 | o => o.DataType, 15 | (o, v) => o.DataType = v); 16 | 17 | private Type? _dataType; 18 | 19 | [InheritDataTypeFromItems(nameof(TreeDataGridEx.ItemsSource), AncestorType = typeof(TreeDataGridEx))] 20 | [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] 21 | public Type? DataType 22 | { 23 | get { return _dataType; } 24 | set { SetAndRaise(DataTypeProperty, ref _dataType, value); } 25 | } 26 | 27 | internal abstract IColumn? Create( 28 | [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] 29 | Type modelType); 30 | } 31 | -------------------------------------------------------------------------------- /TreeDataGridEx/TreeDataGridColumnBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using System.Linq.Expressions; 4 | using System.Reflection; 5 | using Avalonia; 6 | using Avalonia.Controls; 7 | using Avalonia.Controls.Models.TreeDataGrid; 8 | 9 | namespace TreeDataGridEx; 10 | 11 | public abstract class TreeDataGridColumnBase : TreeDataGridColumn 12 | { 13 | public static readonly StyledProperty CanUserResizeColumnProperty = 14 | AvaloniaProperty.Register(nameof(CanUserResizeColumn)); 15 | 16 | public static readonly StyledProperty CanUserSortColumnProperty = 17 | AvaloniaProperty.Register(nameof(CanUserSortColumn)); 18 | 19 | public static readonly StyledProperty MinWidthProperty = 20 | AvaloniaProperty.Register(nameof(MinWidth), new GridLength(30, GridUnitType.Pixel)); 21 | 22 | public static readonly StyledProperty MaxWidthProperty = 23 | AvaloniaProperty.Register(nameof(MaxWidth)); 24 | 25 | public static readonly StyledProperty BeginEditGesturesProperty = 26 | AvaloniaProperty.Register(nameof(BeginEditGestures), BeginEditGestures.Default); 27 | 28 | public static readonly DirectProperty HeaderProperty = 29 | AvaloniaProperty.RegisterDirect( 30 | nameof(Header), 31 | o => o.Header, 32 | (o, v) => o.Header = v); 33 | 34 | public static readonly StyledProperty WidthProperty = 35 | AvaloniaProperty.Register(nameof(Width), GridLength.Auto); 36 | 37 | private object? _header; 38 | 39 | public bool? CanUserResizeColumn 40 | { 41 | get => GetValue(CanUserResizeColumnProperty); 42 | set => SetValue(CanUserResizeColumnProperty, value); 43 | } 44 | 45 | public bool? CanUserSortColumn 46 | { 47 | get => GetValue(CanUserSortColumnProperty); 48 | set => SetValue(CanUserSortColumnProperty, value); 49 | } 50 | 51 | public GridLength MinWidth 52 | { 53 | get => GetValue(MinWidthProperty); 54 | set => SetValue(MinWidthProperty, value); 55 | } 56 | 57 | public GridLength? MaxWidth 58 | { 59 | get => GetValue(MaxWidthProperty); 60 | set => SetValue(MaxWidthProperty, value); 61 | } 62 | 63 | public BeginEditGestures BeginEditGestures 64 | { 65 | get => GetValue(BeginEditGesturesProperty); 66 | set => SetValue(BeginEditGesturesProperty, value); 67 | } 68 | 69 | public object? Header 70 | { 71 | get => _header; 72 | set => SetAndRaise(HeaderProperty, ref _header, value); 73 | } 74 | 75 | public GridLength Width 76 | { 77 | get => GetValue(WidthProperty); 78 | set => SetValue(WidthProperty, value); 79 | } 80 | 81 | [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Func<,>))] 82 | protected LambdaExpression CreateGetterLambdaExpression( 83 | [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] 84 | Type modelType, 85 | PropertyInfo property) 86 | { 87 | var valueType = property.PropertyType; 88 | var modelParameter = Expression.Parameter(modelType, "model"); 89 | var propertyAccess = Expression.Property(modelParameter, property); 90 | var convertedPropertyAccess = Expression.Convert(propertyAccess, valueType); 91 | var lambdaType = typeof(Func<,>).MakeGenericType(modelType, valueType); 92 | return Expression.Lambda(lambdaType, convertedPropertyAccess, modelParameter); 93 | } 94 | 95 | [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Action<,>))] 96 | protected LambdaExpression CreateSetterLambdaExpression( 97 | [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] 98 | Type modelType, 99 | PropertyInfo property) 100 | { 101 | var valueType = property.PropertyType; 102 | var modelParameter = Expression.Parameter(modelType, "model"); 103 | var valueParameter = Expression.Parameter(valueType, "value"); 104 | var propertyAccess = Expression.Property(modelParameter, property); 105 | var assign = Expression.Assign(propertyAccess, Expression.Convert(valueParameter, property.PropertyType)); 106 | var lambdaType = typeof(Action<,>).MakeGenericType(modelType, valueType); 107 | return Expression.Lambda(lambdaType, assign, modelParameter, valueParameter); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /TreeDataGridEx/TreeDataGridColumnsTemplate.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using Avalonia.Markup.Xaml.Templates; 3 | using Avalonia.Metadata; 4 | using Avalonia.Styling; 5 | 6 | namespace TreeDataGridEx; 7 | 8 | public class TreeDataGridColumnsTemplate : ITemplate 9 | { 10 | [Content] 11 | [TemplateContent(TemplateResultType = typeof(ObservableCollection))] 12 | public object? Content { get; set; } 13 | 14 | object? ITemplate.Build() => TemplateContent.Load>(Content)?.Result; 15 | } 16 | -------------------------------------------------------------------------------- /TreeDataGridEx/TreeDataGridEx.axaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /TreeDataGridEx/TreeDataGridEx.axaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.ObjectModel; 4 | using System.Diagnostics.CodeAnalysis; 5 | using System.Linq; 6 | using Avalonia; 7 | using Avalonia.Controls; 8 | using Avalonia.Controls.Models.TreeDataGrid; 9 | using Avalonia.Controls.Primitives; 10 | 11 | namespace TreeDataGridEx; 12 | 13 | public class TreeDataGridEx : TemplatedControl 14 | { 15 | public static readonly StyledProperty AutoDragDropRowsProperty = 16 | TreeDataGrid.AutoDragDropRowsProperty.AddOwner(); 17 | 18 | public static readonly StyledProperty CanUserResizeColumnsProperty = 19 | TreeDataGrid.CanUserResizeColumnsProperty.AddOwner(); 20 | 21 | public static readonly StyledProperty CanUserSortColumnsProperty = 22 | TreeDataGrid.CanUserSortColumnsProperty.AddOwner(); 23 | 24 | public static readonly StyledProperty?> ColumnsProperty = 25 | AvaloniaProperty.Register?>(nameof(Columns)); 26 | 27 | public static readonly StyledProperty ShowColumnHeadersProperty = 28 | TreeDataGrid.ShowColumnHeadersProperty.AddOwner(); 29 | 30 | public static readonly StyledProperty ItemsSourceProperty = 31 | AvaloniaProperty.Register(nameof(ItemsSource)); 32 | 33 | private ITreeDataGridSource? _source; 34 | private TreeDataGrid? _treeDataGrid; 35 | 36 | public bool AutoDragDropRows 37 | { 38 | get => GetValue(AutoDragDropRowsProperty); 39 | set => SetValue(AutoDragDropRowsProperty, value); 40 | } 41 | 42 | public bool CanUserResizeColumns 43 | { 44 | get => GetValue(CanUserResizeColumnsProperty); 45 | set => SetValue(CanUserResizeColumnsProperty, value); 46 | } 47 | 48 | public bool CanUserSortColumns 49 | { 50 | get => GetValue(CanUserSortColumnsProperty); 51 | set => SetValue(CanUserSortColumnsProperty, value); 52 | } 53 | 54 | public ObservableCollection? Columns 55 | { 56 | get => GetValue(ColumnsProperty); 57 | set => SetValue(ColumnsProperty, value); 58 | } 59 | 60 | public bool ShowColumnHeaders 61 | { 62 | get => GetValue(ShowColumnHeadersProperty); 63 | set => SetValue(ShowColumnHeadersProperty, value); 64 | } 65 | 66 | public IEnumerable ItemsSource 67 | { 68 | get => GetValue(ItemsSourceProperty); 69 | set => SetValue(ItemsSourceProperty, value); 70 | } 71 | 72 | public ITreeDataGridSource? Source => _source; 73 | 74 | public TreeDataGrid? TreeDataGrid => _treeDataGrid; 75 | 76 | public TreeDataGridEx() 77 | { 78 | SetCurrentValue(ColumnsProperty, new ObservableCollection()); 79 | } 80 | 81 | protected override void OnApplyTemplate(TemplateAppliedEventArgs e) 82 | { 83 | base.OnApplyTemplate(e); 84 | 85 | _treeDataGrid = e.NameScope.Find("PART_TreeDataGrid"); 86 | 87 | Initialize(); 88 | } 89 | 90 | private void Initialize() 91 | { 92 | (_source as IDisposable)?.Dispose(); 93 | 94 | var itemsSource = ItemsSource; 95 | var columns = Columns; 96 | 97 | if (_treeDataGrid is null || columns is null) 98 | { 99 | return; 100 | } 101 | 102 | var source = CreateSource(itemsSource, columns); 103 | if (source is not null) 104 | { 105 | _source = source; 106 | _treeDataGrid.Source = _source; 107 | } 108 | } 109 | 110 | protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) 111 | { 112 | base.OnPropertyChanged(change); 113 | 114 | if (change.Property == ItemsSourceProperty || change.Property == ColumnsProperty) 115 | { 116 | Initialize(); 117 | } 118 | } 119 | 120 | [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ColumnList<>))] 121 | private static ITreeDataGridSource? CreateSource(IEnumerable items, ObservableCollection columns) 122 | { 123 | var modelType = items.GetType().GenericTypeArguments[0]; 124 | 125 | var source = columns.Any(x => x is TreeDataGridHierarchicalExpanderColumn) 126 | ? CreateHierarchicalSource(modelType, items) 127 | : CreateFlatSource(modelType, items); 128 | if (source is null) 129 | { 130 | return null; 131 | } 132 | 133 | var columnsType = typeof(ColumnList<>).MakeGenericType(modelType); 134 | var add = columnsType.GetMethod("Add"); 135 | if (add is null) 136 | { 137 | return null; 138 | } 139 | 140 | foreach (var column in columns) 141 | { 142 | try 143 | { 144 | var c = column.Create(column.DataType ?? modelType); 145 | if (c is not null) 146 | { 147 | add.Invoke(source.Columns, new object[] { c }); 148 | } 149 | } 150 | catch (Exception) 151 | { 152 | // ignored 153 | } 154 | } 155 | 156 | return source; 157 | } 158 | 159 | [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(FlatTreeDataGridSource<>))] 160 | private static ITreeDataGridSource? CreateFlatSource( 161 | [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] 162 | Type modelType, 163 | IEnumerable items) 164 | { 165 | var type = typeof(FlatTreeDataGridSource<>).MakeGenericType(modelType); 166 | 167 | return (ITreeDataGridSource?)Activator.CreateInstance(type, items); 168 | } 169 | 170 | [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(HierarchicalExpanderColumn<>))] 171 | private static ITreeDataGridSource? CreateHierarchicalSource( 172 | [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] 173 | Type modelType, 174 | IEnumerable items) 175 | { 176 | var type = typeof(HierarchicalTreeDataGridSource<>).MakeGenericType(modelType); 177 | 178 | return (ITreeDataGridSource?)Activator.CreateInstance(type, items); 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /TreeDataGridEx/TreeDataGridEx.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library 5 | net6.0 6 | enable 7 | true 8 | true 9 | True 10 | 11 | 12 | 13 | 11.1.1 14 | 15 | Wiesław Šoltés 16 | Wiesław Šoltés 17 | Copyright © Wiesław Šoltés 2024 18 | https://github.com/wieslawsoltes/TreeDataGridEx 19 | 20 | 21 | 22 | An TreeDataGridEx control for Avalonia. 23 | TreeDataGridEx 24 | MIT 25 | datagrid;treedatagrid;xaml;control;avalonia;avaloniaui 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /TreeDataGridEx/TreeDataGridHierarchicalExpanderColumn.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq.Expressions; 5 | using System.Reflection; 6 | using Avalonia; 7 | using Avalonia.Controls.Models.TreeDataGrid; 8 | using Avalonia.Metadata; 9 | 10 | namespace TreeDataGridEx; 11 | 12 | public class TreeDataGridHierarchicalExpanderColumn : TreeDataGridColumn 13 | { 14 | // TODO: HierarchicalExpanderColumn<>.hasChildrenSelector 15 | 16 | // TODO: HierarchicalExpanderColumn<>.isExpandedSelector 17 | 18 | public static readonly DirectProperty InnerProperty = 19 | AvaloniaProperty.RegisterDirect( 20 | nameof(Inner), 21 | o => o.Inner, 22 | (o, v) => o.Inner = v); 23 | 24 | public static readonly DirectProperty ChildrenNameProperty = 25 | AvaloniaProperty.RegisterDirect( 26 | nameof(ChildrenName), 27 | o => o.ChildrenName, 28 | (o, v) => o.ChildrenName = v); 29 | 30 | private TreeDataGridColumn? _inner; 31 | private string? _childrenName; 32 | 33 | [Content] 34 | [InheritDataTypeFromItems(nameof(TreeDataGridEx.ItemsSource), AncestorType = typeof(TreeDataGridEx))] 35 | public TreeDataGridColumn? Inner 36 | { 37 | get { return _inner; } 38 | set { SetAndRaise(InnerProperty, ref _inner, value); } 39 | } 40 | 41 | [InheritDataTypeFromItems(nameof(TreeDataGridEx.ItemsSource), AncestorType = typeof(TreeDataGridEx))] 42 | public string? ChildrenName 43 | { 44 | get => _childrenName; 45 | set => SetAndRaise(ChildrenNameProperty, ref _childrenName, value); 46 | } 47 | 48 | [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(HierarchicalExpanderColumn<>))] 49 | internal override IColumn? Create( 50 | [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] 51 | Type modelType) 52 | { 53 | var inner = Inner; 54 | var childrenName = ChildrenName; 55 | 56 | if (childrenName is null) 57 | { 58 | return null; 59 | } 60 | 61 | var innerColumn = inner?.Create(modelType); 62 | 63 | var property = modelType.GetProperty(childrenName); 64 | if (property is null) 65 | { 66 | return null; 67 | } 68 | 69 | var childSelector = CreateChildSelectorLambdaExpression(modelType, property).Compile(); 70 | var type = typeof(HierarchicalExpanderColumn<>).MakeGenericType(modelType); 71 | 72 | // TODO: 73 | // - hasChildrenSelector 74 | // - isExpandedSelector 75 | return (IColumn?) Activator.CreateInstance(type, innerColumn, childSelector, null, null); 76 | } 77 | 78 | [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(IEnumerable<>))] 79 | [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(Func<,>))] 80 | private LambdaExpression CreateChildSelectorLambdaExpression( 81 | [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] 82 | Type modelType, 83 | PropertyInfo property) 84 | { 85 | var valueType = typeof(IEnumerable<>).MakeGenericType(modelType); 86 | var modelParameter = Expression.Parameter(modelType, "model"); 87 | var propertyAccess = Expression.Property(modelParameter, property); 88 | var convertedPropertyAccess = Expression.Convert(propertyAccess, valueType); 89 | var lambdaType = typeof(Func<,>).MakeGenericType(modelType, valueType); 90 | return Expression.Lambda(lambdaType, convertedPropertyAccess, modelParameter); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /TreeDataGridEx/TreeDataGridTemplateColumn.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Avalonia; 4 | using Avalonia.Controls.Models.TreeDataGrid; 5 | using Avalonia.Controls.Templates; 6 | using Avalonia.Metadata; 7 | 8 | namespace TreeDataGridEx; 9 | 10 | public class TreeDataGridTemplateColumn : TreeDataGridColumnBase 11 | { 12 | // TODO: TemplateColumnOptions<>.IsTextSearchEnabled 13 | 14 | // TODO: TemplateColumnOptions<>.TextSearchValueSelector 15 | 16 | public static readonly DirectProperty CellTemplateProperty = 17 | AvaloniaProperty.RegisterDirect( 18 | nameof(CellTemplate), 19 | o => o.CellTemplate, 20 | (o, v) => o.CellTemplate = v); 21 | 22 | public static readonly DirectProperty CellEditingTemplateProperty = 23 | AvaloniaProperty.RegisterDirect( 24 | nameof(CellEditingTemplate), 25 | o => o.CellEditingTemplate, 26 | (o, v) => o.CellEditingTemplate = v); 27 | 28 | private IDataTemplate? _cellTemplate; 29 | private IDataTemplate? _cellEditingCellTemplate; 30 | 31 | [Content] 32 | [InheritDataTypeFromItems(nameof(TreeDataGridEx.ItemsSource), AncestorType = typeof(TreeDataGridEx))] 33 | public IDataTemplate? CellTemplate 34 | { 35 | get { return _cellTemplate; } 36 | set { SetAndRaise(CellTemplateProperty, ref _cellTemplate, value); } 37 | } 38 | 39 | [InheritDataTypeFromItems(nameof(TreeDataGridEx.ItemsSource), AncestorType = typeof(TreeDataGridEx))] 40 | public IDataTemplate? CellEditingTemplate 41 | { 42 | get => _cellEditingCellTemplate; 43 | set => SetAndRaise(CellEditingTemplateProperty, ref _cellEditingCellTemplate, value); 44 | } 45 | 46 | [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(TemplateColumn<>))] 47 | [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ColumnOptions<>))] 48 | [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(TemplateColumnOptions<>))] 49 | internal override IColumn? Create( 50 | [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] 51 | Type modelType) 52 | { 53 | var header = Header; 54 | var cellTemplate = CellTemplate; 55 | var cellEditingTemplate = CellEditingTemplate; 56 | var width = Width; 57 | 58 | if (cellTemplate is null) 59 | { 60 | return null; 61 | } 62 | 63 | var type = typeof(TemplateColumn<>).MakeGenericType(modelType); 64 | 65 | var optionsType = typeof(TemplateColumnOptions<>).MakeGenericType(modelType); 66 | var options = Activator.CreateInstance(optionsType); 67 | 68 | // ColumnOptions 69 | optionsType.GetProperty("CanUserResizeColumn")?.SetValue(options, CanUserResizeColumn); 70 | optionsType.GetProperty("CanUserSortColumn")?.SetValue(options, CanUserSortColumn); 71 | optionsType.GetProperty("MinWidth")?.SetValue(options, MinWidth); 72 | optionsType.GetProperty("MaxWidth")?.SetValue(options, MaxWidth); 73 | // TODO: CompareAscending 74 | // TODO: CompareDescending 75 | optionsType.GetProperty("BeginEditGestures")?.SetValue(options, BeginEditGestures); 76 | 77 | // TemplateColumnOptions 78 | // - IsTextSearchEnabled 79 | // - TextSearchValueSelector 80 | 81 | return (IColumn?) Activator.CreateInstance(type, header, cellTemplate, cellEditingTemplate, width, options); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /TreeDataGridEx/TreeDataGridTextColumn.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics.CodeAnalysis; 3 | using Avalonia; 4 | using Avalonia.Controls.Models.TreeDataGrid; 5 | using Avalonia.Data; 6 | using Avalonia.Markup.Xaml.MarkupExtensions; 7 | using Avalonia.Media; 8 | using Avalonia.Metadata; 9 | 10 | namespace TreeDataGridEx; 11 | 12 | public class TreeDataGridTextColumn : TreeDataGridColumnBase 13 | { 14 | public static readonly StyledProperty IsTextSearchEnabledProperty = 15 | AvaloniaProperty.Register(nameof(IsTextSearchEnabled)); 16 | 17 | public static readonly StyledProperty TextTrimmingProperty = 18 | AvaloniaProperty.Register(nameof(TextTrimming), TextTrimming.CharacterEllipsis); 19 | 20 | public static readonly StyledProperty TextWrappingProperty = 21 | AvaloniaProperty.Register(nameof(TextWrapping), TextWrapping.NoWrap); 22 | 23 | public static readonly DirectProperty BindingProperty = 24 | AvaloniaProperty.RegisterDirect( 25 | nameof(Binding), 26 | o => o.Binding, 27 | (o, v) => o.Binding = v); 28 | 29 | private IBinding? _binding; 30 | 31 | public bool IsTextSearchEnabled 32 | { 33 | get => GetValue(IsTextSearchEnabledProperty); 34 | set => SetValue(IsTextSearchEnabledProperty, value); 35 | } 36 | 37 | public TextTrimming TextTrimming 38 | { 39 | get => GetValue(TextTrimmingProperty); 40 | set => SetValue(TextTrimmingProperty, value); 41 | } 42 | 43 | public TextWrapping TextWrapping 44 | { 45 | get => GetValue(TextWrappingProperty); 46 | set => SetValue(TextWrappingProperty, value); 47 | } 48 | 49 | [Content] 50 | [InheritDataTypeFromItems(nameof(TreeDataGridEx.ItemsSource), AncestorType = typeof(TreeDataGridEx))] 51 | [AssignBinding] 52 | public IBinding? Binding 53 | { 54 | get { return _binding; } 55 | set { SetAndRaise(BindingProperty, ref _binding, value); } 56 | } 57 | 58 | [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(TextColumn<,>))] 59 | [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(ColumnOptions<>))] 60 | [DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(TextColumnOptions<>))] 61 | internal override IColumn? Create( 62 | [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] 63 | Type modelType) 64 | { 65 | var header = Header; 66 | var binding = Binding; 67 | var width = Width; 68 | 69 | if (binding is null) 70 | { 71 | return null; 72 | } 73 | 74 | var path = (binding as Binding)?.Path ?? (binding as CompiledBindingExtension)?.Path.ToString(); 75 | if (path is null) 76 | { 77 | return null; 78 | } 79 | 80 | var property = modelType.GetProperty(path); 81 | if (property is null) 82 | { 83 | return null; 84 | } 85 | 86 | var propertyType = property.PropertyType; 87 | var getter = CreateGetterLambdaExpression(modelType, property); 88 | var type = typeof(TextColumn<,>).MakeGenericType(modelType, propertyType); 89 | 90 | var optionsType = typeof(TextColumnOptions<>).MakeGenericType(modelType); 91 | var options = Activator.CreateInstance(optionsType); 92 | 93 | // ColumnOptions 94 | optionsType.GetProperty("CanUserResizeColumn")?.SetValue(options, CanUserResizeColumn); 95 | optionsType.GetProperty("CanUserSortColumn")?.SetValue(options, CanUserSortColumn); 96 | optionsType.GetProperty("MinWidth")?.SetValue(options, MinWidth); 97 | optionsType.GetProperty("MaxWidth")?.SetValue(options, MaxWidth); 98 | // TODO: CompareAscending 99 | // TODO: CompareDescending 100 | optionsType.GetProperty("BeginEditGestures")?.SetValue(options, BeginEditGestures); 101 | 102 | // TextColumnOptions 103 | optionsType.GetProperty("IsTextSearchEnabled")?.SetValue(options, IsTextSearchEnabled); 104 | optionsType.GetProperty("TextTrimming")?.SetValue(options, TextTrimming); 105 | optionsType.GetProperty("TextWrapping")?.SetValue(options, TextWrapping); 106 | 107 | if (!property.CanWrite || (property.SetMethod is not null && !property.SetMethod.IsPublic)) 108 | { 109 | return (IColumn?) Activator.CreateInstance(type, header, getter, width, options); 110 | } 111 | 112 | var setter = CreateSetterLambdaExpression(modelType, property).Compile(); 113 | 114 | return (IColumn?) Activator.CreateInstance(type, header, getter, setter, width, options); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /TreeDataGridExDemo/App.axaml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /TreeDataGridExDemo/App.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.Controls.ApplicationLifetimes; 3 | using Avalonia.Markup.Xaml; 4 | using TreeDataGridExDemo.ViewModels; 5 | using TreeDataGridExDemo.Views; 6 | 7 | namespace TreeDataGridExDemo; 8 | 9 | public partial class App : Application 10 | { 11 | public override void Initialize() 12 | { 13 | AvaloniaXamlLoader.Load(this); 14 | } 15 | 16 | public override void OnFrameworkInitializationCompleted() 17 | { 18 | if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) 19 | { 20 | desktop.MainWindow = new MainWindow 21 | { 22 | DataContext = new MainWindowViewModel(), 23 | }; 24 | } 25 | 26 | base.OnFrameworkInitializationCompleted(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /TreeDataGridExDemo/Assets/avalonia-logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wieslawsoltes/TreeDataGridEx/e38ca9e7b6e087c59bd91e1585d2a9a43dceaefa/TreeDataGridExDemo/Assets/avalonia-logo.ico -------------------------------------------------------------------------------- /TreeDataGridExDemo/Models/Countries.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace TreeDataGridExDemo.Models 5 | { 6 | internal static class Countries 7 | { 8 | private static IReadOnlyList? _all; 9 | private static IReadOnlyList? _regions; 10 | 11 | private static IEnumerable GetCountries() 12 | { 13 | 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); 14 | 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); 15 | yield return new Country("Algeria", "NORTHERN AFRICA", 32930091, 2381740, 13.8, 0.04, -0.39, 31, 6000, 70, 78.1, 17.14, 4.61); 16 | 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); 17 | yield return new Country("Andorra", "WESTERN EUROPE", 71201, 468, 152.1, 0, 6.6, 4.05, 19000, 100, 497.2, 8.71, 6.25); 18 | 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); 19 | 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); 20 | 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); 21 | 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); 22 | 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); 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); 24 | yield return new Country("Australia", "OCEANIA", 20264082, 7686850, 2.6, 0.34, 3.98, 4.69, 29000, 100, 565.5, 12.14, 7.51); 25 | yield return new Country("Austria", "WESTERN EUROPE", 8192880, 83870, 97.7, 0, 2, 4.66, 30000, 98, 452.2, 8.74, 9.76); 26 | 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); 27 | 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); 28 | 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); 29 | 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); 30 | 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); 31 | 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); 32 | yield return new Country("Belgium", "WESTERN EUROPE", 10379067, 30528, 340, 0.22, 1.23, 4.68, 29100, 98, 462.6, 10.38, 10.27); 33 | 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); 34 | 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); 35 | yield return new Country("Bermuda", "NORTHERN AMERICA", 65773, 53, 1241, 194.34, 2.49, 8.53, 36000, 98, 851.4, 11.4, 7.74); 36 | 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); 37 | 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); 38 | 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); 39 | 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); 40 | 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); 41 | 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); 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); 43 | 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); 44 | 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); 45 | 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); 46 | 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); 47 | 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); 48 | 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); 49 | 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); 50 | 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); 51 | 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); 52 | 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); 53 | 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); 54 | 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); 55 | 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); 56 | 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); 57 | 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); 58 | 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); 59 | 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); 60 | yield return new Country("Cook Islands", "OCEANIA", 21388, 240, 89.1, 50, null, null, 5000, 95, 289.9, 21, null); 61 | 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); 62 | 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); 63 | 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); 64 | 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); 65 | 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); 66 | 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); 67 | 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); 68 | 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); 69 | 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); 70 | 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); 71 | 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); 72 | 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); 73 | 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); 74 | 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); 75 | 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); 76 | 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); 77 | 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); 78 | 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); 79 | 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); 80 | 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); 81 | 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); 82 | 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); 83 | 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); 84 | 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); 85 | 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); 86 | 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); 87 | 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); 88 | 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); 89 | 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); 90 | 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); 91 | yield return new Country("Gibraltar", "WESTERN EUROPE", 27928, 7, 3989.7, 171.43, 0, 5.13, 17500, null, 877.7, 10.74, 9.31); 92 | 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); 93 | yield return new Country("Greenland", "NORTHERN AMERICA", 56361, 2166086, 0, 2.04, -8.37, 15.82, 20000, null, 448.9, 15.93, 7.84); 94 | 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); 95 | 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); 96 | yield return new Country("Guam", "OCEANIA", 171019, 541, 316.1, 23.2, 0, 6.94, 21000, 99, 492, 18.79, 4.48); 97 | 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); 98 | 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); 99 | 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); 100 | 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); 101 | 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); 102 | 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); 103 | 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); 104 | 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); 105 | 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); 106 | 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); 107 | 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); 108 | 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); 109 | 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); 110 | 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); 111 | 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); 112 | 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); 113 | 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); 114 | 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); 115 | 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); 116 | 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); 117 | 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); 118 | 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); 119 | 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); 120 | 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); 121 | yield return new Country("Kiribati", "OCEANIA", 105432, 811, 130, 140.94, 0, 48.52, 800, null, 42.7, 30.65, 8.26); 122 | 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); 123 | 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); 124 | 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); 125 | 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); 126 | 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); 127 | 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); 128 | 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); 129 | 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); 130 | 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); 131 | 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); 132 | yield return new Country("Liechtenstein", "WESTERN EUROPE", 33987, 160, 212.4, 0, 4.85, 4.7, 25000, 100, 585.5, 10.21, 7.18); 133 | yield return new Country("Lithuania", "BALTICS", 3585906, 65200, 55, 0.14, -0.71, 6.89, 11400, 99.6, 223.4, 8.75, 10.98); 134 | yield return new Country("Luxembourg", "WESTERN EUROPE", 474413, 2586, 183.5, 0, 8.97, 4.81, 55100, 100, 515.4, 11.94, 8.41); 135 | 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); 136 | yield return new Country("Macedonia", "EASTERN EUROPE", 2050554, 25333, 80.9, 0, -1.45, 10.09, 6700, null, 260, 12.02, 8.77); 137 | 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); 138 | 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); 139 | 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); 140 | 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); 141 | 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); 142 | 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); 143 | 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); 144 | 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); 145 | 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); 146 | 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); 147 | 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); 148 | 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); 149 | 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); 150 | 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); 151 | yield return new Country("Monaco", "WESTERN EUROPE", 32543, 2, 16271.5, 205, 7.75, 5.43, 27000, 99, 1035.6, 9.19, 12.91); 152 | 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); 153 | yield return new Country("Montserrat", "LATIN AMER. & CARIB", 9439, 102, 92.5, 39.22, 0, 7.35, 3400, 97, null, 17.59, 7.1); 154 | 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); 155 | 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); 156 | 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); 157 | yield return new Country("Nauru", "OCEANIA", 13287, 21, 632.7, 142.86, 0, 9.95, 5000, null, 143, 24.76, 6.7); 158 | 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); 159 | 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); 160 | 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); 161 | yield return new Country("New Caledonia", "OCEANIA", 219246, 19060, 11.5, 11.83, 0, 7.72, 15000, 91, 252.2, 18.11, 5.69); 162 | 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); 163 | 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); 164 | 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); 165 | 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); 166 | 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); 167 | 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); 168 | 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); 169 | 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); 170 | yield return new Country("Palau", "OCEANIA", 20579, 458, 44.9, 331.66, 2.85, 14.84, 9000, 92, 325.6, 18.03, 6.8); 171 | 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); 172 | 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); 173 | yield return new Country("Paraguay", "LATIN AMER. & CARIB", 6506464, 406750, 16, 0, -0.08, 25.63, 4700, 94, 49.2, 29.1, 4.49); 174 | 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); 175 | 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); 176 | 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); 177 | 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); 178 | 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); 179 | 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); 180 | 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); 181 | 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); 182 | 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); 183 | 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); 184 | 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); 185 | 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); 186 | 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); 187 | 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); 188 | 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); 189 | 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); 190 | 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); 191 | 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); 192 | 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); 193 | 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); 194 | yield return new Country("Serbia", "EASTERN EUROPE", 9396411, 88361, 106.3, 0, -1.33, 12.89, 2200, 93, 285.8, null, null); 195 | 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); 196 | 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); 197 | 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); 198 | yield return new Country("Slovakia", "EASTERN EUROPE", 5439448, 48845, 111.4, 0, 0.3, 7.41, 13300, null, 220.1, 10.65, 9.45); 199 | 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); 200 | yield return new Country("Solomon Islands", "OCEANIA", 552438, 28450, 19.4, 18.67, 0, 21.29, 1700, null, 13.4, 30.01, 3.92); 201 | 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); 202 | 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); 203 | 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); 204 | 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); 205 | 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); 206 | 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); 207 | 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); 208 | yield return new Country("Sweden", "WESTERN EUROPE", 9016596, 449964, 20, 0.72, 1.67, 2.77, 26800, 99, 715, 10.27, 10.31); 209 | yield return new Country("Switzerland", "WESTERN EUROPE", 7523934, 41290, 182.2, 0, 4.05, 4.39, 32700, 99, 680.9, 9.71, 8.49); 210 | yield return new Country("Syria", "NEAR EAST", 18881361, 185180, 102, 0.1, 0, 29.53, 3300, 76.9, 153.8, 27.76, 4.81); 211 | 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); 212 | 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); 213 | 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); 214 | 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); 215 | 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); 216 | yield return new Country("Tonga", "OCEANIA", 114689, 748, 153.3, 56.02, 0, 12.62, 2200, 98.5, 97.7, 25.37, 5.28); 217 | 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); 218 | 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); 219 | 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); 220 | 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); 221 | 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); 222 | yield return new Country("Tuvalu", "OCEANIA", 11810, 26, 454.2, 92.31, 0, 20.03, 1100, null, 59.3, 22.18, 7.11); 223 | 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); 224 | 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); 225 | 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); 226 | 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); 227 | yield return new Country("United States", "NORTHERN AMERICA", 298444215, 9631420, 31, 0.21, 3.41, 6.5, 37800, 97, 898, 14.14, 8.26); 228 | 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); 229 | 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); 230 | yield return new Country("Vanuatu", "OCEANIA", 208869, 12200, 17.1, 20.72, 0, 55.16, 2900, 53, 32.6, 22.72, 7.82); 231 | 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); 232 | 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); 233 | 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); 234 | yield return new Country("Wallis and Futuna", "OCEANIA", 16025, 274, 58.5, 47.08, null, null, 3700, 50, 118.6, null, null); 235 | 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); 236 | 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); 237 | yield return new Country("Zambia", "SUB-SAHARAN AFRICA", 11502010, 752614, 15.3, 0, 0, 88.29, 800, 80.6, 8.2, 41, 19.93); 238 | 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); 239 | } 240 | 241 | public static IReadOnlyList All => _all ??= GetCountries().ToList(); 242 | public static IReadOnlyList Regions => _regions ??= All.Select(x => x.Region).ToList(); 243 | } 244 | } 245 | -------------------------------------------------------------------------------- /TreeDataGridExDemo/Models/Country.cs: -------------------------------------------------------------------------------- 1 | namespace TreeDataGridExDemo.Models 2 | { 3 | public class Country 4 | { 5 | public bool IsSelected { get; set; } 6 | public string? Name { get; set; } 7 | public string Region { get; private set; } 8 | public int Population { get; private set; } 9 | //Square Miles 10 | public int Area { get; private set; } 11 | //Per Square Mile 12 | public double PopulationDensity { get; private set; } 13 | //Coast / Area 14 | public double CoastLine { get; private set; } 15 | public double? NetMigration { get; private set; } 16 | //per 1000 births 17 | public double? InfantMortality { get; private set; } 18 | public int GDP { get; private set; } 19 | public double? LiteracyPercent { get; private set; } 20 | //per 1000 21 | public double? Phones { get; private set; } 22 | public double? BirthRate { get; private set; } 23 | public double? DeathRate { get; private set; } 24 | 25 | public Country(string name, string region, int population, int area, double density, double coast, double? migration, 26 | double? infantMorality, int gdp, double? literacy, double? phones, double? birth, double? death) 27 | { 28 | Name = name; 29 | Region = region; 30 | Population = population; 31 | Area = area; 32 | PopulationDensity = density; 33 | CoastLine = coast; 34 | NetMigration = migration; 35 | InfantMortality = infantMorality; 36 | GDP = gdp; 37 | LiteracyPercent = literacy; 38 | BirthRate = birth; 39 | DeathRate = death; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /TreeDataGridExDemo/Models/DragDropItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.ObjectModel; 3 | using System.Diagnostics.CodeAnalysis; 4 | using System.Linq; 5 | using ReactiveUI; 6 | 7 | namespace TreeDataGridExDemo.Models 8 | { 9 | public partial class DragDropItem(string name) : ReactiveObject 10 | { 11 | private static readonly Random s_random = new Random(0); 12 | 13 | public string Name { get; } = name; 14 | 15 | [Reactive] 16 | public partial bool AllowDrag { get; set; } 17 | 18 | [Reactive] 19 | public partial bool AllowDrop { get; set; } 20 | 21 | [field: AllowNull, MaybeNull] 22 | public ObservableCollection Children => field ??= CreateRandomItems(); 23 | 24 | public static ObservableCollection CreateRandomItems() 25 | { 26 | var names = new Bogus.DataSets.Name(); 27 | var count = s_random.Next(10); 28 | return new ObservableCollection(Enumerable.Range(0, count) 29 | .Select(_ => new DragDropItem(names.FullName()))); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /TreeDataGridExDemo/Program.cs: -------------------------------------------------------------------------------- 1 | using Avalonia; 2 | using Avalonia.ReactiveUI; 3 | using System; 4 | 5 | namespace TreeDataGridExDemo; 6 | 7 | class Program 8 | { 9 | // Initialization code. Don't use any Avalonia, third-party APIs or any 10 | // SynchronizationContext-reliant code before AppMain is called: things aren't initialized 11 | // yet and stuff might break. 12 | [STAThread] 13 | public static void Main(string[] args) => BuildAvaloniaApp() 14 | .StartWithClassicDesktopLifetime(args); 15 | 16 | // Avalonia configuration, don't remove; also used by visual designer. 17 | public static AppBuilder BuildAvaloniaApp() 18 | => AppBuilder.Configure() 19 | .UsePlatformDetect() 20 | .WithInterFont() 21 | .LogToTrace() 22 | .UseReactiveUI(); 23 | } 24 | -------------------------------------------------------------------------------- /TreeDataGridExDemo/TreeDataGridExDemo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | WinExe 5 | net9.0 6 | enable 7 | true 8 | app.manifest 9 | true 10 | False 11 | 11.2.5 12 | 13 | 14 | 15 | true 16 | $(BaseIntermediateOutputPath)\GeneratedFiles 17 | 18 | 19 | 20 | preview 21 | false 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | all 46 | runtime; build; native; contentfiles; analyzers; buildtransitive 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /TreeDataGridExDemo/ViewLocator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Avalonia.Controls; 3 | using Avalonia.Controls.Templates; 4 | using TreeDataGridExDemo.ViewModels; 5 | 6 | namespace TreeDataGridExDemo; 7 | 8 | public class ViewLocator : IDataTemplate 9 | { 10 | public Control Build(object? data) 11 | { 12 | var name = data?.GetType().FullName?.Replace("ViewModel", "View"); 13 | var type = name is null ? null : Type.GetType(name); 14 | 15 | if (type != null) 16 | { 17 | return (Control)Activator.CreateInstance(type)!; 18 | } 19 | 20 | return new TextBlock { Text = "Not Found: " + name }; 21 | } 22 | 23 | public bool Match(object? data) 24 | { 25 | return data is ViewModelBase; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /TreeDataGridExDemo/ViewModels/MainWindowViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.ObjectModel; 2 | using TreeDataGridExDemo.Models; 3 | 4 | namespace TreeDataGridExDemo.ViewModels; 5 | 6 | public class MainWindowViewModel : ViewModelBase 7 | { 8 | private readonly ObservableCollection _countries = new(Models.Countries.All); 9 | private readonly ObservableCollection _dragAndDropItems = DragDropItem.CreateRandomItems(); 10 | 11 | public ObservableCollection Countries => _countries; 12 | 13 | public ObservableCollection DragAndDropItems => _dragAndDropItems; 14 | } 15 | -------------------------------------------------------------------------------- /TreeDataGridExDemo/ViewModels/ViewModelBase.cs: -------------------------------------------------------------------------------- 1 | using ReactiveUI; 2 | 3 | namespace TreeDataGridExDemo.ViewModels; 4 | 5 | public class ViewModelBase : ReactiveObject 6 | { 7 | } 8 | -------------------------------------------------------------------------------- /TreeDataGridExDemo/Views/MainWindow.axaml: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 71 | 72 | 73 | 75 | 76 | 77 | 78 | 79 | 80 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /TreeDataGridExDemo/Views/MainWindow.axaml.cs: -------------------------------------------------------------------------------- 1 | using Avalonia.Controls; 2 | using Avalonia.Input; 3 | using Avalonia.Interactivity; 4 | using Avalonia.Rendering; 5 | using TreeDataGridExDemo.Models; 6 | 7 | namespace TreeDataGridExDemo.Views; 8 | 9 | public partial class MainWindow : Window 10 | { 11 | public MainWindow() 12 | { 13 | InitializeComponent(); 14 | 15 | // RendererDiagnostics.DebugOverlays = RendererDebugOverlays.Fps |RendererDebugOverlays.LayoutTimeGraph | RendererDebugOverlays.RenderTimeGraph; 16 | 17 | TreeDataGridExDragDrop.Loaded += TreeDataGridExDragDropOnLoaded; 18 | } 19 | 20 | private void TreeDataGridExDragDropOnLoaded(object? sender, RoutedEventArgs e) 21 | { 22 | if (TreeDataGridExDragDrop.TreeDataGrid is { } treeDataGrid) 23 | { 24 | treeDataGrid.AutoDragDropRows = true; 25 | treeDataGrid.RowDragStarted += DragDrop_RowDragStarted; 26 | treeDataGrid.RowDragOver += DragDrop_RowDragOver; 27 | } 28 | } 29 | 30 | private void DragDrop_RowDragStarted(object? sender, TreeDataGridRowDragStartedEventArgs e) 31 | { 32 | foreach (DragDropItem i in e.Models) 33 | { 34 | if (!i.AllowDrag) 35 | e.AllowedEffects = DragDropEffects.None; 36 | } 37 | } 38 | 39 | private void DragDrop_RowDragOver(object? sender, TreeDataGridRowDragEventArgs e) 40 | { 41 | if (e.Position == TreeDataGridRowDropPosition.Inside && 42 | e.TargetRow.Model is DragDropItem i && 43 | !i.AllowDrop) 44 | e.Inner.DragEffects = DragDropEffects.None; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /TreeDataGridExDemo/app.manifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "9.0.100", 4 | "rollForward": "latestMinor", 5 | "allowPrerelease": true 6 | } 7 | } 8 | --------------------------------------------------------------------------------