Browse Source

Merge branch 'master' into master

feature/pen-eraser-detection
Krzysztof Krysiński 10 months ago
committed by GitHub
parent
commit
84518dbe0f
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 42
      Avalonia.sln
  2. 10
      native/Avalonia.Native/src/OSX/AvnView.mm
  3. 2
      native/Avalonia.Native/src/OSX/KeyTransform.mm
  4. 6
      nukebuild/Build.cs
  5. 4
      samples/ControlCatalog/ControlCatalog.csproj
  6. 58
      samples/ControlCatalog/DecoratedWindow.xaml.cs
  7. 27
      samples/ControlCatalog/MainView.xaml
  8. 113
      samples/ControlCatalog/MainView.xaml.cs
  9. 5
      samples/ControlCatalog/MainWindow.xaml
  10. 13
      samples/ControlCatalog/MainWindow.xaml.cs
  11. 10
      samples/ControlCatalog/Pages/AcceleratorPage.xaml.cs
  12. 13
      samples/ControlCatalog/Pages/AcrylicPage.xaml.cs
  13. 30
      samples/ControlCatalog/Pages/AdornerLayerPage.xaml.cs
  14. 35
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
  15. 10
      samples/ControlCatalog/Pages/BorderPage.xaml.cs
  16. 12
      samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml.cs
  17. 2
      samples/ControlCatalog/Pages/ButtonsPage.xaml
  18. 16
      samples/ControlCatalog/Pages/ButtonsPage.xaml.cs
  19. 29
      samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml.cs
  20. 24
      samples/ControlCatalog/Pages/CalendarPage.xaml.cs
  21. 10
      samples/ControlCatalog/Pages/CanvasPage.xaml.cs
  22. 40
      samples/ControlCatalog/Pages/CarouselPage.xaml.cs
  23. 10
      samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs
  24. 14
      samples/ControlCatalog/Pages/ClipboardPage.xaml.cs
  25. 10
      samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs
  26. 14
      samples/ControlCatalog/Pages/ComboBoxPage.xaml.cs
  27. 8
      samples/ControlCatalog/Pages/CompositionPage.axaml.cs
  28. 10
      samples/ControlCatalog/Pages/ContainerQueryPage.xaml.cs
  29. 15
      samples/ControlCatalog/Pages/ContextFlyoutPage.xaml
  30. 48
      samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs
  31. 4
      samples/ControlCatalog/Pages/ContextMenuPage.xaml
  32. 32
      samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs
  33. 10
      samples/ControlCatalog/Pages/CursorPage.xaml.cs
  34. 43
      samples/ControlCatalog/Pages/CustomDrawing.xaml.cs
  35. 7
      samples/ControlCatalog/Pages/DataGridPage.xaml.cs
  36. 17
      samples/ControlCatalog/Pages/DateTimePickerPage.xaml.cs
  37. 77
      samples/ControlCatalog/Pages/DialogsPage.xaml.cs
  38. 19
      samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
  39. 13
      samples/ControlCatalog/Pages/ExpanderPage.xaml.cs
  40. 14
      samples/ControlCatalog/Pages/FlyoutsPage.axaml.cs
  41. 53
      samples/ControlCatalog/Pages/GesturePage.cs
  42. 33
      samples/ControlCatalog/Pages/ImagePage.xaml.cs
  43. 10
      samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml.cs
  44. 8
      samples/ControlCatalog/Pages/ListBoxPage.xaml.cs
  45. 17
      samples/ControlCatalog/Pages/MenuPage.xaml.cs
  46. 23
      samples/ControlCatalog/Pages/NativeEmbedPage.xaml.cs
  47. 12
      samples/ControlCatalog/Pages/NotificationsPage.xaml.cs
  48. 9
      samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs
  49. 6
      samples/ControlCatalog/Pages/OpenGl/OpenGlLeasePage.xaml
  50. 21
      samples/ControlCatalog/Pages/OpenGl/OpenGlLeasePage.xaml.cs
  51. 17
      samples/ControlCatalog/Pages/OpenGlPage.xaml.cs
  52. 15
      samples/ControlCatalog/Pages/PlatformInfoPage.xaml.cs
  53. 4
      samples/ControlCatalog/Pages/PointersPage.xaml
  54. 27
      samples/ControlCatalog/Pages/PointersPage.xaml.cs
  55. 10
      samples/ControlCatalog/Pages/ProgressBarPage.xaml.cs
  56. 10
      samples/ControlCatalog/Pages/RadioButtonPage.xaml.cs
  57. 8
      samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs
  58. 10
      samples/ControlCatalog/Pages/SliderPage.xaml.cs
  59. 13
      samples/ControlCatalog/Pages/SplitViewPage.xaml.cs
  60. 12
      samples/ControlCatalog/Pages/TabControlPage.xaml.cs
  61. 10
      samples/ControlCatalog/Pages/TabStripPage.xaml.cs
  62. 10
      samples/ControlCatalog/Pages/TextBlockPage.xaml.cs
  63. 10
      samples/ControlCatalog/Pages/TextBoxPage.xaml.cs
  64. 13
      samples/ControlCatalog/Pages/ToggleSwitchPage.xaml.cs
  65. 12
      samples/ControlCatalog/Pages/ToolTipPage.xaml.cs
  66. 7
      samples/ControlCatalog/Pages/TransitioningContentControlPage.axaml.cs
  67. 8
      samples/ControlCatalog/Pages/TreeViewPage.xaml.cs
  68. 8
      samples/ControlCatalog/Pages/ViewboxPage.xaml.cs
  69. 13
      samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml.cs
  70. 10
      samples/ControlCatalog/Views/CustomNotificationView.xaml.cs
  71. 17
      src/Android/Avalonia.Android/Automation/ToggleNodeInfoProvider.cs
  72. 2
      src/Android/Avalonia.Android/Platform/ClipboardImpl.cs
  73. 7
      src/Avalonia.Base/Media/FontManager.cs
  74. 62
      src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs
  75. 15
      src/Avalonia.Base/Platform/IFontManagerImpl.cs
  76. 17
      src/Avalonia.Base/Rendering/Composition/Drawing/ServerCompositionSimplePen.cs
  77. 2
      src/Avalonia.Base/Styling/ChildSelector.cs
  78. 2
      src/Avalonia.Base/Styling/DescendentSelector.cs
  79. 2
      src/Avalonia.Base/Styling/NotSelector.cs
  80. 6
      src/Avalonia.Base/Styling/NthChildSelector.cs
  81. 16
      src/Avalonia.Base/Styling/OrSelector.cs
  82. 3
      src/Avalonia.Base/Styling/PropertyEqualsSelector.cs
  83. 41
      src/Avalonia.Base/Styling/Selector.cs
  84. 3
      src/Avalonia.Base/Styling/Selectors.cs
  85. 2
      src/Avalonia.Base/Styling/TemplateSelector.cs
  86. 2
      src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs
  87. 47
      src/Headless/Avalonia.Headless/HeadlessUnitTestIsolationAttribute.cs
  88. 65
      src/Headless/Avalonia.Headless/HeadlessUnitTestSession.cs
  89. 71
      src/Skia/Avalonia.Skia/FontManagerImpl.cs
  90. 27
      src/Windows/Avalonia.Win32.Automation/Marshalling/ComVariant.cs
  91. 7
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  92. 2
      src/Windows/Avalonia.Win32/Win32Platform.cs
  93. 69
      src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs
  94. 1
      src/Windows/Avalonia.Win32/WindowImpl.cs
  95. 19
      tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Or.cs
  96. 8
      tests/Avalonia.Headless.NUnit.PerAssembly.UnitTests/AssemblyInfo.cs
  97. 0
      tests/Avalonia.Headless.NUnit.PerAssembly.UnitTests/Avalonia.Headless.NUnit.PerAssembly.UnitTests.csproj
  98. 1
      tests/Avalonia.Headless.NUnit.PerTest.UnitTests/AssemblyInfo.cs
  99. 31
      tests/Avalonia.Headless.NUnit.PerTest.UnitTests/Avalonia.Headless.NUnit.PerTest.UnitTests.csproj
  100. 63
      tests/Avalonia.Headless.UnitTests/IsolationTests.cs

42
Avalonia.sln

@ -270,10 +270,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.NUnit", "
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Generators.Tests", "tests\Avalonia.Generators.Tests\Avalonia.Generators.Tests.csproj", "{4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.NUnit.UnitTests", "tests\Avalonia.Headless.NUnit.UnitTests\Avalonia.Headless.NUnit.UnitTests.csproj", "{2999D79E-3C20-4A90-B651-CA7E0AC92D35}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Headless.XUnit.UnitTests", "tests\Avalonia.Headless.XUnit.UnitTests\Avalonia.Headless.XUnit.UnitTests.csproj", "{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Metal", "src\Avalonia.Metal\Avalonia.Metal.csproj", "{60B4ED1F-ECFA-453B-8A70-1788261C8355}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Build.Tasks.UnitTest", "tests\Avalonia.Build.Tasks.UnitTest\Avalonia.Build.Tasks.UnitTest.csproj", "{B0FD6A48-FBAB-4676-B36A-DE76B0922B12}"
@ -302,6 +298,14 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.MacCatalyst"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.tvOS", "samples\ControlCatalog.tvOS\ControlCatalog.tvOS.csproj", "{14342787-B4EF-4076-8C91-BA6C523DE8DF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.NUnit.PerAssembly.UnitTests", "tests\Avalonia.Headless.NUnit.PerAssembly.UnitTests\Avalonia.Headless.NUnit.PerAssembly.UnitTests.csproj", "{A175EFAE-476C-4DAA-87D5-742C18CFCC27}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.NUnit.PerTest.UnitTests", "tests\Avalonia.Headless.NUnit.PerTest.UnitTests\Avalonia.Headless.NUnit.PerTest.UnitTests.csproj", "{09EC467F-0F25-4E6F-A836-2BAEC8F6AB0C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.XUnit.PerAssembly.UnitTests", "tests\Avalonia.Headless.XUnit.PerAssembly.UnitTests\Avalonia.Headless.XUnit.PerAssembly.UnitTests.csproj", "{342D2657-2F84-493C-B74B-9D2CAE5D9DAB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Headless.XUnit.PerTest.UnitTests", "tests\Avalonia.Headless.XUnit.PerTest.UnitTests\Avalonia.Headless.XUnit.PerTest.UnitTests.csproj", "{26918642-829D-4FA2-B60A-BE8D83F4E063}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -652,14 +656,6 @@ Global
{4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64}.Release|Any CPU.Build.0 = Release|Any CPU
{2999D79E-3C20-4A90-B651-CA7E0AC92D35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{2999D79E-3C20-4A90-B651-CA7E0AC92D35}.Debug|Any CPU.Build.0 = Debug|Any CPU
{2999D79E-3C20-4A90-B651-CA7E0AC92D35}.Release|Any CPU.ActiveCfg = Release|Any CPU
{2999D79E-3C20-4A90-B651-CA7E0AC92D35}.Release|Any CPU.Build.0 = Release|Any CPU
{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3}.Release|Any CPU.Build.0 = Release|Any CPU
{60B4ED1F-ECFA-453B-8A70-1788261C8355}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{60B4ED1F-ECFA-453B-8A70-1788261C8355}.Debug|Any CPU.Build.0 = Debug|Any CPU
{60B4ED1F-ECFA-453B-8A70-1788261C8355}.Release|Any CPU.ActiveCfg = Release|Any CPU
@ -704,6 +700,22 @@ Global
{14342787-B4EF-4076-8C91-BA6C523DE8DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{14342787-B4EF-4076-8C91-BA6C523DE8DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{14342787-B4EF-4076-8C91-BA6C523DE8DF}.Release|Any CPU.Build.0 = Release|Any CPU
{A175EFAE-476C-4DAA-87D5-742C18CFCC27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A175EFAE-476C-4DAA-87D5-742C18CFCC27}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A175EFAE-476C-4DAA-87D5-742C18CFCC27}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A175EFAE-476C-4DAA-87D5-742C18CFCC27}.Release|Any CPU.Build.0 = Release|Any CPU
{09EC467F-0F25-4E6F-A836-2BAEC8F6AB0C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{09EC467F-0F25-4E6F-A836-2BAEC8F6AB0C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{09EC467F-0F25-4E6F-A836-2BAEC8F6AB0C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{09EC467F-0F25-4E6F-A836-2BAEC8F6AB0C}.Release|Any CPU.Build.0 = Release|Any CPU
{342D2657-2F84-493C-B74B-9D2CAE5D9DAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{342D2657-2F84-493C-B74B-9D2CAE5D9DAB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{342D2657-2F84-493C-B74B-9D2CAE5D9DAB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{342D2657-2F84-493C-B74B-9D2CAE5D9DAB}.Release|Any CPU.Build.0 = Release|Any CPU
{26918642-829D-4FA2-B60A-BE8D83F4E063}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{26918642-829D-4FA2-B60A-BE8D83F4E063}.Debug|Any CPU.Build.0 = Debug|Any CPU
{26918642-829D-4FA2-B60A-BE8D83F4E063}.Release|Any CPU.ActiveCfg = Release|Any CPU
{26918642-829D-4FA2-B60A-BE8D83F4E063}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -779,8 +791,6 @@ Global
{F47F8316-4D4B-4026-8EF3-16B2CFDA8119} = {FF237916-7150-496B-89ED-6CA3292896E7}
{ED976634-B118-43F8-8B26-0279C7A7044F} = {FF237916-7150-496B-89ED-6CA3292896E7}
{4B8EBBEB-A1AD-49EC-8B69-B93ED15BFA64} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{2999D79E-3C20-4A90-B651-CA7E0AC92D35} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{F83FC908-A4E3-40DE-B4CF-A4BA1E92CDB3} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{B0FD6A48-FBAB-4676-B36A-DE76B0922B12} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{9D6AEF22-221F-4F4B-B335-A4BA510F002C} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{5BF0C3B8-E595-4940-AB30-2DA206C2F085} = {9D6AEF22-221F-4F4B-B335-A4BA510F002C}
@ -793,6 +803,10 @@ Global
{255614F5-CB64-4ECA-A026-E0B1AF6A2EF4} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{DE3C28DD-B602-4750-831D-345102A54CA0} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{14342787-B4EF-4076-8C91-BA6C523DE8DF} = {9B9E3891-2366-4253-A952-D08BCEB71098}
{A175EFAE-476C-4DAA-87D5-742C18CFCC27} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{09EC467F-0F25-4E6F-A836-2BAEC8F6AB0C} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{342D2657-2F84-493C-B74B-9D2CAE5D9DAB} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
{26918642-829D-4FA2-B60A-BE8D83F4E063} = {C5A00AC3-B34C-4564-9BDD-2DA473EF4D8B}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {87366D66-1391-4D90-8999-95A620AD786A}

10
native/Avalonia.Native/src/OSX/AvnView.mm

@ -456,13 +456,12 @@ static void ConvertTilt(NSPoint tilt, float* xTilt, float* yTilt)
switch(event.buttonNumber)
{
case 2:
case 3:
[self mouseEvent:event withType:MiddleButtonDown];
break;
case 4:
case 3:
[self mouseEvent:event withType:XButton1Down];
break;
case 5:
case 4:
[self mouseEvent:event withType:XButton2Down];
break;
@ -487,13 +486,12 @@ static void ConvertTilt(NSPoint tilt, float* xTilt, float* yTilt)
switch(event.buttonNumber)
{
case 2:
case 3:
[self mouseEvent:event withType:MiddleButtonUp];
break;
case 4:
case 3:
[self mouseEvent:event withType:XButton1Up];
break;
case 5:
case 4:
[self mouseEvent:event withType:XButton2Up];
break;

2
native/Avalonia.Native/src/OSX/KeyTransform.mm

@ -33,7 +33,7 @@ const KeyInfo keyInfos[] =
{ 0x1A, AvnPhysicalKeyDigit7, AvnKeyD7, '7' },
{ 0x1C, AvnPhysicalKeyDigit8, AvnKeyD8, '8' },
{ 0x19, AvnPhysicalKeyDigit9, AvnKeyD9, '9' },
{ 0x18, AvnPhysicalKeyEqual, AvnKeyOemMinus, '-' },
{ 0x18, AvnPhysicalKeyEqual, AvnKeyOemPlus, '=' },
{ 0x0A, AvnPhysicalKeyIntlBackslash, AvnKeyOem102, 0 },
{ 0x5E, AvnPhysicalKeyIntlRo, AvnKeyOem102, 0 },
{ 0x5D, AvnPhysicalKeyIntlYen, AvnKeyOem5, 0 },

6
nukebuild/Build.cs

@ -297,8 +297,10 @@ partial class Build : NukeBuild
RunCoreTest("Avalonia.Markup.Xaml.UnitTests");
RunCoreTest("Avalonia.Skia.UnitTests");
RunCoreTest("Avalonia.ReactiveUI.UnitTests");
RunCoreTest("Avalonia.Headless.NUnit.UnitTests");
RunCoreTest("Avalonia.Headless.XUnit.UnitTests");
RunCoreTest("Avalonia.Headless.NUnit.PerAssembly.UnitTests");
RunCoreTest("Avalonia.Headless.NUnit.PerTest.UnitTests");
RunCoreTest("Avalonia.Headless.XUnit.PerAssembly.UnitTests");
RunCoreTest("Avalonia.Headless.XUnit.PerTest.UnitTests");
});
Target RunRenderTests => _ => _

4
samples/ControlCatalog/ControlCatalog.csproj

@ -9,9 +9,9 @@
<Compile Update="**\*.xaml.cs">
<DependentUpon>%(Filename)</DependentUpon>
</Compile>
<AvaloniaResource Include="**\*.xaml">
<AvaloniaXaml Include="**\*.xaml">
<SubType>Designer</SubType>
</AvaloniaResource>
</AvaloniaXaml>
<AvaloniaResource Include="Assets\*" />
<AvaloniaResource Include="Assets\Fonts\*" />
</ItemGroup>

58
samples/ControlCatalog/DecoratedWindow.xaml.cs

@ -1,53 +1,45 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using System;
using Avalonia.Input;
namespace ControlCatalog
{
public class DecoratedWindow : Window
public partial class DecoratedWindow : Window
{
public DecoratedWindow()
{
this.InitializeComponent();
}
void SetupSide(string name, StandardCursorType cursor, WindowEdge edge)
{
var ctl = this.Get<Control>(name);
ctl.Cursor = new Cursor(cursor);
ctl.PointerPressed += (i, e) =>
{
if (WindowState == WindowState.Normal)
BeginResizeDrag(edge, e);
};
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
this.Get<Control>("TitleBar").PointerPressed += (i, e) =>
InitializeComponent();
TitleBar.PointerPressed += (i, e) =>
{
BeginMoveDrag(e);
};
SetupSide("Left", StandardCursorType.LeftSide, WindowEdge.West);
SetupSide("Right", StandardCursorType.RightSide, WindowEdge.East);
SetupSide("Top", StandardCursorType.TopSide, WindowEdge.North);
SetupSide("Bottom", StandardCursorType.BottomSide, WindowEdge.South);
SetupSide("TopLeft", StandardCursorType.TopLeftCorner, WindowEdge.NorthWest);
SetupSide("TopRight", StandardCursorType.TopRightCorner, WindowEdge.NorthEast);
SetupSide("BottomLeft", StandardCursorType.BottomLeftCorner, WindowEdge.SouthWest);
SetupSide("BottomRight", StandardCursorType.BottomRightCorner, WindowEdge.SouthEast);
this.Get<Button>("MinimizeButton").Click += delegate { this.WindowState = WindowState.Minimized; };
this.Get<Button>("MaximizeButton").Click += delegate
SetupSide(Left, StandardCursorType.LeftSide, WindowEdge.West);
SetupSide(Right, StandardCursorType.RightSide, WindowEdge.East);
SetupSide(Top, StandardCursorType.TopSide, WindowEdge.North);
SetupSide(Bottom, StandardCursorType.BottomSide, WindowEdge.South);
SetupSide(TopLeft, StandardCursorType.TopLeftCorner, WindowEdge.NorthWest);
SetupSide(TopRight, StandardCursorType.TopRightCorner, WindowEdge.NorthEast);
SetupSide(BottomLeft, StandardCursorType.BottomLeftCorner, WindowEdge.SouthWest);
SetupSide(BottomRight, StandardCursorType.BottomRightCorner, WindowEdge.SouthEast);
MinimizeButton.Click += delegate
{ this.WindowState = WindowState.Minimized; };
MaximizeButton.Click += delegate
{
WindowState = WindowState == WindowState.Maximized ? WindowState.Normal : WindowState.Maximized;
};
this.Get<Button>("CloseButton").Click += delegate
CloseButton.Click += delegate
{
Close();
};
}
private void SetupSide(Control ctl, StandardCursorType cursor, WindowEdge edge)
{
ctl.Cursor = new Cursor(cursor);
ctl.PointerPressed += (i, e) =>
{
if (WindowState == WindowState.Normal)
BeginResizeDrag(edge, e);
};
}
}
}

27
samples/ControlCatalog/MainView.xaml

@ -1,6 +1,7 @@
<UserControl x:Class="ControlCatalog.MainView"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ControlCatalog"
xmlns:controls="using:ControlSamples"
xmlns:models="using:ControlCatalog.Models"
xmlns:pages="using:ControlCatalog.Pages"
@ -210,33 +211,35 @@
<StackPanel Width="152" Spacing="8">
<ComboBox x:Name="Decorations"
HorizontalAlignment="Stretch"
SelectedIndex="0">
SelectedIndex="0"
SelectionChanged="Decorations_SelectionChanged">
<ComboBox.Items>
<SystemDecorations>None</SystemDecorations>
<SystemDecorations>BorderOnly</SystemDecorations>
<SystemDecorations>Full</SystemDecorations>
</ComboBox.Items>
</ComboBox>
<ComboBox x:Name="ThemeVariants"
HorizontalAlignment="Stretch"
<ComboBox HorizontalAlignment="Stretch"
DisplayMemberBinding="{Binding Key, x:DataType=ThemeVariant}"
SelectedIndex="0">
SelectedIndex="0"
SelectionChanged="ThemeVariants_SelectionChanged">
<ComboBox.Items>
<ThemeVariant>Default</ThemeVariant>
<ThemeVariant>Light</ThemeVariant>
<ThemeVariant>Dark</ThemeVariant>
</ComboBox.Items>
</ComboBox>
<ComboBox x:Name="Themes"
HorizontalAlignment="Stretch"
SelectedIndex="0">
<ComboBox HorizontalAlignment="Stretch"
SelectedItem="{x:Static local:App.CurrentTheme}"
SelectionChanged="Themes_SelectionChanged">
<ComboBox.Items>
<models:CatalogTheme>Fluent</models:CatalogTheme>
<models:CatalogTheme>Simple</models:CatalogTheme>
</ComboBox.Items>
</ComboBox>
<ComboBox x:Name="TransparencyLevels"
HorizontalAlignment="Stretch">
<ComboBox HorizontalAlignment="Stretch"
SelectedIndex="0"
SelectionChanged="TransparencyLevels_SelectionChanged">
<ComboBox.Items>
<WindowTransparencyLevel>None</WindowTransparencyLevel>
<WindowTransparencyLevel>Transparent</WindowTransparencyLevel>
@ -245,9 +248,9 @@
<WindowTransparencyLevel>Mica</WindowTransparencyLevel>
</ComboBox.Items>
</ComboBox>
<ComboBox x:Name="FlowDirection"
HorizontalAlignment="Stretch"
SelectedIndex="0">
<ComboBox HorizontalAlignment="Stretch"
SelectedIndex="0"
SelectionChanged="FlowDirection_SelectionChanged">
<ComboBox.Items>
<FlowDirection>LeftToRight</FlowDirection>
<FlowDirection>RightToLeft</FlowDirection>

113
samples/ControlCatalog/MainView.xaml.cs

@ -1,102 +1,85 @@
using System;
using System.Collections;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Media.Immutable;
using Avalonia.Platform;
using Avalonia.VisualTree;
using Avalonia.Styling;
using ControlCatalog.Models;
using ControlCatalog.Pages;
using ControlCatalog.ViewModels;
namespace ControlCatalog
{
public class MainView : UserControl
public partial class MainView : UserControl
{
private Action? _disposeTransparencySetters;
public MainView()
{
AvaloniaXamlLoader.Load(this);
var sideBar = this.Get<TabControl>("Sidebar");
InitializeComponent();
}
var themes = this.Get<ComboBox>("Themes");
themes.SelectedItem = App.CurrentTheme;
themes.SelectionChanged += (sender, e) =>
private void Themes_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (e.AddedItems.Count > 0 && e.AddedItems[0] is CatalogTheme theme)
{
if (themes.SelectedItem is CatalogTheme theme)
{
App.SetCatalogThemes(theme);
}
};
var themeVariants = this.Get<ComboBox>("ThemeVariants");
themeVariants.SelectedItem = Application.Current!.RequestedThemeVariant;
themeVariants.SelectionChanged += (sender, e) =>
App.SetCatalogThemes(theme);
}
}
private void ThemeVariants_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (Application.Current is { } app && e.AddedItems.Count > 0 && e.AddedItems[0] is ThemeVariant themeVariant)
{
if (themeVariants.SelectedItem is ThemeVariant themeVariant)
{
Application.Current!.RequestedThemeVariant = themeVariant;
}
};
app.RequestedThemeVariant = themeVariant;
}
}
var flowDirections = this.Get<ComboBox>("FlowDirection");
flowDirections.SelectionChanged += (sender, e) =>
private void FlowDirection_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (TopLevel.GetTopLevel(this) is { } topLevel && e.AddedItems.Count > 0 && e.AddedItems[0] is FlowDirection flowDirection)
{
if (flowDirections.SelectedItem is FlowDirection flowDirection)
{
TopLevel.GetTopLevel(this)!.FlowDirection = flowDirection;
}
};
topLevel.FlowDirection = flowDirection;
}
}
var decorations = this.Get<ComboBox>("Decorations");
decorations.SelectionChanged += (sender, e) =>
private void Decorations_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
if (VisualRoot is Window window && e.AddedItems.Count > 0 && e.AddedItems[0] is SystemDecorations systemDecorations)
{
if (VisualRoot is Window window
&& decorations.SelectedItem is SystemDecorations systemDecorations)
{
window.SystemDecorations = systemDecorations;
}
};
window.SystemDecorations = systemDecorations;
}
}
var transparencyLevels = this.Get<ComboBox>("TransparencyLevels");
IDisposable? topLevelBackgroundSideSetter = null, sideBarBackgroundSetter = null, paneBackgroundSetter = null;
transparencyLevels.SelectionChanged += (sender, e) =>
private void TransparencyLevels_SelectionChanged(object? sender, SelectionChangedEventArgs e)
{
_disposeTransparencySetters?.Invoke();
if (TopLevel.GetTopLevel(this) is { } topLevel && e.AddedItems.Count > 0 && e.AddedItems[0] is WindowTransparencyLevel transparencyLevel)
{
topLevelBackgroundSideSetter?.Dispose();
sideBarBackgroundSetter?.Dispose();
paneBackgroundSetter?.Dispose();
if (transparencyLevels.SelectedItem is WindowTransparencyLevel selected)
{
var topLevel = (TopLevel)this.GetVisualRoot()!;
topLevel.TransparencyLevelHint = new[] { selected };
topLevel.TransparencyLevelHint = [transparencyLevel];
if (topLevel.ActualTransparencyLevel != WindowTransparencyLevel.None &&
topLevel.ActualTransparencyLevel == selected)
{
var transparentBrush = new ImmutableSolidColorBrush(Colors.White, 0);
var semiTransparentBrush = new ImmutableSolidColorBrush(Colors.Gray, 0.2);
topLevelBackgroundSideSetter = topLevel.SetValue(BackgroundProperty, transparentBrush, Avalonia.Data.BindingPriority.Style);
sideBarBackgroundSetter = sideBar.SetValue(BackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style);
paneBackgroundSetter = sideBar.SetValue(SplitView.PaneBackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style);
}
if (topLevel.ActualTransparencyLevel != WindowTransparencyLevel.None &&
topLevel.ActualTransparencyLevel == transparencyLevel)
{
var transparentBrush = new ImmutableSolidColorBrush(Colors.White, 0);
var semiTransparentBrush = new ImmutableSolidColorBrush(Colors.Gray, 0.2);
_disposeTransparencySetters =
(Action)topLevel.SetValue(BackgroundProperty, transparentBrush, Avalonia.Data.BindingPriority.Style)!.Dispose +
Sidebar.SetValue(BackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style)!.Dispose +
Sidebar.SetValue(SplitView.PaneBackgroundProperty, semiTransparentBrush, Avalonia.Data.BindingPriority.Style)!.Dispose;
}
};
}
}
internal MainWindowViewModel ViewModel => (MainWindowViewModel)DataContext!;
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
var decorations = this.Get<ComboBox>("Decorations");
if (VisualRoot is Window window)
decorations.SelectedIndex = (int)window.SystemDecorations;
Decorations.SelectedIndex = (int)window.SystemDecorations;
var insets = TopLevel.GetTopLevel(this)!.InsetsManager;
if (insets != null)

5
samples/ControlCatalog/MainWindow.xaml

@ -13,7 +13,6 @@
CanResize="{Binding CanResize}"
CanMinimize="{Binding CanMinimize}"
CanMaximize="{Binding CanMaximize}"
x:Name="MainWindow"
Background="Transparent"
x:Class="ControlCatalog.MainWindow" WindowState="{Binding WindowState, Mode=TwoWay}"
x:DataType="vm:MainWindowViewModel">
@ -59,8 +58,8 @@
</DataTemplate>
</Window.DataTemplates>
<Panel>
<Panel Margin="{Binding #MainWindow.OffScreenMargin}">
<local:MainView Margin="{Binding #MainWindow.WindowDecorationMargin}" />
<Panel Margin="{Binding $parent[local:MainWindow].OffScreenMargin}">
<local:MainView Margin="{Binding $parent[local:MainWindow].WindowDecorationMargin}" />
</Panel>
<Border IsVisible="{Binding ExtendClientAreaEnabled}" BorderThickness="1 1 1 0" CornerRadius="4 4 0 0" BorderBrush="#55000000" Height="22" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="250 8 0 0">
<Border.Background>

13
samples/ControlCatalog/MainWindow.xaml.cs

@ -1,21 +1,17 @@
using System;
using System.Runtime.InteropServices;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Notifications;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using ControlCatalog.ViewModels;
namespace ControlCatalog
{
public class MainWindow : Window
public partial class MainWindow : Window
{
private NativeMenu? _recentMenu;
public MainWindow()
{
this.InitializeComponent();
InitializeComponent();
_recentMenu = ((NativeMenu.GetMenu(this)?.Items[0] as NativeMenuItem)?.Menu?.Items[2] as NativeMenuItem)?.Menu;
}
@ -35,10 +31,5 @@ namespace ControlCatalog
{
Close();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

10
samples/ControlCatalog/Pages/AcceleratorPage.xaml.cs

@ -1,18 +1,12 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class AcceleratorPage : UserControl
public partial class AcceleratorPage : UserControl
{
public AcceleratorPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
}
}

13
samples/ControlCatalog/Pages/AcrylicPage.xaml.cs

@ -1,19 +1,12 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Controls;
namespace ControlCatalog.Pages
{
public class AcrylicPage : UserControl
public partial class AcrylicPage : UserControl
{
public AcrylicPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
}
}

30
samples/ControlCatalog/Pages/AdornerLayerPage.xaml.cs

@ -1,47 +1,33 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class AdornerLayerPage : UserControl
public partial class AdornerLayerPage : UserControl
{
private Control? _adorner;
public AdornerLayerPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
private void RemoveAdorner_OnClick(object? sender, RoutedEventArgs e)
{
var adornerButton = this.FindControl<Button>("AdornerButton");
if (adornerButton is { })
var adorner = AdornerLayer.GetAdorner(AdornerButton);
if (adorner is { })
{
var adorner = AdornerLayer.GetAdorner(adornerButton);
if (adorner is { })
{
_adorner = adorner;
}
AdornerLayer.SetAdorner(adornerButton, null);
_adorner = adorner;
}
AdornerLayer.SetAdorner(AdornerButton, null);
}
private void AddAdorner_OnClick(object? sender, RoutedEventArgs e)
{
var adornerButton = this.FindControl<Button>("AdornerButton");
if (adornerButton is { })
if (_adorner is { })
{
if (_adorner is { })
{
AdornerLayer.SetAdorner(adornerButton, _adorner);
}
AdornerLayer.SetAdorner(AdornerButton, _adorner);
}
}
}

35
samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs

@ -1,18 +1,16 @@
using Avalonia.Controls;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Avalonia.Data.Converters;
using Avalonia.Controls;
using Avalonia.Data;
using ControlCatalog.Models;
using Avalonia.Data.Converters;
using Avalonia.LogicalTree;
namespace ControlCatalog.Pages
{
public class AutoCompleteBoxPage : UserControl
public partial class AutoCompleteBoxPage : UserControl
{
public class StateData
{
@ -90,7 +88,7 @@ namespace ControlCatalog.Pages
};
}
public StateData[] States { get; private set; }
private static LinkedList<string>[] BuildAllSentences()
{
return new string[]
@ -107,7 +105,7 @@ namespace ControlCatalog.Pages
public AutoCompleteBoxPage()
{
this.InitializeComponent();
InitializeComponent();
States = BuildAllStates();
Sentences = BuildAllSentences();
@ -125,16 +123,13 @@ namespace ControlCatalog.Pages
binding.Bindings.Add(new Binding("Name"));
binding.Bindings.Add(new Binding("Abbreviation"));
var multibindingBox = this.Get<AutoCompleteBox>("MultiBindingBox");
multibindingBox.ValueMemberBinding = binding;
MultiBindingBox.ValueMemberBinding = binding;
var asyncBox = this.Get<AutoCompleteBox>("AsyncBox");
asyncBox.AsyncPopulator = PopulateAsync;
AsyncBox.AsyncPopulator = PopulateAsync;
var customAutocompleteBox = this.Get<AutoCompleteBox>("CustomAutocompleteBox");
customAutocompleteBox.ItemsSource = Sentences.SelectMany(x => x);
customAutocompleteBox.TextFilter = LastWordContains;
customAutocompleteBox.TextSelector = AppendWord;
CustomAutocompleteBox.ItemsSource = Sentences.SelectMany(x => x);
CustomAutocompleteBox.TextFilter = LastWordContains;
CustomAutocompleteBox.TextSelector = AppendWord;
}
private IEnumerable<AutoCompleteBox> GetAllAutoCompleteBox()
{
@ -145,7 +140,8 @@ namespace ControlCatalog.Pages
private static bool StringContains(string str, string? query)
{
if (query == null) return false;
if (query == null)
return false;
return str.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;
}
private async Task<IEnumerable<object>> PopulateAsync(string? searchText, CancellationToken cancellationToken)
@ -196,10 +192,5 @@ namespace ControlCatalog.Pages
}
return string.Empty;
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

10
samples/ControlCatalog/Pages/BorderPage.xaml.cs

@ -1,18 +1,12 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class BorderPage : UserControl
public partial class BorderPage : UserControl
{
public BorderPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
}
}

12
samples/ControlCatalog/Pages/ButtonSpinnerPage.xaml.cs

@ -1,21 +1,13 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class ButtonSpinnerPage : UserControl
public partial class ButtonSpinnerPage : UserControl
{
public ButtonSpinnerPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
public void OnSpin(object sender, SpinEventArgs e)

2
samples/ControlCatalog/Pages/ButtonsPage.xaml

@ -141,7 +141,7 @@
Padding="15">
<StackPanel Orientation="Vertical"
Spacing="8">
<RepeatButton Name="RepeatButton">
<RepeatButton Name="RepeatButton" Click="OnRepeatButtonClick">
<TextBlock Name="RepeatButtonTextBlock"
Text="Repeat Button: 0" />
</RepeatButton>

16
samples/ControlCatalog/Pages/ButtonsPage.xaml.cs

@ -1,29 +1,21 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public class ButtonsPage : UserControl
public partial class ButtonsPage : UserControl
{
private int repeatButtonClickCount = 0;
public ButtonsPage()
{
InitializeComponent();
this.Get<RepeatButton>("RepeatButton").Click += OnRepeatButtonClick;
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public void OnRepeatButtonClick(object? sender, object args)
public void OnRepeatButtonClick(object? sender, RoutedEventArgs args)
{
repeatButtonClickCount++;
var textBlock = this.Get<TextBlock>("RepeatButtonTextBlock");
textBlock.Text = $"Repeat Button: {repeatButtonClickCount}";
RepeatButtonTextBlock.Text = $"Repeat Button: {repeatButtonClickCount}";
}
}
}

29
samples/ControlCatalog/Pages/CalendarDatePickerPage.xaml.cs

@ -1,36 +1,23 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using System;
using Avalonia.Controls;
namespace ControlCatalog.Pages
{
public class CalendarDatePickerPage : UserControl
public partial class CalendarDatePickerPage : UserControl
{
public CalendarDatePickerPage()
{
InitializeComponent();
var dp1 = this.Get<CalendarDatePicker>("DatePicker1");
var dp2 = this.Get<CalendarDatePicker>("DatePicker2");
var dp3 = this.Get<CalendarDatePicker>("DatePicker3");
var dp4 = this.Get<CalendarDatePicker>("DatePicker4");
var dp5 = this.Get<CalendarDatePicker>("DatePicker5");
dp1.SelectedDate = DateTime.Today;
dp2.SelectedDate = DateTime.Today.AddDays(10);
dp3.SelectedDate = DateTime.Today.AddDays(20);
dp5.SelectedDate = DateTime.Today;
DatePicker1.SelectedDate = DateTime.Today;
DatePicker2.SelectedDate = DateTime.Today.AddDays(10);
DatePicker3.SelectedDate = DateTime.Today.AddDays(20);
DatePicker5.SelectedDate = DateTime.Today;
dp4.TemplateApplied += (s, e) =>
DatePicker4.TemplateApplied += (s, e) =>
{
dp4.BlackoutDates?.AddDatesInPast();
DatePicker4.BlackoutDates?.AddDatesInPast();
};
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

24
samples/ControlCatalog/Pages/CalendarPage.xaml.cs

@ -1,28 +1,20 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using System;
using Avalonia.Controls;
namespace ControlCatalog.Pages
{
public class CalendarPage : UserControl
public partial class CalendarPage : UserControl
{
public CalendarPage()
{
this.InitializeComponent();
InitializeComponent();
var today = DateTime.Today;
var cal1 = this.Get<Calendar>("DisplayDatesCalendar");
cal1.DisplayDateStart = today.AddDays(-25);
cal1.DisplayDateEnd = today.AddDays(25);
var today = DateTime.Today;
DisplayDatesCalendar.DisplayDateStart = today.AddDays(-25);
DisplayDatesCalendar.DisplayDateEnd = today.AddDays(25);
var cal2 = this.Get<Calendar>("BlackoutDatesCalendar");
cal2.BlackoutDates.AddDatesInPast();
cal2.BlackoutDates.Add(new CalendarDateRange(today.AddDays(6)));
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
BlackoutDatesCalendar.BlackoutDates.AddDatesInPast();
BlackoutDatesCalendar.BlackoutDates.Add(new CalendarDateRange(today.AddDays(6)));
}
}
}

10
samples/ControlCatalog/Pages/CanvasPage.xaml.cs

@ -1,18 +1,12 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class CanvasPage : UserControl
public partial class CanvasPage : UserControl
{
public CanvasPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
}
}

40
samples/ControlCatalog/Pages/CarouselPage.xaml.cs

@ -1,53 +1,35 @@
using System;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class CarouselPage : UserControl
public partial class CarouselPage : UserControl
{
private Carousel _carousel;
private Button _left;
private Button _right;
private ComboBox _transition;
private ComboBox _orientation;
public CarouselPage()
{
this.InitializeComponent();
_carousel = this.Get<Carousel>("carousel");
_left = this.Get<Button>("left");
_right = this.Get<Button>("right");
_transition = this.Get<ComboBox>("transition");
_orientation = this.Get<ComboBox>("orientation");
_left.Click += (s, e) => _carousel.Previous();
_right.Click += (s, e) => _carousel.Next();
_transition.SelectionChanged += TransitionChanged;
_orientation.SelectionChanged += TransitionChanged;
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
left.Click += (s, e) => carousel.Previous();
right.Click += (s, e) => carousel.Next();
transition.SelectionChanged += TransitionChanged;
orientation.SelectionChanged += TransitionChanged;
}
private void TransitionChanged(object? sender, SelectionChangedEventArgs e)
{
switch (_transition.SelectedIndex)
switch (transition.SelectedIndex)
{
case 0:
_carousel.PageTransition = null;
carousel.PageTransition = null;
break;
case 1:
_carousel.PageTransition = new PageSlide(TimeSpan.FromSeconds(0.25), _orientation.SelectedIndex == 0 ? PageSlide.SlideAxis.Horizontal : PageSlide.SlideAxis.Vertical);
carousel.PageTransition = new PageSlide(TimeSpan.FromSeconds(0.25), orientation.SelectedIndex == 0 ? PageSlide.SlideAxis.Horizontal : PageSlide.SlideAxis.Vertical);
break;
case 2:
_carousel.PageTransition = new CrossFade(TimeSpan.FromSeconds(0.25));
carousel.PageTransition = new CrossFade(TimeSpan.FromSeconds(0.25));
break;
case 3:
_carousel.PageTransition = new Rotate3DTransition(TimeSpan.FromSeconds(0.5), _orientation.SelectedIndex == 0 ? PageSlide.SlideAxis.Horizontal : PageSlide.SlideAxis.Vertical);
carousel.PageTransition = new Rotate3DTransition(TimeSpan.FromSeconds(0.5), orientation.SelectedIndex == 0 ? PageSlide.SlideAxis.Horizontal : PageSlide.SlideAxis.Vertical);
break;
}
}

10
samples/ControlCatalog/Pages/CheckBoxPage.xaml.cs

@ -1,18 +1,12 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class CheckBoxPage : UserControl
public partial class CheckBoxPage : UserControl
{
public CheckBoxPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
}
}

14
samples/ControlCatalog/Pages/ClipboardPage.xaml.cs

@ -3,12 +3,10 @@ using System.Collections.Generic;
using System.Linq;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Documents;
using Avalonia.Controls.Notifications;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Platform.Storage;
using Avalonia.Threading;
@ -28,16 +26,9 @@ namespace ControlCatalog.Pages
private DataTransfer? _storedDataTransfer;
public ClipboardPage()
{
InitializeComponent();
_clipboardLastDataObjectChecker =
new DispatcherTimer(TimeSpan.FromSeconds(0.5), default, CheckLastDataObject);
InitializeComponent();
}
private TextBox ClipboardContent => this.Get<TextBox>("ClipboardContent");
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private async void CopyText(object? sender, RoutedEventArgs args)
@ -163,11 +154,10 @@ namespace ControlCatalog.Pages
base.OnDetachedFromVisualTree(e);
}
private Run OwnsClipboardDataObject => this.Get<Run>("OwnsClipboardDataObject");
private bool _checkingClipboardDataTransfer;
private async void CheckLastDataObject(object? sender, EventArgs e)
{
if(_checkingClipboardDataTransfer)
if (_checkingClipboardDataTransfer)
return;
try
{

10
samples/ControlCatalog/Pages/ColorPickerPage.xaml.cs

@ -1,7 +1,6 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Layout;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
namespace ControlCatalog.Pages
@ -12,8 +11,6 @@ namespace ControlCatalog.Pages
{
InitializeComponent();
var layoutRoot = this.GetControl<Grid>("LayoutRoot");
// ColorPicker added from code-behind
var colorPicker = new ColorPicker()
{
@ -24,13 +21,8 @@ namespace ControlCatalog.Pages
};
Grid.SetColumn(colorPicker, 2);
Grid.SetRow(colorPicker, 1);
layoutRoot.Children.Add(colorPicker);
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
LayoutRoot.Children.Add(colorPicker);
}
}
}

14
samples/ControlCatalog/Pages/ComboBoxPage.xaml.cs

@ -1,25 +1,17 @@
using System.Linq;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
public class ComboBoxPage : UserControl
public partial class ComboBoxPage : UserControl
{
public ComboBoxPage()
{
this.InitializeComponent();
DataContext = new ComboBoxPageViewModel();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
var fontComboBox = this.Get<ComboBox>("fontComboBox");
InitializeComponent();
fontComboBox.ItemsSource = FontManager.Current.SystemFonts;
fontComboBox.SelectedIndex = 0;
DataContext = new ComboBoxPageViewModel();
}
}
}

8
samples/ControlCatalog/Pages/CompositionPage.axaml.cs

@ -6,7 +6,6 @@ using Avalonia;
using Avalonia.Animation;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Media.Immutable;
using Avalonia.Rendering.Composition;
@ -25,15 +24,14 @@ public partial class CompositionPage : UserControl
public CompositionPage()
{
InitializeComponent();
AttachAnimatedSolidVisual(this.FindControl<Control>("SolidVisualHost")!);
AttachCustomVisual(this.FindControl<Control>("CustomVisualHost")!);
AttachAnimatedSolidVisual(SolidVisualHost);
AttachCustomVisual(CustomVisualHost);
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
this.Get<ItemsControl>("Items").ItemsSource = CreateColorItems();
Items.ItemsSource = CreateColorItems();
}
private static List<CompositionPageColorItem> CreateColorItems()

10
samples/ControlCatalog/Pages/ContainerQueryPage.xaml.cs

@ -1,18 +1,12 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class ContainerQueryPage : UserControl
public partial class ContainerQueryPage : UserControl
{
public ContainerQueryPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
}
}

15
samples/ControlCatalog/Pages/ContextFlyoutPage.xaml

@ -72,16 +72,17 @@
<TextBlock Text="Dynamically Generated"/>
</Border>
<Border x:Name="CustomContextRequestedBorder"
Classes="context-target">
Classes="context-target"
ContextRequested="CustomContextRequested">
<Border.ContextFlyout>
<Flyout Content="Should never be visible" />
</Border.ContextFlyout>
<TextBlock Text="Custom ContextRequested handler" TextWrapping="Wrap" />
</Border>
<Border x:Name="CancellableContextBorder"
Classes="context-target">
Classes="context-target">
<Border.ContextFlyout>
<Flyout>
<Flyout Opening="ContextFlyoutPage_Opening" Closing="ContextFlyoutPage_Closing">
<CheckBox x:Name="CancelCloseCheckBox" Content="Cancel close" />
</Flyout>
</Border.ContextFlyout>
@ -114,28 +115,28 @@
<Setter Property="Opacity" Value="0.5" />
</Style>
</StackPanel.Styles>
<Button Name="CutButton"
<Button Click="CloseFlyout"
Command="{Binding $parent[TextBox].Cut}"
IsEnabled="{Binding $parent[TextBox].CanCut}">
<PathIcon Width="14"
Height="14"
Data="M5.22774,2.08072 C5.43359778,1.94704 5.7011484,1.98419259 5.86368634,2.15675215 L5.91939,2.22774 L12.5191,12.3904 C12.956,12.1419 13.4614,12.0000019 14,12.0000019 C15.6569,12.0000019 17,13.3431 17,15.0000019 C17,16.6569 15.6569,18.0000019 14,18.0000019 C12.3431,18.0000019 11,16.6569 11,15.0000019 C11,14.3201402 11.226152,13.693011 11.6073785,13.1899092 L11.7401,13.0269 L10,10.3474 L8.25991,13.0269 C8.72078,13.5543 9,14.2446 9,15.0000019 C9,16.6569 7.65685,18.0000019 6,18.0000019 C4.34315,18.0000019 3,16.6569 3,15.0000019 C3,13.3431 4.34315,12.0000019 6,12.0000019 C6.46163143,12.0000019 6.89890041,12.1042536 7.28955831,12.2905296 L7.4809,12.3904 L9.40382,9.42936 L5.08072,2.77238 C4.93033,2.54079 4.99615,2.23112 5.22774,2.08072 Z M14,13 C12.8954,13 12,13.8954 12,15 C12,16.1046 12.8954,17 14,17 C15.1046,17 16,16.1046 16,15 C16,13.8954 15.1046,13 14,13 Z M6,13 C4.89543,13 4,13.8954 4,15 C4,16.1046 4.89543,17 6,17 C7.10457,17 8,16.1046 8,15 C8,13.8954 7.10457,13 6,13 Z M14.7723,2.08072 C15.0039,2.23112 15.0697,2.54079 14.9193,2.77238 L11.1924,8.51133 L10.5962,7.59329 L14.0806,2.22774 C14.231,1.99615 14.5407,1.93033 14.7723,2.08072 Z" />
</Button>
<Button Name="CopyButton"
<Button Click="CloseFlyout"
Command="{Binding $parent[TextBox].Copy}"
IsEnabled="{Binding $parent[TextBox].CanCopy}">
<PathIcon Width="14"
Height="14"
Data="M5.50280381,4.62704038 L5.5,6.75 L5.5,17.2542087 C5.5,19.0491342 6.95507456,20.5042087 8.75,20.5042087 L17.3662868,20.5044622 C17.057338,21.3782241 16.2239751,22.0042087 15.2444057,22.0042087 L8.75,22.0042087 C6.12664744,22.0042087 4,19.8775613 4,17.2542087 L4,6.75 C4,5.76928848 4.62744523,4.93512464 5.50280381,4.62704038 Z M17.75,2 C18.9926407,2 20,3.00735931 20,4.25 L20,17.25 C20,18.4926407 18.9926407,19.5 17.75,19.5 L8.75,19.5 C7.50735931,19.5 6.5,18.4926407 6.5,17.25 L6.5,4.25 C6.5,3.00735931 7.50735931,2 8.75,2 L17.75,2 Z M17.75,3.5 L8.75,3.5 C8.33578644,3.5 8,3.83578644 8,4.25 L8,17.25 C8,17.6642136 8.33578644,18 8.75,18 L17.75,18 C18.1642136,18 18.5,17.6642136 18.5,17.25 L18.5,4.25 C18.5,3.83578644 18.1642136,3.5 17.75,3.5 Z" />
</Button>
<Button Name="PasteButton"
<Button Click="CloseFlyout"
Command="{Binding $parent[TextBox].Paste}"
IsEnabled="{Binding $parent[TextBox].CanPaste}">
<PathIcon Width="14"
Height="14"
Data="M13.75,2 C14.940864,2 15.9156449,2.92516159 15.9948092,4.09595119 L16,4.25 L16,4.25 C16,4.16530567 15.9953205,4.0817043 15.9862059,3.99944035 L17.75,4 C18.9926407,4 20,5.00735931 20,6.25 L20,19.75 C20,20.9926407 18.9926407,22 17.75,22 L6.25,22 C5.00735931,22 4,20.9926407 4,19.75 L4,6.25 C4,5.00735931 5.00735931,4 6.25,4 L8.01379413,3.99944035 C8.00733496,4.05773764 8.00310309,4.11670658 8.00118552,4.17626017 L8,4.25 C8,3.00735931 9.00735931,2 10.25,2 L13.75,2 Z M13.75,6.5 L10.25,6.5 C9.45594921,6.5 8.75796956,6.08867052 8.357512,5.4674625 L8.37902077,5.50019943 L8.37902077,5.50019943 L6.25,5.5 C5.83578644,5.5 5.5,5.83578644 5.5,6.25 L5.5,19.75 C5.5,20.1642136 5.83578644,20.5 6.25,20.5 L17.75,20.5 C18.1642136,20.5 18.5,20.1642136 18.5,19.75 L18.5,6.25 C18.5,5.83578644 18.1642136,5.5 17.75,5.5 L15.6209792,5.50019943 L15.642488,5.4674625 C15.2420304,6.08867052 14.5440508,6.5 13.75,6.5 Z M13.75,3.5 L10.25,3.5 C9.83578644,3.5 9.5,3.83578644 9.5,4.25 C9.5,4.66421356 9.83578644,5 10.25,5 L13.75,5 C14.1642136,5 14.5,4.66421356 14.5,4.25 C14.5,3.83578644 14.1642136,3.5 13.75,3.5 Z" />
</Button>
<Button Name="ClearButton" Command="{Binding $parent[TextBox].Clear}">
<Button Click="CloseFlyout" Command="{Binding $parent[TextBox].Clear}">
<PathIcon Width="14"
Height="14"
Data="M3.52499419,3.71761187 L3.61611652,3.61611652 C4.0717282,3.16050485 4.79154862,3.13013074 5.28238813,3.52499419 L5.38388348,3.61611652 L14,12.233 L22.6161165,3.61611652 C23.1042719,3.12796116 23.8957281,3.12796116 24.3838835,3.61611652 C24.8720388,4.10427189 24.8720388,4.89572811 24.3838835,5.38388348 L15.767,14 L24.3838835,22.6161165 C24.8394952,23.0717282 24.8698693,23.7915486 24.4750058,24.2823881 L24.3838835,24.3838835 C23.9282718,24.8394952 23.2084514,24.8698693 22.7176119,24.4750058 L22.6161165,24.3838835 L14,15.767 L5.38388348,24.3838835 C4.89572811,24.8720388 4.10427189,24.8720388 3.61611652,24.3838835 C3.12796116,23.8957281 3.12796116,23.1042719 3.61611652,22.6161165 L12.233,14 L3.61611652,5.38388348 C3.16050485,4.9282718 3.13013074,4.20845138 3.52499419,3.71761187 L3.61611652,3.61611652 L3.52499419,3.71761187 Z" />

48
samples/ControlCatalog/Pages/ContextFlyoutPage.xaml.cs

@ -1,44 +1,17 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using ControlCatalog.ViewModels;
using Avalonia.Interactivity;
using System;
using System.ComponentModel;
using Avalonia.Controls;
using Avalonia.Interactivity;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
public class ContextFlyoutPage : UserControl
public partial class ContextFlyoutPage : UserControl
{
private TextBox _textBox;
public ContextFlyoutPage()
{
InitializeComponent();
DataContext = new ContextPageViewModel();
_textBox = this.Get<TextBox>("TextBox");
var cutButton = this.Get<Button>("CutButton");
cutButton.Click += CloseFlyout;
var copyButton = this.Get<Button>("CopyButton");
copyButton.Click += CloseFlyout;
var pasteButton = this.Get<Button>("PasteButton");
pasteButton.Click += CloseFlyout;
var clearButton = this.Get<Button>("ClearButton");
clearButton.Click += CloseFlyout;
var customContextRequestedBorder = this.Get<Border>("CustomContextRequestedBorder");
customContextRequestedBorder.AddHandler(ContextRequestedEvent, CustomContextRequested, RoutingStrategies.Tunnel);
var cancellableContextBorder = this.Get<Border>("CancellableContextBorder");
var flyout = (Flyout)cancellableContextBorder.ContextFlyout!;
flyout.Closing += ContextFlyoutPage_Closing;
flyout.Opening += ContextFlyoutPage_Opening;
}
private ContextPageViewModel? _model;
@ -55,22 +28,20 @@ namespace ControlCatalog.Pages
private void ContextFlyoutPage_Closing(object? sender, CancelEventArgs e)
{
var cancelCloseCheckBox = this.FindControl<CheckBox>("CancelCloseCheckBox");
e.Cancel = cancelCloseCheckBox?.IsChecked ?? false;
e.Cancel = CancelCloseCheckBox?.IsChecked ?? false;
}
private void ContextFlyoutPage_Opening(object? sender, EventArgs e)
{
if (e is CancelEventArgs cancelArgs)
{
var cancelCloseCheckBox = this.FindControl<CheckBox>("CancelOpenCheckBox");
cancelArgs.Cancel = cancelCloseCheckBox?.IsChecked ?? false;
cancelArgs.Cancel = CancelCloseCheckBox?.IsChecked ?? false;
}
}
private void CloseFlyout(object? sender, RoutedEventArgs e)
{
_textBox.ContextFlyout?.Hide();
TextBox.ContextFlyout?.Hide();
}
public void CustomContextRequested(object? sender, ContextRequestedEventArgs e)
@ -83,10 +54,5 @@ namespace ControlCatalog.Pages
e.Handled = true;
}
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

4
samples/ControlCatalog/Pages/ContextMenuPage.xaml

@ -61,7 +61,7 @@
</Border.ContextMenu>
<TextBlock Text="Dynamically Generated"/>
</Border>
<Border x:Name="CustomContextRequestedBorder">
<Border x:Name="CustomContextRequestedBorder" ContextRequested="CustomContextRequested">
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="Should never be visible" />
@ -71,7 +71,7 @@
</Border>
<Border x:Name="CancellableContextBorder">
<Border.ContextMenu>
<ContextMenu>
<ContextMenu Opening="ContextFlyoutPage_Opening" Closing="ContextFlyoutPage_Closing">
<MenuItem>
<MenuItem.Header>
<CheckBox x:Name="CancelCloseCheckBox" Content="Cancel close" />

32
samples/ControlCatalog/Pages/ContextMenuPage.xaml.cs

@ -1,26 +1,16 @@
using System;
using System.ComponentModel;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
public class ContextMenuPage : UserControl
public partial class ContextMenuPage : UserControl
{
public ContextMenuPage()
{
this.InitializeComponent();
InitializeComponent();
DataContext = new ContextPageViewModel();
var customContextRequestedBorder = this.Get<Border>("CustomContextRequestedBorder");
customContextRequestedBorder.AddHandler(ContextRequestedEvent, CustomContextRequested, RoutingStrategies.Tunnel);
var cancellableContextBorder = this.Get<Border>("CancellableContextBorder");
cancellableContextBorder.ContextMenu!.Closing += ContextFlyoutPage_Closing;
cancellableContextBorder.ContextMenu!.Opening += ContextFlyoutPage_Opening;
}
private ContextPageViewModel? _model;
@ -28,7 +18,7 @@ namespace ControlCatalog.Pages
{
if (_model != null)
_model.View = null;
_model = DataContext as ContextPageViewModel;
_model = DataContext as ContextPageViewModel;
if (_model != null)
_model.View = this;
@ -37,17 +27,12 @@ namespace ControlCatalog.Pages
private void ContextFlyoutPage_Closing(object? sender, CancelEventArgs e)
{
var cancelCloseCheckBox = this.FindControl<CheckBox>("CancelCloseCheckBox");
e.Cancel = cancelCloseCheckBox?.IsChecked ?? false;
e.Cancel = CancelCloseCheckBox?.IsChecked ?? false;
}
private void ContextFlyoutPage_Opening(object? sender, EventArgs e)
private void ContextFlyoutPage_Opening(object? sender, CancelEventArgs e)
{
if (e is CancelEventArgs cancelArgs)
{
var cancelCloseCheckBox = this.FindControl<CheckBox>("CancelOpenCheckBox");
cancelArgs.Cancel = cancelCloseCheckBox?.IsChecked ?? false;
}
e.Cancel = CancelOpenCheckBox?.IsChecked ?? false;
}
public void CustomContextRequested(object? sender, ContextRequestedEventArgs e)
@ -61,10 +46,5 @@ namespace ControlCatalog.Pages
}
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

10
samples/ControlCatalog/Pages/CursorPage.xaml.cs

@ -1,20 +1,14 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
public class CursorPage : UserControl
public partial class CursorPage : UserControl
{
public CursorPage()
{
this.InitializeComponent();
InitializeComponent();
DataContext = new CursorPageViewModel();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

43
samples/ControlCatalog/Pages/CustomDrawing.xaml.cs

@ -1,7 +1,6 @@
using System;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
@ -12,56 +11,30 @@ namespace ControlCatalog.Pages
InitializeComponent();
}
private CustomDrawingExampleControl? _customControl;
public CustomDrawingExampleControl CustomDrawingControl
private void RotateMinus(object? sender, RoutedEventArgs e)
{
get
{
if (_customControl is not null)
return _customControl;
throw new System.Exception("Control did not get initialized");
}
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
var cntrl = this.FindControl<CustomDrawingExampleControl>("CustomDrawingControl");
if (cntrl != null)
{
_customControl = cntrl;
}
else
{
// be sad about it
}
}
private void RotateMinus (object? sender, RoutedEventArgs e)
{
if (_customControl is null) return;
_customControl.Rotation -= Math.PI / 20.0d;
CustomDrawingControl.Rotation -= Math.PI / 20.0d;
}
private void RotatePlus(object? sender, RoutedEventArgs e)
{
if (_customControl is null)
if (CustomDrawingControl is null)
return;
_customControl.Rotation += Math.PI / 20.0d;
CustomDrawingControl.Rotation += Math.PI / 20.0d;
}
private void ZoomIn(object? sender, RoutedEventArgs e)
{
if (_customControl is null)
if (CustomDrawingControl is null)
return;
_customControl.Scale *= 1.2d;
CustomDrawingControl.Scale *= 1.2d;
}
private void ZoomOut(object? sender, RoutedEventArgs e)
{
if (_customControl is null)
if (CustomDrawingControl is null)
return;
_customControl.Scale /= 1.2d;
CustomDrawingControl.Scale /= 1.2d;
}
}
}

7
samples/ControlCatalog/Pages/DataGridPage.xaml.cs

@ -4,8 +4,13 @@ using Avalonia.Interactivity;
namespace ControlCatalog.Pages;
public class DataGridPage : UserControl
public partial class DataGridPage : UserControl
{
public DataGridPage()
{
InitializeComponent();
}
private void OnLinkClicked(object? sender, RoutedEventArgs e)
{
Process.Start(new ProcessStartInfo

17
samples/ControlCatalog/Pages/DateTimePickerPage.xaml.cs

@ -1,30 +1,23 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Controls;
namespace ControlCatalog.Pages
{
public class DateTimePickerPage : UserControl
public partial class DateTimePickerPage : UserControl
{
public DateTimePickerPage()
{
this.InitializeComponent();
this.Get<TextBlock>("DatePickerDesc").Text = "Use a DatePicker to let users set a date in your app, " +
InitializeComponent();
DatePickerDesc.Text = "Use a DatePicker to let users set a date in your app, " +
"for example to schedule an appointment. The DatePicker displays three controls for month, day, and year. " +
"These controls are easy to use with touch or mouse, and they can be styled and configured in several different ways. " +
"Order of month, day, and year is dynamically set based on user date settings";
this.Get<TextBlock>("TimePickerDesc").Text = "Use a TimePicker to let users set a time in your app, for example " +
TimePickerDesc.Text = "Use a TimePicker to let users set a time in your app, for example " +
"to set a reminder. The TimePicker displays four controls for hour, minute, seconds(optional), and AM / PM(if necessary).These controls " +
"are easy to use with touch or mouse, and they can be styled and configured in several different ways. " +
"12 - hour or 24 - hour clock and visibility of AM / PM is dynamically set based on user time settings, or can be overridden.";
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

77
samples/ControlCatalog/Pages/DialogsPage.xaml.cs

@ -21,22 +21,22 @@ using Avalonia.Platform.Storage.FileIO;
namespace ControlCatalog.Pages
{
public class DialogsPage : UserControl
public partial class DialogsPage : UserControl
{
public DialogsPage()
{
this.InitializeComponent();
InitializeComponent();
IStorageFolder? lastSelectedDirectory = null;
IStorageItem? lastSelectedItem = null;
bool ignoreTextChanged = false;
var results = this.Get<ItemsControl>("PickerLastResults");
var resultsVisible = this.Get<TextBlock>("PickerLastResultsVisible");
var bookmarkContainer = this.Get<TextBox>("BookmarkContainer");
var openedFileContent = this.Get<TextBox>("OpenedFileContent");
var openMultiple = this.Get<CheckBox>("OpenMultiple");
var currentFolderBox = this.Get<AutoCompleteBox>("CurrentFolderBox");
var results = PickerLastResults;
var resultsVisible = PickerLastResultsVisible;
var bookmarkContainer = BookmarkContainer;
var openedFileContent = OpenedFileContent;
var openMultiple = OpenMultiple;
var currentFolderBox = CurrentFolderBox;
currentFolderBox.TextChanged += async (sender, args) =>
{
@ -61,7 +61,7 @@ namespace ControlCatalog.Pages
}
catch (SecurityException)
{
}
}
}
@ -78,7 +78,7 @@ namespace ControlCatalog.Pages
List<FilePickerFileType>? GetFileTypes()
{
var selectedItem = (this.Get<ComboBox>("FilterSelector").SelectedItem as ComboBoxItem)?.Content
var selectedItem = (FilterSelector.SelectedItem as ComboBoxItem)?.Content
?? "None";
var binLogType = new FilePickerFileType("Binary Log")
@ -115,7 +115,7 @@ namespace ControlCatalog.Pages
};
}
this.Get<Button>("OpenFile").Click += async delegate
OpenFile.Click += async delegate
{
// Almost guaranteed to exist
var uri = Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName;
@ -132,7 +132,7 @@ namespace ControlCatalog.Pages
results.ItemsSource = result;
resultsVisible.IsVisible = result?.Any() == true;
};
this.Get<Button>("OpenMultipleFiles").Click += async delegate
OpenMultipleFiles.Click += async delegate
{
var result = await new OpenFileDialog()
{
@ -144,7 +144,7 @@ namespace ControlCatalog.Pages
results.ItemsSource = result;
resultsVisible.IsVisible = result?.Any() == true;
};
this.Get<Button>("SaveFile").Click += async delegate
SaveFile.Click += async delegate
{
var filters = GetFilters();
var result = await new SaveFileDialog()
@ -158,7 +158,7 @@ namespace ControlCatalog.Pages
results.ItemsSource = new[] { result };
resultsVisible.IsVisible = result != null;
};
this.Get<Button>("SelectFolder").Click += async delegate
SelectFolder.Click += async delegate
{
var result = await new OpenFolderDialog()
{
@ -176,7 +176,7 @@ namespace ControlCatalog.Pages
resultsVisible.IsVisible = true;
}
};
this.Get<Button>("OpenBoth").Click += async delegate
OpenBoth.Click += async delegate
{
var result = await new OpenFileDialog()
{
@ -190,35 +190,35 @@ namespace ControlCatalog.Pages
results.ItemsSource = result;
resultsVisible.IsVisible = result?.Any() == true;
};
this.Get<Button>("DecoratedWindow").Click += delegate
DecoratedWindow.Click += delegate
{
new DecoratedWindow().Show();
};
this.Get<Button>("DecoratedWindowDialog").Click += delegate
DecoratedWindowDialog.Click += delegate
{
_ = new DecoratedWindow().ShowDialog(GetWindow());
};
this.Get<Button>("Dialog").Click += delegate
Dialog.Click += delegate
{
var window = CreateSampleWindow();
window.Height = 200;
_ = window.ShowDialog(GetWindow());
};
this.Get<Button>("DialogNoTaskbar").Click += delegate
DialogNoTaskbar.Click += delegate
{
var window = CreateSampleWindow();
window.Height = 200;
window.ShowInTaskbar = false;
_ = window.ShowDialog(GetWindow());
};
this.Get<Button>("OwnedWindow").Click += delegate
OwnedWindow.Click += delegate
{
var window = CreateSampleWindow();
window.Show(GetWindow());
};
this.Get<Button>("OwnedWindowNoTaskbar").Click += delegate
OwnedWindowNoTaskbar.Click += delegate
{
var window = CreateSampleWindow();
@ -227,7 +227,7 @@ namespace ControlCatalog.Pages
window.Show(GetWindow());
};
this.Get<Button>("OpenFilePicker").Click += async delegate
OpenFilePicker.Click += async delegate
{
var result = await GetStorageProvider().OpenFilePickerAsync(new FilePickerOpenOptions()
{
@ -240,7 +240,7 @@ namespace ControlCatalog.Pages
await SetPickerResult(result);
};
this.Get<Button>("SaveFilePicker").Click += async delegate
SaveFilePicker.Click += async delegate
{
var fileTypes = GetFileTypes();
var file = await GetStorageProvider().SaveFilePickerAsync(new FilePickerSaveOptions()
@ -276,7 +276,7 @@ namespace ControlCatalog.Pages
await SetPickerResult(file is null ? null : new[] { file });
};
this.Get<Button>("SaveFilePickerWithResult").Click += async delegate
SaveFilePickerWithResult.Click += async delegate
{
var result = await GetStorageProvider().SaveFilePickerWithResultAsync(new FilePickerSaveOptions()
{
@ -318,7 +318,7 @@ namespace ControlCatalog.Pages
await SetPickerResult(result.File is null ? null : new[] { result.File }, result.SelectedFileType);
};
this.Get<Button>("OpenFolderPicker").Click += async delegate
OpenFolderPicker.Click += async delegate
{
var folders = await GetStorageProvider().OpenFolderPickerAsync(new FolderPickerOpenOptions()
{
@ -330,7 +330,7 @@ namespace ControlCatalog.Pages
await SetPickerResult(folders);
};
this.Get<Button>("OpenFileFromBookmark").Click += async delegate
OpenFileFromBookmark.Click += async delegate
{
var file = bookmarkContainer.Text is not null
? await GetStorageProvider().OpenFileBookmarkAsync(bookmarkContainer.Text)
@ -338,7 +338,7 @@ namespace ControlCatalog.Pages
await SetPickerResult(file is null ? null : new[] { file });
};
this.Get<Button>("OpenFolderFromBookmark").Click += async delegate
OpenFolderFromBookmark.Click += async delegate
{
var folder = bookmarkContainer.Text is not null
? await GetStorageProvider().OpenFolderBookmarkAsync(bookmarkContainer.Text)
@ -346,11 +346,11 @@ namespace ControlCatalog.Pages
await SetPickerResult(folder is null ? null : new[] { folder });
};
this.Get<Button>("LaunchUri").Click += async delegate
LaunchUri.Click += async delegate
{
var statusBlock = this.Get<TextBlock>("LaunchStatus");
if (Uri.TryCreate(this.Get<TextBox>("UriToLaunch").Text, UriKind.Absolute, out var uri))
var statusBlock = LaunchStatus;
if (Uri.TryCreate(UriToLaunch.Text, UriKind.Absolute, out var uri))
{
var result = await TopLevel.GetTopLevel(this)!.Launcher.LaunchUriAsync(uri);
statusBlock.Text = "LaunchUriAsync returned " + result;
@ -361,9 +361,9 @@ namespace ControlCatalog.Pages
}
};
this.Get<Button>("LaunchFile").Click += async delegate
LaunchFile.Click += async delegate
{
var statusBlock = this.Get<TextBlock>("LaunchStatus");
var statusBlock = LaunchStatus;
if (lastSelectedItem is not null)
{
var result = await TopLevel.GetTopLevel(this)!.Launcher.LaunchFileAsync(lastSelectedItem);
@ -482,7 +482,7 @@ namespace ControlCatalog.Pages
{
base.OnAttachedToVisualTree(e);
var openedFileContent = this.Get<TextBox>("OpenedFileContent");
var openedFileContent = OpenedFileContent;
try
{
var storageProvider = GetStorageProvider();
@ -541,7 +541,7 @@ CanPickFolder: {storageProvider.CanPickFolder}";
private IStorageProvider GetStorageProvider()
{
var forceManaged = this.Get<CheckBox>("ForceManaged").IsChecked ?? false;
var forceManaged = ForceManaged.IsChecked ?? false;
return forceManaged
? new ManagedStorageProvider(GetWindow()) // NOTE: In your production App use 'AppBuilder.UseManagedSystemDialogs()'
: GetTopLevel().StorageProvider;
@ -555,11 +555,6 @@ CanPickFolder: {storageProvider.CanPickFolder}";
Window GetWindow() => TopLevel.GetTopLevel(this) as Window ?? throw new NullReferenceException("Invalid Owner");
TopLevel GetTopLevel() => TopLevel.GetTopLevel(this) ?? throw new NullReferenceException("Invalid Owner");
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}
#pragma warning restore CS0618 // Type or member is obsolete
}

19
samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs

@ -1,27 +1,21 @@
using System;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Input.Platform;
using Avalonia.Markup.Xaml;
using Avalonia.Platform.Storage;
namespace ControlCatalog.Pages
{
public class DragAndDropPage : UserControl
public partial class DragAndDropPage : UserControl
{
private readonly TextBlock _dropState;
private readonly DataFormat<string> _customFormat =
DataFormat.CreateStringApplicationFormat("xxx-avalonia-controlcatalog-custom");
public DragAndDropPage()
{
InitializeComponent();
_dropState = this.Get<TextBlock>("DropState");
int textCount = 0;
@ -121,7 +115,7 @@ namespace ControlCatalog.Pages
if (e.DataTransfer.Contains(DataFormat.Text))
{
_dropState.Text = e.DataTransfer.TryGetText();
DropState.Text = e.DataTransfer.TryGetText();
}
else if (e.DataTransfer.Contains(DataFormat.File))
{
@ -146,11 +140,11 @@ namespace ControlCatalog.Pages
}
}
_dropState.Text = contentStr;
DropState.Text = contentStr;
}
else if (e.DataTransfer.Contains(_customFormat))
{
_dropState.Text = "Custom: " + e.DataTransfer.TryGetValue(_customFormat);
DropState.Text = "Custom: " + e.DataTransfer.TryGetValue(_customFormat);
}
}
@ -159,10 +153,5 @@ namespace ControlCatalog.Pages
AddHandler(DragDrop.DropEvent, Drop);
AddHandler(DragDrop.DragOverEvent, DragOver);
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

13
samples/ControlCatalog/Pages/ExpanderPage.xaml.cs

@ -1,26 +1,17 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
public class ExpanderPage : UserControl
public partial class ExpanderPage : UserControl
{
public ExpanderPage()
{
this.InitializeComponent();
InitializeComponent();
DataContext = new ExpanderPageViewModel();
var CollapsingDisabledExpander = this.Get<Expander>("CollapsingDisabledExpander");
var ExpandingDisabledExpander = this.Get<Expander>("ExpandingDisabledExpander");
CollapsingDisabledExpander.Collapsing += (s, e) => { e.Cancel = true; };
ExpandingDisabledExpander.Expanding += (s, e) => { e.Cancel = true; };
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

14
samples/ControlCatalog/Pages/FlyoutsPage.axaml.cs

@ -13,11 +13,7 @@ namespace ControlCatalog.Pages
{
InitializeComponent();
var afp = this.FindControl<Panel>("AttachedFlyoutPanel");
if (afp != null)
{
afp.DoubleTapped += Afp_DoubleTapped;
}
AttachedFlyoutPanel.DoubleTapped += Afp_DoubleTapped;
SetXamlTexts();
}
@ -32,7 +28,7 @@ namespace ControlCatalog.Pages
private void SetXamlTexts()
{
var bfxt = this.Get<TextBlock>("ButtonFlyoutXamlText");
var bfxt = ButtonFlyoutXamlText;
bfxt.Text = "<Button Content=\"Click me!\">\n" +
" <Button.Flyout>\n" +
" <Flyout>\n" +
@ -42,7 +38,7 @@ namespace ControlCatalog.Pages
" </Flyout>\n" +
" </Button.Flyout>\n</Button>";
var mfxt = this.Get<TextBlock>("MenuFlyoutXamlText");
var mfxt = this.MenuFlyoutXamlText;
mfxt.Text = "<Button Content=\"Click me!\">\n" +
" <Button.Flyout>\n" +
" <MenuFlyout>\n" +
@ -51,7 +47,7 @@ namespace ControlCatalog.Pages
" </MenuFlyout>\n" +
" </Button.Flyout>\n</Button>";
var afxt = this.Get<TextBlock>("AttachedFlyoutXamlText");
var afxt = this.AttachedFlyoutXamlText;
afxt.Text = "<Panel Name=\"AttachedFlyoutPanel\">\n" +
" <FlyoutBase.AttachedFlyout>\n" +
" <Flyout>\n" +
@ -63,7 +59,7 @@ namespace ControlCatalog.Pages
"\n\n In DoubleTapped handler:\n" +
"FlyoutBase.ShowAttachedFlyout(AttachedFlyoutPanel);";
var sfxt = this.Get<TextBlock>("SharedFlyoutXamlText");
var sfxt = this.SharedFlyoutXamlText;
sfxt.Text = "Declare a flyout in Resources:\n" +
"<Window.Resources>\n" +
" <Flyout x:Key=\"SharedFlyout\">\n" +

53
samples/ControlCatalog/Pages/GesturePage.cs

@ -1,75 +1,61 @@
using System;
using System.Numerics;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml;
using Avalonia.Rendering.Composition;
using Avalonia.Utilities;
namespace ControlCatalog.Pages
{
public class GesturePage : UserControl
public partial class GesturePage : UserControl
{
private bool _isInit;
private double _currentScale;
public GesturePage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
if(_isInit)
if (_isInit)
{
return;
}
_isInit = true;
SetPullHandlers(this.Find<Border>("TopPullZone"), false);
SetPullHandlers(this.Find<Border>("BottomPullZone"), true);
SetPullHandlers(this.Find<Border>("RightPullZone"), true);
SetPullHandlers(this.Find<Border>("LeftPullZone"), false);
SetPullHandlers(TopPullZone, false);
SetPullHandlers(BottomPullZone, true);
SetPullHandlers(RightPullZone, true);
SetPullHandlers(LeftPullZone, false);
var image = this.Get<Image>("PinchImage");
var image = PinchImage;
SetPinchHandlers(image);
var reset = this.Get<Button>("ResetButton");
var reset = ResetButton;
reset.Click += (_, _) =>
{
var compositionVisual = ElementComposition.GetElementVisual(image);
if(compositionVisual!= null)
if (compositionVisual != null)
{
_currentScale = 1;
compositionVisual.Scale = new (1,1,1);
compositionVisual.Scale = new(1, 1, 1);
compositionVisual.Offset = default;
image.InvalidateMeasure();
}
};
if (this.Find<Slider>("AngleSlider") is { } slider &&
this.Find<Panel>("RotationGesture") is { } rotationGesture
)
RotationGesture.AddHandler(Gestures.PinchEvent, (s, e) =>
{
rotationGesture.AddHandler(Gestures.PinchEvent, (s, e) =>
{
slider.Value = e.Angle;
});
}
AngleSlider.Value = e.Angle;
});
}
private void SetPinchHandlers(Control? control)
@ -101,7 +87,7 @@ namespace ControlCatalog.Pages
{
compositionVisual.Scale = new(_currentScale, _currentScale, 1);
if(currentOffset == default)
if (currentOffset == default)
{
currentOffset = compositionVisual.Offset;
}
@ -112,7 +98,7 @@ namespace ControlCatalog.Pages
{
InitComposition(control!);
if(compositionVisual != null)
if (compositionVisual != null)
{
var scale = _currentScale * (float)e.Scale;
@ -159,13 +145,8 @@ namespace ControlCatalog.Pages
});
}
private void SetPullHandlers(Control? control, bool inverse)
private void SetPullHandlers(Control control, bool inverse)
{
if (control == null)
{
return;
}
var ball = control.FindLogicalDescendantOfType<Border>();
Vector3D defaultOffset = default;

33
samples/ControlCatalog/Pages/ImagePage.xaml.cs

@ -1,60 +1,45 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
namespace ControlCatalog.Pages
{
public class ImagePage : UserControl
public partial class ImagePage : UserControl
{
private readonly Image _bitmapImage;
private readonly Image _drawingImage;
private readonly Image _croppedImage;
public ImagePage()
{
InitializeComponent();
_bitmapImage = this.Get<Image>("bitmapImage");
_drawingImage = this.Get<Image>("drawingImage");
_croppedImage = this.Get<Image>("croppedImage");
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public void BitmapStretchChanged(object sender, SelectionChangedEventArgs e)
{
if (_bitmapImage != null)
if (bitmapImage != null)
{
var comboxBox = (ComboBox)sender;
_bitmapImage.Stretch = (Stretch)comboxBox.SelectedIndex;
bitmapImage.Stretch = (Stretch)comboxBox.SelectedIndex;
}
}
public void DrawingStretchChanged(object sender, SelectionChangedEventArgs e)
{
if (_drawingImage != null)
if (drawingImage != null)
{
var comboxBox = (ComboBox)sender;
_drawingImage.Stretch = (Stretch)comboxBox.SelectedIndex;
drawingImage.Stretch = (Stretch)comboxBox.SelectedIndex;
}
}
public void BitmapCropChanged(object sender, SelectionChangedEventArgs e)
{
if (_croppedImage != null)
if (croppedImage != null)
{
var comboxBox = (ComboBox)sender;
if (_croppedImage.Source is CroppedBitmap croppedBitmap)
if (croppedImage.Source is CroppedBitmap croppedBitmap)
{
croppedBitmap.SourceRect = GetCropRect(comboxBox.SelectedIndex);
}
}
}
@ -72,7 +57,7 @@ namespace ControlCatalog.Pages
5 => new PixelRect(new PixelPoint(bitmapWidth - cropSize.Width, bitmapHeight - cropSize.Height), cropSize),
_ => default
};
}
}
}

10
samples/ControlCatalog/Pages/LayoutTransformControlPage.xaml.cs

@ -1,18 +1,12 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class LayoutTransformControlPage : UserControl
public partial class LayoutTransformControlPage : UserControl
{
public LayoutTransformControlPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
}
}

8
samples/ControlCatalog/Pages/ListBoxPage.xaml.cs

@ -1,20 +1,14 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
public class ListBoxPage : UserControl
public partial class ListBoxPage : UserControl
{
public ListBoxPage()
{
InitializeComponent();
DataContext = new ListBoxPageViewModel();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

17
samples/ControlCatalog/Pages/MenuPage.xaml.cs

@ -1,37 +1,28 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Windows.Input;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
public class MenuPage : UserControl
public partial class MenuPage : UserControl
{
public MenuPage()
{
this.InitializeComponent();
InitializeComponent();
DataContext = new MenuPageViewModel();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private MenuPageViewModel? _model;
protected override void OnDataContextChanged(EventArgs e)
{
if (_model != null)
_model.View = null;
_model = DataContext as MenuPageViewModel;
_model = DataContext as MenuPageViewModel;
if (_model != null)
_model.View = this;
base.OnDataContextChanged(e);
}
}
}

23
samples/ControlCatalog/Pages/NativeEmbedPage.xaml.cs

@ -1,26 +1,17 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Avalonia.Platform;
using Avalonia.Interactivity;
using Avalonia.Controls;
using Avalonia.Controls.Platform;
using Avalonia.Markup.Xaml;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Platform;
namespace ControlCatalog.Pages
{
public class NativeEmbedPage : UserControl
public partial class NativeEmbedPage : UserControl
{
public NativeEmbedPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
public async void ShowPopupDelay(object sender, RoutedEventArgs args)
@ -47,8 +38,8 @@ namespace ControlCatalog.Pages
if (change.Property == BoundsProperty)
{
var isMobile = change.GetNewValue<Rect>().Width < 1200;
this.Find<DockPanel>("FirstPanel")!.Classes.Set("mobile", isMobile);
this.Find<DockPanel>("SecondPanel")!.Classes.Set("mobile", isMobile);
FirstPanel.Classes.Set("mobile", isMobile);
SecondPanel.Classes.Set("mobile", isMobile);
}
}
}

12
samples/ControlCatalog/Pages/NotificationsPage.xaml.cs

@ -1,29 +1,23 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Notifications;
using Avalonia.Markup.Xaml;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
public class NotificationsPage : UserControl
public partial class NotificationsPage : UserControl
{
private NotificationViewModel _viewModel;
public NotificationsPage()
{
this.InitializeComponent();
InitializeComponent();
_viewModel = new NotificationViewModel();
DataContext = _viewModel;
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
@ -33,7 +27,7 @@ namespace ControlCatalog.Pages
public void NotificationOnClick()
{
this.Get<WindowNotificationManager>("ControlNotifications").Show("Notification clicked");
ControlNotifications.Show("Notification clicked");
}
}
}

9
samples/ControlCatalog/Pages/NumericUpDownPage.xaml.cs

@ -9,20 +9,15 @@ using MiniMvvm;
namespace ControlCatalog.Pages
{
public class NumericUpDownPage : UserControl
public partial class NumericUpDownPage : UserControl
{
public NumericUpDownPage()
{
this.InitializeComponent();
InitializeComponent();
var viewModel = new NumbersPageViewModel();
DataContext = viewModel;
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
public static IValueConverter CultureConverter =
new FuncValueConverter<CultureInfo, NumberFormatInfo>(c => (c ?? CultureInfo.CurrentCulture).NumberFormat);
}

6
samples/ControlCatalog/Pages/OpenGl/OpenGlLeasePage.xaml

@ -4,7 +4,7 @@
xmlns:pages="using:ControlCatalog.Pages"
xmlns:openGl="clr-namespace:ControlCatalog.Pages.OpenGl">
<Grid>
<Control x:Name="Viewport" />
<openGl:GlPageKnobs x:Name="Knobs" />
<Control x:Name="Viewport" AttachedToVisualTree="ViewportAttachedToVisualTree" DetachedFromVisualTree="ViewportDetachedFromVisualTree"/>
<openGl:GlPageKnobs x:Name="Knobs" PropertyChanged="KnobsPropertyChanged" />
</Grid>
</UserControl>
</UserControl>

21
samples/ControlCatalog/Pages/OpenGl/OpenGlLeasePage.xaml.cs

@ -1,8 +1,6 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.LogicalTree;
using Avalonia.Markup.Xaml;
using Avalonia.Media;
using Avalonia.OpenGL;
using Avalonia.Platform;
@ -14,10 +12,8 @@ using static Avalonia.OpenGL.GlConsts;
namespace ControlCatalog.Pages;
public class OpenGlLeasePage : UserControl
public partial class OpenGlLeasePage : UserControl
{
private readonly Control _viewport;
private readonly GlPageKnobs _knobs;
private CompositionCustomVisual? _visual;
class GlVisual : CompositionCustomVisualHandler
@ -161,12 +157,7 @@ public class OpenGlLeasePage : UserControl
public OpenGlLeasePage()
{
AvaloniaXamlLoader.Load(this);
_viewport = this.FindControl<Control>("Viewport")!;
_viewport.AttachedToVisualTree += ViewportAttachedToVisualTree;
_viewport.DetachedFromVisualTree += ViewportDetachedFromVisualTree;
_knobs = this.FindControl<GlPageKnobs>("Knobs")!;
_knobs.PropertyChanged += KnobsPropertyChanged;
InitializeComponent();
}
private void KnobsPropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs change)
@ -180,16 +171,16 @@ public class OpenGlLeasePage : UserControl
Parameters GetParameters() => new()
{
Yaw = _knobs!.Yaw, Pitch = _knobs.Pitch, Roll = _knobs.Roll, Disco = _knobs.Disco
Yaw = Knobs!.Yaw, Pitch = Knobs.Pitch, Roll = Knobs.Roll, Disco = Knobs.Disco
};
private void ViewportAttachedToVisualTree(object? sender, VisualTreeAttachmentEventArgs e)
{
var visual = ElementComposition.GetElementVisual(_viewport!);
var visual = ElementComposition.GetElementVisual(Viewport);
if(visual == null)
return;
_visual = visual.Compositor.CreateCustomVisual(new GlVisual(new OpenGlContent(), GetParameters()));
ElementComposition.SetElementChildVisual(_viewport, _visual);
ElementComposition.SetElementChildVisual(Viewport, _visual);
UpdateSize(Bounds.Size);
}
@ -210,7 +201,7 @@ public class OpenGlLeasePage : UserControl
{
_visual?.SendHandlerMessage(new DisposeMessage());
_visual = null;
ElementComposition.SetElementChildVisual(_viewport, null);
ElementComposition.SetElementChildVisual(Viewport, null);
base.OnDetachedFromVisualTree(e);
}
}

17
samples/ControlCatalog/Pages/OpenGlPage.xaml.cs

@ -1,9 +1,7 @@
using System.Runtime.InteropServices;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
using Avalonia.OpenGL;
using Avalonia.OpenGL.Controls;
using Avalonia.Rendering.Composition;
@ -13,21 +11,20 @@ using ControlCatalog.Pages.OpenGl;
namespace ControlCatalog.Pages
{
public class OpenGlPage : UserControl
public partial class OpenGlPage : UserControl
{
public OpenGlPage()
{
AvaloniaXamlLoader.Load(this);
this.FindControl<OpenGlPageControl>("GL")
!.Init(this.FindControl<GlPageKnobs>("Knobs")!);
InitializeComponent();
GL.Init(Knobs);
AttachedToVisualTree += delegate
{
if (TopLevel.GetTopLevel(this) is Window)
this.FindControl<Button>("Snapshot")!.IsVisible = true;
Snapshot.IsVisible = true;
};
}
private async void SnapshotClick(object sender, RoutedEventArgs e)
{
var v = ElementComposition.GetElementVisual(this)!;
@ -66,7 +63,7 @@ namespace ControlCatalog.Pages
|| change.Property == GlPageKnobs.DiscoProperty)
RequestNextFrameRendering();
}
protected override unsafe void OnOpenGlInit(GlInterface GL) => _content.Init(GL, GlVersion);
protected override void OnOpenGlDeinit(GlInterface GL) => _content.Deinit(GL);

15
samples/ControlCatalog/Pages/PlatformInfoPage.xaml.cs

@ -1,23 +1,14 @@
using System;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Markup.Xaml.MarkupExtensions;
using Avalonia.Media.Immutable;
using Avalonia.Controls;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
public class PlatformInfoPage : UserControl
public partial class PlatformInfoPage : UserControl
{
public PlatformInfoPage()
{
this.InitializeComponent();
InitializeComponent();
DataContext = new PlatformInformationViewModel();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

4
samples/ControlCatalog/Pages/PointersPage.xaml

@ -48,6 +48,8 @@
Margin="5"
Padding="50"
Background="{DynamicResource SystemAccentColor}"
PointerPressed="Border_PointerPressed" PointerReleased="Border_PointerReleased" PointerCaptureLost="Border_PointerCaptureLost"
PointerMoved="Border_PointerUpdated" PointerEntered="Border_PointerUpdated" PointerExited="Border_PointerUpdated"
ToolTip.Placement="Bottom">
<TextBlock>Capture 1</TextBlock>
</Border>
@ -57,6 +59,8 @@
Margin="5"
Padding="50"
Background="{DynamicResource SystemAccentColor}"
PointerPressed="Border_PointerPressed" PointerReleased="Border_PointerReleased" PointerCaptureLost="Border_PointerCaptureLost"
PointerMoved="Border_PointerUpdated" PointerEntered="Border_PointerUpdated" PointerExited="Border_PointerUpdated"
ToolTip.Placement="Bottom">
<TextBlock>Capture 2</TextBlock>
</Border>

27
samples/ControlCatalog/Pages/PointersPage.xaml.cs

@ -1,32 +1,14 @@
using System;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages;
public class PointersPage : UserControl
public partial class PointersPage : UserControl
{
public PointersPage()
{
this.InitializeComponent();
var border1 = this.Get<Border>("BorderCapture1");
var border2 = this.Get<Border>("BorderCapture2");
border1.PointerPressed += Border_PointerPressed;
border1.PointerReleased += Border_PointerReleased;
border1.PointerCaptureLost += Border_PointerCaptureLost;
border1.PointerMoved += Border_PointerUpdated;
border1.PointerEntered += Border_PointerUpdated;
border1.PointerExited += Border_PointerUpdated;
border2.PointerPressed += Border_PointerPressed;
border2.PointerReleased += Border_PointerReleased;
border2.PointerCaptureLost += Border_PointerCaptureLost;
border2.PointerMoved += Border_PointerUpdated;
border2.PointerEntered += Border_PointerUpdated;
border2.PointerExited += Border_PointerUpdated;
InitializeComponent();
}
private void Border_PointerUpdated(object? sender, PointerEventArgs e)
@ -74,9 +56,4 @@ Position: ??? ???";
e.Handled = true;
e.PreventGestureRecognition();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}

10
samples/ControlCatalog/Pages/ProgressBarPage.xaml.cs

@ -1,18 +1,12 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class ProgressBarPage : UserControl
public partial class ProgressBarPage : UserControl
{
public ProgressBarPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
}
}

10
samples/ControlCatalog/Pages/RadioButtonPage.xaml.cs

@ -1,18 +1,12 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class RadioButtonPage : UserControl
public partial class RadioButtonPage : UserControl
{
public RadioButtonPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
}
}

8
samples/ControlCatalog/Pages/ScrollViewerPage.xaml.cs

@ -1,7 +1,6 @@
using System.Collections.Generic;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Markup.Xaml;
using MiniMvvm;
namespace ControlCatalog.Pages
@ -93,7 +92,7 @@ namespace ControlCatalog.Pages
public List<SnapPointsAlignment> AvailableSnapPointsAlignment { get; }
}
public class ScrollViewerPage : UserControl
public partial class ScrollViewerPage : UserControl
{
public ScrollViewerPage()
{
@ -101,10 +100,5 @@ namespace ControlCatalog.Pages
DataContext = new ScrollViewerPageViewModel();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

10
samples/ControlCatalog/Pages/SliderPage.xaml.cs

@ -1,18 +1,12 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class SliderPage : UserControl
public partial class SliderPage : UserControl
{
public SliderPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
}
}

13
samples/ControlCatalog/Pages/SplitViewPage.xaml.cs

@ -1,21 +1,14 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Controls;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
public class SplitViewPage : UserControl
public partial class SplitViewPage : UserControl
{
public SplitViewPage()
{
this.InitializeComponent();
InitializeComponent();
DataContext = new SplitViewPageViewModel();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

12
samples/ControlCatalog/Pages/TabControlPage.xaml.cs

@ -1,17 +1,12 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
using System.Collections.Generic;
public class TabControlPage : UserControl
public partial class TabControlPage : UserControl
{
public TabControlPage()
{
@ -44,11 +39,6 @@ namespace ControlCatalog.Pages
};
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private static Bitmap LoadBitmap(string uri)
{
return new Bitmap(AssetLoader.Open(new Uri(uri)));

10
samples/ControlCatalog/Pages/TabStripPage.xaml.cs

@ -1,10 +1,9 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
public class TabStripPage : UserControl
public partial class TabStripPage : UserControl
{
public TabStripPage()
{
@ -12,7 +11,7 @@ namespace ControlCatalog.Pages
DataContext = new TabControlPageViewModel
{
Tabs = new []
Tabs = new[]
{
new TabControlPageViewModelItem()
{
@ -30,10 +29,5 @@ namespace ControlCatalog.Pages
}
};
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

10
samples/ControlCatalog/Pages/TextBlockPage.xaml.cs

@ -1,18 +1,12 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class TextBlockPage : UserControl
public partial class TextBlockPage : UserControl
{
public TextBlockPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
}
}

10
samples/ControlCatalog/Pages/TextBoxPage.xaml.cs

@ -1,18 +1,12 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class TextBoxPage : UserControl
public partial class TextBoxPage : UserControl
{
public TextBoxPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
}
}

13
samples/ControlCatalog/Pages/ToggleSwitchPage.xaml.cs

@ -1,19 +1,12 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Controls;
namespace ControlCatalog.Pages
{
public class ToggleSwitchPage : UserControl
public partial class ToggleSwitchPage : UserControl
{
public ToggleSwitchPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
}
}

12
samples/ControlCatalog/Pages/ToolTipPage.xaml.cs

@ -1,22 +1,16 @@
using System;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Controls.Primitives.PopupPositioning;
using Avalonia.Markup.Xaml;
using Avalonia.Interactivity;
namespace ControlCatalog.Pages
{
public class ToolTipPage : UserControl
public partial class ToolTipPage : UserControl
{
public ToolTipPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
private void ToolTipOpening(object? sender, CancelRoutedEventArgs args)

7
samples/ControlCatalog/Pages/TransitioningContentControlPage.axaml.cs

@ -1,6 +1,4 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
@ -10,10 +8,5 @@ namespace ControlCatalog.Pages
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

8
samples/ControlCatalog/Pages/TreeViewPage.xaml.cs

@ -1,20 +1,14 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using ControlCatalog.ViewModels;
namespace ControlCatalog.Pages
{
public class TreeViewPage : UserControl
public partial class TreeViewPage : UserControl
{
public TreeViewPage()
{
InitializeComponent();
DataContext = new TreeViewPageViewModel();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

8
samples/ControlCatalog/Pages/ViewboxPage.xaml.cs

@ -1,18 +1,12 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public class ViewboxPage : UserControl
public partial class ViewboxPage : UserControl
{
public ViewboxPage()
{
InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
}

13
samples/ControlCatalog/Pages/WindowCustomizationsPage.xaml.cs

@ -1,19 +1,12 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Avalonia.Controls;
namespace ControlCatalog.Pages
{
public class WindowCustomizationsPage : UserControl
public partial class WindowCustomizationsPage : UserControl
{
public WindowCustomizationsPage()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
}
}

10
samples/ControlCatalog/Views/CustomNotificationView.xaml.cs

@ -1,18 +1,12 @@
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Views
{
public class CustomNotificationView : UserControl
public partial class CustomNotificationView : UserControl
{
public CustomNotificationView()
{
this.InitializeComponent();
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
InitializeComponent();
}
}
}

17
src/Android/Avalonia.Android/Automation/ToggleNodeInfoProvider.cs

@ -9,13 +9,18 @@ namespace Avalonia.Android.Automation
{
internal class ToggleNodeInfoProvider : NodeInfoProvider<IToggleProvider>
{
private PropertyInfo? _checkedProperty;
private static PropertyInfo? s_checkedProperty;
public ToggleNodeInfoProvider(ExploreByTouchHelper owner, AutomationPeer peer, int virtualViewId) :
base(owner, peer, virtualViewId)
{
}
static ToggleNodeInfoProvider()
{
s_checkedProperty = typeof(AccessibilityNodeInfoCompat).GetProperty(nameof(AccessibilityNodeInfoCompat.Checked));
}
public override bool PerformNodeAction(int action, Bundle? arguments)
{
IToggleProvider provider = GetProvider();
@ -36,11 +41,11 @@ namespace Avalonia.Android.Automation
IToggleProvider provider = GetProvider();
_checkedProperty ??= nodeInfo.GetType().GetProperty(nameof(nodeInfo.Checked));
if (_checkedProperty?.PropertyType == typeof(int))
s_checkedProperty ??= nodeInfo.GetType().GetProperty(nameof(nodeInfo.Checked));
if (s_checkedProperty?.PropertyType == typeof(int))
{
// Needed for Xamarin.AndroidX.Core 1.17+
_checkedProperty.SetValue(this,
s_checkedProperty.SetValue(this,
provider.ToggleState switch
{
ToggleState.On => 1,
@ -48,10 +53,10 @@ namespace Avalonia.Android.Automation
_ => 0
});
}
else if (_checkedProperty?.PropertyType == typeof(bool))
else if (s_checkedProperty?.PropertyType == typeof(bool))
{
// Needed for Xamarin.AndroidX.Core < 1.17
_checkedProperty.SetValue(this, provider.ToggleState == ToggleState.On);
s_checkedProperty.SetValue(this, provider.ToggleState == ToggleState.On);
}
nodeInfo.Checkable = true;

2
src/Android/Avalonia.Android/Platform/ClipboardImpl.cs

@ -85,7 +85,7 @@ namespace Avalonia.Android.Platform
if (DataFormat.Text.Equals(dataFormat))
{
var text = await item.TryGetValueAsync(DataFormat.Text);
return new ClipData.Item(text, string.Empty);
return new ClipData.Item(text);
}
if (DataFormat.File.Equals(dataFormat))

7
src/Avalonia.Base/Media/FontManager.cs

@ -287,6 +287,8 @@ namespace Avalonia.Media
}
if (TryGetFontCollection(source, out var fontCollection) &&
// With composite fonts we need to first check if the font collection contains the family if not we skip it
fontCollection.TryGetGlyphTypeface(familyName, fontStyle, fontWeight, fontStretch, out _) &&
fontCollection.TryMatchCharacter(codepoint, fontStyle, fontWeight, fontStretch, familyName, culture, out typeface))
{
return true;
@ -306,8 +308,9 @@ namespace Avalonia.Media
}
}
//Try to find a match with the system font manager
return PlatformImpl.TryMatchCharacter(codepoint, fontStyle, fontWeight, fontStretch, culture, out typeface);
//Try to find a match with the system font collection
return SystemFonts.TryMatchCharacter(codepoint, fontStyle, fontWeight, fontStretch, fontFamily?.Name,
culture, out typeface);
}
internal IReadOnlyList<Typeface> GetFamilyTypefaces(FontFamily fontFamily)

62
src/Avalonia.Base/Media/Fonts/SystemFontCollection.cs

@ -2,6 +2,7 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using Avalonia.Platform;
@ -15,7 +16,7 @@ namespace Avalonia.Media.Fonts
public SystemFontCollection(FontManager fontManager)
{
_fontManager = fontManager;
_familyNames = fontManager.PlatformImpl.GetInstalledFontFamilyNames().Where(x=> !string.IsNullOrEmpty(x)).ToList();
_familyNames = fontManager.PlatformImpl.GetInstalledFontFamilyNames().Where(x => !string.IsNullOrEmpty(x)).ToList();
}
public override Uri Key => FontManager.SystemFontsKey;
@ -144,21 +145,6 @@ namespace Avalonia.Media.Fonts
}
return;
void AddGlyphTypefaceByFamilyName(string familyName, IGlyphTypeface glyphTypeface)
{
var typefaces = _glyphTypefaceCache.GetOrAdd(familyName,
x =>
{
_familyNames.Insert(0, familyName);
return new ConcurrentDictionary<FontCollectionKey, IGlyphTypeface?>();
});
typefaces.TryAdd(
new FontCollectionKey(glyphTypeface.Style, glyphTypeface.Weight, glyphTypeface.Stretch),
glyphTypeface);
}
}
public bool TryGetFamilyTypefaces(string familyName, [NotNullWhen(true)] out IReadOnlyList<Typeface>? familyTypefaces)
@ -172,5 +158,49 @@ namespace Avalonia.Media.Fonts
return false;
}
public override bool TryMatchCharacter(int codepoint, FontStyle style, FontWeight weight, FontStretch stretch, string? familyName,
CultureInfo? culture, out Typeface match)
{
//TODO12: Think about removing familyName parameter
match = default;
if (_fontManager.PlatformImpl is IFontManagerImpl2 fontManagerImpl2)
{
if (fontManagerImpl2.TryMatchCharacter(codepoint, style, weight, stretch, culture, out var glyphTypeface))
{
AddGlyphTypefaceByFamilyName(glyphTypeface.FamilyName, glyphTypeface);
match = new Typeface(glyphTypeface.FamilyName, glyphTypeface.Style, glyphTypeface.Weight,
glyphTypeface.Stretch);
return true;
}
return false;
}
else
{
return _fontManager.PlatformImpl.TryMatchCharacter(codepoint, style, weight, stretch, culture, out match);
}
}
private void AddGlyphTypefaceByFamilyName(string familyName, IGlyphTypeface glyphTypeface)
{
// Add family name to the collection if not exists
if (!_familyNames.Contains(familyName))
{
_familyNames.Add(familyName);
}
// Get or create the typefaces dictionary for the family name
if (!_glyphTypefaceCache.TryGetValue(familyName, out var typefaces))
{
_glyphTypefaceCache[familyName] = typefaces = new ConcurrentDictionary<FontCollectionKey, IGlyphTypeface?>();
}
// Add the glyph typeface to the cache
typefaces.TryAdd(new FontCollectionKey(glyphTypeface.Style, glyphTypeface.Weight, glyphTypeface.Stretch), glyphTypeface);
}
}
}

15
src/Avalonia.Base/Platform/IFontManagerImpl.cs

@ -65,6 +65,21 @@ namespace Avalonia.Platform
internal interface IFontManagerImpl2 : IFontManagerImpl
{
/// <summary>
/// Tries to match a specified character to a typeface that supports specified font properties.
/// </summary>
/// <param name="codepoint">The codepoint to match against.</param>
/// <param name="fontStyle">The font style.</param>
/// <param name="fontWeight">The font weight.</param>
/// <param name="fontStretch">The font stretch.</param>
/// <param name="culture">The culture.</param>
/// <param name="typeface">The matching typeface.</param>
/// <returns>
/// <c>True</c>, if the <see cref="IFontManagerImpl"/> could match the character to specified parameters, <c>False</c> otherwise.
/// </returns>
bool TryMatchCharacter(int codepoint, FontStyle fontStyle,
FontWeight fontWeight, FontStretch fontStretch, CultureInfo? culture, [NotNullWhen(true)] out IGlyphTypeface? typeface);
/// <summary>
/// Tries to get a list of typefaces for the specified family name.
/// </summary>

17
src/Avalonia.Base/Rendering/Composition/Drawing/ServerCompositionSimplePen.cs

@ -1,12 +1,19 @@
using System;
using Avalonia.Media;
using Avalonia.Media.Immutable;
using Avalonia.Rendering.Composition.Server;
using Avalonia.Rendering.Composition.Transport;
namespace Avalonia.Rendering.Composition.Server;
internal partial class ServerCompositionSimplePen : IPen
{
IDashStyle? IPen.DashStyle => DashStyle;
}
/// <inheritdoc/>
public override void Dispose()
{
// Remove the pen from the brush observers.
// Without this, the pen was being retained in memory by long lived brush resources (e.g. those defined in
// the theme or app resources), hence was causing memory leaks; see Issue #16451
RemoveObserversFromProperty(ref _brush);
_brush = null;
base.Dispose();
}
}

2
src/Avalonia.Base/Styling/ChildSelector.cs

@ -31,7 +31,7 @@ namespace Avalonia.Styling
{
if (_selectorString == null)
{
_selectorString = _parent.ToString(owner) + " > ";
_selectorString = _parent.ToString(owner, true) + " > ";
}
return _selectorString;

2
src/Avalonia.Base/Styling/DescendentSelector.cs

@ -29,7 +29,7 @@ namespace Avalonia.Styling
{
if (_selectorString == null)
{
_selectorString = _parent.ToString(owner) + ' ';
_selectorString = _parent.ToString(owner, true) + ' ';
}
return _selectorString;

2
src/Avalonia.Base/Styling/NotSelector.cs

@ -39,7 +39,7 @@ namespace Avalonia.Styling
{
if (_selectorString == null)
{
_selectorString = $"{_previous?.ToString(owner)}:not({_argument})";
_selectorString = $"{_previous?.ToString(owner, true)}:not({_argument})";
}
return _selectorString;

6
src/Avalonia.Base/Styling/NthChildSelector.cs

@ -109,9 +109,9 @@ namespace Avalonia.Styling
public override string ToString(Style? owner)
{
var expectedCapacity = NthLastChildSelectorName.Length + 8;
var stringBuilder = StringBuilderCache.Acquire(expectedCapacity);
stringBuilder.Append(_previous?.ToString(owner));
var stringBuilder = StringBuilderCache.Acquire(expectedCapacity);
stringBuilder.Append(_previous?.ToString(owner, true));
stringBuilder.Append(':');
stringBuilder.Append(_reversed ? NthLastChildSelectorName : NthChildSelectorName);
stringBuilder.Append('(');

16
src/Avalonia.Base/Styling/OrSelector.cs

@ -45,11 +45,19 @@ namespace Avalonia.Styling
internal override Type? TargetType => _targetType ??= EvaluateTargetType();
/// <inheritdoc/>
public override string ToString(Style? owner)
public override string ToString(Style? owner) => ToString(owner, false);
/// <inheritdoc/>
internal override string ToString(Style? owner, bool hasNext)
{
if (_selectorString == null)
{
_selectorString = string.Join(", ", _selectors.Select(x => x.ToString(owner)));
_selectorString = string.Join(", ", _selectors.Select(x => x.ToString(owner, true)));
if (hasNext)
{
_selectorString = $"({_selectorString})";
}
}
return _selectorString;
@ -97,13 +105,13 @@ namespace Avalonia.Styling
private protected override Selector? MovePrevious() => null;
private protected override Selector? MovePreviousOrParent() => null;
internal override void ValidateNestingSelector(bool inControlTheme)
internal override void ValidateNestingSelector(bool inControlTheme, int templateCount = 0)
{
var count = _selectors.Count;
for (var i = 0; i < count; i++)
{
_selectors[i].ValidateNestingSelector(inControlTheme);
_selectors[i].ValidateNestingSelector(inControlTheme, templateCount);
}
}

3
src/Avalonia.Base/Styling/PropertyEqualsSelector.cs

@ -45,7 +45,7 @@ namespace Avalonia.Styling
if (_previous != null)
{
builder.Append(_previous.ToString(owner));
builder.Append(_previous.ToString(owner, true));
}
builder.Append('[');
@ -85,7 +85,6 @@ namespace Avalonia.Styling
? SelectorMatch.AlwaysThisInstance
: SelectorMatch.NeverThisInstance;
}
}
private protected override Selector? MovePrevious() => _previous;

41
src/Avalonia.Base/Styling/Selector.cs

@ -47,7 +47,7 @@ namespace Avalonia.Styling
// right-to-left, so MatchUntilCombinator reverses this order because the type selector
// will be on the left.
var match = MatchUntilCombinator(control, this, parent, subscribe, out var combinator);
// If the pre-combinator selector matches, we can now match the combinator, if any.
if (match.IsMatch && combinator is object)
{
@ -76,6 +76,10 @@ namespace Avalonia.Styling
/// <param name="owner">The owner style.</param>
public abstract string ToString(Style? owner);
/// <inheritdoc cref="ToString(Style?)"/>
/// <param name="hasNext">Whether there is a selector that comes after this one.</param>
internal virtual string ToString(Style? owner, bool hasNext) => ToString(owner);
/// <summary>
/// Evaluates the selector for a match.
/// </summary>
@ -100,30 +104,31 @@ namespace Avalonia.Styling
/// </summary>
private protected abstract Selector? MovePreviousOrParent();
internal virtual void ValidateNestingSelector(bool inControlTheme)
internal virtual void ValidateNestingSelector(bool inControlTheme, int templateCount = 0)
{
var s = this;
var templateCount = 0;
do
if (inControlTheme)
{
if (inControlTheme)
{
if (!s.InTemplate && s.IsCombinator)
throw new InvalidOperationException(
"ControlTheme style may not directly contain a child or descendent selector.");
if (s is TemplateSelector && templateCount++ > 0)
throw new InvalidOperationException(
"ControlTemplate styles cannot contain multiple template selectors.");
}
if (!s.InTemplate && s.IsCombinator)
throw new InvalidOperationException(
"ControlTheme style may not directly contain a child or descendent selector.");
if (s is TemplateSelector && templateCount++ > 0)
throw new InvalidOperationException(
"ControlTemplate styles cannot contain multiple template selectors.");
}
var previous = s.MovePreviousOrParent();
var previous = s.MovePreviousOrParent();
if (previous is null && s is not NestingSelector)
if (previous is null)
{
if (s is not NestingSelector)
throw new InvalidOperationException("Child styles must have a nesting selector.");
s = previous;
} while (s is not null);
}
else
{
previous.ValidateNestingSelector(inControlTheme, templateCount);
}
}
private static SelectorMatch MatchUntilCombinator(

3
src/Avalonia.Base/Styling/Selectors.cs

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Avalonia.Styling
{
@ -124,7 +123,7 @@ namespace Avalonia.Styling
{
return new NotSelector(previous, argument(null));
}
/// <summary>
/// Returns a selector which inverts the results of selector argument.
/// </summary>

2
src/Avalonia.Base/Styling/TemplateSelector.cs

@ -30,7 +30,7 @@ namespace Avalonia.Styling
{
if (_selectorString == null)
{
_selectorString = _parent.ToString(owner) + " /template/ ";
_selectorString = _parent.ToString(owner, true) + " /template/ ";
}
return _selectorString;

2
src/Avalonia.Base/Styling/TypeNameAndClassSelector.cs

@ -143,7 +143,7 @@ namespace Avalonia.Styling
if (_previous != null)
{
builder.Append(_previous.ToString(owner));
builder.Append(_previous.ToString(owner, true));
}
if (TargetType != null)

47
src/Headless/Avalonia.Headless/HeadlessUnitTestIsolationAttribute.cs

@ -0,0 +1,47 @@
using System;
namespace Avalonia.Headless;
/// <summary>
/// Defines the isolation level for headless unit tests,
/// controlling how <see cref="Avalonia.Application"/> and its
/// associated <see cref="Avalonia.Threading.Dispatcher"/> are managed
/// between test runs.
/// </summary>
public enum AvaloniaTestIsolationLevel
{
/// <summary>
/// Reuses a single <see cref="Avalonia.Application"/> and <see cref="Avalonia.Threading.Dispatcher"/>
/// instance across all tests within the assembly.
/// </summary>
/// <remarks>
/// Tests must not rely on any global or persistent state that could leak between runs.
/// Headless framework won't dispose any resources after tests when using this mode.
/// </remarks>
PerAssembly,
/// <summary>
/// Recreates the <see cref="Avalonia.Application"/> and <see cref="Avalonia.Threading.Dispatcher"/>
/// for each individual test method.
/// </summary>
/// <remarks>
/// This mode ensures complete test isolation, and should be used for tests that modify global
/// application state or rely on a clean dispatcher environment.
/// This is the default isolation level if none is specified.
/// </remarks>
PerTest
}
/// <summary>
/// Specifies how headless unit tests should be isolated from each other,
/// defining when the test runtime should recreate the
/// <see cref="Avalonia.Application"/> and <see cref="Avalonia.Threading.Dispatcher"/> instances.
/// </summary>
[AttributeUsage(AttributeTargets.Assembly)]
public sealed class AvaloniaTestIsolationAttribute(AvaloniaTestIsolationLevel isolationLevel) : Attribute
{
/// <summary>
/// Gets the isolation level for headless tests.
/// </summary>
public AvaloniaTestIsolationLevel IsolationLevel { get; } = isolationLevel;
}

65
src/Headless/Avalonia.Headless/HeadlessUnitTestSession.cs

@ -18,7 +18,6 @@ namespace Avalonia.Headless;
/// All UI tests are supposed to be executed from one of the <see cref="Dispatch"/> methods to keep execution flow on the UI thread.
/// Disposing unit test session stops internal dispatcher loop.
/// </summary>
[Unstable("This API is experimental and might be unstable. Use on your risk. API might or might not be changed in a minor update.")]
public sealed class HeadlessUnitTestSession : IDisposable
{
private static readonly Dictionary<Assembly, HeadlessUnitTestSession> s_session = new();
@ -27,19 +26,25 @@ public sealed class HeadlessUnitTestSession : IDisposable
private readonly CancellationTokenSource _cancellationTokenSource;
private readonly BlockingCollection<(Action, ExecutionContext?)> _queue;
private readonly Task _dispatchTask;
private readonly bool _isolated;
// Only set and used with PerAssembly isolation
private SynchronizationContext? _sharedContext;
internal const DynamicallyAccessedMemberTypes DynamicallyAccessed =
DynamicallyAccessedMemberTypes.PublicMethods |
DynamicallyAccessedMemberTypes.NonPublicMethods |
DynamicallyAccessedMemberTypes.PublicParameterlessConstructor;
private HeadlessUnitTestSession(AppBuilder appBuilder, CancellationTokenSource cancellationTokenSource,
BlockingCollection<(Action, ExecutionContext?)> queue, Task dispatchTask)
private HeadlessUnitTestSession(
AppBuilder appBuilder, CancellationTokenSource cancellationTokenSource,
BlockingCollection<(Action, ExecutionContext?)> queue, Task dispatchTask,
bool isolated)
{
_appBuilder = appBuilder;
_cancellationTokenSource = cancellationTokenSource;
_queue = queue;
_dispatchTask = dispatchTask;
_isolated = isolated;
}
/// <inheritdoc cref="DispatchCore{TResult}"/>
@ -93,7 +98,9 @@ public sealed class HeadlessUnitTestSession : IDisposable
try
{
using var application = EnsureApplication();
using var application = _isolated
? EnsureIsolatedApplication()
: EnsureSharedApplication();
var task = action();
if (task.Status != TaskStatus.RanToCompletion)
{
@ -123,7 +130,27 @@ public sealed class HeadlessUnitTestSession : IDisposable
return tcs.Task;
}
private IDisposable EnsureApplication()
private IDisposable EnsureSharedApplication()
{
var oldContext = SynchronizationContext.Current;
if (Application.Current is null)
{
_appBuilder.SetupUnsafe();
_sharedContext = SynchronizationContext.Current;
}
else
{
SynchronizationContext.SetSynchronizationContext(_sharedContext);
}
return Disposable.Create(() =>
{
Dispatcher.UIThread.RunJobs();
SynchronizationContext.SetSynchronizationContext(oldContext);
});
}
private IDisposable EnsureIsolatedApplication()
{
var scope = AvaloniaLocator.EnterScope();
var oldContext = SynchronizationContext.Current;
@ -167,6 +194,24 @@ public sealed class HeadlessUnitTestSession : IDisposable
public static HeadlessUnitTestSession StartNew(
[DynamicallyAccessedMembers(DynamicallyAccessed)]
Type entryPointType)
{
// Cannot be optional parameter for ABI stability
// ReSharper disable once IntroduceOptionalParameters.Global
return StartNew(entryPointType, AvaloniaTestIsolationLevel.PerTest);
}
/// <summary>
/// Creates instance of <see cref="HeadlessUnitTestSession"/>.
/// </summary>
/// <param name="entryPointType">
/// Parameter from which <see cref="AppBuilder"/> should be created.
/// It either needs to have BuildAvaloniaApp -> AppBuilder method or inherit Application.
/// </param>
/// <param name="isolationLevel">Defines the isolation level for headless unit tests</param>
public static HeadlessUnitTestSession StartNew(
[DynamicallyAccessedMembers(DynamicallyAccessed)]
Type entryPointType,
AvaloniaTestIsolationLevel isolationLevel)
{
var tcs = new TaskCompletionSource<HeadlessUnitTestSession>();
var cancellationTokenSource = new CancellationTokenSource();
@ -178,6 +223,7 @@ public sealed class HeadlessUnitTestSession : IDisposable
try
{
var appBuilder = AppBuilder.Configure(entryPointType);
var runIsolated = isolationLevel == AvaloniaTestIsolationLevel.PerTest;
// If windowing subsystem wasn't initialized by user, force headless with default parameters.
if (appBuilder.WindowingSubsystemName != "Headless")
@ -186,7 +232,7 @@ public sealed class HeadlessUnitTestSession : IDisposable
}
// ReSharper disable once AccessToModifiedClosure
tcs.SetResult(new HeadlessUnitTestSession(appBuilder, cancellationTokenSource, queue, task!));
tcs.SetResult(new HeadlessUnitTestSession(appBuilder, cancellationTokenSource, queue, task!, runIsolated));
}
catch (Exception e)
{
@ -234,9 +280,12 @@ public sealed class HeadlessUnitTestSession : IDisposable
var appBuilderEntryPointType = assembly.GetCustomAttribute<AvaloniaTestApplicationAttribute>()
?.AppBuilderEntryPointType;
var isolationLevel = assembly.GetCustomAttribute<AvaloniaTestIsolationAttribute>()
?.IsolationLevel ?? AvaloniaTestIsolationLevel.PerTest;
session = appBuilderEntryPointType is not null ?
StartNew(appBuilderEntryPointType) :
StartNew(typeof(Application));
StartNew(appBuilderEntryPointType, isolationLevel) :
StartNew(typeof(Application), isolationLevel);
s_session.Add(assembly, session);
}

71
src/Skia/Avalonia.Skia/FontManagerImpl.cs

@ -1,8 +1,11 @@
using System;
#nullable enable
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using Avalonia.Media;
using Avalonia.Platform;
using SkiaSharp;
@ -15,6 +18,7 @@ namespace Avalonia.Skia
public string GetDefaultFontFamilyName()
{
return SKTypeface.Default.FamilyName;
}
@ -32,6 +36,53 @@ namespace Avalonia.Skia
public bool TryMatchCharacter(int codepoint, FontStyle fontStyle,
FontWeight fontWeight, FontStretch fontStretch, CultureInfo? culture, out Typeface fontKey)
{
if (!TryMatchCharacter(codepoint, fontStyle, fontWeight, fontStretch, culture, out SKTypeface? skTypeface))
{
fontKey = default;
return false;
}
fontKey = new Typeface(
skTypeface.FamilyName,
skTypeface.FontStyle.Slant.ToAvalonia(),
(FontWeight)skTypeface.FontStyle.Weight,
(FontStretch)skTypeface.FontStyle.Width);
skTypeface.Dispose();
return true;
}
public bool TryMatchCharacter(
int codepoint,
FontStyle fontStyle,
FontWeight fontWeight,
FontStretch fontStretch,
CultureInfo? culture,
[NotNullWhen(true)] out IGlyphTypeface? glyphTypeface)
{
if (!TryMatchCharacter(codepoint, fontStyle, fontWeight, fontStretch, culture, out SKTypeface? skTypeface))
{
glyphTypeface = null;
return false;
}
glyphTypeface = new GlyphTypefaceImpl(skTypeface, FontSimulations.None);
return true;
}
private bool TryMatchCharacter(
int codepoint,
FontStyle fontStyle,
FontWeight fontWeight,
FontStretch fontStretch,
CultureInfo? culture,
[NotNullWhen(true)] out SKTypeface? skTypeface)
{
SKFontStyle skFontStyle;
@ -59,23 +110,9 @@ namespace Avalonia.Skia
t_languageTagBuffer ??= new string[1];
t_languageTagBuffer[0] = culture.Name;
using var skTypeface = _skFontManager.MatchCharacter(null, skFontStyle, t_languageTagBuffer, codepoint);
skTypeface = _skFontManager.MatchCharacter(null, skFontStyle, t_languageTagBuffer, codepoint);
if (skTypeface != null)
{
// ToDo: create glyph typeface here to get the correct style/weight/stretch
fontKey = new Typeface(
skTypeface.FamilyName,
skTypeface.FontStyle.Slant.ToAvalonia(),
(FontWeight)skTypeface.FontStyle.Weight,
(FontStretch)skTypeface.FontStyle.Width);
return true;
}
fontKey = default;
return false;
return skTypeface != null;
}
public bool TryCreateGlyphTypeface(string familyName, FontStyle style, FontWeight weight,

27
src/Windows/Avalonia.Win32.Automation/Marshalling/ComVariant.cs

@ -18,6 +18,25 @@ internal struct ComVariant : IDisposable
internal const short VARIANT_TRUE = -1;
internal const short VARIANT_FALSE = 0;
#if DEBUG
static unsafe ComVariant()
{
// Variant size is the size of 4 pointers (16 bytes) on a 32-bit processor,
// and 3 pointers (24 bytes) on a 64-bit processor.
// See definition in oaidl.h in the Windows SDK.
int variantSize = sizeof(ComVariant);
if (IntPtr.Size == 4)
{
Debug.Assert(variantSize == (4 * IntPtr.Size));
}
else
{
Debug.Assert(IntPtr.Size == 8);
Debug.Assert(variantSize == (3 * IntPtr.Size));
}
}
#endif
// Most of the data types in the Variant are carried in _typeUnion
[FieldOffset(0)] private TypeUnion _typeUnion;
@ -32,6 +51,13 @@ internal struct ComVariant : IDisposable
public UnionTypes _unionTypes;
}
[StructLayout(LayoutKind.Sequential)]
private struct Record
{
public IntPtr _record;
public IntPtr _recordInfo;
}
[StructLayout(LayoutKind.Explicit)]
private unsafe struct UnionTypes
{
@ -56,6 +82,7 @@ internal struct ComVariant : IDisposable
[FieldOffset(0)] public IntPtr _dispatch;
[FieldOffset(0)] public IntPtr _pvarVal;
[FieldOffset(0)] public IntPtr _byref;
[FieldOffset(0)] public Record _record;
[FieldOffset(0)] public SafeArrayRef parray;
[FieldOffset(0)] public SafeArrayRef*pparray;
}

7
src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs

@ -2298,6 +2298,13 @@ namespace Avalonia.Win32.Interop
public int dwHoverTime;
}
// TrackMouseEvent flags
public const uint TME_HOVER = 0x00000001;
public const uint TME_LEAVE = 0x00000002;
public const uint TME_NONCLIENT = 0x00000010;
public const uint TME_QUERY = 0x40000000;
public const uint TME_CANCEL = 0x80000000;
[Flags]
public enum WindowPlacementFlags : uint
{

2
src/Windows/Avalonia.Win32/Win32Platform.cs

@ -240,7 +240,7 @@ namespace Avalonia.Win32
public IWindowImpl CreateEmbeddableWindow()
{
var embedded = new EmbeddedWindowImpl();
embedded.Show(true, false);
embedded.Show(false, false);
return embedded;
}

69
src/Windows/Avalonia.Win32/WindowImpl.CustomCaptionProc.cs

@ -1,4 +1,5 @@
using System;
using System.Runtime.InteropServices;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Input.Raw;
@ -141,24 +142,64 @@ namespace Avalonia.Win32
case WindowsMessage.WM_NCMOUSEMOVE when !IsMouseInPointerEnabled:
case WindowsMessage.WM_NCLBUTTONDOWN when !IsMouseInPointerEnabled:
case WindowsMessage.WM_NCLBUTTONUP when !IsMouseInPointerEnabled:
if (lRet == IntPtr.Zero
&& ShouldRedirectNonClientInput(hWnd, wParam, lParam))
if (lRet == IntPtr.Zero)
{
e = new RawPointerEventArgs(
_mouseDevice,
unchecked((uint)GetMessageTime()),
Owner,
(WindowsMessage)msg switch
var shouldRedirect = ShouldRedirectNonClientInput(hWnd, wParam, lParam);
if (shouldRedirect)
{
// Track non-client mouse to receive WM_NCMOUSELEAVE
if (!_trackingNonClientMouse)
{
WindowsMessage.WM_NCMOUSEMOVE => RawPointerEventType.Move,
WindowsMessage.WM_NCLBUTTONDOWN => RawPointerEventType.LeftButtonDown,
WindowsMessage.WM_NCLBUTTONUP => RawPointerEventType.LeftButtonUp,
_ => throw new ArgumentOutOfRangeException(nameof(msg), msg, null)
},
PointToClient(PointFromLParam(lParam)),
RawInputModifiers.None);
var tm = new TRACKMOUSEEVENT
{
cbSize = Marshal.SizeOf<TRACKMOUSEEVENT>(),
dwFlags = TME_LEAVE | TME_NONCLIENT,
hwndTrack = _hwnd,
dwHoverTime = 0,
};
TrackMouseEvent(ref tm);
_trackingNonClientMouse = true;
}
e = new RawPointerEventArgs(
_mouseDevice,
unchecked((uint)GetMessageTime()),
Owner,
(WindowsMessage)msg switch
{
WindowsMessage.WM_NCMOUSEMOVE => RawPointerEventType.Move,
WindowsMessage.WM_NCLBUTTONDOWN => RawPointerEventType.LeftButtonDown,
WindowsMessage.WM_NCLBUTTONUP => RawPointerEventType.LeftButtonUp,
_ => throw new ArgumentOutOfRangeException(nameof(msg), msg, null)
},
PointToClient(PointFromLParam(lParam)),
RawInputModifiers.None);
}
else if (_trackingNonClientMouse && (WindowsMessage)msg == WindowsMessage.WM_NCMOUSEMOVE)
{
// Mouse moved in NC area but not over caption buttons - send leave event
_trackingNonClientMouse = false;
e = new RawPointerEventArgs(
_mouseDevice,
unchecked((uint)GetMessageTime()),
Owner,
RawPointerEventType.LeaveWindow,
new Point(-1, -1),
RawInputModifiers.None);
}
}
break;
case WindowsMessage.WM_NCMOUSELEAVE when !IsMouseInPointerEnabled:
_trackingNonClientMouse = false;
e = new RawPointerEventArgs(
_mouseDevice,
unchecked((uint)GetMessageTime()),
Owner,
RawPointerEventType.LeaveWindow,
new Point(-1, -1),
RawInputModifiers.None);
break;
case WindowsMessage.WM_NCPOINTERUPDATE when _wmPointerEnabled:
case WindowsMessage.WM_NCPOINTERDOWN when _wmPointerEnabled:
case WindowsMessage.WM_NCPOINTERUP when _wmPointerEnabled:

1
src/Windows/Avalonia.Win32/WindowImpl.cs

@ -89,6 +89,7 @@ namespace Avalonia.Win32
private IconImpl? _iconImpl;
private readonly Dictionary<(Icons type, uint dpi), Win32Icon> _iconCache = new();
private bool _trackingMouse;//ToDo - there is something missed. Needs investigation @Steven Kirk
private bool _trackingNonClientMouse;
private bool _topmost;
private double _scaling = 1;
private uint _dpi = 96;

19
tests/Avalonia.Base.UnitTests/Styling/SelectorTests_Or.cs

@ -1,3 +1,4 @@
using System;
using Avalonia.Controls;
using Avalonia.Styling;
using Xunit;
@ -89,6 +90,24 @@ namespace Avalonia.Base.UnitTests.Styling
Assert.Equal(null, target.TargetType);
}
[Fact]
public void ValidateNestingSelector_Checks_Children_When_Parent_Is_An_OrSelector()
{
var target = Selectors.Or(
default(Selector).Class("foo"),
default(Selector).Class("bar")
).Name("baz");
Assert.Throws<InvalidOperationException>(() => target.ValidateNestingSelector(false));
target = Selectors.Or(
default(Selector).Nesting().Class("foo"),
default(Selector).Nesting().Class("bar")
).Name("baz");
target.ValidateNestingSelector(false);
}
public class Control1 : Control
{

8
tests/Avalonia.Headless.NUnit.PerAssembly.UnitTests/AssemblyInfo.cs

@ -0,0 +1,8 @@
global using NUnit.Framework;
global using Avalonia.Headless.NUnit;
using Avalonia.Headless;
using Avalonia.Headless.UnitTests;
[assembly: AvaloniaTestApplication(typeof(TestApplication))]
[assembly: AvaloniaTestIsolation(AvaloniaTestIsolationLevel.PerAssembly)]

0
tests/Avalonia.Headless.NUnit.UnitTests/Avalonia.Headless.NUnit.UnitTests.csproj → tests/Avalonia.Headless.NUnit.PerAssembly.UnitTests/Avalonia.Headless.NUnit.PerAssembly.UnitTests.csproj

1
tests/Avalonia.Headless.NUnit.UnitTests/AssemblyInfo.cs → tests/Avalonia.Headless.NUnit.PerTest.UnitTests/AssemblyInfo.cs

@ -5,3 +5,4 @@ using Avalonia.Headless;
using Avalonia.Headless.UnitTests;
[assembly: AvaloniaTestApplication(typeof(TestApplication))]
[assembly: AvaloniaTestIsolation(AvaloniaTestIsolationLevel.PerTest)]

31
tests/Avalonia.Headless.NUnit.PerTest.UnitTests/Avalonia.Headless.NUnit.PerTest.UnitTests.csproj

@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(AvsCurrentTargetFramework)</TargetFramework>
<IsTestProject>true</IsTestProject>
<DefineConstants>$(DefineConstants);NUNIT</DefineConstants>
</PropertyGroup>
<Import Project="..\..\build\UnitTests.NetCore.targets" />
<Import Project="..\..\build\UnitTests.NetFX.props" />
<Import Project="..\..\build\Moq.props" />
<Import Project="..\..\build\Rx.props" />
<Import Project="..\..\build\SharedVersion.props" />
<ItemGroup>
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.4.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
</ItemGroup>
<ItemGroup>
<Compile Include="..\Avalonia.Headless.UnitTests\**\*.cs" />
<Compile Remove="..\Avalonia.Headless.UnitTests\bin\**\*.cs" />
<Compile Remove="..\Avalonia.Headless.UnitTests\obj\**\*.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Themes.Simple\Avalonia.Themes.Simple.csproj" />
<ProjectReference Include="..\..\src\Headless\Avalonia.Headless.NUnit\Avalonia.Headless.NUnit.csproj" />
<ProjectReference Include="..\..\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
</ItemGroup>
</Project>

63
tests/Avalonia.Headless.UnitTests/IsolationTests.cs

@ -0,0 +1,63 @@
using System;
using System.Reflection;
using Avalonia.Threading;
namespace Avalonia.Headless.UnitTests;
public class IsolationTests
{
private static WeakReference<Application> s_previousAppRef;
private static WeakReference<Dispatcher> s_previousDispatcherRef;
#if NUNIT
[AvaloniaTheory, Timeout(10000)]
[TestCase(1), TestCase(2), TestCase(3)]
#elif XUNIT
[AvaloniaTheory]
[InlineData(1), InlineData(2), InlineData(3)]
#endif
public void Application_Instance_Should_Match_Isolation_Level(int runIndex)
{
var currentApp = Application.Current;
var currentDispatcher = Dispatcher.UIThread;
if (s_previousAppRef is not null && s_previousDispatcherRef is not null)
{
var isolationLevel =
GetType().Assembly.GetCustomAttribute<AvaloniaTestIsolationAttribute>()?.IsolationLevel ??
AvaloniaTestIsolationLevel.PerTest;
if (isolationLevel == AvaloniaTestIsolationLevel.PerTest)
{
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
Assert.False(s_previousAppRef.TryGetTarget(out var previousApp),
"Previous Application instance should have been collected.");
Assert.False(s_previousDispatcherRef.TryGetTarget(out var previousDispatcher),
"Previous Dispatcher instance should have been collected.");
Assert.False(previousApp == currentApp);
Assert.False(previousDispatcher == currentDispatcher);
}
else if (isolationLevel == AvaloniaTestIsolationLevel.PerAssembly)
{
Assert.True(s_previousAppRef.TryGetTarget(out var previousApp),
"Previous Application instance should still be alive.");
Assert.True(s_previousDispatcherRef.TryGetTarget(out var previousDispatcher),
"Previous Dispatcher instance should still be alive.");
Assert.True(previousApp == currentApp);
Assert.True(previousDispatcher == currentDispatcher);
}
else
{
throw new InvalidOperationException($"Unknown isolation level: {isolationLevel}");
}
}
s_previousAppRef = new WeakReference<Application>(currentApp);
s_previousDispatcherRef = new WeakReference<Dispatcher>(currentDispatcher);
}
}

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

Loading…
Cancel
Save