├── .editorconfig
├── .gitattributes
├── .github
├── FUNDING.yml
├── dependabot.yml
├── stale.yml
└── workflows
│ └── build.yml
├── .gitignore
├── .nuke
├── build.schema.json
└── parameters.json
├── LICENSE.TXT
├── README.md
├── SECURITY.md
├── TemplatedDataGrid.sln
├── azure-pipelines.yml
├── build.cmd
├── build.ps1
├── build.sh
├── build
├── Avalonia.Desktop.props
├── Avalonia.Diagnostics.props
├── Avalonia.Fonts.Inter.props
├── Avalonia.ReactiveUI.props
├── Avalonia.Themes.Fluent.props
├── Avalonia.props
├── Base.props
├── ReferenceAssemblies.props
├── SignAssembly.props
├── SourceLink.props
├── System.Reactive.props
├── XUnit.props
├── build
│ ├── Build.cs
│ └── _build.csproj
└── templateddatagrid.public.snk
├── global.json
├── nuget.config
├── samples
└── TemplatedDataGridDemo
│ ├── App.axaml
│ ├── App.axaml.cs
│ ├── Assets
│ └── avalonia-logo.ico
│ ├── Program.cs
│ ├── TemplatedDataGridDemo.csproj
│ ├── ViewLocator.cs
│ ├── ViewModels
│ ├── ItemViewModel.cs
│ ├── MainWindowViewModel.cs
│ └── ViewModelBase.cs
│ └── Views
│ ├── MainWindow.axaml
│ └── MainWindow.axaml.cs
├── src
└── TemplatedDataGrid
│ ├── Controls
│ ├── TemplatedListBox.cs
│ └── TemplatedListBoxItem.cs
│ ├── Internal
│ └── AvaloniaObjectExtensions.cs
│ ├── Primitives
│ ├── TemplatedDataGridCellsPresenter.cs
│ ├── TemplatedDataGridColumnHeadersPresenter.cs
│ ├── TemplatedDataGridDetailsPresenter.cs
│ └── TemplatedDataGridRowsPresenter.cs
│ ├── Properties
│ └── AssemblyInfo.cs
│ ├── TemplatedDataGrid.cs
│ ├── TemplatedDataGrid.csproj
│ ├── TemplatedDataGridBoundColumn.cs
│ ├── TemplatedDataGridCell.cs
│ ├── TemplatedDataGridCheckBoxColumn.cs
│ ├── TemplatedDataGridColumn.cs
│ ├── TemplatedDataGridColumnHeader.cs
│ ├── TemplatedDataGridGridLinesVisibility.cs
│ ├── TemplatedDataGridRow.cs
│ ├── TemplatedDataGridRowGroupHeader.cs
│ ├── TemplatedDataGridRowHeader.cs
│ ├── TemplatedDataGridTemplateColumn.cs
│ ├── TemplatedDataGridTextColumn.cs
│ └── Themes
│ └── Fluent.axaml
└── tests
└── TemplatedDataGrid.UnitTests
├── Properties
└── AssemblyInfo.cs
└── TemplatedDataGrid.UnitTests.csproj
/.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 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [wieslawsoltes]
2 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "" # See documentation for possible values
9 | directory: "/" # Location of package manifests
10 | schedule:
11 | interval: "weekly"
12 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/.nuke/build.schema.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "http://json-schema.org/draft-04/schema#",
3 | "title": "Build Schema",
4 | "$ref": "#/definitions/build",
5 | "definitions": {
6 | "build": {
7 | "type": "object",
8 | "properties": {
9 | "Configuration": {
10 | "type": "string",
11 | "description": "configuration"
12 | },
13 | "Continue": {
14 | "type": "boolean",
15 | "description": "Indicates to continue a previously failed build attempt"
16 | },
17 | "Help": {
18 | "type": "boolean",
19 | "description": "Shows the help text for this build assembly"
20 | },
21 | "Host": {
22 | "type": "string",
23 | "description": "Host for execution. Default is 'automatic'",
24 | "enum": [
25 | "AppVeyor",
26 | "AzurePipelines",
27 | "Bamboo",
28 | "Bitrise",
29 | "GitHubActions",
30 | "GitLab",
31 | "Jenkins",
32 | "Rider",
33 | "SpaceAutomation",
34 | "TeamCity",
35 | "Terminal",
36 | "TravisCI",
37 | "VisualStudio",
38 | "VSCode"
39 | ]
40 | },
41 | "NoLogo": {
42 | "type": "boolean",
43 | "description": "Disables displaying the NUKE logo"
44 | },
45 | "Partition": {
46 | "type": "string",
47 | "description": "Partition to use on CI"
48 | },
49 | "Plan": {
50 | "type": "boolean",
51 | "description": "Shows the execution plan (HTML)"
52 | },
53 | "Profile": {
54 | "type": "array",
55 | "description": "Defines the profiles to load",
56 | "items": {
57 | "type": "string"
58 | }
59 | },
60 | "PublishFramework": {
61 | "type": "string",
62 | "description": "publish-framework"
63 | },
64 | "PublishProject": {
65 | "type": "string",
66 | "description": "publish-project"
67 | },
68 | "PublishRuntime": {
69 | "type": "string",
70 | "description": "publish-runtime"
71 | },
72 | "PublishSelfContained": {
73 | "type": "boolean",
74 | "description": "publish-self-contained"
75 | },
76 | "Root": {
77 | "type": "string",
78 | "description": "Root directory during build execution"
79 | },
80 | "Skip": {
81 | "type": "array",
82 | "description": "List of targets to be skipped. Empty list skips all dependencies",
83 | "items": {
84 | "type": "string",
85 | "enum": [
86 | "Clean",
87 | "Compile",
88 | "Pack",
89 | "Publish",
90 | "Restore",
91 | "Test"
92 | ]
93 | }
94 | },
95 | "Solution": {
96 | "type": "string",
97 | "description": "Path to a solution file that is automatically loaded"
98 | },
99 | "Target": {
100 | "type": "array",
101 | "description": "List of targets to be invoked. Default is '{default_target}'",
102 | "items": {
103 | "type": "string",
104 | "enum": [
105 | "Clean",
106 | "Compile",
107 | "Pack",
108 | "Publish",
109 | "Restore",
110 | "Test"
111 | ]
112 | }
113 | },
114 | "Verbosity": {
115 | "type": "string",
116 | "description": "Logging verbosity during build execution. Default is 'Normal'",
117 | "enum": [
118 | "Minimal",
119 | "Normal",
120 | "Quiet",
121 | "Verbose"
122 | ]
123 | },
124 | "VersionSuffix": {
125 | "type": "string",
126 | "description": "version-suffix"
127 | }
128 | }
129 | }
130 | }
131 | }
--------------------------------------------------------------------------------
/.nuke/parameters.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "./build.schema.json",
3 | "Solution": "TemplatedDataGrid.sln"
4 | }
--------------------------------------------------------------------------------
/LICENSE.TXT:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # TemplatedDataGrid
2 |
3 | [](https://dev.azure.com/wieslawsoltes/GitHub/_build/latest?definitionId=91&branchName=main)
4 | [](https://github.com/wieslawsoltes/TemplatedDataGrid/actions/workflows/build.yml)
5 |
6 | [](https://www.nuget.org/packages/TemplatedDataGrid)
7 | [](https://www.nuget.org/packages/TemplatedDataGrid)
8 | [](https://www.myget.org/gallery/templateddatagrid-nightly)
9 |
10 | A DataGrid control based on ListBox and Grid panels.
11 |
12 | 
13 |
14 | ## Building TemplatedDataGrid
15 |
16 | First, clone the repository or download the latest zip.
17 | ```
18 | git clone https://github.com/wieslawsoltes/TemplatedDataGrid.git
19 | ```
20 |
21 | ### Build on Windows using script
22 |
23 | * [.NET Core](https://www.microsoft.com/net/download?initial-os=windows).
24 |
25 | Open up a command-prompt and execute the commands:
26 | ```
27 | .\build.ps1
28 | ```
29 |
30 | ### Build on Linux using script
31 |
32 | * [.NET Core](https://www.microsoft.com/net/download?initial-os=linux).
33 |
34 | Open up a terminal prompt and execute the commands:
35 | ```
36 | ./build.sh
37 | ```
38 |
39 | ### Build on OSX using script
40 |
41 | * [.NET Core](https://www.microsoft.com/net/download?initial-os=macos).
42 |
43 | Open up a terminal prompt and execute the commands:
44 | ```
45 | ./build.sh
46 | ```
47 |
48 | ## NuGet
49 |
50 | TemplatedDataGrid is delivered as a NuGet package.
51 |
52 | You can find the packages here [NuGet](https://www.nuget.org/packages/TemplatedDataGrid/) and install the package like this:
53 |
54 | `Install-Package TemplatedDataGrid`
55 |
56 | or by using nightly build feed:
57 | * Add `https://www.myget.org/F/templateddatagrid-nightly/api/v3/index.json` to your package sources
58 | * Alternative nightly build feed `https://pkgs.dev.azure.com/wieslawsoltes/GitHub/_packaging/Nightly/nuget/v3/index.json`
59 | * Update your package using `TemplatedDataGrid` feed
60 |
61 | and install the package like this:
62 |
63 | `Install-Package TemplatedDataGrid -Pre`
64 |
65 | ### Package Sources
66 |
67 | * https://api.nuget.org/v3/index.json
68 | * https://www.myget.org/F/avalonia-ci/api/v2
69 |
70 | ## Resources
71 |
72 | * [GitHub source code repository.](https://github.com/wieslawsoltes/TemplatedDataGrid)
73 |
74 | ## License
75 |
76 | TemplatedDataGrid is licensed under the [MIT license](LICENSE.TXT).
77 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Security Commitment
4 |
5 | We value the security of our open-source projects and actively encourage responsible reporting of vulnerabilities. However, due to the nature of open-source software provided under the MIT License, the software is delivered "as is," without warranty or any formal commitment to security or support.
6 |
7 | ## Reporting a Vulnerability
8 |
9 | If you identify a security vulnerability, please report it by opening an issue in this repository. We aim to acknowledge receipt of vulnerability reports within a reasonable time frame, though no guaranteed response time is provided.
10 |
11 | ## Liability Disclaimer
12 |
13 | The maintainers and contributors of this project disclaim all liabilities arising from vulnerabilities or security incidents. Users are solely responsible for evaluating and mitigating risks associated with the use of this software.
14 |
15 | By using this project, you acknowledge and accept these terms.
16 |
17 |
--------------------------------------------------------------------------------
/TemplatedDataGrid.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemplatedDataGrid", "src\TemplatedDataGrid\TemplatedDataGrid.csproj", "{941DBB36-F74A-442F-8D4D-F43E6B58F2A9}"
4 | EndProject
5 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{7FDA3505-545B-4392-A3A9-D8F694C3C14F}"
6 | ProjectSection(SolutionItems) = preProject
7 | .editorconfig = .editorconfig
8 | build.ps1 = build.ps1
9 | build.sh = build.sh
10 | global.json = global.json
11 | build\templateddatagrid.public.snk = build\templateddatagrid.public.snk
12 | azure-pipelines.yml = azure-pipelines.yml
13 | EndProjectSection
14 | EndProject
15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "git", "git", "{BFD1765A-82DE-42D3-9359-771EC12D7281}"
16 | ProjectSection(SolutionItems) = preProject
17 | .gitattributes = .gitattributes
18 | .gitignore = .gitignore
19 | EndProjectSection
20 | EndProject
21 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nuget", "nuget", "{3603BA2F-4BE5-49EC-B7D7-BEC6EC9AF630}"
22 | ProjectSection(SolutionItems) = preProject
23 | nuget.config = nuget.config
24 | EndProjectSection
25 | EndProject
26 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "props", "props", "{F9FE1661-0D9F-43E3-8861-9A0AA0BC507B}"
27 | ProjectSection(SolutionItems) = preProject
28 | build\Avalonia.Desktop.props = build\Avalonia.Desktop.props
29 | build\Avalonia.Diagnostics.props = build\Avalonia.Diagnostics.props
30 | build\Avalonia.props = build\Avalonia.props
31 | build\Avalonia.ReactiveUI.props = build\Avalonia.ReactiveUI.props
32 | build\Base.props = build\Base.props
33 | build\ReferenceAssemblies.props = build\ReferenceAssemblies.props
34 | build\SignAssembly.props = build\SignAssembly.props
35 | build\SourceLink.props = build\SourceLink.props
36 | build\XUnit.props = build\XUnit.props
37 | build\System.Reactive.props = build\System.Reactive.props
38 | build\Avalonia.Themes.Fluent.props = build\Avalonia.Themes.Fluent.props
39 | build\Avalonia.Fonts.Inter.props = build\Avalonia.Fonts.Inter.props
40 | EndProjectSection
41 | EndProject
42 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "_build", "build\build\_build.csproj", "{52F8BE66-BC84-4909-AFD2-AE15AFDFF69A}"
43 | EndProject
44 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{F8CF95EB-93C2-4ACE-AEDF-570F68A39601}"
45 | EndProject
46 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{7E493E2E-54BB-4774-8899-205E86AFD281}"
47 | EndProject
48 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{D2A68122-27EA-4B4B-AA14-F93E6CFE8B74}"
49 | EndProject
50 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{A28198F3-8C60-4671-B977-8F83E0167359}"
51 | ProjectSection(SolutionItems) = preProject
52 | LICENSE.TXT = LICENSE.TXT
53 | README.md = README.md
54 | EndProjectSection
55 | EndProject
56 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemplatedDataGridDemo", "samples\TemplatedDataGridDemo\TemplatedDataGridDemo.csproj", "{4E69A42B-0199-4B3C-A0CA-D7418517D99F}"
57 | EndProject
58 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TemplatedDataGrid.UnitTests", "tests\TemplatedDataGrid.UnitTests\TemplatedDataGrid.UnitTests.csproj", "{F80215A5-0643-4A4A-8B63-AE322094524C}"
59 | EndProject
60 | Global
61 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
62 | Debug|Any CPU = Debug|Any CPU
63 | Debug|x64 = Debug|x64
64 | Release|Any CPU = Release|Any CPU
65 | Release|x64 = Release|x64
66 | EndGlobalSection
67 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
68 | {941DBB36-F74A-442F-8D4D-F43E6B58F2A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
69 | {941DBB36-F74A-442F-8D4D-F43E6B58F2A9}.Debug|Any CPU.Build.0 = Debug|Any CPU
70 | {941DBB36-F74A-442F-8D4D-F43E6B58F2A9}.Release|Any CPU.ActiveCfg = Release|Any CPU
71 | {941DBB36-F74A-442F-8D4D-F43E6B58F2A9}.Release|Any CPU.Build.0 = Release|Any CPU
72 | {941DBB36-F74A-442F-8D4D-F43E6B58F2A9}.Debug|x64.ActiveCfg = Debug|x64
73 | {941DBB36-F74A-442F-8D4D-F43E6B58F2A9}.Debug|x64.Build.0 = Debug|x64
74 | {941DBB36-F74A-442F-8D4D-F43E6B58F2A9}.Release|x64.ActiveCfg = Release|x64
75 | {941DBB36-F74A-442F-8D4D-F43E6B58F2A9}.Release|x64.Build.0 = Release|x64
76 | {52F8BE66-BC84-4909-AFD2-AE15AFDFF69A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
77 | {52F8BE66-BC84-4909-AFD2-AE15AFDFF69A}.Debug|Any CPU.Build.0 = Debug|Any CPU
78 | {52F8BE66-BC84-4909-AFD2-AE15AFDFF69A}.Debug|x64.ActiveCfg = Debug|Any CPU
79 | {52F8BE66-BC84-4909-AFD2-AE15AFDFF69A}.Debug|x64.Build.0 = Debug|Any CPU
80 | {52F8BE66-BC84-4909-AFD2-AE15AFDFF69A}.Release|Any CPU.ActiveCfg = Release|Any CPU
81 | {52F8BE66-BC84-4909-AFD2-AE15AFDFF69A}.Release|Any CPU.Build.0 = Release|Any CPU
82 | {52F8BE66-BC84-4909-AFD2-AE15AFDFF69A}.Release|x64.ActiveCfg = Release|Any CPU
83 | {52F8BE66-BC84-4909-AFD2-AE15AFDFF69A}.Release|x64.Build.0 = Release|Any CPU
84 | {4E69A42B-0199-4B3C-A0CA-D7418517D99F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
85 | {4E69A42B-0199-4B3C-A0CA-D7418517D99F}.Debug|Any CPU.Build.0 = Debug|Any CPU
86 | {4E69A42B-0199-4B3C-A0CA-D7418517D99F}.Debug|x64.ActiveCfg = Debug|x64
87 | {4E69A42B-0199-4B3C-A0CA-D7418517D99F}.Debug|x64.Build.0 = Debug|x64
88 | {4E69A42B-0199-4B3C-A0CA-D7418517D99F}.Release|Any CPU.ActiveCfg = Release|Any CPU
89 | {4E69A42B-0199-4B3C-A0CA-D7418517D99F}.Release|Any CPU.Build.0 = Release|Any CPU
90 | {4E69A42B-0199-4B3C-A0CA-D7418517D99F}.Release|x64.ActiveCfg = Release|x64
91 | {4E69A42B-0199-4B3C-A0CA-D7418517D99F}.Release|x64.Build.0 = Release|x64
92 | {F80215A5-0643-4A4A-8B63-AE322094524C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
93 | {F80215A5-0643-4A4A-8B63-AE322094524C}.Debug|Any CPU.Build.0 = Debug|Any CPU
94 | {F80215A5-0643-4A4A-8B63-AE322094524C}.Debug|x64.ActiveCfg = Debug|Any CPU
95 | {F80215A5-0643-4A4A-8B63-AE322094524C}.Debug|x64.Build.0 = Debug|Any CPU
96 | {F80215A5-0643-4A4A-8B63-AE322094524C}.Release|Any CPU.ActiveCfg = Release|Any CPU
97 | {F80215A5-0643-4A4A-8B63-AE322094524C}.Release|Any CPU.Build.0 = Release|Any CPU
98 | {F80215A5-0643-4A4A-8B63-AE322094524C}.Release|x64.ActiveCfg = Release|Any CPU
99 | {F80215A5-0643-4A4A-8B63-AE322094524C}.Release|x64.Build.0 = Release|Any CPU
100 | EndGlobalSection
101 | GlobalSection(NestedProjects) = preSolution
102 | {BFD1765A-82DE-42D3-9359-771EC12D7281} = {7FDA3505-545B-4392-A3A9-D8F694C3C14F}
103 | {3603BA2F-4BE5-49EC-B7D7-BEC6EC9AF630} = {7FDA3505-545B-4392-A3A9-D8F694C3C14F}
104 | {F9FE1661-0D9F-43E3-8861-9A0AA0BC507B} = {7FDA3505-545B-4392-A3A9-D8F694C3C14F}
105 | {52F8BE66-BC84-4909-AFD2-AE15AFDFF69A} = {7FDA3505-545B-4392-A3A9-D8F694C3C14F}
106 | {4E69A42B-0199-4B3C-A0CA-D7418517D99F} = {7E493E2E-54BB-4774-8899-205E86AFD281}
107 | {941DBB36-F74A-442F-8D4D-F43E6B58F2A9} = {F8CF95EB-93C2-4ACE-AEDF-570F68A39601}
108 | {F80215A5-0643-4A4A-8B63-AE322094524C} = {D2A68122-27EA-4B4B-AA14-F93E6CFE8B74}
109 | EndGlobalSection
110 | EndGlobal
111 |
--------------------------------------------------------------------------------
/azure-pipelines.yml:
--------------------------------------------------------------------------------
1 | name: $(date:yyyyMMdd)$(rev:-rr)
2 |
3 | resources:
4 | repositories:
5 | - repository: templates
6 | endpoint: wieslawsoltes
7 | type: github
8 | name: wieslawsoltes/BuildTemplates
9 | ref: refs/tags/v1.0.0
10 |
11 | variables:
12 | BuildConfiguration: 'Release'
13 | BuildPlatform: 'Any CPU'
14 | PublishFramework: 'net9.0'
15 | PublishProject: 'TemplatedDataGridDemo'
16 | PublishRuntime: ''
17 |
18 | jobs:
19 | - template: Test-PowerShell.yml@templates
20 | parameters:
21 | name: 'Test_Windows'
22 | vmImage: 'windows-2022'
23 | BuildConfiguration: ${{ variables.BuildConfiguration }}
24 |
25 | - template: Test-Bash.yml@templates
26 | parameters:
27 | name: 'Test_Linux'
28 | vmImage: 'ubuntu-22.04'
29 | BuildConfiguration: ${{ variables.BuildConfiguration }}
30 |
31 | - template: Test-Bash.yml@templates
32 | parameters:
33 | name: 'Test_macOS'
34 | vmImage: 'macOS-14'
35 | BuildConfiguration: ${{ variables.BuildConfiguration }}
36 |
37 | - template: Pack-MyGet.yml@templates
38 | parameters:
39 | name: 'Pack_MyGet'
40 | vmImage: 'windows-2022'
41 | BuildConfiguration: ${{ variables.BuildConfiguration }}
42 |
43 | - template: Pack-NuGet.yml@templates
44 | parameters:
45 | name: 'Pack_NuGet'
46 | vmImage: 'windows-2022'
47 | BuildConfiguration: ${{ variables.BuildConfiguration }}
48 |
49 | - template: Publish-PowerShell.yml@templates
50 | parameters:
51 | name: 'Publish_Windows'
52 | vmImage: 'windows-2022'
53 | BuildConfiguration: ${{ variables.BuildConfiguration }}
54 | PublishFramework: ${{ variables.PublishFramework }}
55 | PublishProject: ${{ variables.PublishProject }}
56 | PublishRuntime: 'win-x64'
57 |
58 | - template: Publish-Bash.yml@templates
59 | parameters:
60 | name: 'Publish_Linux'
61 | vmImage: 'ubuntu-22.04'
62 | BuildConfiguration: ${{ variables.BuildConfiguration }}
63 | PublishFramework: ${{ variables.PublishFramework }}
64 | PublishProject: ${{ variables.PublishProject }}
65 | PublishRuntime: 'linux-x64'
66 |
67 | - template: Publish-Bash.yml@templates
68 | parameters:
69 | name: 'Publish_macOS'
70 | vmImage: 'macOS-14'
71 | BuildConfiguration: ${{ variables.BuildConfiguration }}
72 | PublishFramework: ${{ variables.PublishFramework }}
73 | PublishProject: ${{ variables.PublishProject }}
74 | PublishRuntime: 'osx-x64'
75 |
--------------------------------------------------------------------------------
/build.cmd:
--------------------------------------------------------------------------------
1 | :; set -eo pipefail
2 | :; SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
3 | :; ${SCRIPT_DIR}/build.sh "$@"
4 | :; exit $?
5 |
6 | @ECHO OFF
7 | powershell -ExecutionPolicy ByPass -NoProfile -File "%~dp0build.ps1" %*
8 |
--------------------------------------------------------------------------------
/build.ps1:
--------------------------------------------------------------------------------
1 | [CmdletBinding()]
2 | Param(
3 | [Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
4 | [string[]]$BuildArguments
5 | )
6 |
7 | Write-Output "PowerShell $($PSVersionTable.PSEdition) version $($PSVersionTable.PSVersion)"
8 |
9 | Set-StrictMode -Version 2.0; $ErrorActionPreference = "Stop"; $ConfirmPreference = "None"; trap { Write-Error $_ -ErrorAction Continue; exit 1 }
10 | $PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
11 |
12 | ###########################################################################
13 | # CONFIGURATION
14 | ###########################################################################
15 |
16 | $BuildProjectFile = "$PSScriptRoot\build\build\_build.csproj"
17 | $TempDirectory = "$PSScriptRoot\\.nuke\temp"
18 |
19 | $DotNetGlobalFile = "$PSScriptRoot\\global.json"
20 | $DotNetInstallUrl = "https://dot.net/v1/dotnet-install.ps1"
21 | $DotNetChannel = "Current"
22 |
23 | $env:DOTNET_SKIP_FIRST_TIME_EXPERIENCE = 1
24 | $env:DOTNET_CLI_TELEMETRY_OPTOUT = 1
25 | $env:DOTNET_MULTILEVEL_LOOKUP = 0
26 |
27 | ###########################################################################
28 | # EXECUTION
29 | ###########################################################################
30 |
31 | function ExecSafe([scriptblock] $cmd) {
32 | & $cmd
33 | if ($LASTEXITCODE) { exit $LASTEXITCODE }
34 | }
35 |
36 | # If dotnet CLI is installed globally and it matches requested version, use for execution
37 | if ($null -ne (Get-Command "dotnet" -ErrorAction SilentlyContinue) -and `
38 | $(dotnet --version) -and $LASTEXITCODE -eq 0) {
39 | $env:DOTNET_EXE = (Get-Command "dotnet").Path
40 | }
41 | else {
42 | # Download install script
43 | $DotNetInstallFile = "$TempDirectory\dotnet-install.ps1"
44 | New-Item -ItemType Directory -Path $TempDirectory -Force | Out-Null
45 | [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
46 | (New-Object System.Net.WebClient).DownloadFile($DotNetInstallUrl, $DotNetInstallFile)
47 |
48 | # If global.json exists, load expected version
49 | if (Test-Path $DotNetGlobalFile) {
50 | $DotNetGlobal = $(Get-Content $DotNetGlobalFile | Out-String | ConvertFrom-Json)
51 | if ($DotNetGlobal.PSObject.Properties["sdk"] -and $DotNetGlobal.sdk.PSObject.Properties["version"]) {
52 | $DotNetVersion = $DotNetGlobal.sdk.version
53 | }
54 | }
55 |
56 | # Install by channel or version
57 | $DotNetDirectory = "$TempDirectory\dotnet-win"
58 | if (!(Test-Path variable:DotNetVersion)) {
59 | ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Channel $DotNetChannel -NoPath }
60 | } else {
61 | ExecSafe { & powershell $DotNetInstallFile -InstallDir $DotNetDirectory -Version $DotNetVersion -NoPath }
62 | }
63 | $env:DOTNET_EXE = "$DotNetDirectory\dotnet.exe"
64 | }
65 |
66 | Write-Output "Microsoft (R) .NET Core SDK version $(& $env:DOTNET_EXE --version)"
67 |
68 | ExecSafe { & $env:DOTNET_EXE build $BuildProjectFile /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet }
69 | ExecSafe { & $env:DOTNET_EXE run --project $BuildProjectFile --no-build -- $BuildArguments }
70 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | bash --version 2>&1 | head -n 1
4 |
5 | set -eo pipefail
6 | SCRIPT_DIR=$(cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd)
7 |
8 | ###########################################################################
9 | # CONFIGURATION
10 | ###########################################################################
11 |
12 | BUILD_PROJECT_FILE="$SCRIPT_DIR/build/build/_build.csproj"
13 | TEMP_DIRECTORY="$SCRIPT_DIR//.nuke/temp"
14 |
15 | DOTNET_GLOBAL_FILE="$SCRIPT_DIR//global.json"
16 | DOTNET_INSTALL_URL="https://dot.net/v1/dotnet-install.sh"
17 | DOTNET_CHANNEL="Current"
18 |
19 | export DOTNET_CLI_TELEMETRY_OPTOUT=1
20 | export DOTNET_SKIP_FIRST_TIME_EXPERIENCE=1
21 | export DOTNET_MULTILEVEL_LOOKUP=0
22 |
23 | ###########################################################################
24 | # EXECUTION
25 | ###########################################################################
26 |
27 | function FirstJsonValue {
28 | perl -nle 'print $1 if m{"'"$1"'": "([^"]+)",?}' <<< "${@:2}"
29 | }
30 |
31 | # If dotnet CLI is installed globally and it matches requested version, use for execution
32 | if [ -x "$(command -v dotnet)" ] && dotnet --version &>/dev/null; then
33 | export DOTNET_EXE="$(command -v dotnet)"
34 | else
35 | # Download install script
36 | DOTNET_INSTALL_FILE="$TEMP_DIRECTORY/dotnet-install.sh"
37 | mkdir -p "$TEMP_DIRECTORY"
38 | curl -Lsfo "$DOTNET_INSTALL_FILE" "$DOTNET_INSTALL_URL"
39 | chmod +x "$DOTNET_INSTALL_FILE"
40 |
41 | # If global.json exists, load expected version
42 | if [[ -f "$DOTNET_GLOBAL_FILE" ]]; then
43 | DOTNET_VERSION=$(FirstJsonValue "version" "$(cat "$DOTNET_GLOBAL_FILE")")
44 | if [[ "$DOTNET_VERSION" == "" ]]; then
45 | unset DOTNET_VERSION
46 | fi
47 | fi
48 |
49 | # Install by channel or version
50 | DOTNET_DIRECTORY="$TEMP_DIRECTORY/dotnet-unix"
51 | if [[ -z ${DOTNET_VERSION+x} ]]; then
52 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --channel "$DOTNET_CHANNEL" --no-path
53 | else
54 | "$DOTNET_INSTALL_FILE" --install-dir "$DOTNET_DIRECTORY" --version "$DOTNET_VERSION" --no-path
55 | fi
56 | export DOTNET_EXE="$DOTNET_DIRECTORY/dotnet"
57 | fi
58 |
59 | echo "Microsoft (R) .NET Core SDK version $("$DOTNET_EXE" --version)"
60 |
61 | "$DOTNET_EXE" build "$BUILD_PROJECT_FILE" /nodeReuse:false /p:UseSharedCompilation=false -nologo -clp:NoSummary --verbosity quiet
62 | "$DOTNET_EXE" run --project "$BUILD_PROJECT_FILE" --no-build -- "$@"
63 |
--------------------------------------------------------------------------------
/build/Avalonia.Desktop.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/build/Avalonia.Diagnostics.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/build/Avalonia.Fonts.Inter.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/build/Avalonia.ReactiveUI.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/build/Avalonia.Themes.Fluent.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/build/Avalonia.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/build/Base.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | 11.2.0
4 |
5 | Wiesław Šoltés
6 | Wiesław Šoltés
7 | Copyright © Wiesław Šoltés 2024
8 | MIT
9 | https://github.com/wieslawsoltes/TemplatedDataGrid
10 |
11 |
12 | latest
13 | preview
14 |
15 |
16 |
--------------------------------------------------------------------------------
/build/ReferenceAssemblies.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/build/SignAssembly.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | True
5 | $(MSBuildThisFileDirectory)\templateddatagrid.public.snk
6 | false
7 | true
8 |
9 |
10 |
--------------------------------------------------------------------------------
/build/SourceLink.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 | false
5 | true
6 | embedded
7 | $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
8 |
9 |
10 | true
11 |
12 |
13 | true
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/build/System.Reactive.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/build/XUnit.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/build/build/Build.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using Nuke.Common;
3 | using Nuke.Common.Git;
4 | using Nuke.Common.ProjectModel;
5 | using Nuke.Common.Tools.DotNet;
6 | using Nuke.Common.IO;
7 | using static Nuke.Common.IO.FileSystemTasks;
8 | using static Nuke.Common.IO.PathConstruction;
9 | using static Nuke.Common.Tools.DotNet.DotNetTasks;
10 |
11 | class Build : NukeBuild
12 | {
13 | public static int Main() => Execute(x => x.Compile);
14 |
15 | [Solution]
16 | readonly Solution Solution;
17 |
18 | [GitRepository]
19 | readonly GitRepository GitRepository;
20 |
21 | [Parameter("configuration")]
22 | public string Configuration { get; set; }
23 |
24 | [Parameter("version-suffix")]
25 | public string VersionSuffix { get; set; }
26 |
27 | [Parameter("publish-framework")]
28 | public string PublishFramework { get; set; }
29 |
30 | [Parameter("publish-runtime")]
31 | public string PublishRuntime { get; set; }
32 |
33 | [Parameter("publish-project")]
34 | public string PublishProject { get; set; }
35 |
36 | [Parameter("publish-self-contained")]
37 | public bool PublishSelfContained { get; set; } = true;
38 |
39 | AbsolutePath SourceDirectory => RootDirectory / "src";
40 |
41 | AbsolutePath TestsDirectory => RootDirectory / "tests";
42 |
43 | AbsolutePath ArtifactsDirectory => RootDirectory / "artifacts";
44 |
45 | protected override void OnBuildInitialized()
46 | {
47 | Configuration = Configuration ?? "Release";
48 | VersionSuffix = VersionSuffix ?? "";
49 | }
50 |
51 | private void DeleteDirectories(IReadOnlyCollection directories)
52 | {
53 | foreach (var directory in directories)
54 | {
55 | DeleteDirectory(directory);
56 | }
57 | }
58 |
59 | Target Clean => _ => _
60 | .Executes(() =>
61 | {
62 | DeleteDirectories(GlobDirectories(SourceDirectory, "**/bin", "**/obj"));
63 | DeleteDirectories(GlobDirectories(TestsDirectory, "**/bin", "**/obj"));
64 | EnsureCleanDirectory(ArtifactsDirectory);
65 | });
66 |
67 | Target Restore => _ => _
68 | .DependsOn(Clean)
69 | .Executes(() =>
70 | {
71 | DotNetRestore(s => s
72 | .SetProjectFile(Solution));
73 | });
74 |
75 | Target Compile => _ => _
76 | .DependsOn(Restore)
77 | .Executes(() =>
78 | {
79 | DotNetBuild(s => s
80 | .SetProjectFile(Solution)
81 | .SetConfiguration(Configuration)
82 | .SetVersionSuffix(VersionSuffix)
83 | .EnableNoRestore());
84 | });
85 |
86 | Target Test => _ => _
87 | .DependsOn(Compile)
88 | .Executes(() =>
89 | {
90 | DotNetTest(s => s
91 | .SetProjectFile(Solution)
92 | .SetConfiguration(Configuration)
93 | .SetLoggers("trx")
94 | .SetResultsDirectory(ArtifactsDirectory / "TestResults")
95 | .EnableNoBuild()
96 | .EnableNoRestore());
97 | });
98 |
99 | Target Pack => _ => _
100 | .DependsOn(Test)
101 | .Executes(() =>
102 | {
103 | DotNetPack(s => s
104 | .SetProject(Solution)
105 | .SetConfiguration(Configuration)
106 | .SetVersionSuffix(VersionSuffix)
107 | .SetOutputDirectory(ArtifactsDirectory / "NuGet")
108 | .EnableNoBuild()
109 | .EnableNoRestore());
110 | });
111 |
112 | Target Publish => _ => _
113 | .DependsOn(Test)
114 | .Requires(() => PublishRuntime)
115 | .Requires(() => PublishFramework)
116 | .Requires(() => PublishProject)
117 | .Executes(() =>
118 | {
119 | DotNetPublish(s => s
120 | .SetProject(Solution.GetProject(PublishProject))
121 | .SetConfiguration(Configuration)
122 | .SetVersionSuffix(VersionSuffix)
123 | .SetFramework(PublishFramework)
124 | .SetRuntime(PublishRuntime)
125 | .SetSelfContained(PublishSelfContained)
126 | .SetOutput(ArtifactsDirectory / "Publish" / PublishProject + "-" + PublishFramework + "-" + PublishRuntime));
127 | });
128 | }
129 |
--------------------------------------------------------------------------------
/build/build/_build.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net9.0
6 | false
7 | False
8 | CS0649;CS0169
9 | 1
10 | true
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/build/templateddatagrid.public.snk:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wieslawsoltes/TemplatedDataGrid/f7723e0f2c6b5ba1d27a52a85351ad7e9b7fdc8e/build/templateddatagrid.public.snk
--------------------------------------------------------------------------------
/global.json:
--------------------------------------------------------------------------------
1 | {
2 | "sdk": {
3 | "version": "9.0.100",
4 | "rollForward": "latestMinor",
5 | "allowPrerelease": true
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/nuget.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/samples/TemplatedDataGridDemo/App.axaml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/samples/TemplatedDataGridDemo/App.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls.ApplicationLifetimes;
3 | using Avalonia.Markup.Xaml;
4 | using TemplatedDataGridDemo.ViewModels;
5 | using TemplatedDataGridDemo.Views;
6 |
7 | namespace TemplatedDataGridDemo
8 | {
9 | public 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 | }
30 |
--------------------------------------------------------------------------------
/samples/TemplatedDataGridDemo/Assets/avalonia-logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wieslawsoltes/TemplatedDataGrid/f7723e0f2c6b5ba1d27a52a85351ad7e9b7fdc8e/samples/TemplatedDataGridDemo/Assets/avalonia-logo.ico
--------------------------------------------------------------------------------
/samples/TemplatedDataGridDemo/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Avalonia;
3 | using Avalonia.ReactiveUI;
4 |
5 | namespace TemplatedDataGridDemo
6 | {
7 | class Program
8 | {
9 | [STAThread]
10 | public static void Main(string[] args)
11 | {
12 | BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
13 | }
14 |
15 | public static AppBuilder BuildAvaloniaApp()
16 | {
17 | GC.KeepAlive(typeof(TemplatedDataGrid.TemplatedDataGrid).Assembly);
18 |
19 | return AppBuilder.Configure()
20 | .UsePlatformDetect()
21 | .LogToTrace()
22 | .UseReactiveUI();
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/samples/TemplatedDataGridDemo/TemplatedDataGridDemo.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Exe
4 |
5 |
6 | WinExe
7 |
8 |
9 | net9.0
10 | False
11 | enable
12 | TemplatedDataGridDemo
13 | Debug;Release
14 | AnyCPU;x64
15 |
16 |
17 |
18 | True
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | all
41 | runtime; build; native; contentfiles; analyzers; buildtransitive
42 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/samples/TemplatedDataGridDemo/ViewLocator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Avalonia.Controls;
3 | using Avalonia.Controls.Templates;
4 | using TemplatedDataGridDemo.ViewModels;
5 |
6 | namespace TemplatedDataGridDemo
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 | }
29 |
--------------------------------------------------------------------------------
/samples/TemplatedDataGridDemo/ViewModels/ItemViewModel.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 |
3 | namespace TemplatedDataGridDemo.ViewModels
4 | {
5 | public partial class ItemViewModel : ViewModelBase
6 | {
7 | [Reactive]
8 | public partial string Column1 { get; set; }
9 |
10 | [Reactive]
11 | public partial string Column2 { get; set; }
12 |
13 | [Reactive]
14 | public partial string Column3 { get; set; }
15 |
16 | [Reactive]
17 | public partial bool Column4 { get; set; }
18 |
19 | [Reactive]
20 | public partial int Column5 { get; set; }
21 |
22 | [Reactive]
23 | public partial Thickness Margin { get; set; }
24 |
25 | public ItemViewModel(string column1, string column2, string column3, bool column4, int column5, Thickness margin)
26 | {
27 | _column1 = column1;
28 | _column2 = column2;
29 | _column3 = column3;
30 | _column4 = column4;
31 | _column5 = column5;
32 | _margin = margin;
33 | }
34 |
35 | public override string ToString()
36 | {
37 | return $"{_column5}";
38 | }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/samples/TemplatedDataGridDemo/ViewModels/MainWindowViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using System.ComponentModel;
5 | using System.Linq;
6 | using System.Reactive.Linq;
7 | using System.Reactive.Subjects;
8 | using System.Threading.Tasks;
9 | using System.Windows.Input;
10 | using Avalonia;
11 | using DynamicData;
12 | using DynamicData.Binding;
13 | using ReactiveUI;
14 |
15 | namespace TemplatedDataGridDemo.ViewModels
16 | {
17 | public partial class MainWindowViewModel : ViewModelBase
18 | {
19 | private ReadOnlyObservableCollection? _items;
20 |
21 | public ReadOnlyObservableCollection? Items => _items;
22 |
23 | [Reactive]
24 | public partial ItemViewModel? SelectedItem { get; set; }
25 |
26 | [Reactive]
27 | public partial ListSortDirection? SortingStateColumn1 { get; set; }
28 |
29 | [Reactive]
30 | public partial ListSortDirection? SortingStateColumn2 { get; set; }
31 |
32 | [Reactive]
33 | public partial ListSortDirection? SortingStateColumn3 { get; set; }
34 |
35 | [Reactive]
36 | public partial ListSortDirection? SortingStateColumn4 { get; set; }
37 |
38 | [Reactive]
39 | public partial ListSortDirection? SortingStateColumn5 { get; set; }
40 |
41 | public ICommand SortCommand { get; }
42 |
43 | public ICommand AddItemCommand { get; }
44 |
45 | public ICommand InsertItemCommand { get; }
46 |
47 | public ICommand RemoveItemCommand { get; }
48 |
49 | public ICommand SelectFirstItemCommand { get; }
50 |
51 | public MainWindowViewModel()
52 | {
53 | var itemsSourceList = new SourceList();
54 | var comparerSubject = new Subject>();
55 | var isSortingEnabled = false;
56 | var totalItems = 1_000;
57 | var enableRandom = false;
58 | var randomSize = 100;
59 | var rand = new Random();
60 | var items = new List();
61 |
62 | for (var i = 0; i < totalItems; i++)
63 | {
64 | items.Add(CreateItem(i));
65 | }
66 | itemsSourceList.AddRange(items);
67 |
68 | IDisposable? subscription = null;
69 | SortingStateColumn5 = ListSortDirection.Ascending;
70 | EnableSort(x => x.Column5, SortingStateColumn5);
71 |
72 | ItemViewModel CreateItem(int index)
73 | {
74 | return new ItemViewModel(
75 | $"Template1 {index}-1",
76 | $"Template2 {index}-2",
77 | $"Template3 {index}-3",
78 | rand.NextDouble() > 0.5,
79 | index,
80 | enableRandom
81 | ? new Thickness(0, rand.NextDouble() * randomSize, 0, rand.NextDouble() * randomSize)
82 | : new Thickness(0));
83 | }
84 |
85 | IObservable> GetSortObservable(IComparer comparer)
86 | {
87 | return itemsSourceList!
88 | .Connect()
89 | .ObserveOn(RxApp.MainThreadScheduler)
90 | .Sort(comparer, comparerChanged: comparerSubject)
91 | .Bind(out _items);
92 | }
93 |
94 | IObservable> GetDefaultObservable()
95 | {
96 | return itemsSourceList!
97 | .Connect()
98 | .ObserveOn(RxApp.MainThreadScheduler)
99 | .Bind(out _items);
100 | }
101 |
102 | void EnableSort(Func expression, ListSortDirection? listSortDirection)
103 | {
104 | var sortExpressionComparer = listSortDirection == ListSortDirection.Ascending
105 | ? SortExpressionComparer.Ascending(expression)
106 | : SortExpressionComparer.Descending(expression);
107 |
108 | if (!isSortingEnabled)
109 | {
110 | subscription?.Dispose();
111 | subscription = GetSortObservable(sortExpressionComparer).Subscribe();
112 | isSortingEnabled = true;
113 | this.RaisePropertyChanged(nameof(Items));
114 | }
115 | else
116 | {
117 | comparerSubject.OnNext(sortExpressionComparer);
118 | }
119 | }
120 |
121 | void DisableSort()
122 | {
123 | if (isSortingEnabled)
124 | {
125 | subscription?.Dispose();
126 | subscription = GetDefaultObservable().Subscribe();
127 | isSortingEnabled = false;
128 | this.RaisePropertyChanged(nameof(Items));
129 | }
130 | }
131 |
132 | void Sort(string? sortMemberPath)
133 | {
134 | switch (sortMemberPath)
135 | {
136 | case null:
137 | DisableSort();
138 | break;
139 | case "Column1":
140 | EnableSort(x => x.Column1, SortingStateColumn1);
141 | break;
142 | case "Column2":
143 | EnableSort(x => x.Column2, SortingStateColumn2);
144 | break;
145 | case "Column3":
146 | EnableSort(x => x.Column3, SortingStateColumn3);
147 | break;
148 | case "Column4":
149 | EnableSort(x => x.Column4, SortingStateColumn4);
150 | break;
151 | case "Column5":
152 | EnableSort(x => x.Column5, SortingStateColumn5);
153 | break;
154 | }
155 | }
156 |
157 | SortCommand = ReactiveCommand.CreateFromTask(async sortMemberPath =>
158 | {
159 | await Task.Run(() =>
160 | {
161 | Sort(sortMemberPath);
162 | });
163 | });
164 |
165 | InsertItemCommand = ReactiveCommand.Create(() =>
166 | {
167 | if (_items is null)
168 | {
169 | return;
170 | }
171 | var index = _items.Count;
172 | var item = CreateItem(index);
173 | itemsSourceList.Insert(0, item);
174 | });
175 |
176 | AddItemCommand = ReactiveCommand.Create(() =>
177 | {
178 | if (_items is null)
179 | {
180 | return;
181 | }
182 | var index = _items.Count;
183 | var item = CreateItem(index);
184 | itemsSourceList.Add(item);
185 | });
186 |
187 | RemoveItemCommand = ReactiveCommand.Create((item) =>
188 | {
189 | if (item is not null)
190 | {
191 | itemsSourceList.Remove(item);
192 | }
193 | });
194 |
195 | SelectFirstItemCommand = ReactiveCommand.Create(() =>
196 | {
197 | if (_items is null)
198 | {
199 | return;
200 | }
201 | SelectedItem = _items.FirstOrDefault();
202 | });
203 | }
204 | }
205 | }
206 |
--------------------------------------------------------------------------------
/samples/TemplatedDataGridDemo/ViewModels/ViewModelBase.cs:
--------------------------------------------------------------------------------
1 | using ReactiveUI;
2 |
3 | namespace TemplatedDataGridDemo.ViewModels
4 | {
5 | public class ViewModelBase : ReactiveObject
6 | {
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/samples/TemplatedDataGridDemo/Views/MainWindow.axaml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
23 |
28 |
31 |
32 |
33 |
34 |
35 |
40 |
41 |
42 |
43 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
74 |
75 |
80 |
81 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
125 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
--------------------------------------------------------------------------------
/samples/TemplatedDataGridDemo/Views/MainWindow.axaml.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Markup.Xaml;
4 | using Avalonia.Rendering;
5 |
6 | namespace TemplatedDataGridDemo.Views
7 | {
8 | public partial class MainWindow : Window
9 | {
10 | public MainWindow()
11 | {
12 | InitializeComponent();
13 | this.AttachDevTools();
14 | RendererDiagnostics.DebugOverlays = RendererDebugOverlays.Fps;
15 | // RendererDiagnostics.DebugOverlays = RendererDebugOverlays.Fps | RendererDebugOverlays.LayoutTimeGraph | RendererDebugOverlays.RenderTimeGraph;
16 | }
17 |
18 | private void InitializeComponent()
19 | {
20 | AvaloniaXamlLoader.Load(this);
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/TemplatedDataGrid/Controls/TemplatedListBox.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Avalonia.Controls;
3 | using Avalonia.Styling;
4 |
5 | namespace TemplatedDataGrid.Controls
6 | {
7 | public class TemplatedListBox : ListBox, IStyleable
8 | {
9 | Type IStyleable.StyleKey => typeof(ListBox);
10 |
11 | protected override Control CreateContainerForItemOverride(object? item, int index, object? recycleKey)
12 | {
13 | return new TemplatedListBoxItem();
14 | }
15 |
16 | protected override bool NeedsContainerOverride(object? item, int index, out object? recycleKey)
17 | {
18 | return NeedsContainer(item, out recycleKey);
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/TemplatedDataGrid/Controls/TemplatedListBoxItem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Avalonia;
3 | using Avalonia.Controls;
4 | using Avalonia.Styling;
5 |
6 | namespace TemplatedDataGrid.Controls
7 | {
8 | public class TemplatedListBoxItem : ListBoxItem, IStyleable
9 | {
10 | Type IStyleable.StyleKey => typeof(ListBoxItem);
11 |
12 | protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
13 | {
14 | base.OnAttachedToVisualTree(e);
15 | #if DEBUG
16 | Console.WriteLine($"[TemplatedListBoxItem.Attached] {DataContext}");
17 | #endif
18 | }
19 |
20 | protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
21 | {
22 | base.OnDetachedFromVisualTree(e);
23 | #if DEBUG
24 | Console.WriteLine($"[TemplatedListBoxItem.Detach] {DataContext}");
25 | #endif
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/TemplatedDataGrid/Internal/AvaloniaObjectExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reactive.Disposables;
3 | using Avalonia;
4 | using Avalonia.Data;
5 |
6 | namespace TemplatedDataGrid.Internal
7 | {
8 | internal static class AvaloniaObjectExtensions
9 | {
10 | public static void OneWayBind(
11 | this AvaloniaObject target,
12 | AvaloniaProperty targetProperty,
13 | AvaloniaObject source,
14 | AvaloniaProperty sourceProperty,
15 | CompositeDisposable compositeDisposable)
16 | {
17 | var targetDisposable = target.Bind(targetProperty, source.GetObservable(sourceProperty));
18 | compositeDisposable.Add(targetDisposable);
19 | }
20 |
21 | public static void TwoWayBind(
22 | this AvaloniaObject target,
23 | AvaloniaProperty targetProperty,
24 | AvaloniaObject source,
25 | AvaloniaProperty sourceProperty,
26 | CompositeDisposable compositeDisposable)
27 | {
28 | var targetDisposable = target.Bind(targetProperty, source.GetObservable(sourceProperty));
29 | var sourceDisposable = source.Bind(sourceProperty, target.GetObservable(targetProperty));
30 | compositeDisposable.Add(targetDisposable);
31 | compositeDisposable.Add(sourceDisposable);
32 | }
33 |
34 | public static void OneWayBind(
35 | this AvaloniaObject target,
36 | AvaloniaProperty targetProperty,
37 | IObservable> source,
38 | CompositeDisposable compositeDisposable)
39 | {
40 | var targetDisposable = target.Bind(targetProperty, source);
41 | compositeDisposable.Add(targetDisposable);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/TemplatedDataGrid/Primitives/TemplatedDataGridCellsPresenter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Reactive.Disposables;
4 | using System.Reactive.Linq;
5 | using Avalonia;
6 | using Avalonia.Collections;
7 | using Avalonia.Controls;
8 | using Avalonia.Controls.Primitives;
9 | using Avalonia.Data;
10 | using TemplatedDataGrid.Internal;
11 |
12 | namespace TemplatedDataGrid.Primitives
13 | {
14 | public class TemplatedDataGridCellsPresenter : TemplatedControl
15 | {
16 | internal static readonly DirectProperty SelectedItemProperty =
17 | AvaloniaProperty.RegisterDirect(
18 | nameof(SelectedItem),
19 | o => o.SelectedItem,
20 | (o, v) => o.SelectedItem = v,
21 | defaultBindingMode: BindingMode.TwoWay);
22 |
23 | internal static readonly DirectProperty SelectedCellProperty =
24 | AvaloniaProperty.RegisterDirect(
25 | nameof(SelectedCell),
26 | o => o.SelectedCell,
27 | (o, v) => o.SelectedCell = v,
28 | defaultBindingMode: BindingMode.TwoWay);
29 |
30 | internal static readonly DirectProperty?> ColumnsProperty =
31 | AvaloniaProperty.RegisterDirect?>(
32 | nameof(Columns),
33 | o => o.Columns,
34 | (o, v) => o.Columns = v);
35 |
36 | internal static readonly DirectProperty> CellsProperty =
37 | AvaloniaProperty.RegisterDirect>(
38 | nameof(Cells),
39 | o => o.Cells,
40 | (o, v) => o.Cells = v);
41 |
42 | private object? _selectedItem;
43 | private object? _selectedCell;
44 | private AvaloniaList? _columns;
45 | private AvaloniaList _cells = new ();
46 | private Grid? _root;
47 | private readonly List _rootChildren = new ();
48 |
49 | internal object? SelectedItem
50 | {
51 | get => _selectedItem;
52 | set => SetAndRaise(SelectedItemProperty, ref _selectedItem, value);
53 | }
54 |
55 | internal object? SelectedCell
56 | {
57 | get => _selectedCell;
58 | set => SetAndRaise(SelectedCellProperty, ref _selectedCell, value);
59 | }
60 |
61 | internal AvaloniaList? Columns
62 | {
63 | get => _columns;
64 | set => SetAndRaise(ColumnsProperty, ref _columns, value);
65 | }
66 |
67 | internal AvaloniaList Cells
68 | {
69 | get => _cells;
70 | set => SetAndRaise(CellsProperty, ref _cells, value);
71 | }
72 |
73 | internal CompositeDisposable? RootDisposables { get; set; }
74 |
75 | protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
76 | {
77 | base.OnApplyTemplate(e);
78 |
79 | _root = e.NameScope.Find("PART_Root");
80 |
81 | InvalidateRoot();
82 | }
83 |
84 | protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
85 | {
86 | base.OnAttachedToVisualTree(e);
87 | #if DEBUG
88 | Console.WriteLine($"[TemplatedDataGridCellsPresenter.Attached] {DataContext}");
89 | #endif
90 | }
91 |
92 | protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
93 | {
94 | base.OnDetachedFromVisualTree(e);
95 | #if DEBUG
96 | Console.WriteLine($"[TemplatedDataGridCellsPresenter.Detach] {DataContext}");
97 | #endif
98 | Detach();
99 | }
100 |
101 | protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
102 | {
103 | base.OnPropertyChanged(change);
104 |
105 | if (change.Property == SelectedItemProperty)
106 | {
107 | // TODO:
108 | }
109 |
110 | if (change.Property == SelectedCellProperty)
111 | {
112 | // TODO:
113 | }
114 |
115 | if (change.Property == ColumnsProperty)
116 | {
117 | InvalidateRoot();
118 | }
119 | }
120 |
121 | private void InvalidateRoot()
122 | {
123 | Detach();
124 | Attach();
125 | }
126 |
127 | internal void Attach()
128 | {
129 | if (_root is null)
130 | {
131 | return;
132 | }
133 |
134 | RootDisposables = new CompositeDisposable();
135 |
136 | var columns = Columns;
137 | if (columns is null)
138 | {
139 | return;
140 | }
141 |
142 | // Generate ColumnDefinitions
143 |
144 | var columnDefinitions = new List();
145 |
146 | for (var i = 0; i < columns.Count; i++)
147 | {
148 | var column = columns[i];
149 | var isStarWidth = column.Width.IsStar;
150 | var isAutoWidth = column.Width.IsAuto;
151 | var isPixelWidth = column.Width.IsAbsolute;
152 |
153 | var columnDefinition = new ColumnDefinition();
154 |
155 | columnDefinition.OneWayBind(ColumnDefinition.MinWidthProperty, column, TemplatedDataGridColumn.MinWidthProperty, RootDisposables);
156 | columnDefinition.TwoWayBind(ColumnDefinition.MaxWidthProperty, column, TemplatedDataGridColumn.MaxWidthProperty, RootDisposables);
157 |
158 | if (isStarWidth)
159 | {
160 | columnDefinition.OneWayBind(ColumnDefinition.WidthProperty,
161 | column.GetObservable(TemplatedDataGridColumn.ActualWidthProperty)
162 | .Select(x => new BindingValue(new GridLength(x, GridUnitType.Pixel))),
163 | RootDisposables);
164 | }
165 |
166 | if (isAutoWidth)
167 | {
168 | columnDefinition.OneWayBind(ColumnDefinition.WidthProperty, column, TemplatedDataGridColumn.WidthProperty, RootDisposables);
169 | columnDefinition.SetValue(DefinitionBase.SharedSizeGroupProperty, $"Column{i}");
170 |
171 | RootDisposables.Add(Disposable.Create(() =>
172 | {
173 | columnDefinition.SetValue(DefinitionBase.SharedSizeGroupProperty, default);
174 | }));
175 | }
176 |
177 | if (isPixelWidth)
178 | {
179 | columnDefinition.OneWayBind(ColumnDefinition.WidthProperty, column, TemplatedDataGridColumn.WidthProperty, RootDisposables);
180 | }
181 |
182 | columnDefinitions.Add(columnDefinition);
183 |
184 | // Generate DataGridCell's
185 |
186 | var cell = new TemplatedDataGridCell
187 | {
188 | [Grid.ColumnProperty] = columnDefinitions.Count - 1
189 | };
190 |
191 | cell.TwoWayBind(TemplatedDataGridCell.SelectedItemProperty, this, TemplatedDataGridCellsPresenter.SelectedItemProperty, RootDisposables);
192 | cell.TwoWayBind(TemplatedDataGridCell.SelectedCellProperty, this, TemplatedDataGridCellsPresenter.SelectedCellProperty, RootDisposables);
193 | cell.OneWayBind(TemplatedDataGridCell.ContentProperty, this, TemplatedDataGridCellsPresenter.DataContextProperty, RootDisposables);
194 | cell.OneWayBind(TemplatedDataGridCell.CellTemplateProperty, column, TemplatedDataGridColumn.CellTemplateProperty, RootDisposables);
195 |
196 | _cells.Add(cell);
197 | _rootChildren.Add(cell);
198 |
199 | if (i < columns.Count)
200 | {
201 | columnDefinitions.Add(new ColumnDefinition(new GridLength(1, GridUnitType.Pixel)));
202 | }
203 | }
204 |
205 | columnDefinitions.Add(new ColumnDefinition(GridLength.Auto));
206 |
207 | _root.ColumnDefinitions.Clear();
208 | _root.ColumnDefinitions.AddRange(columnDefinitions);
209 |
210 | foreach (var child in _rootChildren)
211 | {
212 | _root.Children.Add(child);
213 | }
214 |
215 | RootDisposables.Add(Disposable.Create(() =>
216 | {
217 | foreach (var child in _rootChildren)
218 | {
219 | _root.Children.Remove(child);
220 | }
221 |
222 | _root.RowDefinitions.Clear();
223 | _root.ColumnDefinitions.Clear();
224 | }));
225 | }
226 |
227 | internal void Detach()
228 | {
229 | RootDisposables?.Dispose();
230 | RootDisposables = null;
231 |
232 | if (_root is { })
233 | {
234 | _root.ColumnDefinitions.Clear();
235 |
236 | foreach (var child in _rootChildren)
237 | {
238 | _root.Children.Remove(child);
239 | }
240 | }
241 |
242 | _cells.Clear();
243 | }
244 | }
245 | }
246 |
--------------------------------------------------------------------------------
/src/TemplatedDataGrid/Primitives/TemplatedDataGridColumnHeadersPresenter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Reactive.Disposables;
4 | using System.Reactive.Linq;
5 | using Avalonia;
6 | using Avalonia.Collections;
7 | using Avalonia.Controls;
8 | using Avalonia.Controls.Primitives;
9 | using Avalonia.Controls.Shapes;
10 | using Avalonia.Data;
11 | using TemplatedDataGrid.Internal;
12 |
13 | namespace TemplatedDataGrid.Primitives
14 | {
15 | public class TemplatedDataGridColumnHeadersPresenter : TemplatedControl
16 | {
17 | internal static readonly DirectProperty?> ColumnsProperty =
18 | AvaloniaProperty.RegisterDirect?>(
19 | nameof(Columns),
20 | o => o.Columns,
21 | (o, v) => o.Columns = v);
22 |
23 | internal static readonly DirectProperty> ColumnHeadersProperty =
24 | AvaloniaProperty.RegisterDirect>(
25 | nameof(ColumnHeaders),
26 | o => o.ColumnHeaders,
27 | (o, v) => o.ColumnHeaders = v);
28 |
29 | internal static readonly StyledProperty CanUserSortColumnsProperty =
30 | AvaloniaProperty.Register(nameof(CanUserSortColumns));
31 |
32 | internal static readonly StyledProperty CanUserResizeColumnsProperty =
33 | AvaloniaProperty.Register(nameof(CanUserResizeColumns));
34 |
35 | private AvaloniaList? _columns;
36 | private AvaloniaList _columnHeaders = new AvaloniaList();
37 | private Grid? _root;
38 | private readonly List _rootChildren = new List();
39 |
40 | internal AvaloniaList? Columns
41 | {
42 | get => _columns;
43 | set => SetAndRaise(ColumnsProperty, ref _columns, value);
44 | }
45 |
46 | internal AvaloniaList ColumnHeaders
47 | {
48 | get => _columnHeaders;
49 | set => SetAndRaise(ColumnHeadersProperty, ref _columnHeaders, value);
50 | }
51 |
52 | internal bool CanUserSortColumns
53 | {
54 | get => GetValue(CanUserSortColumnsProperty);
55 | set => SetValue(CanUserSortColumnsProperty, value);
56 | }
57 |
58 | internal bool CanUserResizeColumns
59 | {
60 | get => GetValue(CanUserResizeColumnsProperty);
61 | set => SetValue(CanUserResizeColumnsProperty, value);
62 | }
63 |
64 | internal CompositeDisposable? RootDisposables { get; set; }
65 |
66 | protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
67 | {
68 | base.OnApplyTemplate(e);
69 |
70 | _root = e.NameScope.Find("PART_Root");
71 |
72 | InvalidateRoot();
73 | }
74 |
75 | protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
76 | {
77 | base.OnAttachedToVisualTree(e);
78 | #if DEBUG
79 | Console.WriteLine($"[TemplatedDataGridColumnHeadersPresenter.Attached] {DataContext}");
80 | #endif
81 | }
82 |
83 | protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
84 | {
85 | base.OnDetachedFromVisualTree(e);
86 | #if DEBUG
87 | Console.WriteLine($"[TemplatedDataGridColumnHeadersPresenter.Detach] {DataContext}");
88 | #endif
89 | }
90 |
91 | protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
92 | {
93 | base.OnPropertyChanged(change);
94 |
95 | if (change.Property == ColumnsProperty)
96 | {
97 | InvalidateRoot();
98 | }
99 | }
100 |
101 | private void InvalidateRoot()
102 | {
103 | RootDisposables?.Dispose();
104 | RootDisposables = null;
105 |
106 | if (_root is null)
107 | {
108 | return;
109 | }
110 |
111 | RootDisposables = new CompositeDisposable();
112 |
113 | foreach (var child in _rootChildren)
114 | {
115 | _root.Children.Remove(child);
116 | }
117 |
118 | _columnHeaders.Clear();
119 |
120 | var columns = Columns;
121 | if (columns is null)
122 | {
123 | return;
124 | }
125 |
126 | // Generate RowDefinitions
127 |
128 | var rowDefinitions = new List();
129 |
130 | rowDefinitions.Add(new RowDefinition(GridLength.Auto));
131 |
132 | // Generate ColumnDefinitions
133 |
134 | var columnDefinitions = new List();
135 | var splitterColumnIndexes = new List();
136 |
137 | for (var i = 0; i < columns.Count; i++)
138 | {
139 | var column = columns[i];
140 | var isStarWidth = column.Width.IsStar;
141 | var isAutoWidth = column.Width.IsAuto;
142 | var isPixelWidth = column.Width.IsAbsolute;
143 |
144 | var columnDefinition = new ColumnDefinition();
145 |
146 | columnDefinition.TwoWayBind(ColumnDefinition.MinWidthProperty, column, TemplatedDataGridColumn.MinWidthProperty, RootDisposables);
147 | columnDefinition.TwoWayBind(ColumnDefinition.MaxWidthProperty, column, TemplatedDataGridColumn.MaxWidthProperty, RootDisposables);
148 |
149 | if (isStarWidth)
150 | {
151 | columnDefinition.TwoWayBind(ColumnDefinition.WidthProperty, column, TemplatedDataGridColumn.WidthProperty, RootDisposables);
152 | }
153 |
154 | if (isAutoWidth)
155 | {
156 | columnDefinition.TwoWayBind(ColumnDefinition.WidthProperty, column, TemplatedDataGridColumn.WidthProperty, RootDisposables);
157 | columnDefinition.SetValue(DefinitionBase.SharedSizeGroupProperty, $"Column{i}");
158 |
159 | RootDisposables.Add(Disposable.Create(() =>
160 | {
161 | columnDefinition.SetValue(DefinitionBase.SharedSizeGroupProperty, default);
162 | }));
163 | }
164 |
165 | if (isPixelWidth)
166 | {
167 | columnDefinition.TwoWayBind(ColumnDefinition.WidthProperty, column, TemplatedDataGridColumn.WidthProperty, RootDisposables);
168 | }
169 |
170 | columnDefinitions.Add(columnDefinition);
171 |
172 | // Generate DataGridColumnHeader's
173 | var columnHeader = new TemplatedDataGridColumnHeader
174 | {
175 | [Grid.RowProperty] = 0,
176 | [Grid.ColumnProperty] = columnDefinitions.Count - 1,
177 | [TemplatedDataGridColumnHeader.ColumnProperty] = column
178 | };
179 |
180 | columnHeader.OneWayBind(TemplatedDataGridColumnHeader.HeaderProperty, column, TemplatedDataGridColumn.HeaderProperty, RootDisposables);
181 | columnHeader.OneWayBind(TemplatedDataGridColumnHeader.CanUserSortColumnsProperty, this, TemplatedDataGridColumnHeadersPresenter.CanUserSortColumnsProperty, RootDisposables);
182 | columnHeader.OneWayBind(TemplatedDataGridColumnHeader.CanUserResizeColumnsProperty, this, TemplatedDataGridColumnHeadersPresenter.CanUserResizeColumnsProperty, RootDisposables);
183 | columnHeader.OneWayBind(TemplatedDataGridColumnHeader.ColumnHeadersProperty, this, TemplatedDataGridColumnHeadersPresenter.ColumnHeadersProperty, RootDisposables);
184 |
185 | _columnHeaders.Add(columnHeader);
186 | _rootChildren.Add(columnHeader);
187 |
188 | column.OneWayBind(
189 | TemplatedDataGridColumn.ActualWidthProperty,
190 | columnHeader.GetObservable(Visual.BoundsProperty)
191 | .Select(_ => new BindingValue(columnDefinition.ActualWidth)),
192 | RootDisposables);
193 |
194 | if (i < columns.Count)
195 | {
196 | columnDefinitions.Add(new ColumnDefinition(new GridLength(1, GridUnitType.Pixel)));
197 | splitterColumnIndexes.Add(columnDefinitions.Count - 1);
198 | }
199 | }
200 |
201 | columnDefinitions.Add(new ColumnDefinition(GridLength.Auto));
202 |
203 | // Generate Vertical Grid Lines
204 | // Generate GridSplitter's
205 |
206 | foreach (var columnIndex in splitterColumnIndexes)
207 | {
208 | var verticalColumnGridLine = new Rectangle
209 | {
210 | [Grid.RowProperty] = 0,
211 | [Grid.RowSpanProperty] = 1,
212 | [Grid.ColumnProperty] = columnIndex,
213 | [Rectangle.IsHitTestVisibleProperty] = false
214 | };
215 | ((IPseudoClasses)verticalColumnGridLine.Classes).Add(":vertical");
216 | _rootChildren.Add(verticalColumnGridLine);
217 |
218 | var verticalGridSplitter = new GridSplitter
219 | {
220 | [Grid.RowProperty] = 0,
221 | [Grid.RowSpanProperty] = 1,
222 | [Grid.ColumnProperty] = columnIndex
223 | };
224 |
225 | verticalGridSplitter.OneWayBind(GridSplitter.IsEnabledProperty, this, TemplatedDataGridColumnHeadersPresenter.CanUserResizeColumnsProperty, RootDisposables);
226 |
227 | _rootChildren.Add(verticalGridSplitter);
228 | }
229 |
230 | _root.RowDefinitions.Clear();
231 | _root.RowDefinitions.AddRange(rowDefinitions);
232 |
233 | _root.ColumnDefinitions.Clear();
234 | _root.ColumnDefinitions.AddRange(columnDefinitions);
235 |
236 | foreach (var child in _rootChildren)
237 | {
238 | _root.Children.Add(child);
239 | }
240 |
241 | RootDisposables.Add(Disposable.Create(() =>
242 | {
243 | foreach (var child in _rootChildren)
244 | {
245 | _root.Children.Remove(child);
246 | }
247 |
248 | _root.RowDefinitions.Clear();
249 | _root.ColumnDefinitions.Clear();
250 | }));
251 | }
252 | }
253 | }
254 |
--------------------------------------------------------------------------------
/src/TemplatedDataGrid/Primitives/TemplatedDataGridDetailsPresenter.cs:
--------------------------------------------------------------------------------
1 | using Avalonia.Controls.Primitives;
2 |
3 | namespace TemplatedDataGrid.Primitives
4 | {
5 | public class TemplatedDataGridDetailsPresenter : TemplatedControl
6 | {
7 | protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
8 | {
9 | base.OnApplyTemplate(e);
10 |
11 | // TODO: Implement DataGridDetailsPresenter
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/TemplatedDataGrid/Primitives/TemplatedDataGridRowsPresenter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Reactive.Disposables;
4 | using Avalonia;
5 | using Avalonia.Collections;
6 | using Avalonia.Controls;
7 | using Avalonia.Controls.Primitives;
8 | using Avalonia.Controls.Templates;
9 | using Avalonia.Data;
10 | using TemplatedDataGrid.Controls;
11 | using TemplatedDataGrid.Internal;
12 |
13 | namespace TemplatedDataGrid.Primitives
14 | {
15 | public class TemplatedDataGridRowsPresenter : TemplatedControl
16 | {
17 | internal static readonly StyledProperty ItemTemplateProperty =
18 | AvaloniaProperty.Register(nameof(ItemTemplate));
19 |
20 | internal static readonly DirectProperty ItemsProperty =
21 | AvaloniaProperty.RegisterDirect(
22 | nameof(Items),
23 | o => o.Items,
24 | (o, v) => o.Items = v);
25 |
26 | internal static readonly StyledProperty AutoScrollToSelectedItemProperty =
27 | AvaloniaProperty.Register(nameof(AutoScrollToSelectedItem));
28 |
29 | internal static readonly DirectProperty SelectedItemProperty =
30 | AvaloniaProperty.RegisterDirect(
31 | nameof(SelectedItem),
32 | o => o.SelectedItem,
33 | (o, v) => o.SelectedItem = v,
34 | defaultBindingMode: BindingMode.TwoWay);
35 |
36 | internal static readonly DirectProperty SelectedCellProperty =
37 | AvaloniaProperty.RegisterDirect(
38 | nameof(SelectedCell),
39 | o => o.SelectedCell,
40 | (o, v) => o.SelectedCell = v,
41 | defaultBindingMode: BindingMode.TwoWay);
42 |
43 | internal static readonly DirectProperty ScrollProperty =
44 | AvaloniaProperty.RegisterDirect(
45 | nameof(Scroll),
46 | o => o.Scroll,
47 | (o, v) => o.Scroll = v);
48 |
49 | internal static readonly DirectProperty?> ColumnsProperty =
50 | AvaloniaProperty.RegisterDirect?>(
51 | nameof(Columns),
52 | o => o.Columns,
53 | (o, v) => o.Columns = v);
54 |
55 | internal static readonly DirectProperty> RowsProperty =
56 | AvaloniaProperty.RegisterDirect>(
57 | nameof(Rows),
58 | o => o.Rows,
59 | (o, v) => o.Rows = v);
60 |
61 | internal static readonly StyledProperty GridLinesVisibilityProperty =
62 | AvaloniaProperty.Register(nameof(GridLinesVisibility));
63 |
64 | private IScrollable? _scroll;
65 | private AvaloniaList? _columns;
66 | private IEnumerable? _items;
67 | private object? _selectedItem;
68 | private object? _selectedCell;
69 | private AvaloniaList _rows = new ();
70 | private ListBox? _listBox;
71 |
72 | internal IDataTemplate ItemTemplate
73 | {
74 | get => GetValue(ItemTemplateProperty);
75 | set => SetValue(ItemTemplateProperty, value);
76 | }
77 |
78 | internal IEnumerable? Items
79 | {
80 | get => _items;
81 | set => SetAndRaise(ItemsProperty, ref _items, value);
82 | }
83 |
84 | internal object? SelectedItem
85 | {
86 | get => _selectedItem;
87 | set => SetAndRaise(SelectedItemProperty, ref _selectedItem, value);
88 | }
89 |
90 | internal bool AutoScrollToSelectedItem
91 | {
92 | get => GetValue(AutoScrollToSelectedItemProperty);
93 | set => SetValue(AutoScrollToSelectedItemProperty, value);
94 | }
95 |
96 | internal object? SelectedCell
97 | {
98 | get => _selectedCell;
99 | set => SetAndRaise(SelectedCellProperty, ref _selectedCell, value);
100 | }
101 |
102 | internal IScrollable? Scroll
103 | {
104 | get => _scroll;
105 | set => SetAndRaise(ScrollProperty, ref _scroll, value);
106 | }
107 |
108 | internal AvaloniaList? Columns
109 | {
110 | get => _columns;
111 | set => SetAndRaise(ColumnsProperty, ref _columns, value);
112 | }
113 |
114 | internal AvaloniaList Rows
115 | {
116 | get => _rows;
117 | set => SetAndRaise(RowsProperty, ref _rows, value);
118 | }
119 |
120 | internal TemplatedDataGridGridLinesVisibility GridLinesVisibility
121 | {
122 | get => GetValue(GridLinesVisibilityProperty);
123 | set => SetValue(GridLinesVisibilityProperty, value);
124 | }
125 |
126 | protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
127 | {
128 | base.OnApplyTemplate(e);
129 |
130 | _listBox = e.NameScope.Find("PART_ListBox");
131 |
132 | if (_listBox is { })
133 | {
134 | var listBoxDisposables = new CompositeDisposable();
135 |
136 | _listBox.OneWayBind(ItemsControl.ItemsSourceProperty, this, ItemsProperty, listBoxDisposables);
137 | _listBox.OneWayBind(SelectingItemsControl.AutoScrollToSelectedItemProperty, this, AutoScrollToSelectedItemProperty, listBoxDisposables);
138 | _listBox.OneWayBind(ItemsControl.ItemTemplateProperty, this, ItemTemplateProperty, listBoxDisposables);
139 |
140 | this.TwoWayBind(SelectedItemProperty, _listBox, SelectingItemsControl.SelectedItemProperty, listBoxDisposables);
141 | this.OneWayBind(ScrollProperty, _listBox, ListBox.ScrollProperty, listBoxDisposables);
142 |
143 | _listBox.ContainerPrepared += (sender, args) =>
144 | {
145 | var container = args.Container;
146 | var item = args.Container.DataContext;
147 | var index = args.Index;
148 | #if DEBUG
149 | Console.WriteLine($"[ContainerPrepared] item='{item}', index='{index}'");
150 | #endif
151 | TemplatedDataGridRow.SetItem(container, container.DataContext);
152 | TemplatedDataGridRow.SetIndex(container, index);
153 | };
154 |
155 | _listBox.ContainerClearing += (sender, args) =>
156 | {
157 | var container = args.Container;
158 | var item = args.Container.DataContext;
159 | #if DEBUG
160 | Console.WriteLine($"[ContainerPrepared] item='{item}'");
161 | #endif
162 | // var listBoxItem = container.ContainerControl as ListBoxItem;
163 | // var presenter = listBoxItem?.Presenter;
164 | // var row = presenter?.Child as TemplatedDataGridRow;
165 | // row?.Detach();
166 | TemplatedDataGridRow.SetItem(container, null);
167 | TemplatedDataGridRow.SetIndex(container, -1);
168 | };
169 |
170 | _listBox.ContainerIndexChanged += (sender, args) =>
171 | {
172 | var container = args.Container;
173 | var item = args.Container.DataContext;
174 | var index = args.NewIndex;
175 | #if DEBUG
176 | Console.WriteLine($"[ContainerIndexChanged] item='{item}', index='{index}'");
177 | #endif
178 | TemplatedDataGridRow.SetItem(container, item);
179 | TemplatedDataGridRow.SetIndex(container, index);
180 | };
181 | }
182 |
183 | InvalidateItemTemplate();
184 | }
185 |
186 | protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
187 | {
188 | base.OnPropertyChanged(change);
189 |
190 | if (change.Property == SelectedItemProperty)
191 | {
192 | // TODO:
193 | }
194 |
195 | if (change.Property == SelectedCellProperty)
196 | {
197 | // TODO:
198 | }
199 |
200 | if (change.Property == ColumnsProperty)
201 | {
202 | InvalidateItemTemplate();
203 | }
204 | }
205 | #if DEBUG
206 | private int _rowId;
207 | #endif
208 | private void InvalidateItemTemplate()
209 | {
210 | _rows.Clear();
211 |
212 | ItemTemplate = new FuncDataTemplate(
213 | _ => true,
214 | (_, _) =>
215 | {
216 | var row = new TemplatedDataGridRow();
217 |
218 | var disposables = new CompositeDisposable();
219 |
220 | row.TwoWayBind(TemplatedDataGridRow.SelectedItemProperty, this, TemplatedDataGridRowsPresenter.SelectedItemProperty, disposables);
221 | row.TwoWayBind(TemplatedDataGridRow.SelectedCellProperty, this, TemplatedDataGridRowsPresenter.SelectedCellProperty, disposables);
222 | row.OneWayBind(TemplatedDataGridRow.ColumnsProperty, this, TemplatedDataGridRowsPresenter.ColumnsProperty, disposables);
223 | row.OneWayBind(TemplatedDataGridRow.GridLinesVisibilityProperty, this, TemplatedDataGridRowsPresenter.GridLinesVisibilityProperty, disposables);
224 |
225 | row.TemplateDisposables = disposables;
226 |
227 | #if DEBUG
228 | row.Tag = _rowId++;
229 |
230 | row.DataContextChanged += (_, _) =>
231 | {
232 | //Console.WriteLine($"[DataContextChanged] Row='{row.Tag}', DataContext='{row.DataContext}'");
233 | };
234 |
235 | row.AttachedToVisualTree += (_, _) =>
236 | {
237 | //Console.WriteLine($"[AttachedToVisualTree] Row='{row.Tag}'");
238 | };
239 |
240 | row.DetachedFromVisualTree += (_, _) =>
241 | {
242 | //Console.WriteLine($"[DetachedFromVisualTree] Row='{row.Tag}'");
243 | };
244 |
245 | row.AttachedToLogicalTree += (_, _) =>
246 | {
247 | //Console.WriteLine($"[AttachedToLogicalTree] Row='{row.Tag}'");
248 | };
249 |
250 | row.DetachedFromLogicalTree += (_, _) =>
251 | {
252 | //Console.WriteLine($"[DetachedFromLogicalTree] Row='{row.Tag}'");
253 | };
254 | #endif
255 |
256 | _rows.Add(row);
257 | return row;
258 | },
259 | supportsRecycling: true);
260 | }
261 | }
262 | }
263 |
--------------------------------------------------------------------------------
/src/TemplatedDataGrid/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using Avalonia.Metadata;
2 |
3 | [assembly: XmlnsDefinition("https://github.com/avaloniaui", "TemplatedDataGrid")]
4 | [assembly: XmlnsDefinition("https://github.com/avaloniaui", "TemplatedDataGrid.Controls")]
5 | [assembly: XmlnsDefinition("https://github.com/avaloniaui", "TemplatedDataGrid.Primitives")]
6 |
--------------------------------------------------------------------------------
/src/TemplatedDataGrid/TemplatedDataGrid.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Reactive.Disposables;
5 | using System.Reactive.Linq;
6 | using Avalonia;
7 | using Avalonia.Collections;
8 | using Avalonia.Controls;
9 | using Avalonia.Controls.Primitives;
10 | using Avalonia.Controls.Shapes;
11 | using Avalonia.Data;
12 | using TemplatedDataGrid.Internal;
13 | using TemplatedDataGrid.Primitives;
14 |
15 | namespace TemplatedDataGrid
16 | {
17 | public class TemplatedDataGrid : TemplatedControl
18 | {
19 | public static readonly DirectProperty> ColumnsProperty =
20 | AvaloniaProperty.RegisterDirect>(
21 | nameof(Columns),
22 | o => o.Columns);
23 |
24 | public static readonly DirectProperty ItemsProperty =
25 | AvaloniaProperty.RegisterDirect(
26 | nameof(Items),
27 | o => o.Items,
28 | (o, v) => o.Items = v);
29 |
30 | public static readonly DirectProperty SelectedItemProperty =
31 | AvaloniaProperty.RegisterDirect(
32 | nameof(SelectedItem),
33 | o => o.SelectedItem,
34 | (o, v) => o.SelectedItem = v,
35 | defaultBindingMode: BindingMode.TwoWay);
36 |
37 | public static readonly StyledProperty AutoScrollToSelectedItemProperty =
38 | AvaloniaProperty.Register(nameof(AutoScrollToSelectedItem));
39 |
40 | internal static readonly DirectProperty SelectedCellProperty =
41 | AvaloniaProperty.RegisterDirect(
42 | nameof(SelectedCell),
43 | o => o.SelectedCell,
44 | (o, v) => o.SelectedCell = v,
45 | defaultBindingMode: BindingMode.TwoWay);
46 |
47 | public static readonly StyledProperty CanUserSortColumnsProperty =
48 | AvaloniaProperty.Register(nameof(CanUserSortColumns), true);
49 |
50 | public static readonly StyledProperty CanUserResizeColumnsProperty =
51 | AvaloniaProperty.Register(nameof(CanUserResizeColumns));
52 |
53 | public static readonly StyledProperty GridLinesVisibilityProperty =
54 | AvaloniaProperty.Register(nameof(GridLinesVisibility));
55 |
56 | public static readonly StyledProperty IsReadOnlyProperty =
57 | AvaloniaProperty.Register(nameof(IsReadOnly));
58 |
59 | public static readonly StyledProperty AutoGenerateColumnsProperty =
60 | AvaloniaProperty.Register(nameof(AutoGenerateColumns));
61 |
62 | private AvaloniaList _columns;
63 | private IEnumerable? _items;
64 | private object? _selectedItem;
65 | private object? _selectedCell;
66 | private Panel? _panel;
67 | private Grid? _root;
68 | private readonly List _rootChildren = new List();
69 | private TemplatedDataGridColumnHeadersPresenter? _columnHeadersPresenter;
70 | private ScrollViewer? _columnHeadersPresenterScrollViewer;
71 | private TemplatedDataGridRowsPresenter? _rowsPresenter;
72 |
73 | public TemplatedDataGrid()
74 | {
75 | _columns = new AvaloniaList();
76 | }
77 |
78 | public AvaloniaList Columns
79 | {
80 | get => _columns;
81 | private set => SetAndRaise(ColumnsProperty, ref _columns, value);
82 | }
83 |
84 | public IEnumerable? Items
85 | {
86 | get => _items;
87 | set => SetAndRaise(ItemsProperty, ref _items, value);
88 | }
89 |
90 | public bool AutoScrollToSelectedItem
91 | {
92 | get => GetValue(AutoScrollToSelectedItemProperty);
93 | set => SetValue(AutoScrollToSelectedItemProperty, value);
94 | }
95 |
96 | public object? SelectedItem
97 | {
98 | get => _selectedItem;
99 | set => SetAndRaise(SelectedItemProperty, ref _selectedItem, value);
100 | }
101 |
102 | internal object? SelectedCell
103 | {
104 | get => _selectedCell;
105 | set => SetAndRaise(SelectedCellProperty, ref _selectedCell, value);
106 | }
107 |
108 | public bool CanUserSortColumns
109 | {
110 | get => GetValue(CanUserSortColumnsProperty);
111 | set => SetValue(CanUserSortColumnsProperty, value);
112 | }
113 |
114 | public bool CanUserResizeColumns
115 | {
116 | get => GetValue(CanUserResizeColumnsProperty);
117 | set => SetValue(CanUserResizeColumnsProperty, value);
118 | }
119 |
120 | public TemplatedDataGridGridLinesVisibility GridLinesVisibility
121 | {
122 | get => GetValue(GridLinesVisibilityProperty);
123 | set => SetValue(GridLinesVisibilityProperty, value);
124 | }
125 |
126 | public bool IsReadOnly
127 | {
128 | get => GetValue(IsReadOnlyProperty);
129 | set => SetValue(IsReadOnlyProperty, value);
130 | }
131 |
132 | public bool AutoGenerateColumns
133 | {
134 | get => GetValue(AutoGenerateColumnsProperty);
135 | set => SetValue(AutoGenerateColumnsProperty, value);
136 | }
137 |
138 | internal CompositeDisposable? RootDisposables { get; set; }
139 |
140 | internal CompositeDisposable? ColumnHeadersDisposables { get; set; }
141 |
142 | internal CompositeDisposable? RowsDisposables { get; set; }
143 |
144 | internal IDisposable? RowsPresenterScrollDisposable { get; set; }
145 |
146 | protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
147 | {
148 | base.OnApplyTemplate(e);
149 |
150 | _panel = e.NameScope.Find("PART_Panel");
151 | _root = e.NameScope.Find("PART_Root");
152 | _columnHeadersPresenter = e.NameScope.Find("PART_ColumnHeadersPresenter");
153 | _columnHeadersPresenterScrollViewer = e.NameScope.Find("PART_ColumnHeadersPresenterScrollViewer");
154 | _rowsPresenter = e.NameScope.Find("PART_RowsPresenter");
155 |
156 | InvalidateRoot();
157 | InvalidateColumnHeadersPresenter();
158 | InvalidateRowsPresenter();
159 | }
160 |
161 | protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
162 | {
163 | base.OnPropertyChanged(change);
164 |
165 | if (change.Property == SelectedItemProperty)
166 | {
167 | // TODO:
168 | }
169 |
170 | if (change.Property == ColumnsProperty)
171 | {
172 | InvalidateRoot();
173 | InvalidateColumnHeadersPresenter();
174 | InvalidateRowsPresenter();
175 | }
176 |
177 | if (change.Property == ItemsProperty)
178 | {
179 | InvalidateRowsPresenter();
180 | }
181 |
182 | if (change.Property == SelectedCellProperty)
183 | {
184 | // TODO:
185 | }
186 | }
187 |
188 | private void InvalidateRoot()
189 | {
190 | RootDisposables?.Dispose();
191 | RootDisposables = null;
192 |
193 | if (_root is null || _panel is null)
194 | {
195 | return;
196 | }
197 |
198 | RootDisposables = new CompositeDisposable();
199 |
200 | foreach (var child in _rootChildren)
201 | {
202 | _root.Children.Remove(child);
203 | }
204 |
205 | var columns = Columns;
206 |
207 | // Generate RowDefinitions
208 |
209 | var rowDefinitions = new List();
210 |
211 | rowDefinitions.Add(new RowDefinition(new GridLength(1, GridUnitType.Star)));
212 |
213 | // Generate ColumnDefinitions
214 |
215 | var columnDefinitions = new List();
216 | var splitterColumnIndexes = new List();
217 | var isSharedSizeScope = false;
218 |
219 | for (var i = 0; i < columns.Count; i++)
220 | {
221 | var column = columns[i];
222 | var isStarWidth = column.Width.IsStar;
223 | var isAutoWidth = column.Width.IsAuto;
224 | var isPixelWidth = column.Width.IsAbsolute;
225 |
226 | var columnDefinition = new ColumnDefinition();
227 |
228 | columnDefinition.OneWayBind(ColumnDefinition.MinWidthProperty, column, TemplatedDataGridColumn.MinWidthProperty, RootDisposables);
229 | columnDefinition.OneWayBind(ColumnDefinition.MaxWidthProperty, column, TemplatedDataGridColumn.MaxWidthProperty, RootDisposables);
230 |
231 | if (isStarWidth)
232 | {
233 | columnDefinition.OneWayBind(
234 | ColumnDefinition.WidthProperty,
235 | column.GetObservable(TemplatedDataGridColumn.ActualWidthProperty)
236 | .Select(x => new BindingValue(new GridLength(x, GridUnitType.Pixel))),
237 | RootDisposables);
238 | }
239 |
240 | if (isAutoWidth)
241 | {
242 | columnDefinition.OneWayBind(ColumnDefinition.WidthProperty, column, TemplatedDataGridColumn.WidthProperty, RootDisposables);
243 | columnDefinition.SetValue(DefinitionBase.SharedSizeGroupProperty, $"Column{i}");
244 |
245 | RootDisposables.Add(Disposable.Create(() =>
246 | {
247 | columnDefinition.SetValue(DefinitionBase.SharedSizeGroupProperty, default);
248 | }));
249 |
250 | isSharedSizeScope = true;
251 | }
252 |
253 | if (isPixelWidth)
254 | {
255 | columnDefinition.OneWayBind(ColumnDefinition.WidthProperty, column, TemplatedDataGridColumn.WidthProperty, RootDisposables);
256 | }
257 |
258 | columnDefinitions.Add(columnDefinition);
259 |
260 | if (i < columns.Count)
261 | {
262 | columnDefinitions.Add(new ColumnDefinition(new GridLength(1, GridUnitType.Pixel)));
263 | splitterColumnIndexes.Add(columnDefinitions.Count - 1);
264 | }
265 | }
266 |
267 | columnDefinitions.Add(new ColumnDefinition(GridLength.Auto));
268 |
269 | Grid.SetIsSharedSizeScope(_panel, isSharedSizeScope);
270 |
271 | _root.RowDefinitions.Clear();
272 | _root.RowDefinitions.AddRange(rowDefinitions);
273 |
274 | _root.ColumnDefinitions.Clear();
275 | _root.ColumnDefinitions.AddRange(columnDefinitions);
276 |
277 | // Generate Vertical Grid Lines
278 |
279 | foreach (var columnIndex in splitterColumnIndexes)
280 | {
281 | var verticalRowGridLine = new Rectangle
282 | {
283 | [Grid.RowProperty] = 1,
284 | [Grid.RowSpanProperty] = 1,
285 | [Grid.ColumnProperty] = columnIndex,
286 | [Rectangle.IsHitTestVisibleProperty] = false
287 | };
288 |
289 | verticalRowGridLine.OneWayBind(
290 | Rectangle.IsVisibleProperty,
291 | this.GetObservable(TemplatedDataGrid.GridLinesVisibilityProperty)
292 | .Select(x => new BindingValue(x.HasFlag(TemplatedDataGridGridLinesVisibility.Vertical))),
293 | RootDisposables);
294 |
295 | ((IPseudoClasses)verticalRowGridLine.Classes).Add(":vertical");
296 | _rootChildren.Add(verticalRowGridLine);
297 | }
298 |
299 | if (_columnHeadersPresenter is { })
300 | {
301 | _columnHeadersPresenter.SetValue(Grid.RowProperty, 0);
302 | _columnHeadersPresenter.SetValue(Grid.ColumnProperty, 0);
303 | _columnHeadersPresenter.SetValue(Grid.ColumnSpanProperty, columns.Count + (columns.Count - 1));
304 | }
305 |
306 | if (_rowsPresenter is { })
307 | {
308 | _rowsPresenter.SetValue(Grid.RowProperty, 2);
309 | _rowsPresenter.SetValue(Grid.ColumnProperty, 0);
310 | _rowsPresenter.SetValue(Grid.ColumnSpanProperty, columns.Count + (columns.Count - 1));
311 | }
312 |
313 | foreach (var child in _rootChildren)
314 | {
315 | _root.Children.Add(child);
316 | }
317 |
318 | RootDisposables.Add(Disposable.Create(() =>
319 | {
320 | foreach (var child in _rootChildren)
321 | {
322 | _root.Children.Remove(child);
323 | }
324 |
325 | _root.RowDefinitions.Clear();
326 | _root.ColumnDefinitions.Clear();
327 | }));
328 | }
329 |
330 | private void InvalidateColumnHeadersPresenter()
331 | {
332 | ColumnHeadersDisposables?.Dispose();
333 | ColumnHeadersDisposables = null;
334 |
335 | if (_columnHeadersPresenter is { } && _rowsPresenter is { } && _columnHeadersPresenterScrollViewer is { })
336 | {
337 | ColumnHeadersDisposables = new CompositeDisposable();
338 |
339 | _columnHeadersPresenter.OneWayBind(TemplatedDataGridColumnHeadersPresenter.ColumnsProperty, this, TemplatedDataGrid.ColumnsProperty, ColumnHeadersDisposables);
340 | _columnHeadersPresenter.OneWayBind(TemplatedDataGridColumnHeadersPresenter.CanUserSortColumnsProperty, this, TemplatedDataGrid.CanUserSortColumnsProperty, ColumnHeadersDisposables);
341 | _columnHeadersPresenter.OneWayBind(TemplatedDataGridColumnHeadersPresenter.CanUserResizeColumnsProperty, this, TemplatedDataGrid.CanUserResizeColumnsProperty, ColumnHeadersDisposables);
342 |
343 | var scrollDisposable = _rowsPresenter.GetObservable(TemplatedDataGridRowsPresenter.ScrollProperty).Subscribe(_ =>
344 | {
345 | RowsPresenterScrollDisposable?.Dispose();
346 |
347 | if (_rowsPresenter?.Scroll is ScrollViewer scrollViewer)
348 | {
349 | scrollViewer.ScrollChanged += ScrollViewer_OnScrollChanged;
350 |
351 | RowsPresenterScrollDisposable = Disposable.Create(
352 | () => scrollViewer.ScrollChanged -= ScrollViewer_OnScrollChanged);
353 | }
354 | });
355 | ColumnHeadersDisposables.Add(scrollDisposable);
356 | };
357 | }
358 |
359 | private void ScrollViewer_OnScrollChanged(object? sender, ScrollChangedEventArgs e)
360 | {
361 | if (_rowsPresenter?.Scroll is ScrollViewer scrollViewer && _columnHeadersPresenterScrollViewer is { })
362 | {
363 | var (x, _) = scrollViewer.Offset;
364 | _columnHeadersPresenterScrollViewer.Offset = new Vector(x, 0);
365 | }
366 | }
367 |
368 | private void InvalidateRowsPresenter()
369 | {
370 | RowsDisposables?.Dispose();
371 | RowsDisposables = null;
372 |
373 | if (_rowsPresenter is { })
374 | {
375 | RowsDisposables = new CompositeDisposable();
376 |
377 | _rowsPresenter.OneWayBind(TemplatedDataGridRowsPresenter.ColumnsProperty, this, TemplatedDataGrid.ColumnsProperty, RowsDisposables);
378 | _rowsPresenter.OneWayBind(TemplatedDataGridRowsPresenter.ItemsProperty, this, TemplatedDataGrid.ItemsProperty, RowsDisposables);
379 | _rowsPresenter.OneWayBind(TemplatedDataGridRowsPresenter.AutoScrollToSelectedItemProperty, this, TemplatedDataGrid.AutoScrollToSelectedItemProperty, RowsDisposables);
380 | _rowsPresenter.TwoWayBind(TemplatedDataGridRowsPresenter.SelectedItemProperty, this, TemplatedDataGrid.SelectedItemProperty, RowsDisposables);
381 | _rowsPresenter.TwoWayBind(TemplatedDataGridRowsPresenter.SelectedCellProperty, this, TemplatedDataGrid.SelectedCellProperty, RowsDisposables);
382 | _rowsPresenter.OneWayBind(TemplatedDataGridRowsPresenter.GridLinesVisibilityProperty, this, TemplatedDataGrid.GridLinesVisibilityProperty, RowsDisposables);
383 | }
384 | }
385 | }
386 | }
387 |
--------------------------------------------------------------------------------
/src/TemplatedDataGrid/TemplatedDataGrid.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | Library
4 | netstandard2.0;net461;net6.0;net8.0
5 | True
6 | enable
7 | TemplatedDataGrid
8 | Debug;Release
9 | AnyCPU;x64
10 | true
11 |
12 |
13 |
14 | TemplatedDataGrid
15 | A DataGrid control based on ListBox and Grid panels.
16 | datagrid;panel;control;xaml;axaml;avalonia;avaloniaui
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/TemplatedDataGrid/TemplatedDataGridBoundColumn.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Data;
3 |
4 | namespace TemplatedDataGrid
5 | {
6 | public abstract class TemplatedDataGridBoundColumn : TemplatedDataGridColumn
7 | {
8 | public static readonly StyledProperty BindingProperty =
9 | AvaloniaProperty.Register(nameof(Binding));
10 |
11 | [AssignBinding]
12 | public IBinding? Binding
13 | {
14 | get => GetValue(BindingProperty);
15 | set => SetValue(BindingProperty, value);
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/TemplatedDataGrid/TemplatedDataGridCell.cs:
--------------------------------------------------------------------------------
1 | using Avalonia;
2 | using Avalonia.Controls;
3 | using Avalonia.Controls.Metadata;
4 | using Avalonia.Controls.Primitives;
5 | using Avalonia.Controls.Templates;
6 | using Avalonia.Data;
7 | using Avalonia.Input;
8 | using Avalonia.Layout;
9 | using Avalonia.Metadata;
10 |
11 | namespace TemplatedDataGrid
12 | {
13 | [PseudoClasses(":pressed", ":selected")]
14 | public class TemplatedDataGridCell : TemplatedControl
15 | {
16 | internal static readonly DirectProperty SelectedItemProperty =
17 | AvaloniaProperty.RegisterDirect(
18 | nameof(SelectedItem),
19 | o => o.SelectedItem,
20 | (o, v) => o.SelectedItem = v,
21 | defaultBindingMode: BindingMode.TwoWay);
22 |
23 | internal static readonly DirectProperty SelectedCellProperty =
24 | AvaloniaProperty.RegisterDirect(
25 | nameof(SelectedCell),
26 | o => o.SelectedCell,
27 | (o, v) => o.SelectedCell = v,
28 | defaultBindingMode: BindingMode.TwoWay);
29 |
30 | public static readonly StyledProperty CellTemplateProperty =
31 | AvaloniaProperty.Register(nameof(CellTemplate));
32 |
33 | public static readonly StyledProperty