├── AvaloniaAccordion.sln
├── AvaloniaAccordion
├── Accordion.axaml
├── Accordion.axaml.cs
├── Accordion.properties.cs
├── AvaloniaAccordion.csproj
└── Converters.cs
├── LICENSE
├── Readme.md
├── TestAccordion
├── App.axaml
├── App.axaml.cs
├── MainWindow.axaml
├── MainWindow.axaml.cs
├── Program.cs
└── TestAccordion.csproj
├── icon.png
├── icon.svg
└── screenshot.png
/AvaloniaAccordion.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.31005.135
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestAccordion", "TestAccordion\TestAccordion.csproj", "{B008BCFF-0915-4EFA-AAD6-A69F8AC4E08D}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AvaloniaAccordion", "AvaloniaAccordion\AvaloniaAccordion.csproj", "{A3FB540B-FF11-4C47-8AB6-1582FAE90A0E}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {B008BCFF-0915-4EFA-AAD6-A69F8AC4E08D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {B008BCFF-0915-4EFA-AAD6-A69F8AC4E08D}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {B008BCFF-0915-4EFA-AAD6-A69F8AC4E08D}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {B008BCFF-0915-4EFA-AAD6-A69F8AC4E08D}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {A3FB540B-FF11-4C47-8AB6-1582FAE90A0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {A3FB540B-FF11-4C47-8AB6-1582FAE90A0E}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {A3FB540B-FF11-4C47-8AB6-1582FAE90A0E}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {A3FB540B-FF11-4C47-8AB6-1582FAE90A0E}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {7C612F9E-A21E-4798-874F-4A119ADF7D5B}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/AvaloniaAccordion/Accordion.axaml:
--------------------------------------------------------------------------------
1 |
15 |
16 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
32 |
33 |
36 |
37 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
62 |
63 |
64 |
65 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
--------------------------------------------------------------------------------
/AvaloniaAccordion/Accordion.axaml.cs:
--------------------------------------------------------------------------------
1 | /*
2 | AvaloniaAccordion - An accordion-like expandable control for Avalonia.
3 | Copyright (C) 2021 Giorgio Bianchini
4 |
5 | This program is free software: you can redistribute it and/or modify
6 | it under the terms of the GNU General Public License as published by
7 | the Free Software Foundation, version 3.
8 | This program is distributed in the hope that it will be useful,
9 | but WITHOUT ANY WARRANTY; without even the implied warranty of
10 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 | GNU General Public License for more details.
12 | You should have received a copy of the GNU General Public License
13 | along with this program. If not, see .
14 | */
15 |
16 | using Avalonia;
17 | using Avalonia.Controls;
18 | using Avalonia.Controls.Shapes;
19 | using Avalonia.Markup.Xaml;
20 | using Avalonia.Media;
21 | using Avalonia.VisualTree;
22 | using System;
23 |
24 | namespace AvaloniaAccordion
25 | {
26 | public partial class Accordion : UserControl
27 | {
28 | ///
29 | /// If the accordion is open, forces a recomputation of the height to make sure that all the contents fit.
30 | ///
31 | public void InvalidateHeight()
32 | {
33 | if (this.IsOpen)
34 | {
35 | AccordionHeightConverter.ReturnLastValue = true;
36 | AccordionAngleConverter.ReturnLastValue = true;
37 | this.IsOpen = false;
38 |
39 | _ = Avalonia.Threading.Dispatcher.UIThread.InvokeAsync(async () =>
40 | {
41 | AccordionHeightConverter.ReturnLastValue = false;
42 | AccordionAngleConverter.ReturnLastValue = false;
43 | this.IsOpen = true;
44 |
45 | Accordion parentAccordion = this.FindAncestorOfType();
46 |
47 | if (parentAccordion != null)
48 | {
49 | await System.Threading.Tasks.Task.Delay(TimeSpan.FromMilliseconds(Math.Max(10, this.TransitionDuration.TotalMilliseconds * 2)));
50 | parentAccordion.InvalidateHeight();
51 | await System.Threading.Tasks.Task.Delay(TimeSpan.FromMilliseconds(Math.Max(10, this.TransitionDuration.TotalMilliseconds)));
52 | parentAccordion.InvalidateHeight();
53 | }
54 |
55 | }, Avalonia.Threading.DispatcherPriority.MinValue);
56 | }
57 | }
58 |
59 |
60 | private Grid ContentGridParent;
61 | private AccordionHeightConverter AccordionHeightConverter;
62 | private AccordionAngleConverter AccordionAngleConverter;
63 |
64 | ///
65 | protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
66 | {
67 | base.OnPropertyChanged(change);
68 |
69 | if (change.Property == Accordion.TransitionDurationProperty)
70 | {
71 | if (ContentGridParent != null)
72 | {
73 | ((Avalonia.Animation.DoubleTransition)ContentGridParent.Transitions[0]).Duration = change.NewValue.GetValueOrDefault();
74 |
75 | ((Avalonia.Animation.DoubleTransition)((RotateTransform)this.FindControl("ArrowPathLeft").RenderTransform).Transitions[0]).Duration = change.NewValue.GetValueOrDefault();
76 | ((Avalonia.Animation.DoubleTransition)((RotateTransform)this.FindControl("ArrowPathRight").RenderTransform).Transitions[0]).Duration = change.NewValue.GetValueOrDefault();
77 | }
78 | }
79 | else if (change.Property == Accordion.AccordionContentProperty)
80 | {
81 | this.InvalidateHeight();
82 | }
83 | else if (change.Property == Accordion.BottomBarThicknessProperty)
84 | {
85 | if (this.IsOpen)
86 | {
87 | this.FindControl