diff --git a/src/Avalonia.FuncUI.TreeDataGrid/Avalonia.FuncUI.TreeDataGrid.fsproj b/src/Avalonia.FuncUI.TreeDataGrid/Avalonia.FuncUI.TreeDataGrid.fsproj
new file mode 100644
index 00000000..b6a932e0
--- /dev/null
+++ b/src/Avalonia.FuncUI.TreeDataGrid/Avalonia.FuncUI.TreeDataGrid.fsproj
@@ -0,0 +1,38 @@
+
+
+
+ net8.0
+ true
+ $(FuncUIVersion)
+ Avalonia.FuncUI.TreeDataGrid
+ Avalonia.FuncUI.TreeDataGrid
+ true
+ Avalonia FuncUI TreeDataGrid
+ $(FuncUIVersion)
+ TreeDataGrid bindings for Avalonia.FuncUI
+ nuget_icon.png
+ true
+ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb
+
+
+
+
+
+
+
+
+ True
+ \
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Avalonia.FuncUI.TreeDataGrid/Library.fs b/src/Avalonia.FuncUI.TreeDataGrid/Library.fs
new file mode 100644
index 00000000..472477e7
--- /dev/null
+++ b/src/Avalonia.FuncUI.TreeDataGrid/Library.fs
@@ -0,0 +1,53 @@
+namespace Avalonia.FuncUI.DSL
+
+open Avalonia.Controls
+open Avalonia.FuncUI.Types
+open Avalonia.FuncUI.Builder
+open Avalonia.Controls.Models.TreeDataGrid
+open Avalonia.Controls.Primitives
+open System
+
+[]
+module TreeDataGrid =
+
+ let create (attrs: IAttr list): IView =
+ ViewBuilder.Create(attrs)
+
+ type TreeDataGrid with
+
+ static member autoDragDropRows<'t when 't :> TreeDataGrid>(autoDragDropRows: bool) : IAttr<'t> =
+ AttrBuilder<'t>.CreateProperty(TreeDataGrid.AutoDragDropRowsProperty, autoDragDropRows, ValueNone)
+
+ static member canUserResizeColumns<'t when 't :> TreeDataGrid>(canUserResizeColumns: bool) : IAttr<'t> =
+ AttrBuilder<'t>.CreateProperty(TreeDataGrid.CanUserResizeColumnsProperty, canUserResizeColumns, ValueNone)
+
+ static member canUserSortColumns<'t when 't :> TreeDataGrid>(canUserSortColumns: bool) : IAttr<'t> =
+ AttrBuilder<'t>.CreateProperty(TreeDataGrid.CanUserSortColumnsProperty, canUserSortColumns, ValueNone)
+
+ static member columns<'t when 't :> TreeDataGrid>(columns: IColumns) : IAttr<'t> =
+ AttrBuilder<'t>.CreateProperty(TreeDataGrid.ColumnsProperty, columns, ValueNone)
+
+ static member elementFactory<'t when 't :> TreeDataGrid>(factory: TreeDataGridElementFactory) : IAttr<'t> =
+ AttrBuilder<'t>.CreateProperty(TreeDataGrid.ElementFactoryProperty, factory, ValueNone)
+
+ static member rows<'t when 't :> TreeDataGrid>(rows: IRows) : IAttr<'t> =
+ AttrBuilder<'t>.CreateProperty(TreeDataGrid.RowsProperty, rows, ValueNone)
+
+ static member scroll<'t when 't :> TreeDataGrid>(scrollable: IScrollable) : IAttr<'t> =
+ AttrBuilder<'t>.CreateProperty(TreeDataGrid.ScrollProperty, scrollable, ValueNone)
+
+ static member showColumnHeaders<'t when 't :> TreeDataGrid>(showHeaders: bool) : IAttr<'t> =
+ AttrBuilder<'t>.CreateProperty(TreeDataGrid.ShowColumnHeadersProperty, showHeaders, ValueNone)
+
+ static member source<'t when 't :> TreeDataGrid>(dataSource: ITreeDataGridSource) : IAttr<'t> =
+ AttrBuilder<'t>.CreateProperty(TreeDataGrid.SourceProperty, dataSource, ValueNone)
+
+ static member rowDragStarted<'t when 't :> TreeDataGrid>(func: TreeDataGridRowDragStartedEventArgs -> unit, ?subPatchOptions) =
+ AttrBuilder<'t>.CreateSubscription(TreeDataGrid.RowDragStartedEvent, func, ?subPatchOptions = subPatchOptions)
+
+ static member rowDragOver<'t when 't :> TreeDataGrid>(func: TreeDataGridRowDragEventArgs -> unit, ?subPatchOptions) =
+ AttrBuilder<'t>.CreateSubscription(TreeDataGrid.RowDragOverEvent, func, ?subPatchOptions = subPatchOptions)
+
+ static member rowDrop<'t when 't :> TreeDataGrid>(func: TreeDataGridRowDragEventArgs -> unit, ?subPatchOptions) =
+ AttrBuilder<'t>.CreateSubscription(TreeDataGrid.RowDropEvent, func, ?subPatchOptions = subPatchOptions)
+
diff --git a/src/Avalonia.FuncUI.sln b/src/Avalonia.FuncUI.sln
index 137bfe25..eba9cdd2 100644
--- a/src/Avalonia.FuncUI.sln
+++ b/src/Avalonia.FuncUI.sln
@@ -47,9 +47,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_Solution Items", "_Solutio
ProjectSection(SolutionItems) = preProject
Directory.Build.props = Directory.Build.props
NuGet.config = NuGet.config
- ..\README.md = ..\README.md
..\.github\workflows\publish.yml = ..\.github\workflows\publish.yml
..\.github\workflows\pull-requests.yml = ..\.github\workflows\pull-requests.yml
+ ..\README.md = ..\README.md
EndProjectSection
EndProject
Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "Examples.InlineText", "Examples\Examples.InlineText\Examples.InlineText.fsproj", "{B8D8C84B-05AD-475B-BE81-A30544CE0149}"
@@ -86,6 +86,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Examples.SettingFocus", "Ex
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Examples.Experimental.ViewModelComponent.CounterApp", "Examples\Experimental\Examples.Experimental.ViewModelComponent.CounterApp\Examples.Experimental.ViewModelComponent.CounterApp.fsproj", "{C59DF473-A6F9-4D94-9308-9381F36CB71D}"
EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Avalonia.FuncUI.TreeDataGrid", "Avalonia.FuncUI.TreeDataGrid\Avalonia.FuncUI.TreeDataGrid.fsproj", "{44CEE173-0C6F-F12A-063B-EE64187DC6CD}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -208,6 +210,10 @@ Global
{C59DF473-A6F9-4D94-9308-9381F36CB71D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C59DF473-A6F9-4D94-9308-9381F36CB71D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C59DF473-A6F9-4D94-9308-9381F36CB71D}.Release|Any CPU.Build.0 = Release|Any CPU
+ {44CEE173-0C6F-F12A-063B-EE64187DC6CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {44CEE173-0C6F-F12A-063B-EE64187DC6CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {44CEE173-0C6F-F12A-063B-EE64187DC6CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {44CEE173-0C6F-F12A-063B-EE64187DC6CD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -221,27 +227,27 @@ Global
{1401B46D-1F14-4D29-8E45-96E62D46405F} = {F6F4AAF7-2BDA-4D2F-B78D-F6D8A03F660E}
{7A0CA9E2-AFB8-4BA0-A725-F80EB98717C4} = {F6F4AAF7-2BDA-4D2F-B78D-F6D8A03F660E}
{C9BF89F2-6DE8-4F37-A6AD-6D40E982F265} = {F6F4AAF7-2BDA-4D2F-B78D-F6D8A03F660E}
+ {64F427DB-02F2-499C-B4F2-25BC18F65E85} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
+ {769FB456-1B73-4D6C-BA86-47DB484E3651} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
+ {8212649E-0ECD-49CD-BBBA-13AC0151666A} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
+ {EB476DAF-C691-46B4-84FC-CA64E4C64A1A} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
+ {BF5DC7CC-7ABF-40AB-97DD-6774C17FE005} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
+ {5CC37986-D6E0-438B-B895-BC82DFD22307} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
+ {B8D8C84B-05AD-475B-BE81-A30544CE0149} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
{24AD0B82-9D10-4929-8FFE-E5E048C11842} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
{4121CB46-AEBD-4047-B032-8682ADFF02A6} = {5DB968C6-935E-41AC-A252-644B4886AF8A}
{01631D41-54F9-4815-8B26-9F1BEEC22F7A} = {5DB968C6-935E-41AC-A252-644B4886AF8A}
{AD6796F9-45CA-4FBA-9673-E6FC9214C80D} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
+ {90E18FDC-8B2A-4C21-B60D-B5B7A14648A3} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
+ {71C50981-2E3D-476B-9FA5-971671A9BB7A} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
{8E3586BB-D43F-4FD7-9F8E-05F55EAEE850} = {681AB0E5-A733-4CFE-872A-10B810DAF86D}
{222A135C-23A4-471B-8A6A-630B7EEFAD4F} = {AD6796F9-45CA-4FBA-9673-E6FC9214C80D}
{1EEA7F36-3DCD-4A42-BE59-1E02E1B8E3BE} = {37594D05-9BE4-40C2-AC5B-83B0AC9F7B15}
{86429C85-F3D8-4F7D-A66F-24151C7236B9} = {37594D05-9BE4-40C2-AC5B-83B0AC9F7B15}
{BB03E86D-4DAF-468F-BD0C-D85D6641F02D} = {86429C85-F3D8-4F7D-A66F-24151C7236B9}
+ {70BDCE72-149A-435A-9910-E79DE329F978} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
{EC63B886-E809-4B74-B533-BFF3D60017C9} = {F6F4AAF7-2BDA-4D2F-B78D-F6D8A03F660E}
- {BF5DC7CC-7ABF-40AB-97DD-6774C17FE005} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
- {8212649E-0ECD-49CD-BBBA-13AC0151666A} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
- {5CC37986-D6E0-438B-B895-BC82DFD22307} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
- {71C50981-2E3D-476B-9FA5-971671A9BB7A} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
- {EB476DAF-C691-46B4-84FC-CA64E4C64A1A} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
- {90E18FDC-8B2A-4C21-B60D-B5B7A14648A3} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
- {769FB456-1B73-4D6C-BA86-47DB484E3651} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
- {B8D8C84B-05AD-475B-BE81-A30544CE0149} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
- {64F427DB-02F2-499C-B4F2-25BC18F65E85} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
{2C7F4542-65CE-4EC6-9876-C1C2215EE006} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
- {70BDCE72-149A-435A-9910-E79DE329F978} = {84811DB3-C276-4F0D-B3BA-78B88E2C6EF0}
{C59DF473-A6F9-4D94-9308-9381F36CB71D} = {24AD0B82-9D10-4929-8FFE-E5E048C11842}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
diff --git a/src/Examples/Component Examples/Examples.TreeDataGridPlayground/Examples.TreeDataGridPlayground.fsproj b/src/Examples/Component Examples/Examples.TreeDataGridPlayground/Examples.TreeDataGridPlayground.fsproj
new file mode 100644
index 00000000..4ea8be22
--- /dev/null
+++ b/src/Examples/Component Examples/Examples.TreeDataGridPlayground/Examples.TreeDataGridPlayground.fsproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ net8.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Examples/Component Examples/Examples.TreeDataGridPlayground/Program.fs b/src/Examples/Component Examples/Examples.TreeDataGridPlayground/Program.fs
new file mode 100644
index 00000000..cdd24d50
--- /dev/null
+++ b/src/Examples/Component Examples/Examples.TreeDataGridPlayground/Program.fs
@@ -0,0 +1,110 @@
+namespace Examples.TreeDataGridPlaygrounf
+
+open System.Collections.ObjectModel
+open Avalonia
+open Avalonia.Controls
+open Avalonia.Controls.ApplicationLifetimes
+open Avalonia.FuncUI.Hosts
+open Avalonia.Themes.Fluent
+open Avalonia.FuncUI
+open Avalonia.FuncUI.DSL
+open Avalonia.Controls.Models.TreeDataGrid
+
+type Person (name, age, male, children) =
+ member val Name = name with get, set
+ member val Age = age with get, set
+ member val IsMale = male with get, set
+ member val Children = children with get, set
+
+[]
+type Views =
+
+ static member main () =
+ Component (fun ctx ->
+ let data = ctx.useState (
+ ObservableCollection [
+ Person("John", 63, true, [
+ Person("Bob", 32, true, [])
+ Person("Jill", 30, false, [
+ Person("Peter", 12, true, [])
+ ])
+ ])
+ Person("Jane", 25, false, [
+ Person("Tim", 6, true, [])
+ ])
+ Person("Bob", 16, true, [])
+ ]
+ )
+
+ let selectedItem = ctx.useState None
+
+ DockPanel.create [
+ DockPanel.children [
+ TextBlock.create [
+ TextBlock.dock Dock.Top
+ TextBlock.margin 10
+ TextBlock.text $"""Selected: {(selectedItem.Current |> Option.defaultValue (Person("", 0, false, []))).Name}"""
+ ]
+ TreeDataGrid.create [
+ TreeDataGrid.dock Dock.Top
+
+ TreeDataGrid.init (fun ctrl ->
+ let dataSource = new HierarchicalTreeDataGridSource(data.Current)
+
+ dataSource.Columns.Add(
+ HierarchicalExpanderColumn(
+ TextColumn("Name", _.Name),
+ _.Children
+ )
+ )
+
+ dataSource.Columns.Add (TextColumn("Age", _.Age))
+ dataSource.Columns.Add (CheckBoxColumn("IsMale", _.IsMale, fun o v -> o.IsMale <- v))
+
+ dataSource.RowSelection.SelectionChanged.Add (fun (args) ->
+ (if args.SelectedItems.Count = 0 then
+ None
+ else
+ Some args.SelectedItems[0])
+ |> selectedItem.Set
+ )
+
+ ctrl.Source <- dataSource
+ )
+ ]
+ ]
+ ]
+ )
+
+type MainWindow() as this =
+ inherit HostWindow()
+ do
+ base.Title <- "TreeDataGrid Playground"
+ base.Width <- 500.0
+ base.Height <- 500.0
+ this.Content <- Views.main ()
+
+type App() =
+ inherit Application()
+
+ override this.Initialize() =
+ this.Styles.Add (FluentTheme())
+ this.RequestedThemeVariant <- Styling.ThemeVariant.Dark
+ this.Styles.Load "avares://Avalonia.Controls.TreeDataGrid/Themes/Fluent.axaml"
+
+ override this.OnFrameworkInitializationCompleted() =
+ match this.ApplicationLifetime with
+ | :? IClassicDesktopStyleApplicationLifetime as desktopLifetime ->
+ desktopLifetime.MainWindow <- MainWindow()
+
+ | _ -> ()
+
+module Program =
+
+ []
+ let main(args: string[]) =
+ AppBuilder
+ .Configure()
+ .UsePlatformDetect()
+ .UseSkia()
+ .StartWithClassicDesktopLifetime(args)