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)