Browse Source

[Feature] Page-based navigation system (#20794)

* Added CommandBar

* Added ContentPage

* Added NavigationPage

* Added TabbedPage

* Added DrawerPage

* More showcases

* More tests

* Added Connected Animations

* More samples

* More fixes

* More fixes

* Updated samples

* More fixes

* Fix build errors

* More fixes

* More changes

* More changes

* More fixes

* Added benchmarks

* Updated SwipeGestureRecognizer

* Fixes based on PR feedback

* More fixes

* Removed Appearing/Disappearing events

* Fix merge issue

* More changes

* Fix build

* Remove TabbedPage brush properties

* New showcase sample

* More changes

* Moved navigation brushes properties to resources

* Fix default connected animation duration fallback

* CommandBar returns empty list with null

* Ignore AVP1030 on PrimaryCommands/SecondaryCommands for now

* Updated samples

* More changes and tests

* Updated samples

* Updated render tests

* Updated tests

* Added Page AutomationPeers

---------

Co-authored-by: Julien Lebosquain <julien@lebosquain.net>
pull/20817/head
Javier Suárez 3 weeks ago
committed by GitHub
parent
commit
7bc087c658
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. BIN
      samples/ControlCatalog/Assets/CurvedHeader/avatar.jpg
  2. BIN
      samples/ControlCatalog/Assets/CurvedHeader/featured.jpg
  3. BIN
      samples/ControlCatalog/Assets/CurvedHeader/product1.jpg
  4. BIN
      samples/ControlCatalog/Assets/CurvedHeader/product2.jpg
  5. BIN
      samples/ControlCatalog/Assets/CurvedHeader/product3.jpg
  6. BIN
      samples/ControlCatalog/Assets/CurvedHeader/update1.jpg
  7. BIN
      samples/ControlCatalog/Assets/CurvedHeader/update2.jpg
  8. BIN
      samples/ControlCatalog/Assets/CurvedHeader/update3.jpg
  9. BIN
      samples/ControlCatalog/Assets/ModernApp/avatar.jpg
  10. BIN
      samples/ControlCatalog/Assets/ModernApp/dest_alps.jpg
  11. BIN
      samples/ControlCatalog/Assets/ModernApp/dest_forest.jpg
  12. BIN
      samples/ControlCatalog/Assets/ModernApp/dest_norway.jpg
  13. BIN
      samples/ControlCatalog/Assets/ModernApp/exp_angkor.jpg
  14. BIN
      samples/ControlCatalog/Assets/ModernApp/exp_tokyo.jpg
  15. BIN
      samples/ControlCatalog/Assets/ModernApp/gallery_alpine.jpg
  16. BIN
      samples/ControlCatalog/Assets/ModernApp/gallery_bay.jpg
  17. BIN
      samples/ControlCatalog/Assets/ModernApp/gallery_city.jpg
  18. BIN
      samples/ControlCatalog/Assets/ModernApp/gallery_paris.jpg
  19. BIN
      samples/ControlCatalog/Assets/ModernApp/gallery_tropical.jpg
  20. BIN
      samples/ControlCatalog/Assets/ModernApp/gallery_venice.jpg
  21. BIN
      samples/ControlCatalog/Assets/ModernApp/story1.jpg
  22. BIN
      samples/ControlCatalog/Assets/ModernApp/story2.jpg
  23. BIN
      samples/ControlCatalog/Assets/ModernApp/story3.jpg
  24. BIN
      samples/ControlCatalog/Assets/Movies/cast1.jpg
  25. BIN
      samples/ControlCatalog/Assets/Movies/cast2.jpg
  26. BIN
      samples/ControlCatalog/Assets/Movies/continue1.jpg
  27. BIN
      samples/ControlCatalog/Assets/Movies/hero.jpg
  28. BIN
      samples/ControlCatalog/Assets/Movies/morelike1.jpg
  29. BIN
      samples/ControlCatalog/Assets/Movies/search1.jpg
  30. BIN
      samples/ControlCatalog/Assets/Movies/toprated1.jpg
  31. BIN
      samples/ControlCatalog/Assets/Movies/toprated2.jpg
  32. BIN
      samples/ControlCatalog/Assets/Movies/toprated3.jpg
  33. BIN
      samples/ControlCatalog/Assets/Movies/toprated4.jpg
  34. BIN
      samples/ControlCatalog/Assets/Movies/trending1.jpg
  35. BIN
      samples/ControlCatalog/Assets/Movies/trending2.jpg
  36. BIN
      samples/ControlCatalog/Assets/Pulse/cat_hiit.jpg
  37. BIN
      samples/ControlCatalog/Assets/Pulse/cat_strength.jpg
  38. BIN
      samples/ControlCatalog/Assets/Pulse/cat_yoga.jpg
  39. BIN
      samples/ControlCatalog/Assets/Pulse/ex_bench.jpg
  40. BIN
      samples/ControlCatalog/Assets/Pulse/ex_deadlifts.jpg
  41. BIN
      samples/ControlCatalog/Assets/Pulse/ex_overhead.jpg
  42. BIN
      samples/ControlCatalog/Assets/Pulse/ex_pullups.jpg
  43. BIN
      samples/ControlCatalog/Assets/Pulse/ex_squats.jpg
  44. BIN
      samples/ControlCatalog/Assets/Pulse/profile_avatar.jpg
  45. BIN
      samples/ControlCatalog/Assets/Pulse/rec_fullbody.jpg
  46. BIN
      samples/ControlCatalog/Assets/Pulse/rec_mobility.jpg
  47. BIN
      samples/ControlCatalog/Assets/Pulse/rec_powercore.jpg
  48. BIN
      samples/ControlCatalog/Assets/Pulse/workout_hero.jpg
  49. BIN
      samples/ControlCatalog/Assets/Restaurant/dish1.jpg
  50. BIN
      samples/ControlCatalog/Assets/Restaurant/dish2.jpg
  51. BIN
      samples/ControlCatalog/Assets/Restaurant/dish3.jpg
  52. BIN
      samples/ControlCatalog/Assets/Restaurant/dish4.jpg
  53. BIN
      samples/ControlCatalog/Assets/Restaurant/featured_dish.jpg
  54. BIN
      samples/ControlCatalog/Assets/Restaurant/user_avatar.jpg
  55. BIN
      samples/ControlCatalog/Assets/RetroGaming/cyber_city.jpg
  56. BIN
      samples/ControlCatalog/Assets/RetroGaming/dungeon_bit.jpg
  57. BIN
      samples/ControlCatalog/Assets/RetroGaming/forest_spirit.jpg
  58. BIN
      samples/ControlCatalog/Assets/RetroGaming/hero.jpg
  59. BIN
      samples/ControlCatalog/Assets/RetroGaming/neon_ninja.jpg
  60. BIN
      samples/ControlCatalog/Assets/RetroGaming/neon_racer.jpg
  61. BIN
      samples/ControlCatalog/Assets/RetroGaming/pixel_quest.jpg
  62. BIN
      samples/ControlCatalog/Assets/RetroGaming/space_voids.jpg
  63. 6
      samples/ControlCatalog/ControlCatalog.csproj
  64. 26
      samples/ControlCatalog/MainView.xaml
  65. 120
      samples/ControlCatalog/Pages/CommandBar/CommandBarCustomizationPage.xaml
  66. 90
      samples/ControlCatalog/Pages/CommandBar/CommandBarCustomizationPage.xaml.cs
  67. 75
      samples/ControlCatalog/Pages/CommandBar/CommandBarDynamicOverflowPage.xaml
  68. 45
      samples/ControlCatalog/Pages/CommandBar/CommandBarDynamicOverflowPage.xaml.cs
  69. 94
      samples/ControlCatalog/Pages/CommandBar/CommandBarFirstLookPage.xaml
  70. 27
      samples/ControlCatalog/Pages/CommandBar/CommandBarFirstLookPage.xaml.cs
  71. 47
      samples/ControlCatalog/Pages/CommandBar/CommandBarLabelPositionPage.xaml
  72. 12
      samples/ControlCatalog/Pages/CommandBar/CommandBarLabelPositionPage.xaml.cs
  73. 74
      samples/ControlCatalog/Pages/CommandBar/CommandBarOverflowPage.xaml
  74. 54
      samples/ControlCatalog/Pages/CommandBar/CommandBarOverflowPage.xaml.cs
  75. 77
      samples/ControlCatalog/Pages/CommandBar/CommandBarTogglePage.xaml
  76. 50
      samples/ControlCatalog/Pages/CommandBar/CommandBarTogglePage.xaml.cs
  77. 8
      samples/ControlCatalog/Pages/CommandBarPage.xaml
  78. 158
      samples/ControlCatalog/Pages/CommandBarPage.xaml.cs
  79. 11
      samples/ControlCatalog/Pages/ConnectedAnimationDemoPage.xaml
  80. 144
      samples/ControlCatalog/Pages/ConnectedAnimationDemoPage.xaml.cs
  81. 11
      samples/ControlCatalog/Pages/ContentDemoPage.xaml
  82. 160
      samples/ControlCatalog/Pages/ContentDemoPage.xaml.cs
  83. 63
      samples/ControlCatalog/Pages/ContentPage/ContentPageCommandBarPage.xaml
  84. 212
      samples/ControlCatalog/Pages/ContentPage/ContentPageCommandBarPage.xaml.cs
  85. 106
      samples/ControlCatalog/Pages/ContentPage/ContentPageCustomizationPage.xaml
  86. 64
      samples/ControlCatalog/Pages/ContentPage/ContentPageCustomizationPage.xaml.cs
  87. 66
      samples/ControlCatalog/Pages/ContentPage/ContentPageEventsPage.xaml
  88. 103
      samples/ControlCatalog/Pages/ContentPage/ContentPageEventsPage.xaml.cs
  89. 48
      samples/ControlCatalog/Pages/ContentPage/ContentPageFirstLookPage.xaml
  90. 93
      samples/ControlCatalog/Pages/ContentPage/ContentPageFirstLookPage.xaml.cs
  91. 104
      samples/ControlCatalog/Pages/ContentPage/ContentPagePerformancePage.xaml
  92. 125
      samples/ControlCatalog/Pages/ContentPage/ContentPagePerformancePage.xaml.cs
  93. 171
      samples/ControlCatalog/Pages/ContentPage/ContentPageSafeAreaPage.xaml
  94. 70
      samples/ControlCatalog/Pages/ContentPage/ContentPageSafeAreaPage.xaml.cs
  95. 11
      samples/ControlCatalog/Pages/DrawerDemoPage.xaml
  96. 73
      samples/ControlCatalog/Pages/DrawerDemoPage.xaml.cs
  97. 400
      samples/ControlCatalog/Pages/DrawerPage/ControlsGalleryAppPage.xaml
  98. 464
      samples/ControlCatalog/Pages/DrawerPage/ControlsGalleryAppPage.xaml.cs
  99. 103
      samples/ControlCatalog/Pages/DrawerPage/DrawerPageCompactPage.xaml
  100. 78
      samples/ControlCatalog/Pages/DrawerPage/DrawerPageCompactPage.xaml.cs

BIN
samples/ControlCatalog/Assets/CurvedHeader/avatar.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 264 KiB

BIN
samples/ControlCatalog/Assets/CurvedHeader/featured.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 334 KiB

BIN
samples/ControlCatalog/Assets/CurvedHeader/product1.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 383 KiB

BIN
samples/ControlCatalog/Assets/CurvedHeader/product2.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 KiB

BIN
samples/ControlCatalog/Assets/CurvedHeader/product3.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

BIN
samples/ControlCatalog/Assets/CurvedHeader/update1.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

BIN
samples/ControlCatalog/Assets/CurvedHeader/update2.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

BIN
samples/ControlCatalog/Assets/CurvedHeader/update3.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 KiB

BIN
samples/ControlCatalog/Assets/ModernApp/avatar.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

BIN
samples/ControlCatalog/Assets/ModernApp/dest_alps.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
samples/ControlCatalog/Assets/ModernApp/dest_forest.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
samples/ControlCatalog/Assets/ModernApp/dest_norway.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
samples/ControlCatalog/Assets/ModernApp/exp_angkor.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

BIN
samples/ControlCatalog/Assets/ModernApp/exp_tokyo.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

BIN
samples/ControlCatalog/Assets/ModernApp/gallery_alpine.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

BIN
samples/ControlCatalog/Assets/ModernApp/gallery_bay.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

BIN
samples/ControlCatalog/Assets/ModernApp/gallery_city.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

BIN
samples/ControlCatalog/Assets/ModernApp/gallery_paris.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
samples/ControlCatalog/Assets/ModernApp/gallery_tropical.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
samples/ControlCatalog/Assets/ModernApp/gallery_venice.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
samples/ControlCatalog/Assets/ModernApp/story1.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

BIN
samples/ControlCatalog/Assets/ModernApp/story2.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
samples/ControlCatalog/Assets/ModernApp/story3.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

BIN
samples/ControlCatalog/Assets/Movies/cast1.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

BIN
samples/ControlCatalog/Assets/Movies/cast2.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 375 KiB

BIN
samples/ControlCatalog/Assets/Movies/continue1.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 KiB

BIN
samples/ControlCatalog/Assets/Movies/hero.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

BIN
samples/ControlCatalog/Assets/Movies/morelike1.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 KiB

BIN
samples/ControlCatalog/Assets/Movies/search1.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

BIN
samples/ControlCatalog/Assets/Movies/toprated1.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 KiB

BIN
samples/ControlCatalog/Assets/Movies/toprated2.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

BIN
samples/ControlCatalog/Assets/Movies/toprated3.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 270 KiB

BIN
samples/ControlCatalog/Assets/Movies/toprated4.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 KiB

BIN
samples/ControlCatalog/Assets/Movies/trending1.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

BIN
samples/ControlCatalog/Assets/Movies/trending2.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

BIN
samples/ControlCatalog/Assets/Pulse/cat_hiit.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 354 KiB

BIN
samples/ControlCatalog/Assets/Pulse/cat_strength.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 KiB

BIN
samples/ControlCatalog/Assets/Pulse/cat_yoga.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 KiB

BIN
samples/ControlCatalog/Assets/Pulse/ex_bench.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

BIN
samples/ControlCatalog/Assets/Pulse/ex_deadlifts.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

BIN
samples/ControlCatalog/Assets/Pulse/ex_overhead.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 KiB

BIN
samples/ControlCatalog/Assets/Pulse/ex_pullups.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 KiB

BIN
samples/ControlCatalog/Assets/Pulse/ex_squats.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 KiB

BIN
samples/ControlCatalog/Assets/Pulse/profile_avatar.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 KiB

BIN
samples/ControlCatalog/Assets/Pulse/rec_fullbody.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 293 KiB

BIN
samples/ControlCatalog/Assets/Pulse/rec_mobility.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 KiB

BIN
samples/ControlCatalog/Assets/Pulse/rec_powercore.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 KiB

BIN
samples/ControlCatalog/Assets/Pulse/workout_hero.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

BIN
samples/ControlCatalog/Assets/Restaurant/dish1.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
samples/ControlCatalog/Assets/Restaurant/dish2.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

BIN
samples/ControlCatalog/Assets/Restaurant/dish3.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
samples/ControlCatalog/Assets/Restaurant/dish4.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
samples/ControlCatalog/Assets/Restaurant/featured_dish.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

BIN
samples/ControlCatalog/Assets/Restaurant/user_avatar.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

BIN
samples/ControlCatalog/Assets/RetroGaming/cyber_city.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 346 KiB

BIN
samples/ControlCatalog/Assets/RetroGaming/dungeon_bit.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 458 KiB

BIN
samples/ControlCatalog/Assets/RetroGaming/forest_spirit.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 416 KiB

BIN
samples/ControlCatalog/Assets/RetroGaming/hero.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

BIN
samples/ControlCatalog/Assets/RetroGaming/neon_ninja.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

BIN
samples/ControlCatalog/Assets/RetroGaming/neon_racer.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 KiB

BIN
samples/ControlCatalog/Assets/RetroGaming/pixel_quest.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 KiB

BIN
samples/ControlCatalog/Assets/RetroGaming/space_voids.jpg

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 KiB

6
samples/ControlCatalog/ControlCatalog.csproj

@ -13,6 +13,12 @@
</AvaloniaXaml>
<AvaloniaResource Include="Assets\*" />
<AvaloniaResource Include="Assets\Fonts\*" />
<AvaloniaResource Include="Assets\Restaurant\*" />
<AvaloniaResource Include="Assets\Pulse\*" />
<AvaloniaResource Include="Assets\Movies\*" />
<AvaloniaResource Include="Assets\CurvedHeader\*" />
<AvaloniaResource Include="Assets\RetroGaming\*" />
<AvaloniaResource Include="Assets\ModernApp\*" />
</ItemGroup>
<ItemGroup>
<None Remove="Assets\image1.jpg" />

26
samples/ControlCatalog/MainView.xaml

@ -49,9 +49,15 @@
<TabItem Header="Canvas">
<pages:CanvasPage />
</TabItem>
<TabItem Header="CommandBar"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<pages:CommandBarPage />
</TabItem>
<TabItem Header="Carousel">
<pages:CarouselPage />
</TabItem>
<TabItem Header="CheckBox">
<pages:CheckBoxPage />
</TabItem>
@ -67,6 +73,11 @@
<TabItem Header="Container Queries">
<pages:ContainerQueryPage />
</TabItem>
<TabItem Header="ContentPage"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<pages:ContentDemoPage />
</TabItem>
<TabItem Header="ContextFlyout">
<pages:ContextFlyoutPage />
</TabItem>
@ -99,6 +110,11 @@
<TabItem Header="Drag+Drop">
<pages:DragAndDropPage />
</TabItem>
<TabItem Header="DrawerPage"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<pages:DrawerDemoPage />
</TabItem>
<TabItem Header="Expander">
<pages:ExpanderPage />
</TabItem>
@ -128,6 +144,11 @@
<TabItem Header="Menu">
<pages:MenuPage />
</TabItem>
<TabItem Header="NavigationPage"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<pages:NavigationDemoPage />
</TabItem>
<TabItem Header="Notifications">
<pages:NotificationsPage />
</TabItem>
@ -167,6 +188,11 @@
<TabItem Header="SplitView">
<pages:SplitViewPage />
</TabItem>
<TabItem Header="TabbedPage"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled">
<pages:TabbedDemoPage />
</TabItem>
<TabItem Header="TabControl">
<pages:TabControlPage />
</TabItem>

120
samples/ControlCatalog/Pages/CommandBar/CommandBarCustomizationPage.xaml

@ -0,0 +1,120 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CommandBarCustomizationPage">
<UserControl.Resources>
<StreamGeometry x:Key="AddIcon">M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z</StreamGeometry>
<StreamGeometry x:Key="SaveIcon">M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z</StreamGeometry>
<StreamGeometry x:Key="ShareIcon">M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.61 20.92,19C20.92,17.39 19.61,16.08 18,16.08Z</StreamGeometry>
</UserControl.Resources>
<DockPanel>
<ScrollViewer DockPanel.Dock="Right" Width="260">
<StackPanel Margin="12" Spacing="8">
<TextBlock Text="Configuration" FontWeight="SemiBold" FontSize="16"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock Text="Live Preview" FontWeight="SemiBold" FontSize="13" />
<CommandBar x:Name="LiveBar">
<CommandBar.PrimaryCommands>
<AppBarButton Label="New"><AppBarButton.Icon><PathIcon Data="{StaticResource AddIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Save"><AppBarButton.Icon><PathIcon Data="{StaticResource SaveIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Share"><AppBarButton.Icon><PathIcon Data="{StaticResource ShareIcon}" /></AppBarButton.Icon></AppBarButton>
</CommandBar.PrimaryCommands>
</CommandBar>
<Separator />
<TextBlock Text="Background Preset" />
<ComboBox x:Name="BgPresetCombo"
SelectedIndex="0"
HorizontalAlignment="Stretch"
SelectionChanged="OnBgPresetChanged">
<ComboBoxItem Content="Default" />
<ComboBoxItem Content="Blue (#0078D4)" />
<ComboBoxItem Content="Dark (#1C1C1E)" />
<ComboBoxItem Content="Gradient" />
<ComboBoxItem Content="Transparent" />
</ComboBox>
<TextBlock Text="Foreground" />
<ComboBox x:Name="FgCombo"
SelectedIndex="0"
HorizontalAlignment="Stretch"
SelectionChanged="OnFgChanged">
<ComboBoxItem Content="Default" />
<ComboBoxItem Content="White" />
<ComboBoxItem Content="Black" />
</ComboBox>
<TextBlock Text="Corner Radius" />
<Slider x:Name="RadiusSlider"
Minimum="0"
Maximum="24"
Value="0"
ValueChanged="OnRadiusChanged" />
<TextBlock x:Name="RadiusLabel"
Text="0"
HorizontalAlignment="Center"
Opacity="0.7" />
<TextBlock Text="Border Thickness" />
<Slider x:Name="BorderSlider"
Minimum="0"
Maximum="4"
Value="0"
TickFrequency="1"
IsSnapToTickEnabled="True"
ValueChanged="OnBorderChanged" />
<TextBlock x:Name="BorderLabel"
Text="0"
HorizontalAlignment="Center"
Opacity="0.7" />
<Separator />
<TextBlock Text="About" FontWeight="SemiBold" />
<TextBlock Text="CommandBar inherits from TemplatedControl, so Background, Foreground, BorderBrush, BorderThickness, and CornerRadius are available out of the box."
FontSize="12" Opacity="0.7" TextWrapping="Wrap" />
<TextBlock Text="Foreground is an inherited property: setting it on the CommandBar cascades to all PathIcons inside AppBarButton/AppBarToggleButton."
FontSize="12" Opacity="0.7" TextWrapping="Wrap" Margin="0,4,0,0" />
</StackPanel>
</ScrollViewer>
<Border DockPanel.Dock="Right" Width="1" Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<ScrollViewer>
<StackPanel Spacing="16" Margin="12,12,12,0">
<TextBlock Classes="h2">Customize the CommandBar appearance using Background, Foreground, BorderBrush, and CornerRadius.</TextBlock>
<TextBlock Text="Style Presets" FontWeight="SemiBold" />
<TextBlock Text="Default theme" FontSize="12" Opacity="0.7" />
<CommandBar OverflowButtonVisibility="Collapsed">
<AppBarButton Label="New"><AppBarButton.Icon><PathIcon Data="{StaticResource AddIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Save"><AppBarButton.Icon><PathIcon Data="{StaticResource SaveIcon}" /></AppBarButton.Icon></AppBarButton>
</CommandBar>
<TextBlock Text="Solid color" FontSize="12" Opacity="0.7" />
<CommandBar Background="#0078D4" Foreground="White" OverflowButtonVisibility="Collapsed">
<AppBarButton Label="New"><AppBarButton.Icon><PathIcon Data="{StaticResource AddIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Save"><AppBarButton.Icon><PathIcon Data="{StaticResource SaveIcon}" /></AppBarButton.Icon></AppBarButton>
</CommandBar>
<TextBlock Text="Dark" FontSize="12" Opacity="0.7" />
<CommandBar Background="#1C1C1E" Foreground="White" OverflowButtonVisibility="Collapsed">
<AppBarButton Label="New"><AppBarButton.Icon><PathIcon Data="{StaticResource AddIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Save"><AppBarButton.Icon><PathIcon Data="{StaticResource SaveIcon}" /></AppBarButton.Icon></AppBarButton>
</CommandBar>
<TextBlock Text="Pill shape (CornerRadius=20)" FontSize="12" Opacity="0.7" />
<CommandBar Background="#F0F0F0"
CornerRadius="20"
BorderBrush="#CCCCCC"
BorderThickness="1"
OverflowButtonVisibility="Collapsed">
<AppBarButton Label="New"><AppBarButton.Icon><PathIcon Data="{StaticResource AddIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Save"><AppBarButton.Icon><PathIcon Data="{StaticResource SaveIcon}" /></AppBarButton.Icon></AppBarButton>
</CommandBar>
</StackPanel>
</ScrollViewer>
</DockPanel>
</UserControl>

90
samples/ControlCatalog/Pages/CommandBar/CommandBarCustomizationPage.xaml.cs

@ -0,0 +1,90 @@
using Avalonia.Controls;
using Avalonia.Media;
namespace ControlCatalog.Pages
{
public partial class CommandBarCustomizationPage : UserControl
{
public CommandBarCustomizationPage()
{
InitializeComponent();
}
private void OnBgPresetChanged(object? sender, SelectionChangedEventArgs e)
{
if (LiveBar == null)
return;
switch (BgPresetCombo.SelectedIndex)
{
case 1:
LiveBar.Background = new SolidColorBrush(Color.Parse("#0078D4"));
break;
case 2:
LiveBar.Background = new SolidColorBrush(Color.Parse("#1C1C1E"));
break;
case 3:
LiveBar.Background = new LinearGradientBrush
{
StartPoint = new Avalonia.RelativePoint(0, 0, Avalonia.RelativeUnit.Relative),
EndPoint = new Avalonia.RelativePoint(1, 0, Avalonia.RelativeUnit.Relative),
GradientStops =
{
new GradientStop(Color.Parse("#3F51B5"), 0),
new GradientStop(Color.Parse("#E91E63"), 1)
}
};
break;
case 4:
LiveBar.Background = Brushes.Transparent;
break;
default:
LiveBar.ClearValue(BackgroundProperty);
break;
}
}
private void OnFgChanged(object? sender, SelectionChangedEventArgs e)
{
if (LiveBar == null)
return;
switch (FgCombo.SelectedIndex)
{
case 1:
LiveBar.Foreground = Brushes.White;
break;
case 2:
LiveBar.Foreground = Brushes.Black;
break;
default:
LiveBar.ClearValue(ForegroundProperty);
break;
}
}
private void OnRadiusChanged(object? sender, Avalonia.Controls.Primitives.RangeBaseValueChangedEventArgs e)
{
if (LiveBar == null)
return;
var r = (int)RadiusSlider.Value;
LiveBar.CornerRadius = new Avalonia.CornerRadius(r);
RadiusLabel.Text = $"{r}";
}
private void OnBorderChanged(object? sender, Avalonia.Controls.Primitives.RangeBaseValueChangedEventArgs e)
{
if (LiveBar == null)
return;
var t = (int)BorderSlider.Value;
LiveBar.BorderThickness = new Avalonia.Thickness(t);
BorderLabel.Text = $"{t}";
if (t > 0)
LiveBar.BorderBrush = Brushes.Gray;
else
LiveBar.BorderBrush = null;
}
}
}

75
samples/ControlCatalog/Pages/CommandBar/CommandBarDynamicOverflowPage.xaml

@ -0,0 +1,75 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CommandBarDynamicOverflowPage">
<UserControl.Resources>
<StreamGeometry x:Key="AddIcon">M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z</StreamGeometry>
<StreamGeometry x:Key="SaveIcon">M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z</StreamGeometry>
<StreamGeometry x:Key="ShareIcon">M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.61 20.92,19C20.92,17.39 19.61,16.08 18,16.08Z</StreamGeometry>
<StreamGeometry x:Key="BoldIcon">M15.6,10.79C17.04,10.07 18,8.64 18,7C18,4.79 16.21,3 14,3H7V21H14.73C16.78,21 18.5,19.37 18.5,17.32C18.5,15.82 17.72,14.53 16.5,13.77C16.2,13.59 15.9,13.44 15.6,13.32V10.79M10,6.5H13C13.83,6.5 14.5,7.17 14.5,8C14.5,8.83 13.83,9.5 13,9.5H10V6.5M13.5,17.5H10V14H13.5C14.33,14 15,14.67 15,15.5C15,16.33 14.33,17.5 13.5,17.5Z</StreamGeometry>
<StreamGeometry x:Key="ItalicIcon">M10,4V7H12.21L8.79,15H6V18H14V15H11.79L15.21,7H18V4H10Z</StreamGeometry>
<StreamGeometry x:Key="DeleteIcon">M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z</StreamGeometry>
</UserControl.Resources>
<DockPanel>
<ScrollViewer DockPanel.Dock="Right" Width="260">
<StackPanel Margin="12" Spacing="8">
<TextBlock Text="Configuration" FontWeight="SemiBold" FontSize="16"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock Text="Container Width" FontWeight="SemiBold" />
<Slider x:Name="WidthSlider"
Minimum="120"
Maximum="700"
Value="400"
ValueChanged="OnWidthChanged" />
<TextBlock x:Name="WidthLabel"
Text="400"
HorizontalAlignment="Center"
Opacity="0.7" />
<Separator />
<CheckBox x:Name="DynamicOverflowCheck"
Content="IsDynamicOverflowEnabled"
IsChecked="True"
IsCheckedChanged="OnDynamicOverflowChanged" />
<Separator />
<TextBlock Text="Overflow Priority" FontWeight="SemiBold" />
<TextBlock Text="DynamicOverflowOrder priority (lower = stays visible longer):"
FontSize="12" Opacity="0.7" TextWrapping="Wrap" />
<TextBlock Text="0: New, Save (always shown last to overflow)" FontSize="11" Opacity="0.7" />
<TextBlock Text="1: Share" FontSize="11" Opacity="0.7" />
<TextBlock Text="2: Bold, Italic" FontSize="11" Opacity="0.7" />
<TextBlock Text="3: Delete (overflows first)" FontSize="11" Opacity="0.7" />
</StackPanel>
</ScrollViewer>
<Border DockPanel.Dock="Right" Width="1" Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<StackPanel Margin="12" Spacing="8">
<TextBlock x:Name="StatusText"
Text="Showing 6 of 6 commands, 0 in overflow"
FontSize="12"
Opacity="0.7" />
<Border x:Name="BarContainer"
Width="400"
HorizontalAlignment="Left"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1"
CornerRadius="4"
ClipToBounds="True">
<CommandBar x:Name="DemoBar" IsDynamicOverflowEnabled="True">
<CommandBar.PrimaryCommands>
<AppBarButton Label="New" DynamicOverflowOrder="0"><AppBarButton.Icon><PathIcon Data="{StaticResource AddIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Save" DynamicOverflowOrder="0"><AppBarButton.Icon><PathIcon Data="{StaticResource SaveIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Share" DynamicOverflowOrder="1"><AppBarButton.Icon><PathIcon Data="{StaticResource ShareIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Bold" DynamicOverflowOrder="2"><AppBarButton.Icon><PathIcon Data="{StaticResource BoldIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Italic" DynamicOverflowOrder="2"><AppBarButton.Icon><PathIcon Data="{StaticResource ItalicIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Delete" DynamicOverflowOrder="3"><AppBarButton.Icon><PathIcon Data="{StaticResource DeleteIcon}" /></AppBarButton.Icon></AppBarButton>
</CommandBar.PrimaryCommands>
</CommandBar>
</Border>
</StackPanel>
</DockPanel>
</UserControl>

45
samples/ControlCatalog/Pages/CommandBar/CommandBarDynamicOverflowPage.xaml.cs

@ -0,0 +1,45 @@
using System.Collections.Specialized;
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public partial class CommandBarDynamicOverflowPage : UserControl
{
public CommandBarDynamicOverflowPage()
{
InitializeComponent();
((INotifyCollectionChanged)DemoBar.OverflowItems).CollectionChanged += OnOverflowChanged;
UpdateStatus();
}
private void OnWidthChanged(object? sender, Avalonia.Controls.Primitives.RangeBaseValueChangedEventArgs e)
{
if (BarContainer == null)
return;
var width = (int)WidthSlider.Value;
BarContainer.Width = width;
WidthLabel.Text = $"{width}";
}
private void OnDynamicOverflowChanged(object? sender, RoutedEventArgs e)
{
if (DemoBar == null)
return;
DemoBar.IsDynamicOverflowEnabled = DynamicOverflowCheck.IsChecked == true;
}
private void OnOverflowChanged(object? sender, NotifyCollectionChangedEventArgs e)
{
UpdateStatus();
}
private void UpdateStatus()
{
var total = DemoBar.PrimaryCommands.Count;
var overflow = DemoBar.OverflowItems.Count;
var visible = total - overflow;
StatusText.Text = $"Showing {visible} of {total} commands, {overflow} in overflow";
}
}
}

94
samples/ControlCatalog/Pages/CommandBar/CommandBarFirstLookPage.xaml

@ -0,0 +1,94 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CommandBarFirstLookPage">
<UserControl.Resources>
<StreamGeometry x:Key="AddIcon">M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z</StreamGeometry>
<StreamGeometry x:Key="SaveIcon">M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z</StreamGeometry>
<StreamGeometry x:Key="ShareIcon">M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.61 20.92,19C20.92,17.39 19.61,16.08 18,16.08Z</StreamGeometry>
<StreamGeometry x:Key="BoldIcon">M15.6,10.79C17.04,10.07 18,8.64 18,7C18,4.79 16.21,3 14,3H7V21H14.73C16.78,21 18.5,19.37 18.5,17.32C18.5,15.82 17.72,14.53 16.5,13.77C16.2,13.59 15.9,13.44 15.6,13.32V10.79M10,6.5H13C13.83,6.5 14.5,7.17 14.5,8C14.5,8.83 13.83,9.5 13,9.5H10V6.5M13.5,17.5H10V14H13.5C14.33,14 15,14.67 15,15.5C15,16.33 14.33,17.5 13.5,17.5Z</StreamGeometry>
<StreamGeometry x:Key="ItalicIcon">M10,4V7H12.21L8.79,15H6V18H14V15H11.79L15.21,7H18V4H10Z</StreamGeometry>
<StreamGeometry x:Key="ExportIcon">M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20M16,11V18.1L13.9,16L11.1,18.8L8.3,16L11.1,13.2L9,11.1L16,11Z</StreamGeometry>
<StreamGeometry x:Key="PrintIcon">M18,3H6V7H18M19,12A1,1 0 0,1 18,11A1,1 0 0,1 19,10A1,1 0 0,1 20,11A1,1 0 0,1 19,12M16,19H8V14H16M19,8H5A3,3 0 0,0 2,11V17H6V21H18V17H22V11A3,3 0 0,0 19,8Z</StreamGeometry>
<StreamGeometry x:Key="DeleteIcon">M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z</StreamGeometry>
</UserControl.Resources>
<DockPanel>
<ScrollViewer DockPanel.Dock="Right" Width="260">
<StackPanel Margin="12" Spacing="8">
<TextBlock Text="Configuration" FontWeight="SemiBold" FontSize="16"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock Text="Primary Commands" FontWeight="SemiBold" />
<TextBlock Text="Buttons displayed directly in the bar: New, Save, Bold, Italic, Share."
FontSize="12" Opacity="0.7" TextWrapping="Wrap" />
<Separator />
<TextBlock Text="Secondary Commands" FontWeight="SemiBold" />
<TextBlock Text="Buttons in the overflow menu (three-dot icon): Export, Print, Delete."
FontSize="12" Opacity="0.7" TextWrapping="Wrap" />
<Separator />
<TextBlock Text="Content Area" FontWeight="SemiBold" />
<TextBlock Text="Custom content (e.g. 'Document Editor' text) displayed on the left side of the bar."
FontSize="12" Opacity="0.7" TextWrapping="Wrap" />
<Separator />
<TextBlock Text="Separators" FontWeight="SemiBold" />
<TextBlock Text="AppBarSeparator creates visual dividers between command groups."
FontSize="12" Opacity="0.7" TextWrapping="Wrap" />
</StackPanel>
</ScrollViewer>
<Border DockPanel.Dock="Right" Width="1" Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<ScrollViewer>
<StackPanel Spacing="12" Margin="12,12,12,0">
<TextBlock Classes="h2">A toolbar supporting primary and secondary commands with an optional overflow menu.</TextBlock>
<CommandBar>
<CommandBar.Content>
<TextBlock Text="Document Editor" VerticalAlignment="Center" Margin="8,0" />
</CommandBar.Content>
<CommandBar.PrimaryCommands>
<AppBarButton Label="New" Click="OnButtonClick">
<AppBarButton.Icon><PathIcon Data="{StaticResource AddIcon}" /></AppBarButton.Icon>
</AppBarButton>
<AppBarButton Label="Save" Click="OnButtonClick">
<AppBarButton.Icon><PathIcon Data="{StaticResource SaveIcon}" /></AppBarButton.Icon>
</AppBarButton>
<AppBarSeparator />
<AppBarToggleButton Label="Bold" IsCheckedChanged="OnToggleChanged">
<AppBarToggleButton.Icon><PathIcon Data="{StaticResource BoldIcon}" /></AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarToggleButton Label="Italic" IsCheckedChanged="OnToggleChanged">
<AppBarToggleButton.Icon><PathIcon Data="{StaticResource ItalicIcon}" /></AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarSeparator />
<AppBarButton Label="Share" Click="OnButtonClick">
<AppBarButton.Icon><PathIcon Data="{StaticResource ShareIcon}" /></AppBarButton.Icon>
</AppBarButton>
</CommandBar.PrimaryCommands>
<CommandBar.SecondaryCommands>
<AppBarButton Label="Export" Click="OnButtonClick">
<AppBarButton.Icon><PathIcon Data="{StaticResource ExportIcon}" /></AppBarButton.Icon>
</AppBarButton>
<AppBarButton Label="Print" Click="OnButtonClick">
<AppBarButton.Icon><PathIcon Data="{StaticResource PrintIcon}" /></AppBarButton.Icon>
</AppBarButton>
<AppBarSeparator />
<AppBarButton Label="Delete" Click="OnButtonClick">
<AppBarButton.Icon><PathIcon Data="{StaticResource DeleteIcon}" /></AppBarButton.Icon>
</AppBarButton>
</CommandBar.SecondaryCommands>
</CommandBar>
<TextBlock x:Name="StatusText"
Text="Ready"
FontSize="13"
Opacity="0.7" />
</StackPanel>
</ScrollViewer>
</DockPanel>
</UserControl>

27
samples/ControlCatalog/Pages/CommandBar/CommandBarFirstLookPage.xaml.cs

@ -0,0 +1,27 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public partial class CommandBarFirstLookPage : UserControl
{
public CommandBarFirstLookPage()
{
InitializeComponent();
}
private void OnButtonClick(object? sender, RoutedEventArgs e)
{
if (sender is AppBarButton btn)
StatusText.Text = $"{btn.Label} clicked";
}
private void OnToggleChanged(object? sender, RoutedEventArgs e)
{
if (sender is AppBarToggleButton btn)
StatusText.Text = btn.IsChecked == true
? $"{btn.Label} enabled"
: $"{btn.Label} disabled";
}
}
}

47
samples/ControlCatalog/Pages/CommandBar/CommandBarLabelPositionPage.xaml

@ -0,0 +1,47 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CommandBarLabelPositionPage">
<UserControl.Resources>
<StreamGeometry x:Key="AddIcon">M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z</StreamGeometry>
<StreamGeometry x:Key="SaveIcon">M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z</StreamGeometry>
<StreamGeometry x:Key="ShareIcon">M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.61 20.92,19C20.92,17.39 19.61,16.08 18,16.08Z</StreamGeometry>
<StreamGeometry x:Key="DeleteIcon">M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z</StreamGeometry>
</UserControl.Resources>
<ScrollViewer>
<StackPanel Spacing="16" Margin="12,12,12,0">
<TextBlock Classes="h2">The DefaultLabelPosition property controls how button labels are displayed.</TextBlock>
<TextBlock Text="Bottom (default)" FontWeight="SemiBold" />
<TextBlock Text="Label appears below the icon." FontSize="12" Opacity="0.7" />
<CommandBar DefaultLabelPosition="Bottom" OverflowButtonVisibility="Collapsed">
<AppBarButton Label="New"><AppBarButton.Icon><PathIcon Data="{StaticResource AddIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Save"><AppBarButton.Icon><PathIcon Data="{StaticResource SaveIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Share"><AppBarButton.Icon><PathIcon Data="{StaticResource ShareIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Delete"><AppBarButton.Icon><PathIcon Data="{StaticResource DeleteIcon}" /></AppBarButton.Icon></AppBarButton>
</CommandBar>
<Separator />
<TextBlock Text="Right" FontWeight="SemiBold" />
<TextBlock Text="Label appears to the right of the icon." FontSize="12" Opacity="0.7" />
<CommandBar DefaultLabelPosition="Right" OverflowButtonVisibility="Collapsed">
<AppBarButton Label="New"><AppBarButton.Icon><PathIcon Data="{StaticResource AddIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Save"><AppBarButton.Icon><PathIcon Data="{StaticResource SaveIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Share"><AppBarButton.Icon><PathIcon Data="{StaticResource ShareIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Delete"><AppBarButton.Icon><PathIcon Data="{StaticResource DeleteIcon}" /></AppBarButton.Icon></AppBarButton>
</CommandBar>
<Separator />
<TextBlock Text="Collapsed (icon only)" FontWeight="SemiBold" />
<TextBlock Text="No label shown. Use ToolTip.Tip to preserve accessibility." FontSize="12" Opacity="0.7" />
<CommandBar DefaultLabelPosition="Collapsed" OverflowButtonVisibility="Collapsed">
<AppBarButton ToolTip.Tip="New"><AppBarButton.Icon><PathIcon Data="{StaticResource AddIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton ToolTip.Tip="Save"><AppBarButton.Icon><PathIcon Data="{StaticResource SaveIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton ToolTip.Tip="Share"><AppBarButton.Icon><PathIcon Data="{StaticResource ShareIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton ToolTip.Tip="Delete"><AppBarButton.Icon><PathIcon Data="{StaticResource DeleteIcon}" /></AppBarButton.Icon></AppBarButton>
</CommandBar>
</StackPanel>
</ScrollViewer>
</UserControl>

12
samples/ControlCatalog/Pages/CommandBar/CommandBarLabelPositionPage.xaml.cs

@ -0,0 +1,12 @@
using Avalonia.Controls;
namespace ControlCatalog.Pages
{
public partial class CommandBarLabelPositionPage : UserControl
{
public CommandBarLabelPositionPage()
{
InitializeComponent();
}
}
}

74
samples/ControlCatalog/Pages/CommandBar/CommandBarOverflowPage.xaml

@ -0,0 +1,74 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CommandBarOverflowPage">
<UserControl.Resources>
<StreamGeometry x:Key="AddIcon">M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z</StreamGeometry>
<StreamGeometry x:Key="SaveIcon">M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z</StreamGeometry>
<StreamGeometry x:Key="ShareIcon">M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.61 20.92,19C20.92,17.39 19.61,16.08 18,16.08Z</StreamGeometry>
<StreamGeometry x:Key="ExportIcon">M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20M16,11V18.1L13.9,16L11.1,18.8L8.3,16L11.1,13.2L9,11.1L16,11Z</StreamGeometry>
<StreamGeometry x:Key="DeleteIcon">M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z</StreamGeometry>
</UserControl.Resources>
<DockPanel>
<ScrollViewer DockPanel.Dock="Right" Width="260">
<StackPanel Margin="12" Spacing="8">
<TextBlock Text="Configuration" FontWeight="SemiBold" FontSize="16"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock Text="OverflowButtonVisibility" />
<ComboBox x:Name="OverflowVisCombo"
SelectedIndex="0"
HorizontalAlignment="Stretch"
SelectionChanged="OnOverflowVisChanged">
<ComboBoxItem Content="Auto" />
<ComboBoxItem Content="Visible" />
<ComboBoxItem Content="Collapsed" />
</ComboBox>
<CheckBox x:Name="IsOpenCheck"
Content="IsOpen"
IsCheckedChanged="OnIsOpenChanged" />
<CheckBox x:Name="IsStickyCheck"
Content="IsSticky"
IsCheckedChanged="OnIsStickyChanged" />
<Separator />
<Button Content="+ Add Primary"
HorizontalAlignment="Stretch"
Click="OnAddPrimary" />
<Button Content="+ Add Secondary"
HorizontalAlignment="Stretch"
Click="OnAddSecondary" />
<Separator />
<TextBlock Text="About" FontWeight="SemiBold" />
<TextBlock Text="Auto: overflow button shows when secondary commands exist. Visible: always show. Collapsed: never show."
FontSize="12" Opacity="0.7" TextWrapping="Wrap" />
<TextBlock Text="IsSticky prevents the overflow from closing when clicking outside."
FontSize="12" Opacity="0.7" TextWrapping="Wrap" Margin="0,4,0,0" />
</StackPanel>
</ScrollViewer>
<Border DockPanel.Dock="Right" Width="1" Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<ScrollViewer>
<StackPanel Spacing="12" Margin="12,12,12,0">
<TextBlock Classes="h2">The overflow (···) button reveals secondary commands. Configure its visibility and behavior.</TextBlock>
<CommandBar x:Name="DemoBar" IsDynamicOverflowEnabled="True">
<CommandBar.PrimaryCommands>
<AppBarButton Label="New"><AppBarButton.Icon><PathIcon Data="{StaticResource AddIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Save"><AppBarButton.Icon><PathIcon Data="{StaticResource SaveIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Share"><AppBarButton.Icon><PathIcon Data="{StaticResource ShareIcon}" /></AppBarButton.Icon></AppBarButton>
</CommandBar.PrimaryCommands>
<CommandBar.SecondaryCommands>
<AppBarButton Label="Export"><AppBarButton.Icon><PathIcon Data="{StaticResource ExportIcon}" /></AppBarButton.Icon></AppBarButton>
<AppBarButton Label="Delete"><AppBarButton.Icon><PathIcon Data="{StaticResource DeleteIcon}" /></AppBarButton.Icon></AppBarButton>
</CommandBar.SecondaryCommands>
</CommandBar>
</StackPanel>
</ScrollViewer>
</DockPanel>
</UserControl>

54
samples/ControlCatalog/Pages/CommandBar/CommandBarOverflowPage.xaml.cs

@ -0,0 +1,54 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public partial class CommandBarOverflowPage : UserControl
{
private int _primaryCount;
private int _secondaryCount;
public CommandBarOverflowPage()
{
InitializeComponent();
}
private void OnOverflowVisChanged(object? sender, SelectionChangedEventArgs e)
{
if (DemoBar == null)
return;
DemoBar.OverflowButtonVisibility = OverflowVisCombo.SelectedIndex switch
{
1 => CommandBarOverflowButtonVisibility.Visible,
2 => CommandBarOverflowButtonVisibility.Collapsed,
_ => CommandBarOverflowButtonVisibility.Auto
};
}
private void OnIsOpenChanged(object? sender, RoutedEventArgs e)
{
if (DemoBar == null)
return;
DemoBar.IsOpen = IsOpenCheck.IsChecked == true;
}
private void OnIsStickyChanged(object? sender, RoutedEventArgs e)
{
if (DemoBar == null)
return;
DemoBar.IsSticky = IsStickyCheck.IsChecked == true;
}
private void OnAddPrimary(object? sender, RoutedEventArgs e)
{
_primaryCount++;
DemoBar.PrimaryCommands.Add(new AppBarButton { Label = $"Cmd {_primaryCount}" });
}
private void OnAddSecondary(object? sender, RoutedEventArgs e)
{
_secondaryCount++;
DemoBar.SecondaryCommands.Add(new AppBarButton { Label = $"Sec {_secondaryCount}" });
}
}
}

77
samples/ControlCatalog/Pages/CommandBar/CommandBarTogglePage.xaml

@ -0,0 +1,77 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CommandBarTogglePage">
<UserControl.Resources>
<StreamGeometry x:Key="BoldIcon">M15.6,10.79C17.04,10.07 18,8.64 18,7C18,4.79 16.21,3 14,3H7V21H14.73C16.78,21 18.5,19.37 18.5,17.32C18.5,15.82 17.72,14.53 16.5,13.77C16.2,13.59 15.9,13.44 15.6,13.32V10.79M10,6.5H13C13.83,6.5 14.5,7.17 14.5,8C14.5,8.83 13.83,9.5 13,9.5H10V6.5M13.5,17.5H10V14H13.5C14.33,14 15,14.67 15,15.5C15,16.33 14.33,17.5 13.5,17.5Z</StreamGeometry>
<StreamGeometry x:Key="ItalicIcon">M10,4V7H12.21L8.79,15H6V18H14V15H11.79L15.21,7H18V4H10Z</StreamGeometry>
<StreamGeometry x:Key="UnderlineIcon">M5,21H19V19H5V21M12,17A6,6 0 0,0 18,11V3H15.5V11A3.5,3.5 0 0,1 12,14.5A3.5,3.5 0 0,1 8.5,11V3H6V11A6,6 0 0,0 12,17Z</StreamGeometry>
<StreamGeometry x:Key="FavoriteIcon">M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z</StreamGeometry>
</UserControl.Resources>
<DockPanel>
<ScrollViewer DockPanel.Dock="Right" Width="260">
<StackPanel Margin="12" Spacing="8">
<TextBlock Text="Configuration" FontWeight="SemiBold" FontSize="16"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock Text="Toggle State" FontWeight="SemiBold" FontSize="13" />
<CheckBox x:Name="ForceBoldCheck"
Content="Bold"
IsCheckedChanged="OnForceBoldChanged" />
<CheckBox x:Name="ForceItalicCheck"
Content="Italic"
IsCheckedChanged="OnForceItalicChanged" />
<CheckBox x:Name="ForceUnderlineCheck"
Content="Underline"
IsCheckedChanged="OnForceUnderlineChanged" />
<Separator />
<TextBlock Text="About" FontWeight="SemiBold" />
<TextBlock Text="AppBarToggleButton maintains checked/unchecked state. When checked, it shows a highlight background. Bind IsChecked to a boolean property."
FontSize="12" Opacity="0.7" TextWrapping="Wrap" />
</StackPanel>
</ScrollViewer>
<Border DockPanel.Dock="Right" Width="1" Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<ScrollViewer>
<StackPanel Spacing="12" Margin="12,12,12,0">
<TextBlock Classes="h2">AppBarToggleButton supports on/off states for formatting and feature toggles.</TextBlock>
<TextBlock Text="Text Formatting" FontWeight="SemiBold" />
<CommandBar x:Name="FormatBar" OverflowButtonVisibility="Collapsed">
<AppBarToggleButton x:Name="BoldToggle"
Label="Bold"
IsCheckedChanged="OnFormatChanged">
<AppBarToggleButton.Icon><PathIcon Data="{StaticResource BoldIcon}" /></AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarToggleButton x:Name="ItalicToggle"
Label="Italic"
IsCheckedChanged="OnFormatChanged">
<AppBarToggleButton.Icon><PathIcon Data="{StaticResource ItalicIcon}" /></AppBarToggleButton.Icon>
</AppBarToggleButton>
<AppBarToggleButton x:Name="UnderlineToggle"
Label="Underline"
IsCheckedChanged="OnFormatChanged">
<AppBarToggleButton.Icon><PathIcon Data="{StaticResource UnderlineIcon}" /></AppBarToggleButton.Icon>
</AppBarToggleButton>
</CommandBar>
<TextBlock x:Name="FormatStatus"
Text="Active: (none)"
FontSize="13"
Opacity="0.7" />
<Separator />
<TextBlock Text="Favorite" FontWeight="SemiBold" />
<CommandBar OverflowButtonVisibility="Collapsed">
<AppBarToggleButton Label="Favorite">
<AppBarToggleButton.Icon><PathIcon Data="{StaticResource FavoriteIcon}" /></AppBarToggleButton.Icon>
</AppBarToggleButton>
</CommandBar>
</StackPanel>
</ScrollViewer>
</DockPanel>
</UserControl>

50
samples/ControlCatalog/Pages/CommandBar/CommandBarTogglePage.xaml.cs

@ -0,0 +1,50 @@
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public partial class CommandBarTogglePage : UserControl
{
public CommandBarTogglePage()
{
InitializeComponent();
}
private void OnFormatChanged(object? sender, RoutedEventArgs e)
{
if (FormatStatus == null)
return;
var active = new List<string>();
if (BoldToggle.IsChecked == true) active.Add("Bold");
if (ItalicToggle.IsChecked == true) active.Add("Italic");
if (UnderlineToggle.IsChecked == true) active.Add("Underline");
FormatStatus.Text = active.Count > 0 ? $"Active: {string.Join(", ", active)}" : "Active: (none)";
ForceBoldCheck.IsChecked = BoldToggle.IsChecked;
ForceItalicCheck.IsChecked = ItalicToggle.IsChecked;
ForceUnderlineCheck.IsChecked = UnderlineToggle.IsChecked;
}
private void OnForceBoldChanged(object? sender, RoutedEventArgs e)
{
if (BoldToggle == null)
return;
BoldToggle.IsChecked = ForceBoldCheck.IsChecked;
}
private void OnForceItalicChanged(object? sender, RoutedEventArgs e)
{
if (ItalicToggle == null)
return;
ItalicToggle.IsChecked = ForceItalicCheck.IsChecked;
}
private void OnForceUnderlineChanged(object? sender, RoutedEventArgs e)
{
if (UnderlineToggle == null)
return;
UnderlineToggle.IsChecked = ForceUnderlineCheck.IsChecked;
}
}
}

8
samples/ControlCatalog/Pages/CommandBarPage.xaml

@ -0,0 +1,8 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.CommandBarPage">
<UserControl.Resources>
<SolidColorBrush x:Key="NavigationBarBackground" Color="Transparent" />
</UserControl.Resources>
<NavigationPage x:Name="SampleNav" />
</UserControl>

158
samples/ControlCatalog/Pages/CommandBarPage.xaml.cs

@ -0,0 +1,158 @@
using System;
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
namespace ControlCatalog.Pages
{
public partial class CommandBarPage : UserControl
{
private static readonly (string Group, string Title, string Description, Func<UserControl> Factory)[] Demos =
{
// Overview
("Overview", "First Look", "A CommandBar with primary commands, secondary overflow menu, and custom content area.", () => new CommandBarFirstLookPage()),
("Overview", "Toggle Buttons", "AppBarToggleButton for stateful actions like Bold, Italic, and Favorite.", () => new CommandBarTogglePage()),
// Appearance
("Appearance", "Label Positions", "Configure label position: Bottom (default), Right, or Collapsed (icon only).", () => new CommandBarLabelPositionPage()),
("Appearance", "Customization", "Background, Foreground, BorderBrush, BorderThickness, and CornerRadius.", () => new CommandBarCustomizationPage()),
// Features
("Features", "Overflow Menu", "Secondary commands appear in an overflow popup. Configure visibility and sticky behavior.", () => new CommandBarOverflowPage()),
("Features", "Dynamic Overflow", "IsDynamicOverflowEnabled moves primary commands to overflow as space shrinks.", () => new CommandBarDynamicOverflowPage()),
};
public CommandBarPage()
{
InitializeComponent();
Loaded += OnLoaded;
}
private async void OnLoaded(object? sender, RoutedEventArgs e)
{
await SampleNav.PushAsync(CreateHomePage(), null);
}
private ContentPage CreateHomePage()
{
var stack = new StackPanel
{
Margin = new Avalonia.Thickness(12),
Spacing = 16
};
var groups = new Dictionary<string, WrapPanel>();
var groupOrder = new List<string>();
foreach (var (group, title, description, factory) in Demos)
{
if (!groups.ContainsKey(group))
{
groups[group] = new WrapPanel
{
Orientation = Orientation.Horizontal,
HorizontalAlignment = HorizontalAlignment.Left
};
groupOrder.Add(group);
}
var demoFactory = factory;
var demoTitle = title;
var card = new Button
{
Width = 170,
MinHeight = 80,
Margin = new Avalonia.Thickness(0, 0, 8, 8),
VerticalAlignment = VerticalAlignment.Top,
HorizontalContentAlignment = HorizontalAlignment.Left,
VerticalContentAlignment = VerticalAlignment.Top,
Padding = new Avalonia.Thickness(12, 8),
Content = new StackPanel
{
Spacing = 4,
Children =
{
new TextBlock
{
Text = title,
FontSize = 13,
FontWeight = FontWeight.SemiBold,
TextWrapping = TextWrapping.Wrap
},
new TextBlock
{
Text = description,
FontSize = 11,
Opacity = 0.6,
TextWrapping = TextWrapping.Wrap
}
}
}
};
card.Click += async (s, e) =>
{
var headerGrid = new Grid { ColumnDefinitions = new ColumnDefinitions("*, Auto") };
headerGrid.Children.Add(new TextBlock
{
Text = demoTitle,
VerticalAlignment = VerticalAlignment.Center
});
var closeBtn = new Button
{
Content = new PathIcon
{
Data = Geometry.Parse("M4.397 4.397a1 1 0 0 1 1.414 0L12 10.585l6.19-6.188a1 1 0 0 1 1.414 1.414L13.413 12l6.19 6.189a1 1 0 0 1-1.414 1.414L12 13.413l-6.189 6.19a1 1 0 0 1-1.414-1.414L10.585 12 4.397 5.811a1 1 0 0 1 0-1.414z")
},
Background = Brushes.Transparent,
BorderThickness = new Avalonia.Thickness(0),
Padding = new Avalonia.Thickness(8, 4),
VerticalAlignment = VerticalAlignment.Center
};
Grid.SetColumn(closeBtn, 1);
headerGrid.Children.Add(closeBtn);
closeBtn.Click += async (_, _) => await SampleNav.PopAsync(null);
var page = new ContentPage
{
Header = headerGrid,
Content = demoFactory(),
HorizontalContentAlignment = HorizontalAlignment.Stretch,
VerticalContentAlignment = VerticalAlignment.Stretch
};
NavigationPage.SetHasBackButton(page, false);
await SampleNav.PushAsync(page, null);
};
groups[group].Children.Add(card);
}
foreach (var groupName in groupOrder)
{
stack.Children.Add(new TextBlock
{
Text = groupName,
FontSize = 13,
FontWeight = FontWeight.SemiBold,
Margin = new Avalonia.Thickness(0, 0, 0, 4),
Opacity = 0.6
});
stack.Children.Add(groups[groupName]);
}
var homePage = new ContentPage
{
Content = new ScrollViewer { Content = stack },
HorizontalContentAlignment = HorizontalAlignment.Stretch,
VerticalContentAlignment = VerticalAlignment.Stretch
};
NavigationPage.SetHasNavigationBar(homePage, false);
return homePage;
}
}
}

11
samples/ControlCatalog/Pages/ConnectedAnimationDemoPage.xaml

@ -0,0 +1,11 @@
<UserControl x:Class="ControlCatalog.Pages.ConnectedAnimationDemoPage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<NavigationPage x:Name="SampleNav">
<NavigationPage.Styles>
<Style Selector="NavigationPage#SampleNav /template/ Border#PART_NavigationBar">
<Setter Property="Background" Value="Transparent" />
</Style>
</NavigationPage.Styles>
</NavigationPage>
</UserControl>

144
samples/ControlCatalog/Pages/ConnectedAnimationDemoPage.xaml.cs

@ -0,0 +1,144 @@
using System;
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
namespace ControlCatalog.Pages
{
public partial class ConnectedAnimationDemoPage : UserControl
{
private static readonly (string Group, string Title, string Description, Func<UserControl> Factory)[] Demos = [];
public ConnectedAnimationDemoPage()
{
InitializeComponent();
Loaded += OnLoaded;
}
private async void OnLoaded(object? sender, RoutedEventArgs e)
{
await SampleNav.PushAsync(CreateHomePage(), null);
}
private ContentPage CreateHomePage()
{
var stack = new StackPanel
{
Margin = new Avalonia.Thickness(12),
Spacing = 16
};
var groups = new Dictionary<string, WrapPanel>();
var groupOrder = new List<string>();
foreach (var (group, title, description, factory) in Demos)
{
if (!groups.ContainsKey(group))
{
groups[group] = new WrapPanel
{
Orientation = Orientation.Horizontal,
HorizontalAlignment = HorizontalAlignment.Left
};
groupOrder.Add(group);
}
var demoFactory = factory;
var demoTitle = title;
var card = new Button
{
Width = 200,
MinHeight = 80,
Margin = new Avalonia.Thickness(0, 0, 8, 8),
VerticalAlignment = VerticalAlignment.Top,
HorizontalContentAlignment = HorizontalAlignment.Left,
VerticalContentAlignment = VerticalAlignment.Top,
Padding = new Avalonia.Thickness(12, 8),
Content = new StackPanel
{
Spacing = 4,
Children =
{
new TextBlock
{
Text = title,
FontSize = 13,
FontWeight = FontWeight.SemiBold,
TextWrapping = TextWrapping.Wrap
},
new TextBlock
{
Text = description,
FontSize = 11,
Opacity = 0.6,
TextWrapping = TextWrapping.Wrap
}
}
}
};
card.Click += async (s, ev) =>
{
var headerGrid = new Grid { ColumnDefinitions = new ColumnDefinitions("*, Auto") };
headerGrid.Children.Add(new TextBlock
{
Text = demoTitle,
VerticalAlignment = VerticalAlignment.Center
});
var closeBtn = new Button
{
Content = new PathIcon
{
Data = Geometry.Parse("M4.397 4.397a1 1 0 0 1 1.414 0L12 10.585l6.19-6.188a1 1 0 0 1 1.414 1.414L13.413 12l6.19 6.189a1 1 0 0 1-1.414 1.414L12 13.413l-6.189 6.19a1 1 0 0 1-1.414-1.414L10.585 12 4.397 5.811a1 1 0 0 1 0-1.414z")
},
Background = Brushes.Transparent,
BorderThickness = new Avalonia.Thickness(0),
Padding = new Avalonia.Thickness(8, 4),
VerticalAlignment = VerticalAlignment.Center
};
Grid.SetColumn(closeBtn, 1);
headerGrid.Children.Add(closeBtn);
closeBtn.Click += async (_, _) => await SampleNav.PopAsync(null);
var page = new ContentPage
{
Header = headerGrid,
Content = demoFactory(),
HorizontalContentAlignment = HorizontalAlignment.Stretch,
VerticalContentAlignment = VerticalAlignment.Stretch
};
NavigationPage.SetHasBackButton(page, false);
await SampleNav.PushAsync(page, null);
};
groups[group].Children.Add(card);
}
foreach (var groupName in groupOrder)
{
stack.Children.Add(new TextBlock
{
Text = groupName,
FontSize = 13,
FontWeight = FontWeight.SemiBold,
Margin = new Avalonia.Thickness(0, 0, 0, 4),
Opacity = 0.6
});
stack.Children.Add(groups[groupName]);
}
var homePage = new ContentPage
{
Content = new ScrollViewer { Content = stack },
HorizontalContentAlignment = HorizontalAlignment.Stretch,
VerticalContentAlignment = VerticalAlignment.Stretch
};
NavigationPage.SetHasNavigationBar(homePage, false);
return homePage;
}
}
}

11
samples/ControlCatalog/Pages/ContentDemoPage.xaml

@ -0,0 +1,11 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ContentDemoPage">
<NavigationPage x:Name="SampleNav">
<NavigationPage.Styles>
<Style Selector="NavigationPage#SampleNav /template/ Border#PART_NavigationBar">
<Setter Property="Background" Value="Transparent" />
</Style>
</NavigationPage.Styles>
</NavigationPage>
</UserControl>

160
samples/ControlCatalog/Pages/ContentDemoPage.xaml.cs

@ -0,0 +1,160 @@
using System;
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
namespace ControlCatalog.Pages
{
public partial class ContentDemoPage : UserControl
{
private static readonly (string Group, string Title, string Description, Func<UserControl> Factory)[] Demos =
{
// Overview
("Overview", "First Look", "Basic ContentPage with header, content, and background inside a NavigationPage.", () => new ContentPageFirstLookPage()),
// Appearance
("Appearance", "Customization", "Adjust content alignment, background color, and padding to understand how ContentPage adapts its layout.", () => new ContentPageCustomizationPage()),
// Features
("Features", "CommandBar", "Attach a CommandBar to the top or bottom of a ContentPage. Add and remove items at runtime.", () => new ContentPageCommandBarPage()),
("Features", "Safe Area", "Understand how AutomaticallyApplySafeAreaPadding absorbs platform insets.", () => new ContentPageSafeAreaPage()),
("Features", "Events", "Observe page lifecycle events: NavigatedTo, NavigatedFrom, and Navigating.", () => new ContentPageEventsPage()),
// Performance
("Performance", "Performance Monitor", "Push ContentPages of varying weight (50 KB to 2 MB) and observe live instance count and managed heap. Pop pages and force GC to confirm memory is released.", () => new ContentPagePerformancePage()),
};
public ContentDemoPage()
{
InitializeComponent();
Loaded += OnLoaded;
}
private async void OnLoaded(object? sender, RoutedEventArgs e)
{
await SampleNav.PushAsync(CreateHomePage(), null);
}
private ContentPage CreateHomePage()
{
var stack = new StackPanel
{
Margin = new Avalonia.Thickness(12),
Spacing = 16
};
var groups = new Dictionary<string, WrapPanel>();
var groupOrder = new List<string>();
foreach (var (group, title, description, factory) in Demos)
{
if (!groups.ContainsKey(group))
{
groups[group] = new WrapPanel
{
Orientation = Orientation.Horizontal,
HorizontalAlignment = HorizontalAlignment.Left
};
groupOrder.Add(group);
}
var demoFactory = factory;
var demoTitle = title;
var card = new Button
{
Width = 170,
MinHeight = 80,
Margin = new Avalonia.Thickness(0, 0, 8, 8),
VerticalAlignment = VerticalAlignment.Top,
HorizontalContentAlignment = HorizontalAlignment.Left,
VerticalContentAlignment = VerticalAlignment.Top,
Padding = new Avalonia.Thickness(12, 8),
Content = new StackPanel
{
Spacing = 4,
Children =
{
new TextBlock
{
Text = title,
FontSize = 13,
FontWeight = FontWeight.SemiBold,
TextWrapping = TextWrapping.Wrap
},
new TextBlock
{
Text = description,
FontSize = 11,
Opacity = 0.6,
TextWrapping = TextWrapping.Wrap
}
}
}
};
card.Click += async (s, e) =>
{
var headerGrid = new Grid { ColumnDefinitions = new ColumnDefinitions("*, Auto") };
headerGrid.Children.Add(new TextBlock
{
Text = demoTitle,
VerticalAlignment = VerticalAlignment.Center
});
var closeBtn = new Button
{
Content = new PathIcon
{
Data = Geometry.Parse("M4.397 4.397a1 1 0 0 1 1.414 0L12 10.585l6.19-6.188a1 1 0 0 1 1.414 1.414L13.413 12l6.19 6.189a1 1 0 0 1-1.414 1.414L12 13.413l-6.189 6.19a1 1 0 0 1-1.414-1.414L10.585 12 4.397 5.811a1 1 0 0 1 0-1.414z")
},
Background = Brushes.Transparent,
BorderThickness = new Avalonia.Thickness(0),
Padding = new Avalonia.Thickness(8, 4),
VerticalAlignment = VerticalAlignment.Center
};
Grid.SetColumn(closeBtn, 1);
headerGrid.Children.Add(closeBtn);
closeBtn.Click += async (_, _) => await SampleNav.PopAsync(null);
var page = new ContentPage
{
Header = headerGrid,
Content = demoFactory(),
HorizontalContentAlignment = HorizontalAlignment.Stretch,
VerticalContentAlignment = VerticalAlignment.Stretch
};
NavigationPage.SetHasBackButton(page, false);
await SampleNav.PushAsync(page, null);
};
groups[group].Children.Add(card);
}
foreach (var groupName in groupOrder)
{
stack.Children.Add(new TextBlock
{
Text = groupName,
FontSize = 13,
FontWeight = FontWeight.SemiBold,
Margin = new Avalonia.Thickness(0, 0, 0, 4),
Opacity = 0.6
});
stack.Children.Add(groups[groupName]);
}
var homePage = new ContentPage
{
Content = new ScrollViewer { Content = stack },
HorizontalContentAlignment = HorizontalAlignment.Stretch,
VerticalContentAlignment = VerticalAlignment.Stretch
};
NavigationPage.SetHasNavigationBar(homePage, false);
return homePage;
}
}
}

63
samples/ControlCatalog/Pages/ContentPage/ContentPageCommandBarPage.xaml

@ -0,0 +1,63 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ContentPageCommandBarPage">
<DockPanel>
<ScrollViewer DockPanel.Dock="Right" Width="260">
<StackPanel Margin="12" Spacing="8">
<TextBlock Text="Configuration" FontWeight="SemiBold" FontSize="16"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock Text="Position" FontWeight="SemiBold" />
<ComboBox x:Name="PositionCombo"
SelectedIndex="0"
HorizontalAlignment="Stretch"
SelectionChanged="OnPositionChanged">
<ComboBoxItem Content="Top" />
<ComboBoxItem Content="Bottom" />
</ComboBox>
<Separator />
<TextBlock Text="Manage Items" FontWeight="SemiBold" />
<CheckBox x:Name="UseIconCheck" Content="Use Icon (primary items)" />
<Button Content="Add Primary Item" HorizontalAlignment="Stretch" Click="OnAddPrimary" />
<Button Content="Add Secondary Item" HorizontalAlignment="Stretch" Click="OnAddSecondary" />
<Button Content="Add Separator" HorizontalAlignment="Stretch" Click="OnAddSeparator" />
<Button Content="Clear All" HorizontalAlignment="Stretch" Click="OnClearAll" />
<Separator />
<TextBlock Text="Navigation" FontWeight="SemiBold" />
<Button Content="Push Page" HorizontalAlignment="Stretch" Click="OnPush" />
<Button Content="Pop Page" HorizontalAlignment="Stretch" Click="OnPop" />
<Separator />
<TextBlock Text="About" FontWeight="SemiBold" />
<TextBlock Text="• NavigationPage.SetTopCommandBar(page, bar): places a CommandBar inside the navigation bar area at the top."
FontSize="12" Opacity="0.7" TextWrapping="Wrap" />
<TextBlock Text="• NavigationPage.SetBottomCommandBar(page, bar): places a CommandBar below the page content at the bottom."
FontSize="12" Opacity="0.7" TextWrapping="Wrap" />
<TextBlock Text="• Each ContentPage has its own CommandBar. It is replaced when navigating between pages."
FontSize="12" Opacity="0.7" TextWrapping="Wrap" />
</StackPanel>
</ScrollViewer>
<Border DockPanel.Dock="Right" Width="1" Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<Grid Margin="12" RowDefinitions="Auto,*">
<TextBlock x:Name="StatusText"
Text="No items added"
FontSize="12"
Opacity="0.7"
Margin="0,0,0,8" />
<Border Grid.Row="1"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1"
CornerRadius="6"
ClipToBounds="True">
<NavigationPage x:Name="DemoNav" />
</Border>
</Grid>
</DockPanel>
</UserControl>

212
samples/ControlCatalog/Pages/ContentPage/ContentPageCommandBarPage.xaml.cs

@ -0,0 +1,212 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
namespace ControlCatalog.Pages
{
public partial class ContentPageCommandBarPage : UserControl
{
private static readonly (string Label, string PathData)[] IconPresets =
{
("Add", "M19,13H13V19H11V13H5V11H11V5H13V11H19V13Z"),
("Save", "M15,9H5V5H15M12,19A3,3 0 0,1 9,16A3,3 0 0,1 12,13A3,3 0 0,1 15,16A3,3 0 0,1 12,19M17,3H5C3.89,3 3,3.9 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V7L17,3Z"),
("Share", "M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.61 20.92,19C20.92,17.39 19.61,16.08 18,16.08Z"),
("Favorite", "M12,21.35L10.55,20.03C5.4,15.36 2,12.27 2,8.5C2,5.41 4.42,3 7.5,3C9.24,3 10.91,3.81 12,5.08C13.09,3.81 14.76,3 16.5,3C19.58,3 22,5.41 22,8.5C22,12.27 18.6,15.36 13.45,20.03L12,21.35Z"),
("Delete", "M19,4H15.5L14.5,3H9.5L8.5,4H5V6H19M6,19A2,2 0 0,0 8,21H16A2,2 0 0,0 18,19V7H6V19Z"),
};
private int _itemCounter;
private string _position = "Top";
private readonly List<ICommandBarElement> _primaryItems = new();
private readonly List<ICommandBarElement> _secondaryItems = new();
public ContentPageCommandBarPage()
{
InitializeComponent();
Loaded += OnLoaded;
}
private async void OnLoaded(object? sender, RoutedEventArgs e)
{
var rootPage = new ContentPage
{
Header = "CommandBar Demo",
Background = new SolidColorBrush(Color.Parse("#E3F2FD")),
Content = new StackPanel
{
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Spacing = 12,
Children =
{
new TextBlock
{
Text = "Root Page",
FontSize = 24,
FontWeight = FontWeight.Bold,
HorizontalAlignment = HorizontalAlignment.Center,
},
new TextBlock
{
Text = "Add items using the panel on the right,\nthen change the position to Top or Bottom.",
FontSize = 14,
TextWrapping = TextWrapping.Wrap,
HorizontalAlignment = HorizontalAlignment.Center,
TextAlignment = TextAlignment.Center,
Opacity = 0.7,
}
}
},
HorizontalContentAlignment = HorizontalAlignment.Stretch,
VerticalContentAlignment = VerticalAlignment.Stretch
};
await DemoNav.PushAsync(rootPage);
}
private void OnPositionChanged(object? sender, SelectionChangedEventArgs e)
{
if (PositionCombo == null)
return;
_position = PositionCombo.SelectedIndex == 1 ? "Bottom" : "Top";
RebuildCommandBar();
}
private void OnAddPrimary(object? sender, RoutedEventArgs e)
{
_itemCounter++;
var btn = new AppBarButton { Label = $"Action {_itemCounter}" };
if (UseIconCheck.IsChecked == true)
{
var preset = IconPresets[(_itemCounter - 1) % IconPresets.Length];
btn.Label = preset.Label;
btn.Icon = new PathIcon { Data = StreamGeometry.Parse(preset.PathData) };
btn.IsCompact = true;
}
_primaryItems.Add(btn);
RebuildCommandBar();
}
private void OnAddSecondary(object? sender, RoutedEventArgs e)
{
_itemCounter++;
_secondaryItems.Add(new AppBarButton { Label = $"Item {_itemCounter}" });
RebuildCommandBar();
}
private void OnAddSeparator(object? sender, RoutedEventArgs e)
{
_primaryItems.Add(new AppBarSeparator());
RebuildCommandBar();
}
private void OnClearAll(object? sender, RoutedEventArgs e)
{
_primaryItems.Clear();
_secondaryItems.Clear();
_itemCounter = 0;
ClearCommandBarFromActivePage();
StatusText.Text = "No items added";
}
private async void OnPush(object? sender, RoutedEventArgs e)
{
var next = DemoNav.StackDepth + 1;
var page = new ContentPage
{
Header = $"Page {next}",
Background = new SolidColorBrush(Color.Parse("#E8F5E9")),
Content = new StackPanel
{
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Spacing = 8,
Children =
{
new TextBlock
{
Text = $"Page {next}",
FontSize = 28,
FontWeight = FontWeight.Bold,
HorizontalAlignment = HorizontalAlignment.Center,
},
new TextBlock
{
Text = "New page: no CommandBar set",
FontSize = 14,
Opacity = 0.6,
HorizontalAlignment = HorizontalAlignment.Center,
}
}
},
HorizontalContentAlignment = HorizontalAlignment.Stretch,
VerticalContentAlignment = VerticalAlignment.Stretch
};
await DemoNav.PushAsync(page);
}
private async void OnPop(object? sender, RoutedEventArgs e) => await DemoNav.PopAsync();
private void ClearCommandBarFromActivePage()
{
if (DemoNav.CurrentPage is Page activePage)
{
NavigationPage.SetTopCommandBar(activePage, null);
NavigationPage.SetBottomCommandBar(activePage, null);
}
}
private void RebuildCommandBar()
{
if (DemoNav == null || DemoNav.CurrentPage is not Page activePage)
return;
if (_primaryItems.Count == 0 && _secondaryItems.Count == 0)
{
ClearCommandBarFromActivePage();
StatusText.Text = "No items added";
return;
}
NavigationPage.SetTopCommandBar(activePage, null);
NavigationPage.SetBottomCommandBar(activePage, null);
var commandBar = new CommandBar { IsDynamicOverflowEnabled = true };
foreach (var item in _primaryItems)
{
if (item is AppBarButton btn)
{
PathIcon? icon = null;
if (btn.Icon is PathIcon src)
icon = new PathIcon { Data = src.Data };
commandBar.PrimaryCommands.Add(new AppBarButton
{
Label = btn.Label,
Icon = icon,
IsCompact = btn.IsCompact,
});
}
else if (item is AppBarSeparator)
commandBar.PrimaryCommands.Add(new AppBarSeparator());
}
foreach (var item in _secondaryItems)
{
if (item is AppBarButton btn)
commandBar.SecondaryCommands.Add(new AppBarButton { Label = btn.Label });
}
if (_position == "Top")
NavigationPage.SetTopCommandBar(activePage, commandBar);
else
NavigationPage.SetBottomCommandBar(activePage, commandBar);
var primaryCount = _primaryItems.Count(i => i is AppBarButton);
var secondaryCount = _secondaryItems.Count;
StatusText.Text = $"{primaryCount} primary, {secondaryCount} secondary ({_position})";
}
}
}

106
samples/ControlCatalog/Pages/ContentPage/ContentPageCustomizationPage.xaml

@ -0,0 +1,106 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ContentPageCustomizationPage">
<DockPanel>
<ScrollViewer DockPanel.Dock="Right" Width="260">
<StackPanel Margin="12" Spacing="8">
<TextBlock Text="Configuration" FontWeight="SemiBold" FontSize="16"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock Text="Background" FontWeight="SemiBold" />
<ComboBox x:Name="BackgroundCombo"
SelectedIndex="0"
HorizontalAlignment="Stretch"
SelectionChanged="OnBackgroundChanged">
<ComboBoxItem Content="Default" />
<ComboBoxItem Content="Light Blue" />
<ComboBoxItem Content="Light Green" />
<ComboBoxItem Content="Light Purple" />
<ComboBoxItem Content="Warm White" />
</ComboBox>
<Separator />
<TextBlock Text="Horizontal Content Alignment" FontWeight="SemiBold" />
<ComboBox x:Name="HAlignCombo"
SelectedIndex="3"
HorizontalAlignment="Stretch"
SelectionChanged="OnHAlignChanged">
<ComboBoxItem Content="Left" />
<ComboBoxItem Content="Center" />
<ComboBoxItem Content="Right" />
<ComboBoxItem Content="Stretch" />
</ComboBox>
<TextBlock Text="Vertical Content Alignment" FontWeight="SemiBold" />
<ComboBox x:Name="VAlignCombo"
SelectedIndex="3"
HorizontalAlignment="Stretch"
SelectionChanged="OnVAlignChanged">
<ComboBoxItem Content="Top" />
<ComboBoxItem Content="Center" />
<ComboBoxItem Content="Bottom" />
<ComboBoxItem Content="Stretch" />
</ComboBox>
<Separator />
<TextBlock Text="Padding" FontWeight="SemiBold" />
<Slider x:Name="PaddingSlider"
Minimum="0"
Maximum="48"
Value="0"
TickFrequency="8"
IsSnapToTickEnabled="True"
ValueChanged="OnPaddingChanged" />
<TextBlock x:Name="PaddingLabel"
Text="0 px"
HorizontalAlignment="Center"
Opacity="0.7" />
<Separator />
<TextBlock Text="About" FontWeight="SemiBold" />
<TextBlock Text="• HorizontalContentAlignment / VerticalContentAlignment position the content inside the page area."
FontSize="12" Opacity="0.7" TextWrapping="Wrap" />
<TextBlock Text="• Padding adds space between the page border and its content presenter."
FontSize="12" Opacity="0.7" TextWrapping="Wrap" />
<TextBlock Text="• Background accepts any Avalonia brush, including gradients."
FontSize="12" Opacity="0.7" TextWrapping="Wrap" />
</StackPanel>
</ScrollViewer>
<Border DockPanel.Dock="Right" Width="1" Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<Border Margin="12"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1"
CornerRadius="6"
ClipToBounds="True">
<ContentPage x:Name="SamplePage"
Header="Customization"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch">
<Border Background="#E0E0E0"
CornerRadius="8"
Padding="24"
Width="180"
Height="100">
<StackPanel Spacing="4"
HorizontalAlignment="Center"
VerticalAlignment="Center">
<TextBlock Text="Content Area"
FontSize="16"
FontWeight="SemiBold"
HorizontalAlignment="Center" />
<TextBlock Text="Adjust alignment and padding"
FontSize="12"
Opacity="0.6"
TextWrapping="Wrap"
TextAlignment="Center" />
</StackPanel>
</Border>
</ContentPage>
</Border>
</DockPanel>
</UserControl>

64
samples/ControlCatalog/Pages/ContentPage/ContentPageCustomizationPage.xaml.cs

@ -0,0 +1,64 @@
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
namespace ControlCatalog.Pages
{
public partial class ContentPageCustomizationPage : UserControl
{
public ContentPageCustomizationPage()
{
InitializeComponent();
}
private void OnBackgroundChanged(object? sender, SelectionChangedEventArgs e)
{
if (SamplePage == null)
return;
SamplePage.Background = BackgroundCombo.SelectedIndex switch
{
1 => new SolidColorBrush(Color.Parse("#E3F2FD")),
2 => new SolidColorBrush(Color.Parse("#E8F5E9")),
3 => new SolidColorBrush(Color.Parse("#F3E5F5")),
4 => new SolidColorBrush(Color.Parse("#FFF8E1")),
_ => null
};
}
private void OnHAlignChanged(object? sender, SelectionChangedEventArgs e)
{
if (SamplePage == null)
return;
SamplePage.HorizontalContentAlignment = HAlignCombo.SelectedIndex switch
{
0 => HorizontalAlignment.Left,
1 => HorizontalAlignment.Center,
2 => HorizontalAlignment.Right,
_ => HorizontalAlignment.Stretch
};
}
private void OnVAlignChanged(object? sender, SelectionChangedEventArgs e)
{
if (SamplePage == null)
return;
SamplePage.VerticalContentAlignment = VAlignCombo.SelectedIndex switch
{
0 => VerticalAlignment.Top,
1 => VerticalAlignment.Center,
2 => VerticalAlignment.Bottom,
_ => VerticalAlignment.Stretch
};
}
private void OnPaddingChanged(object? sender, Avalonia.Controls.Primitives.RangeBaseValueChangedEventArgs e)
{
if (SamplePage == null)
return;
var padding = (int)PaddingSlider.Value;
SamplePage.Padding = new Avalonia.Thickness(padding);
PaddingLabel.Text = $"{padding} px";
}
}
}

66
samples/ControlCatalog/Pages/ContentPage/ContentPageEventsPage.xaml

@ -0,0 +1,66 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ContentPageEventsPage">
<DockPanel>
<ScrollViewer DockPanel.Dock="Right" Width="260">
<StackPanel Margin="12" Spacing="8">
<TextBlock Text="Configuration" FontSize="16" FontWeight="SemiBold"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock Text="Navigation" FontSize="13" FontWeight="SemiBold" />
<StackPanel Spacing="6">
<Button Content="Push Page" HorizontalAlignment="Stretch" Click="OnPush" />
<Button Content="Pop Page" HorizontalAlignment="Stretch" Click="OnPop" />
</StackPanel>
<Separator />
<TextBlock Text="Navigating (cancel)" FontSize="13" FontWeight="SemiBold" />
<CheckBox x:Name="BlockNavCheck" Content="Block navigation" />
<TextBlock Text="When checked, the Navigating handler sets Cancel=true and prevents the navigation from proceeding."
TextWrapping="Wrap" FontSize="11" Opacity="0.6" />
<Separator />
<DockPanel>
<TextBlock DockPanel.Dock="Left" Text="Event Log" FontSize="13" FontWeight="SemiBold"
VerticalAlignment="Center" />
<Button DockPanel.Dock="Right" Content="Clear" Click="OnClearLog"
HorizontalAlignment="Right" Padding="8,2" FontSize="11" />
</DockPanel>
<Border Background="{DynamicResource SystemControlBackgroundBaseLowBrush}"
CornerRadius="4" Padding="8" MinHeight="120" MaxHeight="300">
<ScrollViewer x:Name="LogScrollViewer">
<ItemsControl x:Name="EventLogItems">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}"
FontFamily="Cascadia Code,Consolas,Menlo,monospace"
FontSize="11" Margin="0,1" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</Border>
<Separator />
<TextBlock Text="About" FontSize="13" FontWeight="SemiBold" />
<TextBlock TextWrapping="Wrap" FontSize="12" Opacity="0.7"
Text="ContentPage exposes lifecycle events directly on the page. NavigatedTo fires after the page arrives, providing the previous page and navigation type. NavigatedFrom fires after departure. Navigating fires before leaving and can cancel the navigation by setting Cancel=true." />
</StackPanel>
</ScrollViewer>
<Border DockPanel.Dock="Right" Width="1" Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<Border Margin="12"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1"
CornerRadius="8"
ClipToBounds="True">
<NavigationPage x:Name="DemoNav" />
</Border>
</DockPanel>
</UserControl>

103
samples/ControlCatalog/Pages/ContentPage/ContentPageEventsPage.xaml.cs

@ -0,0 +1,103 @@
using System.Collections.ObjectModel;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
namespace ControlCatalog.Pages
{
public partial class ContentPageEventsPage : UserControl
{
private int _pageCount;
private readonly ObservableCollection<string> _logItems = new();
public ContentPageEventsPage()
{
InitializeComponent();
Loaded += OnLoaded;
}
private async void OnLoaded(object? sender, RoutedEventArgs e)
{
EventLogItems.ItemsSource = _logItems;
var root = MakePage("Root", "Navigate to see lifecycle events in the log below.");
SubscribeEvents(root);
await DemoNav.PushAsync(root);
}
private void SubscribeEvents(ContentPage page)
{
page.NavigatedTo += (_, e) => AddLog($"[{page.Header}] NavigatedTo (type: {e.NavigationType})");
page.NavigatedFrom += (_, _) => AddLog($"[{page.Header}] NavigatedFrom");
page.Navigating += async args =>
{
if (BlockNavCheck.IsChecked == true)
{
args.Cancel = true;
AddLog($"[{page.Header}] Navigating — BLOCKED");
await Task.Delay(600);
args.Cancel = false;
AddLog($"[{page.Header}] Navigating — unblocked");
}
else
{
AddLog($"[{page.Header}] Navigating");
}
};
}
private async void OnPush(object? sender, RoutedEventArgs e)
{
_pageCount++;
var page = MakePage($"Page {_pageCount}", "Navigate back to see Navigating/NavigatedFrom.");
SubscribeEvents(page);
await DemoNav.PushAsync(page);
}
private async void OnPop(object? sender, RoutedEventArgs e) => await DemoNav.PopAsync();
private void OnClearLog(object? sender, RoutedEventArgs e) => _logItems.Clear();
private void AddLog(string message)
{
_logItems.Add(message);
LogScrollViewer.ScrollToEnd();
}
private static ContentPage MakePage(string header, string body) =>
new ContentPage
{
Header = header,
Content = new StackPanel
{
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Spacing = 8,
Children =
{
new TextBlock
{
Text = header,
FontSize = 24,
FontWeight = FontWeight.Bold,
HorizontalAlignment = HorizontalAlignment.Center,
},
new TextBlock
{
Text = body,
FontSize = 13,
Opacity = 0.6,
TextWrapping = TextWrapping.Wrap,
TextAlignment = TextAlignment.Center,
HorizontalAlignment = HorizontalAlignment.Center,
MaxWidth = 260,
}
}
},
HorizontalContentAlignment = HorizontalAlignment.Stretch,
VerticalContentAlignment = VerticalAlignment.Stretch,
};
}
}

48
samples/ControlCatalog/Pages/ContentPage/ContentPageFirstLookPage.xaml

@ -0,0 +1,48 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ContentPageFirstLookPage">
<DockPanel>
<ScrollViewer DockPanel.Dock="Right" Width="260">
<StackPanel Margin="12" Spacing="8">
<TextBlock Text="Configuration" FontWeight="SemiBold" FontSize="16"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock Text="ContentPage is the fundamental page type. It hosts a single piece of content and integrates with NavigationPage, TabbedPage, and CarouselPage. The Header property sets the navigation bar title."
FontSize="12" Opacity="0.7" TextWrapping="Wrap" />
<Separator />
<TextBlock Text="Navigation" FontWeight="SemiBold" />
<Button Content="Push Page" HorizontalAlignment="Stretch" Click="OnPush" />
<Button Content="Pop Page" HorizontalAlignment="Stretch" Click="OnPop" />
<Button Content="Pop to Root" HorizontalAlignment="Stretch" Click="OnPopToRoot" />
<Separator />
<TextBlock Text="Key Properties" FontWeight="SemiBold" />
<TextBlock Text="• Header: nav bar title (string or Control)" FontSize="12" Opacity="0.8" TextWrapping="Wrap" />
<TextBlock Text="• Content: any Avalonia control" FontSize="12" Opacity="0.8" TextWrapping="Wrap" />
<TextBlock Text="• Background: page background brush" FontSize="12" Opacity="0.8" TextWrapping="Wrap" />
<TextBlock Text="• HorizontalContentAlignment" FontSize="12" Opacity="0.8" />
<TextBlock Text="• VerticalContentAlignment" FontSize="12" Opacity="0.8" />
<TextBlock Text="• AutomaticallyApplySafeAreaPadding" FontSize="12" Opacity="0.8" />
<Separator />
<TextBlock x:Name="StatusText"
Text="Depth: 1 | Current: Root Page"
FontSize="11"
Opacity="0.7" />
</StackPanel>
</ScrollViewer>
<Border DockPanel.Dock="Right" Width="1" Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<Border Margin="12"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1"
CornerRadius="6"
ClipToBounds="True">
<NavigationPage x:Name="DemoNav" />
</Border>
</DockPanel>
</UserControl>

93
samples/ControlCatalog/Pages/ContentPage/ContentPageFirstLookPage.xaml.cs

@ -0,0 +1,93 @@
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
namespace ControlCatalog.Pages
{
public partial class ContentPageFirstLookPage : UserControl
{
private static readonly Color[] PageColors =
[
Color.FromRgb(0xE3, 0xF2, 0xFD), // blue
Color.FromRgb(0xF3, 0xE5, 0xF5), // purple
Color.FromRgb(0xE8, 0xF5, 0xE9), // green
Color.FromRgb(0xFF, 0xF8, 0xE1), // amber
Color.FromRgb(0xFB, 0xE9, 0xE7), // deep orange
];
private int _pageCount;
public ContentPageFirstLookPage()
{
InitializeComponent();
Loaded += OnLoaded;
}
private async void OnLoaded(object? sender, RoutedEventArgs e)
{
await DemoNav.PushAsync(MakePage("Root Page", "ContentPage inside a NavigationPage.\nUse the options to navigate."));
UpdateStatus();
}
private async void OnPush(object? sender, RoutedEventArgs e)
{
_pageCount++;
await DemoNav.PushAsync(MakePage($"Page {_pageCount}", $"ContentPage #{_pageCount}.\nNavigate back using the back button."));
UpdateStatus();
}
private async void OnPop(object? sender, RoutedEventArgs e)
{
await DemoNav.PopAsync();
UpdateStatus();
}
private async void OnPopToRoot(object? sender, RoutedEventArgs e)
{
await DemoNav.PopToRootAsync();
_pageCount = 0;
UpdateStatus();
}
private void UpdateStatus()
{
StatusText.Text = $"Depth: {DemoNav.StackDepth} | Current: {DemoNav.CurrentPage?.Header}";
}
private ContentPage MakePage(string header, string body) =>
new ContentPage
{
Header = header,
Background = new SolidColorBrush(PageColors[_pageCount % PageColors.Length]),
Content = new StackPanel
{
HorizontalAlignment = HorizontalAlignment.Center,
VerticalAlignment = VerticalAlignment.Center,
Spacing = 10,
Children =
{
new TextBlock
{
Text = header,
FontSize = 20,
FontWeight = FontWeight.SemiBold,
HorizontalAlignment = HorizontalAlignment.Center
},
new TextBlock
{
Text = body,
FontSize = 13,
Opacity = 0.7,
TextWrapping = TextWrapping.Wrap,
TextAlignment = TextAlignment.Center,
MaxWidth = 260
}
}
},
HorizontalContentAlignment = HorizontalAlignment.Stretch,
VerticalContentAlignment = VerticalAlignment.Stretch
};
}
}

104
samples/ControlCatalog/Pages/ContentPage/ContentPagePerformancePage.xaml

@ -0,0 +1,104 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ContentPagePerformancePage">
<Grid ColumnDefinitions="*,300">
<!-- Left: NavigationPage + log -->
<DockPanel Grid.Column="0" Margin="16">
<TextBlock DockPanel.Dock="Top"
Text="Push pages of varying weight and observe how GC reclaims memory after pop."
FontSize="13" Opacity="0.6" Margin="0,0,0,12" />
<DockPanel DockPanel.Dock="Bottom" Margin="0,12,0,0" Height="140">
<DockPanel DockPanel.Dock="Top" Margin="0,0,0,4">
<TextBlock Text="Operation Log" FontSize="13" FontWeight="SemiBold"
VerticalAlignment="Center" />
<Button x:Name="ClearLogButton" Content="Clear" FontSize="11"
Padding="8,2" HorizontalAlignment="Right" Click="OnClearLog" />
</DockPanel>
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1" CornerRadius="4" ClipToBounds="True">
<ScrollViewer x:Name="LogScrollViewer">
<StackPanel x:Name="LogPanel" Spacing="0" />
</ScrollViewer>
</Border>
</DockPanel>
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1" CornerRadius="8" ClipToBounds="True">
<NavigationPage x:Name="NavPage">
<NavigationPage.Resources>
<SolidColorBrush x:Key="NavigationBarBackground" Color="Transparent" />
</NavigationPage.Resources>
</NavigationPage>
</Border>
</DockPanel>
<!-- Right: dashboard -->
<Border Grid.Column="1"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1,0,0,0"
Padding="16">
<ScrollViewer>
<StackPanel Spacing="12">
<TextBlock Text="Metrics" FontSize="16" FontWeight="SemiBold"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock x:Name="StackDepthText" Text="Stack Depth: 0" FontSize="13" />
<TextBlock x:Name="LiveInstancesText" Text="Live Page Instances: 0" FontSize="13" />
<TextBlock x:Name="TotalCreatedText" Text="Total Pages Created: 0" FontSize="13" />
<DockPanel>
<TextBlock x:Name="ManagedMemoryText" Text="Managed Heap: 0.0 MB" FontSize="13" />
<TextBlock x:Name="MemoryDeltaText" Text="" FontSize="13"
FontWeight="SemiBold" Margin="6,0,0,0" />
</DockPanel>
<Separator Margin="0,4" />
<TextBlock Text="Content Weight" FontSize="16" FontWeight="SemiBold"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<ComboBox x:Name="WeightCombo" HorizontalAlignment="Stretch" SelectedIndex="0">
<ComboBoxItem Content="Lightweight (~50 KB)" />
<ComboBoxItem Content="Moderate (~500 KB)" />
<ComboBoxItem Content="Heavy (~2 MB)" />
</ComboBox>
<TextBlock Text="Sets the memory allocated by each new page. Push pages, pop them, then Force GC to see the heap drop."
TextWrapping="Wrap" FontSize="11" Opacity="0.6" />
<Separator Margin="0,4" />
<TextBlock Text="Actions" FontSize="16" FontWeight="SemiBold"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<Button x:Name="PushButton" Content="Push Page" HorizontalAlignment="Stretch" Click="OnPush" />
<Button x:Name="Push5Button" Content="Push 5 Pages" HorizontalAlignment="Stretch" Click="OnPush5" />
<Button x:Name="PopButton" Content="Pop Page" HorizontalAlignment="Stretch" Click="OnPop" />
<Button x:Name="PopToRootButton" Content="Pop to Root" HorizontalAlignment="Stretch" Click="OnPopToRoot" />
<Separator Margin="0,4" />
<Button x:Name="ForceGCButton" Content="Force GC + Refresh"
HorizontalAlignment="Stretch" Click="OnForceGC"
Background="{DynamicResource SystemControlHighlightAccentBrush}"
Foreground="White" />
<CheckBox x:Name="AutoRefreshCheck" Content="Auto-refresh (2s)"
FontSize="13" IsCheckedChanged="OnAutoRefreshChanged" />
<Separator Margin="0,4" />
<TextBlock Text="Stack" FontSize="16" FontWeight="SemiBold"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock Text="▲ Current (top)" FontSize="11" Opacity="0.45" Margin="0,-4,0,0" />
<StackPanel x:Name="StackVisPanel" Spacing="4" />
<TextBlock Text="▼ Root (bottom)" FontSize="11" Opacity="0.45" Margin="0,4,0,0" />
</StackPanel>
</ScrollViewer>
</Border>
</Grid>
</UserControl>

125
samples/ControlCatalog/Pages/ContentPage/ContentPagePerformancePage.xaml.cs

@ -0,0 +1,125 @@
using System;
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public partial class ContentPagePerformancePage : UserControl
{
private static readonly int[] AllocationSizes = [51_200, 512_000, 2_097_152];
private readonly NavigationPerformanceMonitorHelper _perf = new();
private int _pageCounter;
private readonly List<(Border Container, Border Badge, TextBlock IndexText,
TextBlock TitleText, TextBlock BadgeText)> _stackRowCache = new();
public ContentPagePerformancePage()
{
InitializeComponent();
}
protected override async void OnLoaded(RoutedEventArgs e)
{
base.OnLoaded(e);
NavPage.Pushed += OnStackChanged;
NavPage.Popped += OnStackChanged;
NavPage.PoppedToRoot += OnStackChanged;
_perf.InitHeap();
_pageCounter++;
await NavPage.PushAsync(BuildPage("Home", _pageCounter));
Log("Init", "Pushed root page");
}
protected override void OnUnloaded(RoutedEventArgs e)
{
base.OnUnloaded(e);
_perf.StopAutoRefresh();
}
private void OnStackChanged(object? sender, NavigationEventArgs e) => RefreshAll();
private async void OnPush(object? sender, RoutedEventArgs e)
{
_pageCounter++;
var page = BuildPage($"Page {_pageCounter}", _pageCounter);
await NavPage.PushAsync(page);
Log("Push", $"Pushed \"{page.Header}\"");
}
private async void OnPush5(object? sender, RoutedEventArgs e)
{
int first = _pageCounter + 1;
for (int i = 0; i < 5; i++)
{
_pageCounter++;
await NavPage.PushAsync(BuildPage($"Page {_pageCounter}", _pageCounter));
}
Log("Push ×5", $"Pushed pages {first}–{_pageCounter}");
}
private async void OnPop(object? sender, RoutedEventArgs e)
{
if (NavPage.StackDepth > 1)
{
var popped = NavPage.CurrentPage;
await NavPage.PopAsync();
Log("Pop", $"Popped \"{popped?.Header}\"");
}
}
private async void OnPopToRoot(object? sender, RoutedEventArgs e)
{
if (NavPage.StackDepth > 1)
{
int removed = NavPage.StackDepth - 1;
await NavPage.PopToRootAsync();
Log("PopToRoot", $"Removed {removed} page(s)");
}
}
private void OnForceGC(object? sender, RoutedEventArgs e)
{
_perf.ForceGC(RefreshAll);
Log("GC", "Forced full garbage collection");
}
private void OnClearLog(object? sender, RoutedEventArgs e) => LogPanel.Children.Clear();
private void OnAutoRefreshChanged(object? sender, RoutedEventArgs e) =>
_perf.OnAutoRefreshChanged(AutoRefreshCheck, RefreshAll);
private void RefreshAll()
{
StackDepthText.Text = $"Stack Depth: {NavPage.StackDepth}";
LiveInstancesText.Text = $"Live Page Instances: {_perf.CountLiveInstances()}";
TotalCreatedText.Text = $"Total Pages Created: {_perf.TotalCreated}";
_perf.UpdateHeapDelta(ManagedMemoryText, MemoryDeltaText);
NavigationPerformanceMonitorHelper.RefreshStackPanel(
StackVisPanel, _stackRowCache,
NavPage.NavigationStack, NavPage.CurrentPage);
}
private void Log(string action, string detail) =>
_perf.LogOperation(action, detail, LogPanel, LogScrollViewer,
$"depth {NavPage.StackDepth}");
private ContentPage BuildPage(string title, int index)
{
var weightIndex = WeightCombo.SelectedIndex >= 0 ? WeightCombo.SelectedIndex : 0;
var allocBytes = AllocationSizes[Math.Clamp(weightIndex, 0, AllocationSizes.Length - 1)];
var weightLabel = weightIndex switch { 1 => "~500 KB", 2 => "~2 MB", _ => "~50 KB" };
var page = NavigationDemoHelper.MakePage(title, $"Stack position #{index} · Weight: {weightLabel}\n\n" +
"Pop this page and force GC to see the heap drop by the weight shown above. " +
"Live Instances decreases once the GC finalizes the page.", index);
page.Tag = new byte[allocBytes];
_perf.TrackPage(page);
return page;
}
}
}

171
samples/ControlCatalog/Pages/ContentPage/ContentPageSafeAreaPage.xaml

@ -0,0 +1,171 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ContentPageSafeAreaPage">
<DockPanel>
<ScrollViewer DockPanel.Dock="Right" Width="260">
<StackPanel Margin="12" Spacing="8">
<TextBlock Text="Configuration" FontWeight="SemiBold" FontSize="16"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<CheckBox x:Name="AutoApplyCheck"
Content="AutomaticallyApplySafeAreaPadding"
IsChecked="True"
IsCheckedChanged="OnAutoApplyChanged" />
<TextBlock TextWrapping="Wrap" FontSize="12" Opacity="0.7">
When enabled, ContentPage absorbs SafeAreaPadding into the content
presenter, keeping page content in the visible area. Set to false
to manage insets manually.
</TextBlock>
<Separator />
<TextBlock Text="Simulated Insets" FontWeight="SemiBold" FontSize="13" />
<TextBlock Text="Top" FontSize="12" Opacity="0.8" />
<DockPanel>
<TextBlock x:Name="TopValue"
DockPanel.Dock="Right"
Text="44"
FontSize="12" Opacity="0.6"
VerticalAlignment="Center"
Width="28" TextAlignment="Right" />
<Slider x:Name="TopSlider"
Minimum="0" Maximum="80" Value="44"
TickFrequency="1"
ValueChanged="OnInsetChanged" />
</DockPanel>
<TextBlock Text="Bottom" FontSize="12" Opacity="0.8" />
<DockPanel>
<TextBlock x:Name="BottomValue"
DockPanel.Dock="Right"
Text="34"
FontSize="12" Opacity="0.6"
VerticalAlignment="Center"
Width="28" TextAlignment="Right" />
<Slider x:Name="BottomSlider"
Minimum="0" Maximum="60" Value="34"
TickFrequency="1"
ValueChanged="OnInsetChanged" />
</DockPanel>
<TextBlock Text="Left" FontSize="12" Opacity="0.8" />
<DockPanel>
<TextBlock x:Name="LeftValue"
DockPanel.Dock="Right"
Text="0"
FontSize="12" Opacity="0.6"
VerticalAlignment="Center"
Width="28" TextAlignment="Right" />
<Slider x:Name="LeftSlider"
Minimum="0" Maximum="60" Value="0"
TickFrequency="1"
ValueChanged="OnInsetChanged" />
</DockPanel>
<TextBlock Text="Right" FontSize="12" Opacity="0.8" />
<DockPanel>
<TextBlock x:Name="RightValue"
DockPanel.Dock="Right"
Text="0"
FontSize="12" Opacity="0.6"
VerticalAlignment="Center"
Width="28" TextAlignment="Right" />
<Slider x:Name="RightSlider"
Minimum="0" Maximum="60" Value="0"
TickFrequency="1"
ValueChanged="OnInsetChanged" />
</DockPanel>
<Separator />
<TextBlock Text="About" FontWeight="SemiBold" FontSize="13" />
<StackPanel Spacing="4">
<TextBlock TextWrapping="Wrap" FontSize="12" Opacity="0.8">
• On real devices SafeAreaPadding is set by PageNavigationHost
from IInsetsManager (iOS, Android).
</TextBlock>
<TextBlock TextWrapping="Wrap" FontSize="12" Opacity="0.8">
• ContentPage.AutomaticallyApplySafeAreaPadding defaults to true.
Set to false when your layout manages insets manually.
</TextBlock>
<TextBlock TextWrapping="Wrap" FontSize="12" Opacity="0.8">
• Child pages inside a ContentPage receive the remaining insets
via SafeAreaPadding propagation.
</TextBlock>
</StackPanel>
</StackPanel>
</ScrollViewer>
<Border DockPanel.Dock="Right" Width="1" Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<Border Margin="12"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1"
CornerRadius="12"
ClipToBounds="True"
MaxWidth="320"
HorizontalAlignment="Center">
<Panel>
<ContentPage x:Name="SamplePage"
Header="Safe Area Demo"
HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch">
<Grid RowDefinitions="Auto,*">
<Border Grid.Row="0"
Background="{DynamicResource SystemControlBackgroundChromeMediumBrush}"
Padding="12,8">
<StackPanel Spacing="2">
<TextBlock x:Name="SafeAreaInfo"
FontSize="11" FontFamily="Consolas,Courier New,monospace"
Opacity="0.8" />
<TextBlock x:Name="AutoApplyInfo"
FontSize="11" Opacity="0.8" />
</StackPanel>
</Border>
<StackPanel Grid.Row="1"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Spacing="8">
<TextBlock Text="Page Content"
FontSize="18" FontWeight="Bold"
HorizontalAlignment="Center" />
<TextBlock TextWrapping="Wrap"
TextAlignment="Center"
FontSize="12" Opacity="0.6">
When enabled, the content presenter absorbs the inset,
keeping content visible above the status bar and home indicator.
</TextBlock>
</StackPanel>
</Grid>
</ContentPage>
<!-- Pink overlays to simulate device insets -->
<Border x:Name="TopInsetIndicator"
Background="#FF5252" Opacity="0.35"
VerticalAlignment="Top">
<TextBlock Text="Safe Area (simulated)"
FontSize="10" Opacity="0.9"
HorizontalAlignment="Center" VerticalAlignment="Center"
Margin="0,4" />
</Border>
<Border x:Name="BottomInsetIndicator"
Background="#FF5252" Opacity="0.35"
VerticalAlignment="Bottom">
<TextBlock Text="Safe Area (simulated)"
FontSize="10" Opacity="0.9"
HorizontalAlignment="Center" VerticalAlignment="Center"
Margin="0,4" />
</Border>
<Border x:Name="LeftInsetIndicator"
Background="#FF5252" Opacity="0.35"
HorizontalAlignment="Left" />
<Border x:Name="RightInsetIndicator"
Background="#FF5252" Opacity="0.35"
HorizontalAlignment="Right" />
</Panel>
</Border>
</DockPanel>
</UserControl>

70
samples/ControlCatalog/Pages/ContentPage/ContentPageSafeAreaPage.xaml.cs

@ -0,0 +1,70 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public partial class ContentPageSafeAreaPage : UserControl
{
public ContentPageSafeAreaPage()
{
InitializeComponent();
Loaded += OnLoaded;
}
private void OnLoaded(object? sender, RoutedEventArgs e)
{
SyncIndicators();
}
private void OnAutoApplyChanged(object? sender, RoutedEventArgs e)
{
if (SamplePage == null)
return;
SamplePage.AutomaticallyApplySafeAreaPadding = AutoApplyCheck.IsChecked == true;
SyncIndicators();
}
private void OnInsetChanged(object? sender, RangeBaseValueChangedEventArgs e)
{
SyncIndicators();
}
private void SyncIndicators()
{
if (SamplePage == null)
return;
var top = (int)TopSlider.Value;
var bottom = (int)BottomSlider.Value;
var left = (int)LeftSlider.Value;
var right = (int)RightSlider.Value;
TopValue.Text = $"{top}";
BottomValue.Text = $"{bottom}";
LeftValue.Text = $"{left}";
RightValue.Text = $"{right}";
TopInsetIndicator.IsVisible = top > 0;
TopInsetIndicator.Height = top;
BottomInsetIndicator.IsVisible = bottom > 0;
BottomInsetIndicator.Height = bottom;
LeftInsetIndicator.IsVisible = left > 0;
LeftInsetIndicator.Width = left;
LeftInsetIndicator.Margin = new Thickness(0, top, 0, bottom);
RightInsetIndicator.IsVisible = right > 0;
RightInsetIndicator.Width = right;
RightInsetIndicator.Margin = new Thickness(0, top, 0, bottom);
var insets = new Thickness(left, top, right, bottom);
SamplePage.SafeAreaPadding = insets;
SafeAreaInfo.Text = $"SafeAreaPadding: L={left} T={top} R={right} B={bottom}";
AutoApplyInfo.Text = $"AutoApply: {SamplePage.AutomaticallyApplySafeAreaPadding} → " +
(SamplePage.AutomaticallyApplySafeAreaPadding ? "insets absorbed by presenter" : "insets ignored");
}
}
}

11
samples/ControlCatalog/Pages/DrawerDemoPage.xaml

@ -0,0 +1,11 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.DrawerDemoPage">
<NavigationPage x:Name="SampleNav">
<NavigationPage.Styles>
<Style Selector="NavigationPage#SampleNav /template/ Border#PART_NavigationBar">
<Setter Property="Background" Value="Transparent" />
</Style>
</NavigationPage.Styles>
</NavigationPage>
</UserControl>

73
samples/ControlCatalog/Pages/DrawerDemoPage.xaml.cs

@ -0,0 +1,73 @@
using System;
using Avalonia.Controls;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public partial class DrawerDemoPage : UserControl
{
private static readonly (string Group, string Title, string Description, Func<UserControl> Factory)[] Demos =
{
// Overview
("Overview", "First Look", "Basic DrawerPage with a navigation drawer, menu items, and detail content.",
() => new DrawerPageFirstLookPage()),
// Features
("Features", "Navigation",
"Master-detail pattern: select a drawer menu item to navigate the detail via a NavigationPage with hamburger-to-back-button transition.",
() => new DrawerPageNavigationPage()),
("Features", "Compact Rail",
"CompactOverlay and CompactInline layout modes: a narrow icon rail is always visible and expands on open. Adjust rail width and open pane width.",
() => new DrawerPageCompactPage()),
("Features", "Events",
"Opened, Closing, and Closed drawer events plus NavigatedTo and NavigatedFrom page lifecycle events. Enable 'Cancel next close' to prevent the drawer from closing.",
() => new DrawerPageEventsPage()),
("Features", "RTL Layout", "Right-to-left layout: drawer opens from the right edge with mirrored gestures.",
() => new DrawerPageRtlPage()),
// Appearance
("Appearance", "Customization",
"Customize drawer behavior, layout mode, length, colors, header and footer.",
() => new DrawerPageCustomizationPage()),
("Appearance", "Custom Flyout",
"Dark overlay menu with staggered item animations and CrossFade page transitions on the detail NavigationPage.",
() => new DrawerPageCustomFlyoutPage()),
("Appearance", "Transitions",
"Configure the detail NavigationPage transition. Choose CrossFade, PageSlide, or CompositePageTransition to animate detail page changes.",
() => new DrawerPageTransitionsPage()),
// Performance
("Performance", "Performance Monitor",
"Track detail page swaps, live page instances, and managed heap size. Observe how GC reclaims memory after swapping pages.",
() => new DrawerPagePerformancePage()),
// Showcases
("Showcases", "AvaloniaFlix",
"Streaming app with DrawerPage wrapping NavigationPage. Hamburger auto-injected at root, back arrow on detail, and dark themed flyout menu.",
() => new AvaloniaFlixAppPage()),
("Showcases", "L'Avenir Restaurant",
"Restaurant app with DrawerPage as the root container, NavigationPage for detail navigation, and TabbedPage bottom tabs for Menu, Reservations, and Profile.",
() => new LAvenirAppPage()),
("Showcases", "EcoTracker",
"Sustainability tracker with CompactInline drawer, eco leaf hamburger icon, crossfade compact/open menu transitions, and green-themed Home, Stats, Habits, and Community pages.",
() => new EcoTrackerAppPage()),
("Showcases", "ModernApp",
"Travel social app using a top-placement DrawerPage. A slide-down nav pane gives access to Discover, My Trips, Profile, and Settings. Features destination cards, story circles, an experience feed, a stats profile, and a travel gallery.",
() => new ModernAppPage()),
("Showcases", "Controls Gallery App",
"Controls gallery app using DrawerPage CompactInline mode. Dark Fluent palette, accent pill selection indicator, search box that fades in when open, expandable category groups, and Settings pinned to the footer.",
() => new ControlsGalleryAppPage()),
};
public DrawerDemoPage()
{
InitializeComponent();
Loaded += OnLoaded;
}
private async void OnLoaded(object? sender, RoutedEventArgs e)
{
await SampleNav.PushAsync(NavigationDemoHelper.CreateGalleryHomePage(SampleNav, Demos), null);
}
}
}

400
samples/ControlCatalog/Pages/DrawerPage/ControlsGalleryAppPage.xaml

@ -0,0 +1,400 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.ControlsGalleryAppPage">
<UserControl.Resources>
<SolidColorBrush x:Key="NavPaneBg" Color="#202020"/>
<SolidColorBrush x:Key="NavContentBg" Color="#141414"/>
<SolidColorBrush x:Key="NavItemHover" Color="#1AFFFFFF"/>
<SolidColorBrush x:Key="NavItemSelected" Color="#0FFFFFFF"/>
<SolidColorBrush x:Key="NavAccent" Color="#60CDFF"/>
<SolidColorBrush x:Key="NavText" Color="#FFFFFF"/>
<SolidColorBrush x:Key="NavTextSecondary" Color="#C8FFFFFF"/>
<SolidColorBrush x:Key="NavBorder" Color="#2EFFFFFF"/>
<SolidColorBrush x:Key="NavSearchBg" Color="#0BFFFFFF"/>
</UserControl.Resources>
<DockPanel>
<ScrollViewer x:Name="InfoPanel" DockPanel.Dock="Right" Width="280">
<StackPanel Margin="16" Spacing="12">
<TextBlock Text="Controls Gallery App" FontSize="15" FontWeight="SemiBold"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock TextWrapping="Wrap" FontSize="12" Opacity="0.7"
Text="Controls gallery app built with DrawerPage CompactInline mode. Dark Fluent palette, accent pill indicator, search box that fades in when open, expandable groups, and Settings pinned to the footer." />
<Separator />
<TextBlock Text="Layout" FontSize="13" FontWeight="SemiBold" />
<StackPanel Spacing="4">
<TextBlock FontSize="12" TextWrapping="Wrap" Text="• CompactInline — icon rail always visible" />
<TextBlock FontSize="12" TextWrapping="Wrap" Text="• CompactDrawerLength = 48 (icon only)" />
<TextBlock FontSize="12" TextWrapping="Wrap" Text="• DrawerLength = 280 (icon + label)" />
<TextBlock FontSize="12" TextWrapping="Wrap" Text="• Left accent pill on selected item" />
<TextBlock FontSize="12" TextWrapping="Wrap" Text="• Search box fades in when open" />
<TextBlock FontSize="12" TextWrapping="Wrap" Text="• Settings pinned to drawer footer" />
</StackPanel>
<Separator />
<TextBlock Text="Key Code" FontSize="13" FontWeight="SemiBold" />
<Border Background="{DynamicResource SystemControlBackgroundBaseLowBrush}"
CornerRadius="4" Padding="8">
<TextBlock FontFamily="Cascadia Code,Consolas,Menlo,monospace"
FontSize="10" TextWrapping="Wrap"
Text="&lt;DrawerPage&#xA; DrawerLayoutBehavior=&quot;CompactInline&quot;&#xA; CompactDrawerLength=&quot;48&quot;&#xA; DrawerLength=&quot;280&quot;&#xA; DrawerBackground=&quot;#202020&quot;&gt;" />
</Border>
</StackPanel>
</ScrollViewer>
<Border BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1" CornerRadius="8" ClipToBounds="True">
<DrawerPage x:Name="NavDrawer"
DrawerLayoutBehavior="CompactInline"
CompactDrawerLength="48"
DrawerLength="280"
Background="{StaticResource NavContentBg}"
DrawerBackground="{StaticResource NavPaneBg}">
<!-- ── Drawer header: hamburger + app title ── -->
<DrawerPage.DrawerHeader>
<StackPanel Background="{StaticResource NavPaneBg}">
<!-- Hamburger row -->
<Button x:Name="BtnHamburger" Click="OnHamburgerClick"
Width="48" Height="40" Padding="0" Margin="0,8,0,0"
HorizontalAlignment="Left"
Background="Transparent" BorderThickness="0"
ToolTip.Tip="Navigation menu"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center">
<PathIcon Width="16" Height="16"
Foreground="{StaticResource NavText}"
Data="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" />
</Button>
<!-- Search box — fades in when drawer is open -->
<Border Margin="8,8,8,4"
Background="{StaticResource NavSearchBg}"
BorderBrush="{StaticResource NavBorder}"
BorderThickness="1" CornerRadius="4" Height="32"
Classes="openFade"
Classes.drawerOpen="{Binding #NavDrawer.IsOpen}">
<Grid ColumnDefinitions="Auto,*" Margin="8,0">
<PathIcon Grid.Column="0" Width="12" Height="12"
Foreground="{StaticResource NavTextSecondary}"
Data="M15.5 14h-.79l-.28-.27A6.471 6.471 0 0 0 16 9.5 6.5 6.5 0 1 0 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z" />
<TextBox x:Name="SearchBox" Grid.Column="1"
PlaceholderText="Search"
Background="Transparent" BorderThickness="0"
Padding="6,0,0,0"
Foreground="{StaticResource NavText}"
PlaceholderForeground="{StaticResource NavTextSecondary}"
FontSize="12"
VerticalAlignment="Center"
VerticalContentAlignment="Center"
TextChanged="OnSearchTextChanged" />
</Grid>
</Border>
</StackPanel>
</DrawerPage.DrawerHeader>
<!-- ── Nav items ── -->
<DrawerPage.Drawer>
<ContentPage Background="Transparent">
<StackPanel Margin="0,4,0,0" Spacing="2">
<!-- What's New (selected) -->
<Button x:Name="BtnWhatsNew" Tag="WhatsNew"
Classes="navItem navItemSelected"
Click="OnNavItemClick" ToolTip.Tip="What's New">
<Panel>
<Border Classes="navPill" Width="3" Height="16" CornerRadius="2"
Background="#60CDFF" HorizontalAlignment="Left" VerticalAlignment="Center"
Margin="4,0,0,0" />
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<PathIcon Width="16" Height="16" Margin="16,0,0,0"
Foreground="{StaticResource NavText}"
Data="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 14.5v-9l6 4.5-6 4.5z" />
<TextBlock Text="What's New" FontSize="13" Margin="12,0,0,0"
Foreground="{StaticResource NavText}"
VerticalAlignment="Center"
Classes="openFade"
Classes.drawerOpen="{Binding #NavDrawer.IsOpen}" />
</StackPanel>
</Panel>
</Button>
<!-- All Controls -->
<Button x:Name="BtnAllControls" Tag="AllControls"
Classes="navItem"
Click="OnNavItemClick" ToolTip.Tip="All Controls">
<Panel>
<Border Classes="navPill" Width="3" Height="16" CornerRadius="2"
Background="#60CDFF" HorizontalAlignment="Left" VerticalAlignment="Center"
Margin="4,0,0,0" />
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<PathIcon Width="16" Height="16" Margin="16,0,0,0"
Foreground="{StaticResource NavText}"
Data="M3 3h8v8H3V3m10 0h8v8h-8V3M3 13h8v8H3v-8m10 0h8v8h-8v-8" />
<TextBlock Text="All Controls" FontSize="13" Margin="12,0,0,0"
Foreground="{StaticResource NavText}"
VerticalAlignment="Center"
Classes="openFade"
Classes.drawerOpen="{Binding #NavDrawer.IsOpen}" />
</StackPanel>
</Panel>
</Button>
<!-- Separator -->
<Border Height="1" Background="{StaticResource NavBorder}" Margin="8,4" />
<!-- Basic Input (expandable) -->
<Button x:Name="BtnBasicInput" Tag="BasicInput"
Classes="navItem"
Click="OnNavItemClick" ToolTip.Tip="Basic Input">
<Panel>
<Border Classes="navPill" Width="3" Height="16" CornerRadius="2"
Background="#60CDFF" HorizontalAlignment="Left" VerticalAlignment="Center"
Margin="4,0,0,0" />
<Grid ColumnDefinitions="Auto,*" VerticalAlignment="Center">
<PathIcon Grid.Column="0" Width="16" Height="16" Margin="16,0,0,0"
Foreground="{StaticResource NavText}"
Data="M9 11H7v2h2v-2zm4 0h-2v2h2v-2zm4 0h-2v2h2v-2zm2-7h-1V2h-2v2H8V2H6v2H5c-1.11 0-1.99.9-1.99 2L3 20c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V6c0-1.1-.9-2-2-2zm0 16H5V9h14v11z" />
<TextBlock Grid.Column="1" Text="Basic Input" FontSize="13" Margin="12,0,0,0"
Foreground="{StaticResource NavText}"
VerticalAlignment="Center"
Classes="openFade"
Classes.drawerOpen="{Binding #NavDrawer.IsOpen}" />
</Grid>
</Panel>
</Button>
<!-- Collections -->
<Button x:Name="BtnCollections" Tag="Collections"
Classes="navItem"
Click="OnNavItemClick" ToolTip.Tip="Collections">
<Panel>
<Border Classes="navPill" Width="3" Height="16" CornerRadius="2"
Background="#60CDFF" HorizontalAlignment="Left" VerticalAlignment="Center"
Margin="4,0,0,0" />
<Grid ColumnDefinitions="Auto,*" VerticalAlignment="Center">
<PathIcon Grid.Column="0" Width="16" Height="16" Margin="16,0,0,0"
Foreground="{StaticResource NavText}"
Data="M4 6H2v14c0 1.1.9 2 2 2h14v-2H4V6zm16-4H8c-1.1 0-2 .9-2 2v12c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm-1 9H9V9h10v2zm-4 4H9v-2h6v2zm4-8H9V5h10v2z" />
<TextBlock Grid.Column="1" Text="Collections" FontSize="13" Margin="12,0,0,0"
Foreground="{StaticResource NavText}"
VerticalAlignment="Center"
Classes="openFade"
Classes.drawerOpen="{Binding #NavDrawer.IsOpen}" />
</Grid>
</Panel>
</Button>
<!-- Media -->
<Button x:Name="BtnMedia" Tag="Media"
Classes="navItem"
Click="OnNavItemClick" ToolTip.Tip="Media">
<Panel>
<Border Classes="navPill" Width="3" Height="16" CornerRadius="2"
Background="#60CDFF" HorizontalAlignment="Left" VerticalAlignment="Center"
Margin="4,0,0,0" />
<Grid ColumnDefinitions="Auto,*" VerticalAlignment="Center">
<PathIcon Grid.Column="0" Width="16" Height="16" Margin="16,0,0,0"
Foreground="{StaticResource NavText}"
Data="M21 3H3C2 3 1 4 1 5v14c0 1.1.9 2 2 2h18c1.1 0 2-.9 2-2V5c0-1-1-2-2-2zm0 16H3V5h18v14zM8 15c0 1.66 1.34 3 3 3s3-1.34 3-3V9h3V7h-5v8c0 .55-.45 1-1 1s-1-.45-1-1V7H8v8z" />
<TextBlock Grid.Column="1" Text="Media" FontSize="13" Margin="12,0,0,0"
Foreground="{StaticResource NavText}"
VerticalAlignment="Center"
Classes="openFade"
Classes.drawerOpen="{Binding #NavDrawer.IsOpen}" />
</Grid>
</Panel>
</Button>
<!-- Menus and Toolbars -->
<Button x:Name="BtnMenus" Tag="Menus"
Classes="navItem"
Click="OnNavItemClick" ToolTip.Tip="Menus and Toolbars">
<Panel>
<Border Classes="navPill" Width="3" Height="16" CornerRadius="2"
Background="#60CDFF" HorizontalAlignment="Left" VerticalAlignment="Center"
Margin="4,0,0,0" />
<Grid ColumnDefinitions="Auto,*" VerticalAlignment="Center">
<PathIcon Grid.Column="0" Width="16" Height="16" Margin="16,0,0,0"
Foreground="{StaticResource NavText}"
Data="M3 18h18v-2H3v2zm0-5h18v-2H3v2zm0-7v2h18V6H3z" />
<TextBlock Grid.Column="1" Text="Menus and Toolbars" FontSize="13" Margin="12,0,0,0"
Foreground="{StaticResource NavText}"
VerticalAlignment="Center"
Classes="openFade"
Classes.drawerOpen="{Binding #NavDrawer.IsOpen}" />
</Grid>
</Panel>
</Button>
<!-- Navigation -->
<Button x:Name="BtnNavigation" Tag="Navigation"
Classes="navItem"
Click="OnNavItemClick" ToolTip.Tip="Navigation">
<Panel>
<Border Classes="navPill" Width="3" Height="16" CornerRadius="2"
Background="#60CDFF" HorizontalAlignment="Left" VerticalAlignment="Center"
Margin="4,0,0,0" />
<Grid ColumnDefinitions="Auto,*" VerticalAlignment="Center">
<PathIcon Grid.Column="0" Width="16" Height="16" Margin="16,0,0,0"
Foreground="{StaticResource NavText}"
Data="M12 2L4.5 20.29l.71.71L12 18l6.79 3 .71-.71z" />
<TextBlock Grid.Column="1" Text="Navigation" FontSize="13" Margin="12,0,0,0"
Foreground="{StaticResource NavText}"
VerticalAlignment="Center"
Classes="openFade"
Classes.drawerOpen="{Binding #NavDrawer.IsOpen}" />
</Grid>
</Panel>
</Button>
<!-- Text -->
<Button x:Name="BtnText" Tag="Text"
Classes="navItem"
Click="OnNavItemClick" ToolTip.Tip="Text">
<Panel>
<Border Classes="navPill" Width="3" Height="16" CornerRadius="2"
Background="#60CDFF" HorizontalAlignment="Left" VerticalAlignment="Center"
Margin="4,0,0,0" />
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<PathIcon Width="16" Height="16" Margin="16,0,0,0"
Foreground="{StaticResource NavText}"
Data="M5 17v2h14v-2H5zm4.5-4.2h5l.9 2.2h2.1L12.75 4h-1.5L6.5 15h2.1l.9-2.2zM12 5.98L13.87 11h-3.74L12 5.98z" />
<TextBlock Text="Text" FontSize="13" Margin="12,0,0,0"
Foreground="{StaticResource NavText}"
VerticalAlignment="Center"
Classes="openFade"
Classes.drawerOpen="{Binding #NavDrawer.IsOpen}" />
</StackPanel>
</Panel>
</Button>
</StackPanel>
</ContentPage>
</DrawerPage.Drawer>
<!-- ── Settings pinned to footer ── -->
<DrawerPage.DrawerFooter>
<StackPanel Background="{StaticResource NavPaneBg}" Margin="0,0,0,4">
<Border Height="1" Background="{StaticResource NavBorder}" Margin="4,0" />
<Button x:Name="BtnSettings" Tag="Settings"
Classes="navItem"
Click="OnNavItemClick" ToolTip.Tip="Settings"
Margin="0,4,0,4">
<Panel>
<Border Classes="navPill" Width="3" Height="16" CornerRadius="2"
Background="#60CDFF" HorizontalAlignment="Left" VerticalAlignment="Center"
Margin="4,0,0,0" />
<StackPanel Orientation="Horizontal" VerticalAlignment="Center">
<PathIcon Width="16" Height="16" Margin="16,0,0,0"
Foreground="{StaticResource NavText}"
Data="M19.14,12.94c0.04-0.3,0.06-0.61,0.06-0.94c0-0.32-0.02-0.64-0.07-0.94l2.03-1.58c0.18-0.14,0.23-0.41,0.12-0.61l-1.92-3.32c-0.12-0.22-0.37-0.29-0.59-0.22l-2.39,0.96c-0.5-0.38-1.03-0.7-1.62-0.94L14.4,2.81c-0.04-0.24-0.24-0.41-0.48-0.41h-3.84c-0.24,0-0.43,0.17-0.47,0.41L9.25,5.35C8.66,5.59,8.12,5.92,7.63,6.29L5.24,5.33c-0.22-0.08-0.47,0-0.59,0.22L2.74,8.87C2.62,9.08,2.66,9.34,2.86,9.48l2.03,1.58C4.84,11.36,4.8,11.69,4.8,12s0.02,0.64,0.07,0.94l-2.03,1.58c-0.18,0.14-0.23,0.41-0.12,0.61l1.92,3.32c0.12,0.22,0.37,0.29,0.59,0.22l2.39-0.96c0.5,0.38,1.03,0.7,1.62,0.94l0.36,2.54c0.05,0.24,0.24,0.41,0.48,0.41h3.84c0.24,0,0.44-0.17,0.47-0.41l0.36-2.54c0.59-0.24,1.13-0.56,1.62-0.94l2.39,0.96c0.22,0.08,0.47,0,0.59-0.22l1.92-3.32c0.12-0.22,0.07-0.47-0.12-0.61L19.14,12.94zM12,15.6c-1.98,0-3.6-1.62-3.6-3.6s1.62-3.6,3.6-3.6s3.6,1.62,3.6,3.6S13.98,15.6,12,15.6z" />
<TextBlock Text="Settings" FontSize="13" Margin="12,0,0,0"
Foreground="{StaticResource NavText}"
VerticalAlignment="Center"
Classes="openFade"
Classes.drawerOpen="{Binding #NavDrawer.IsOpen}" />
</StackPanel>
</Panel>
</Button>
</StackPanel>
</DrawerPage.DrawerFooter>
<!-- ── Detail area — BarHeight=0 hides the navigation bar entirely ── -->
<DrawerPage.Content>
<NavigationPage x:Name="DetailNav"
Background="{StaticResource NavContentBg}"
BarHeight="0" />
</DrawerPage.Content>
</DrawerPage>
</Border>
</DockPanel>
<UserControl.Styles>
<!-- openFade: collapsed (removed from layout) when closed, visible when open -->
<Style Selector=":is(Control).openFade">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector=":is(Control).openFade.drawerOpen">
<Setter Property="IsVisible" Value="True" />
</Style>
<!-- Hamburger button -->
<Style Selector="Button#BtnHamburger /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Transparent" />
<Setter Property="CornerRadius" Value="4" />
</Style>
<Style Selector="Button#BtnHamburger:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="#1AFFFFFF" />
</Style>
<Style Selector="Button#BtnHamburger:pressed /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="#2AFFFFFF" />
</Style>
<!-- Nav item base -->
<Style Selector="Button.navItem">
<Setter Property="HorizontalAlignment" Value="Stretch" />
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
<Setter Property="Height" Value="40" />
<Setter Property="Padding" Value="0" />
<Setter Property="CornerRadius" Value="4" />
</Style>
<Style Selector="Button.navItem /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Padding" Value="0" />
</Style>
<Style Selector="Button.navItem:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="#1AFFFFFF" />
</Style>
<Style Selector="Button.navItem:pressed /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="#2AFFFFFF" />
</Style>
<!-- navPill: hidden by default, visible only on selected item -->
<Style Selector="Button.navItem Border.navPill">
<Setter Property="IsVisible" Value="False" />
</Style>
<Style Selector="Button.navItemSelected Border.navPill">
<Setter Property="IsVisible" Value="True" />
</Style>
<!-- SearchBox: fully transparent, suppress all themed states -->
<Style Selector="TextBox#SearchBox">
<Setter Property="MinHeight" Value="0" />
<Setter Property="CaretBrush" Value="{StaticResource NavAccent}" />
<Setter Property="SelectionBrush" Value="#4060CDFF" />
</Style>
<Style Selector="TextBox#SearchBox /template/ Border#PART_BorderElement">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="MinHeight" Value="0" />
</Style>
<Style Selector="TextBox#SearchBox:pointerover /template/ Border#PART_BorderElement">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
</Style>
<Style Selector="TextBox#SearchBox:focus-within /template/ Border#PART_BorderElement">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
</Style>
<Style Selector="TextBox#SearchBox:error /template/ Border#PART_BorderElement">
<Setter Property="Background" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
</Style>
<!-- Nav item selected state -->
<Style Selector="Button.navItemSelected /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="#0FFFFFFF" />
</Style>
<Style Selector="Button.navItemSelected:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="#1AFFFFFF" />
</Style>
</UserControl.Styles>
</UserControl>

464
samples/ControlCatalog/Pages/DrawerPage/ControlsGalleryAppPage.xaml.cs

@ -0,0 +1,464 @@
using System;
using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Layout;
using Avalonia.Media;
namespace ControlCatalog.Pages;
public partial class ControlsGalleryAppPage : UserControl
{
static readonly Color Accent = Color.Parse("#60CDFF");
static readonly Color ContentBg = Color.Parse("#141414");
static readonly Color CardBg = Color.Parse("#1F1F1F");
static readonly Color BorderCol = Color.Parse("#2EFFFFFF");
static readonly Color TextCol = Color.Parse("#FFFFFF");
static readonly Color TextSec = Color.Parse("#C8FFFFFF");
static readonly Color TextMuted = Color.Parse("#80FFFFFF");
DrawerPage? _drawer;
NavigationPage? _detailNav;
Button? _selectedBtn;
TextBox? _searchBox;
ContentPage? _preSearchPage;
bool _isSearching;
public ControlsGalleryAppPage()
{
InitializeComponent();
_drawer = this.FindControl<DrawerPage>("NavDrawer");
_detailNav = this.FindControl<NavigationPage>("DetailNav");
_selectedBtn = this.FindControl<Button>("BtnWhatsNew");
_searchBox = this.FindControl<TextBox>("SearchBox");
if (_detailNav != null)
_ = _detailNav.PushAsync(BuildWhatsNewPage());
}
private void OnHamburgerClick(object? sender, RoutedEventArgs e)
{
if (_drawer != null)
_drawer.IsOpen = !_drawer.IsOpen;
}
private async void OnNavItemClick(object? sender, RoutedEventArgs e)
{
if (sender is not Button btn || btn.Tag is not string tag) return;
if (btn == _selectedBtn) return;
if (_selectedBtn != null)
_selectedBtn.Classes.Remove("navItemSelected");
_selectedBtn = btn;
btn.Classes.Add("navItemSelected");
if (_detailNav == null) return;
var page = tag switch
{
"WhatsNew" => BuildWhatsNewPage(),
"AllControls" => BuildAllControlsPage(),
"BasicInput" => BuildCategoryPage("Basic Input",
"Buttons, checkboxes, radio buttons, sliders, and toggle switches."),
"Collections" => BuildCategoryPage("Collections",
"List view, tree view, data grid, flip view, and more."),
"Media" => BuildCategoryPage("Media",
"Image, web view, map control, and media player."),
"Menus" => BuildCategoryPage("Menus and Toolbars",
"Menus, context menus, command bar, and toolbar."),
"Navigation" => BuildCategoryPage("Navigation",
"Navigation view, pivot, tab control, and breadcrumb bar."),
"Text" => BuildCategoryPage("Text",
"Text block, text box, auto-suggest box, and rich text."),
"Settings" => BuildSettingsPage(),
_ => BuildWhatsNewPage(),
};
NavigationPage.SetHasBackButton(page, false);
await _detailNav.ReplaceAsync(page, new CrossFade(TimeSpan.FromMilliseconds(180)));
}
private void OnSearchTextChanged(object? sender, TextChangedEventArgs e)
{
if (_detailNav == null) return;
var query = _searchBox?.Text?.Trim() ?? "";
if (query.Length == 0)
{
if (_isSearching)
{
_isSearching = false;
var restore = _preSearchPage ?? BuildWhatsNewPage();
_preSearchPage = null;
NavigationPage.SetHasBackButton(restore, false);
_ = _detailNav.ReplaceAsync(restore, new CrossFade(TimeSpan.FromMilliseconds(180)));
}
return;
}
if (!_isSearching)
{
_preSearchPage = _detailNav.CurrentPage as ContentPage;
_isSearching = true;
}
var resultsPage = BuildSearchResultsPage(query);
NavigationPage.SetHasBackButton(resultsPage, false);
_ = _detailNav.ReplaceAsync(resultsPage, null);
}
ContentPage BuildSearchResultsPage(string query)
{
var page = new ContentPage { Background = new SolidColorBrush(ContentBg) };
page.Header = "Search";
var scroll = new ScrollViewer();
var root = new StackPanel { Spacing = 0 };
(string Category, string[] Controls)[] allSections =
{
("Basic Input", new[] { "Button", "CheckBox", "ComboBox", "RadioButton", "Slider", "ToggleButton", "ToggleSwitch" }),
("Collections", new[] { "DataGrid", "ItemsControl", "ListBox", "ListView", "TreeView" }),
("Date & Time", new[] { "CalendarDatePicker", "DatePicker", "TimePicker" }),
("Layout", new[] { "Border", "Grid", "Panel", "StackPanel", "WrapPanel" }),
("Navigation", new[] { "DrawerPage", "NavigationPage", "TabControl", "TabbedPage" }),
("Text", new[] { "AutoCompleteBox", "RichTextBox", "TextBlock", "TextBox" }),
};
var q = query.ToLowerInvariant();
bool anyMatch = false;
foreach (var (category, controls) in allSections)
{
var matches = Array.FindAll(controls, c => c.ToLowerInvariant().Contains(q));
if (matches.Length == 0) continue;
anyMatch = true;
root.Children.Add(SectionHeader(category));
var chips = new WrapPanel { Margin = new Thickness(24, 4, 24, 0), Orientation = Orientation.Horizontal };
foreach (var ctrl in matches)
chips.Children.Add(new Border
{
Margin = new Thickness(0, 0, 8, 8),
Padding = new Thickness(12, 6),
CornerRadius = new CornerRadius(4),
Background = new SolidColorBrush(CardBg),
BorderBrush = new SolidColorBrush(BorderCol),
BorderThickness = new Thickness(1),
Child = Txt(ctrl, 12, FontWeight.Normal, TextCol),
});
root.Children.Add(chips);
}
if (!anyMatch)
{
var empty = new StackPanel
{
HorizontalAlignment = HorizontalAlignment.Center,
Margin = new Thickness(0, 40, 0, 0),
Spacing = 8,
};
empty.Children.Add(Txt("No results", 16, FontWeight.SemiBold, TextCol));
empty.Children.Add(Txt($"No controls match \"{query}\"", 13, FontWeight.Normal, TextSec));
root.Children.Add(empty);
}
scroll.Content = root;
page.Content = scroll;
return page;
}
ContentPage BuildWhatsNewPage()
{
var page = new ContentPage { Background = new SolidColorBrush(ContentBg) };
page.Header = "What's New";
var scroll = new ScrollViewer();
var root = new StackPanel { Spacing = 0 };
var heroBrush = new LinearGradientBrush
{
StartPoint = new RelativePoint(0, 0, RelativeUnit.Relative),
EndPoint = new RelativePoint(1, 1, RelativeUnit.Relative),
};
heroBrush.GradientStops.Add(new GradientStop(Color.Parse("#003B6F"), 0));
heroBrush.GradientStops.Add(new GradientStop(Color.Parse("#0078D4"), 0.5));
heroBrush.GradientStops.Add(new GradientStop(Color.Parse("#60CDFF"), 1));
var heroContent = new StackPanel
{
Margin = new Thickness(24), Spacing = 8, VerticalAlignment = VerticalAlignment.Bottom,
};
heroContent.Children.Add(Txt("NEW IN AVALONIA UI", 11, FontWeight.SemiBold, Accent));
heroContent.Children.Add(Txt("Controls Gallery", 28, FontWeight.Bold, TextCol));
heroContent.Children.Add(new TextBlock
{
Text = "Explore all controls, styles, and animations available in Avalonia.",
FontSize = 13,
Foreground = new SolidColorBrush(Color.FromArgb(200, 255, 255, 255)),
TextWrapping = TextWrapping.Wrap,
});
root.Children.Add(new Border
{
Height = 200,
Margin = new Thickness(24, 24, 24, 0),
CornerRadius = new CornerRadius(8),
Background = heroBrush,
Child = heroContent,
});
root.Children.Add(SectionHeader("New Controls"));
var wrap = new WrapPanel { Margin = new Thickness(24, 8, 24, 0), Orientation = Orientation.Horizontal, };
(string Icon, string Title, string Desc)[] newControls =
{
("M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5",
"DrawerPage", "Master-detail navigation with compact icon rail"),
("M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z",
"NavigationPage", "Push/pop stack with animated transitions"),
("M3 3h8v8H3zm10 0h8v8h-8zM3 13h8v8H3zm10 0h8v8h-8z",
"TabbedPage", "Multi-tab layout with swipe navigation"),
("M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z",
"ContentPage", "Single-content page with lifecycle events"),
};
foreach (var (icon, title, desc) in newControls)
wrap.Children.Add(ControlCard(icon, title, desc));
root.Children.Add(wrap);
root.Children.Add(SectionHeader("Recently Updated"));
var updList = new StackPanel { Margin = new Thickness(24, 8, 24, 24), Spacing = 4 };
(string Name, string Change)[] updates =
{
("CommandBar", "Overflow menu and compact label mode"),
("ContentPage", "Lifecycle events and navigation bar customization"),
};
foreach (var (name, change) in updates)
{
var infoStack = new StackPanel { Spacing = 2 };
infoStack.Children.Add(Txt(name, 13, FontWeight.SemiBold, TextCol));
infoStack.Children.Add(Txt(change, 11, FontWeight.Normal, TextSec));
updList.Children.Add(new Border
{
Padding = new Thickness(12, 10),
CornerRadius = new CornerRadius(4),
Background = new SolidColorBrush(CardBg),
BorderBrush = new SolidColorBrush(BorderCol),
BorderThickness = new Thickness(0, 0, 0, 1),
Child = infoStack,
});
}
root.Children.Add(updList);
scroll.Content = root;
page.Content = scroll;
return page;
}
Border ControlCard(string iconData, string title, string desc)
{
var stack = new StackPanel { Spacing = 8 };
stack.Children.Add(new PathIcon
{
Width = 20,
Height = 20,
Data = Geometry.Parse(iconData),
Foreground = new SolidColorBrush(Accent),
HorizontalAlignment = HorizontalAlignment.Left,
});
stack.Children.Add(Txt(title, 14, FontWeight.SemiBold, TextCol));
stack.Children.Add(new TextBlock
{
Text = desc, FontSize = 11, Foreground = new SolidColorBrush(TextSec), TextWrapping = TextWrapping.Wrap,
});
return new Border
{
Width = 182,
Height = 130,
Margin = new Thickness(0, 0, 12, 12),
CornerRadius = new CornerRadius(6),
Background = new SolidColorBrush(CardBg),
BorderBrush = new SolidColorBrush(BorderCol),
BorderThickness = new Thickness(1),
Padding = new Thickness(16),
Child = stack,
};
}
ContentPage BuildAllControlsPage()
{
var page = new ContentPage { Background = new SolidColorBrush(ContentBg) };
page.Header = "All Controls";
var scroll = new ScrollViewer();
var root = new StackPanel { Spacing = 0 };
(string Category, string[] Controls)[] sections =
{
("Basic Input",
new[]
{
"Button", "CheckBox", "ComboBox", "RadioButton", "Slider", "ToggleButton", "ToggleSwitch"
}),
("Collections", new[] { "DataGrid", "ItemsControl", "ListBox", "ListView", "TreeView" }),
("Date & Time", new[] { "CalendarDatePicker", "DatePicker", "TimePicker" }),
("Layout", new[] { "Border", "Grid", "Panel", "StackPanel", "WrapPanel" }),
("Navigation", new[] { "DrawerPage", "NavigationPage", "TabControl", "TabbedPage" }),
("Text", new[] { "AutoCompleteBox", "RichTextBox", "TextBlock", "TextBox" }),
};
foreach (var (category, controls) in sections)
{
root.Children.Add(SectionHeader(category));
var chips = new WrapPanel { Margin = new Thickness(24, 4, 24, 0), Orientation = Orientation.Horizontal };
foreach (var ctrl in controls)
{
chips.Children.Add(new Border
{
Margin = new Thickness(0, 0, 8, 8),
Padding = new Thickness(12, 6),
CornerRadius = new CornerRadius(4),
Background = new SolidColorBrush(CardBg),
BorderBrush = new SolidColorBrush(BorderCol),
BorderThickness = new Thickness(1),
Child = Txt(ctrl, 12, FontWeight.Normal, TextCol),
});
}
root.Children.Add(chips);
}
scroll.Content = root;
page.Content = scroll;
return page;
}
ContentPage BuildCategoryPage(string title, string description)
{
var page = new ContentPage { Background = new SolidColorBrush(ContentBg) };
page.Header = title;
var scroll = new ScrollViewer();
var root = new StackPanel { Spacing = 0 };
var headerStack = new StackPanel { Spacing = 4 };
headerStack.Children.Add(Txt(title, 20, FontWeight.SemiBold, TextCol));
headerStack.Children.Add(Txt(description, 13, FontWeight.Normal, TextSec));
root.Children.Add(new Border
{
Margin = new Thickness(24, 24, 24, 0),
Padding = new Thickness(16),
CornerRadius = new CornerRadius(6),
Background = new SolidColorBrush(CardBg),
Child = headerStack,
});
root.Children.Add(SectionHeader("Controls"));
var list = new StackPanel { Margin = new Thickness(24, 4, 24, 24), Spacing = 4 };
string[] sampleNames = { "Primary Control", "Secondary Control", "Advanced Control", "Variant A", "Variant B" };
for (int i = 0; i < sampleNames.Length; i++)
{
var rowGrid = new Grid { ColumnDefinitions = new ColumnDefinitions("*,Auto") };
var label = new StackPanel { Spacing = 2 };
label.Children.Add(Txt(sampleNames[i], 13, FontWeight.SemiBold, TextCol));
label.Children.Add(Txt($"Example usage in {title}", 11, FontWeight.Normal, TextSec));
rowGrid.Children.Add(label);
if (i < 2)
{
var badge = new Border
{
Padding = new Thickness(6, 2),
CornerRadius = new CornerRadius(10),
Background = new SolidColorBrush(Color.FromArgb(30, 96, 205, 255)),
Child = Txt("NEW", 10, FontWeight.Bold, Accent),
HorizontalAlignment = HorizontalAlignment.Right,
VerticalAlignment = VerticalAlignment.Center,
};
Grid.SetColumn(badge, 1);
rowGrid.Children.Add(badge);
}
list.Children.Add(new Border
{
Padding = new Thickness(16, 12),
CornerRadius = new CornerRadius(4),
Background = new SolidColorBrush(CardBg),
BorderBrush = new SolidColorBrush(BorderCol),
BorderThickness = new Thickness(0, 0, 0, 1),
Child = rowGrid,
});
}
root.Children.Add(list);
scroll.Content = root;
page.Content = scroll;
return page;
}
ContentPage BuildSettingsPage()
{
var page = new ContentPage { Background = new SolidColorBrush(ContentBg) };
page.Header = "Settings";
var scroll = new ScrollViewer();
var root = new StackPanel { Spacing = 0 };
root.Children.Add(SectionHeader("Appearance"));
var appearList = new StackPanel { Margin = new Thickness(24, 4, 24, 0), Spacing = 2 };
appearList.Children.Add(SettingsRow("App theme", "Dark"));
appearList.Children.Add(SettingsRow("Accent color", "#60CDFF"));
appearList.Children.Add(SettingsRow("Font size", "Medium"));
root.Children.Add(appearList);
root.Children.Add(SectionHeader("About"));
var aboutList = new StackPanel { Margin = new Thickness(24, 4, 24, 24), Spacing = 2 };
aboutList.Children.Add(SettingsRow("Version", "1.0.0"));
aboutList.Children.Add(SettingsRow("Framework", "Avalonia"));
aboutList.Children.Add(SettingsRow("Theme", "Fluent"));
root.Children.Add(aboutList);
scroll.Content = root;
page.Content = scroll;
return page;
}
Border SectionHeader(string title) => new()
{
Margin = new Thickness(24, 20, 24, 0),
Padding = new Thickness(0, 0, 0, 8),
BorderBrush = new SolidColorBrush(BorderCol),
BorderThickness = new Thickness(0, 0, 0, 1),
Child = Txt(title, 13, FontWeight.SemiBold, Accent),
};
static Border SettingsRow(string label, string value)
{
var grid = new Grid { ColumnDefinitions = new ColumnDefinitions("*,Auto") };
grid.Children.Add(Txt(label, 13, FontWeight.Normal, TextCol));
var val = Txt(value, 12, FontWeight.Normal, TextMuted);
val.VerticalAlignment = VerticalAlignment.Center;
Grid.SetColumn(val, 1);
grid.Children.Add(val);
return new Border
{
Padding = new Thickness(16, 14),
CornerRadius = new CornerRadius(4),
Background = new SolidColorBrush(CardBg),
BorderBrush = new SolidColorBrush(BorderCol),
BorderThickness = new Thickness(0, 0, 0, 1),
Child = grid,
};
}
static TextBlock Txt(string text, double size, FontWeight weight, Color color) => new()
{
Text = text, FontSize = size, FontWeight = weight, Foreground = new SolidColorBrush(color),
};
}

103
samples/ControlCatalog/Pages/DrawerPage/DrawerPageCompactPage.xaml

@ -0,0 +1,103 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ControlCatalog.Pages.DrawerPageCompactPage">
<DockPanel>
<ScrollViewer DockPanel.Dock="Right" Width="240">
<StackPanel Margin="12" Spacing="8">
<TextBlock Text="Configuration" FontWeight="SemiBold" FontSize="16"
Foreground="{DynamicResource SystemControlHighlightAccentBrush}" />
<TextBlock Text="Layout" FontSize="13" FontWeight="SemiBold" />
<ComboBox x:Name="LayoutCombo" SelectedIndex="0"
SelectionChanged="OnLayoutChanged" HorizontalAlignment="Stretch">
<ComboBoxItem Content="CompactOverlay" />
<ComboBoxItem Content="CompactInline" />
</ComboBox>
<Separator />
<TextBlock Text="Rail Width" FontSize="12" Opacity="0.7" />
<StackPanel Orientation="Horizontal" Spacing="8">
<Slider x:Name="CompactLengthSlider" Minimum="40" Maximum="120" Value="56"
Width="150" ValueChanged="OnCompactLengthChanged" />
<TextBlock x:Name="CompactLengthText" Text="56" VerticalAlignment="Center" />
</StackPanel>
<TextBlock Text="Open Pane Width" FontSize="12" Opacity="0.7" />
<StackPanel Orientation="Horizontal" Spacing="8">
<Slider x:Name="DrawerLengthSlider" Minimum="150" Maximum="400" Value="240"
Width="150" ValueChanged="OnDrawerLengthChanged" />
<TextBlock x:Name="DrawerLengthText" Text="240" VerticalAlignment="Center" />
</StackPanel>
<Separator />
<TextBlock Text="Status" FontWeight="SemiBold" FontSize="13" />
<TextBlock x:Name="StatusText" Text="Drawer: Closed" Opacity="0.7" TextWrapping="Wrap" />
</StackPanel>
</ScrollViewer>
<Border DockPanel.Dock="Right" Width="1" Background="{DynamicResource SystemControlForegroundBaseMediumLowBrush}" />
<Border Margin="12"
BorderBrush="{DynamicResource SystemControlForegroundBaseMediumLowBrush}"
BorderThickness="1"
CornerRadius="6"
ClipToBounds="True">
<DrawerPage x:Name="DemoDrawer"
DrawerLayoutBehavior="CompactOverlay"
CompactDrawerLength="56"
DrawerLength="240">
<DrawerPage.Drawer>
<StackPanel Spacing="2" Margin="0,8">
<Button HorizontalAlignment="Stretch" Background="Transparent"
Click="OnMenuItemClick" Tag="Home" ToolTip.Tip="Home">
<StackPanel HorizontalAlignment="Center" Spacing="3">
<PathIcon Width="20" Height="20"
Data="M10,20V14H14V20H19V12H22L12,3L2,12H5V20H10Z"
HorizontalAlignment="Center" />
<TextBlock Text="Home" FontSize="9" HorizontalAlignment="Center" />
</StackPanel>
</Button>
<Button HorizontalAlignment="Stretch" Background="Transparent"
Click="OnMenuItemClick" Tag="Inbox" ToolTip.Tip="Inbox">
<StackPanel HorizontalAlignment="Center" Spacing="3">
<PathIcon Width="20" Height="20"
Data="M20,8L12,13L4,8V6L12,11L20,6M20,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V6C22,4.89 21.1,4 20,4Z"
HorizontalAlignment="Center" />
<TextBlock Text="Inbox" FontSize="9" HorizontalAlignment="Center" />
</StackPanel>
</Button>
<Button HorizontalAlignment="Stretch" Background="Transparent"
Click="OnMenuItemClick" Tag="Profile" ToolTip.Tip="Profile">
<StackPanel HorizontalAlignment="Center" Spacing="3">
<PathIcon Width="20" Height="20"
Data="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z"
HorizontalAlignment="Center" />
<TextBlock Text="Profile" FontSize="9" HorizontalAlignment="Center" />
</StackPanel>
</Button>
<Button HorizontalAlignment="Stretch" Background="Transparent"
Click="OnMenuItemClick" Tag="Settings" ToolTip.Tip="Settings">
<StackPanel HorizontalAlignment="Center" Spacing="3">
<PathIcon Width="20" Height="20"
Data="M12,15.5A3.5,3.5 0 0,1 8.5,12A3.5,3.5 0 0,1 12,8.5A3.5,3.5 0 0,1 15.5,12A3.5,3.5 0 0,1 12,15.5M19.43,12.97C19.47,12.65 19.5,12.33 19.5,12C19.5,11.67 19.47,11.34 19.43,11L21.54,9.37C21.73,9.22 21.78,8.95 21.66,8.73L19.66,5.27C19.54,5.05 19.27,4.96 19.05,5.05L16.56,6.05C16.04,5.66 15.5,5.32 14.87,5.07L14.5,2.42C14.46,2.18 14.25,2 14,2H10C9.75,2 9.54,2.18 9.5,2.42L9.13,5.07C8.5,5.32 7.96,5.66 7.44,6.05L4.95,5.05C4.73,4.96 4.46,5.05 4.34,5.27L2.34,8.73C2.21,8.95 2.27,9.22 2.46,9.37L4.57,11C4.53,11.34 4.5,11.67 4.5,12C4.5,12.33 4.53,12.65 4.57,12.97L2.46,14.63C2.27,14.78 2.21,15.05 2.34,15.27L4.34,18.73C4.46,18.95 4.73,19.04 4.95,18.95L7.44,17.94C7.96,18.34 8.5,18.68 9.13,18.93L9.5,21.58C9.54,21.82 9.75,22 10,22H14C14.25,22 14.46,21.82 14.5,21.58L14.87,18.93C15.5,18.68 16.04,18.34 16.56,17.94L19.05,18.95C19.27,19.04 19.54,18.95 19.66,18.73L21.66,15.27C21.78,15.05 21.73,14.78 21.54,14.63L19.43,12.97Z"
HorizontalAlignment="Center" />
<TextBlock Text="Settings" FontSize="9" HorizontalAlignment="Center" />
</StackPanel>
</Button>
</StackPanel>
</DrawerPage.Drawer>
<DrawerPage.Content>
<ContentPage x:Name="DetailPage" Header="Home">
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center" Spacing="8">
<TextBlock x:Name="DetailTitleText" Text="Home" FontSize="24" FontWeight="Bold" HorizontalAlignment="Center" />
<TextBlock Text="Select an item from the compact rail on the left."
FontSize="13" Opacity="0.7" TextWrapping="Wrap" TextAlignment="Center" MaxWidth="280" />
</StackPanel>
</ContentPage>
</DrawerPage.Content>
</DrawerPage>
</Border>
</DockPanel>
</UserControl>

78
samples/ControlCatalog/Pages/DrawerPage/DrawerPageCompactPage.xaml.cs

@ -0,0 +1,78 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public partial class DrawerPageCompactPage : UserControl
{
private bool _isLoaded;
public DrawerPageCompactPage()
{
InitializeComponent();
}
protected override void OnLoaded(RoutedEventArgs e)
{
base.OnLoaded(e);
_isLoaded = true;
DemoDrawer.Opened += OnDrawerStatusChanged;
DemoDrawer.Closed += OnDrawerStatusChanged;
}
protected override void OnUnloaded(RoutedEventArgs e)
{
base.OnUnloaded(e);
DemoDrawer.Opened -= OnDrawerStatusChanged;
DemoDrawer.Closed -= OnDrawerStatusChanged;
}
private void OnDrawerStatusChanged(object? sender, System.EventArgs e) => UpdateStatus();
private void OnLayoutChanged(object? sender, SelectionChangedEventArgs e)
{
if (!_isLoaded)
return;
DemoDrawer.DrawerLayoutBehavior = LayoutCombo.SelectedIndex switch
{
0 => DrawerLayoutBehavior.CompactOverlay,
1 => DrawerLayoutBehavior.CompactInline,
_ => DrawerLayoutBehavior.CompactOverlay
};
}
private void OnCompactLengthChanged(object? sender, RangeBaseValueChangedEventArgs e)
{
if (!_isLoaded)
return;
DemoDrawer.CompactDrawerLength = e.NewValue;
CompactLengthText.Text = ((int)e.NewValue).ToString();
}
private void OnDrawerLengthChanged(object? sender, RangeBaseValueChangedEventArgs e)
{
if (!_isLoaded)
return;
DemoDrawer.DrawerLength = e.NewValue;
DrawerLengthText.Text = ((int)e.NewValue).ToString();
}
private void OnMenuItemClick(object? sender, RoutedEventArgs e)
{
if (!_isLoaded)
return;
if (sender is not Button button)
return;
var item = button.Tag?.ToString() ?? "Home";
DetailTitleText.Text = item;
DetailPage.Header = item;
DemoDrawer.IsOpen = false;
}
private void UpdateStatus()
{
StatusText.Text = $"Drawer: {(DemoDrawer.IsOpen ? "Open" : "Closed")}";
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save