Browse Source

Merge remote-tracking branch 'origin/fix_animations_render' into fix_animations_render

pull/9646/head
Adir Hudayfi 4 years ago
parent
commit
b68433b17a
  1. 18
      .editorconfig
  2. 6
      Avalonia.sln
  3. 2
      build/SharedVersion.props
  4. 16
      build/TrimmingEnable.props
  5. 9
      native/Avalonia.Native/src/OSX/WindowImpl.mm
  6. 1
      nukebuild/Build.cs
  7. 2
      nukebuild/_build.csproj
  8. 19
      packages/Avalonia/AvaloniaBuildTasks.targets
  9. 14
      samples/BindingDemo/App.xaml
  10. 9
      samples/ControlCatalog.NetCore/Program.cs
  11. 11
      samples/ControlCatalog.NetCore/Properties/launchSettings.json
  12. 10
      samples/ControlCatalog/ControlCatalog.csproj
  13. 29
      samples/ControlCatalog/Converter/DegToRadConverter.cs
  14. 3
      samples/ControlCatalog/MainView.xaml
  15. 25
      samples/ControlCatalog/Pages/AutoCompleteBoxPage.xaml.cs
  16. 2
      samples/ControlCatalog/Pages/CompositionPage.axaml.cs
  17. 107
      samples/ControlCatalog/Pages/CustomDrawing.xaml
  18. 67
      samples/ControlCatalog/Pages/CustomDrawing.xaml.cs
  19. 215
      samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs
  20. 18
      samples/ControlCatalog/Pages/ExpanderPage.xaml
  21. 6
      samples/ControlCatalog/Pages/ExpanderPage.xaml.cs
  22. 2
      samples/ControlCatalog/Pages/ImagePage.xaml.cs
  23. 2
      samples/ControlCatalog/Pages/OpenGlPage.xaml.cs
  24. 3
      samples/ControlCatalog/Pages/ScreenPage.cs
  25. 2
      samples/ControlCatalog/Pages/TabControlPage.xaml.cs
  26. 6
      samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs
  27. 3
      samples/Directory.Build.props
  28. 15
      samples/IntegrationTestApp/MainWindow.axaml
  29. 3
      samples/IntegrationTestApp/MainWindow.axaml.cs
  30. 8
      samples/IntegrationTestApp/ShowWindowTest.axaml
  31. 1
      src/Android/Avalonia.Android/Avalonia.Android.csproj
  32. 2
      src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs
  33. 2
      src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs
  34. 2
      src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs
  35. 2
      src/Avalonia.Base/Animation/Animation.cs
  36. 4
      src/Avalonia.Base/Animation/AnimatorKeyFrame.cs
  37. 2
      src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs
  38. 2
      src/Avalonia.Base/Animation/CrossFade.cs
  39. 2
      src/Avalonia.Base/Animation/KeySpline.cs
  40. 2
      src/Avalonia.Base/Animation/PageSlide.cs
  41. 2
      src/Avalonia.Base/AttachedProperty.cs
  42. 10
      src/Avalonia.Base/Avalonia.Base.csproj
  43. 2
      src/Avalonia.Base/AvaloniaObject.cs
  44. 100
      src/Avalonia.Base/AvaloniaObjectExtensions.cs
  45. 24
      src/Avalonia.Base/AvaloniaProperty.cs
  46. 6
      src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs
  47. 4
      src/Avalonia.Base/AvaloniaPropertyChangedEventArgs`1.cs
  48. 14
      src/Avalonia.Base/AvaloniaPropertyRegistry.cs
  49. 4
      src/Avalonia.Base/AvaloniaProperty`1.cs
  50. 4
      src/Avalonia.Base/ClassBindingManager.cs
  51. 4
      src/Avalonia.Base/Collections/AvaloniaListConverter.cs
  52. 121
      src/Avalonia.Base/Compatibility/TrimmingAttributes.cs
  53. 6
      src/Avalonia.Base/Controls/Classes.cs
  54. 2
      src/Avalonia.Base/Controls/ISetInheritanceParent.cs
  55. 3
      src/Avalonia.Base/Controls/ResourceNodeExtensions.cs
  56. 2
      src/Avalonia.Base/CornerRadius.cs
  57. 4
      src/Avalonia.Base/Data/BindingOperations.cs
  58. 3
      src/Avalonia.Base/Data/BindingValue.cs
  59. 2
      src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs
  60. 2
      src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs
  61. 4
      src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs
  62. 2
      src/Avalonia.Base/Data/Core/BindingExpression.cs
  63. 2
      src/Avalonia.Base/Data/Core/ExpressionNode.cs
  64. 5
      src/Avalonia.Base/Data/Core/ExpressionObserver.cs
  65. 4
      src/Avalonia.Base/Data/Core/Parsers/ExpressionTreeParser.cs
  66. 6
      src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs
  67. 3
      src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs
  68. 6
      src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs
  69. 3
      src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs
  70. 3
      src/Avalonia.Base/Data/Core/Plugins/IDataValidationPlugin.cs
  71. 3
      src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessorPlugin.cs
  72. 3
      src/Avalonia.Base/Data/Core/Plugins/IStreamPlugin.cs
  73. 5
      src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs
  74. 10
      src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs
  75. 9
      src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs
  76. 5
      src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs
  77. 9
      src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs
  78. 3
      src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs
  79. 2
      src/Avalonia.Base/Data/Core/StreamNode.cs
  80. 2
      src/Avalonia.Base/Data/IBinding.cs
  81. 6
      src/Avalonia.Base/Data/IndexerBinding.cs
  82. 2
      src/Avalonia.Base/Data/InstancedBinding.cs
  83. 2
      src/Avalonia.Base/Diagnostics/IAvaloniaObjectDebug.cs
  84. 30
      src/Avalonia.Base/Diagnostics/TrimmingMessages.cs
  85. 10
      src/Avalonia.Base/DirectProperty.cs
  86. 6
      src/Avalonia.Base/DirectPropertyBase.cs
  87. 79
      src/Avalonia.Base/IAvaloniaObject.cs
  88. 2
      src/Avalonia.Base/IDataContextProvider.cs
  89. 4
      src/Avalonia.Base/IDirectPropertyAccessor.cs
  90. 39
      src/Avalonia.Base/IStyledElement.cs
  91. 20
      src/Avalonia.Base/Input/AccessKeyHandler.cs
  92. 9
      src/Avalonia.Base/Input/DragDropDevice.cs
  93. 2
      src/Avalonia.Base/Input/DragEventArgs.cs
  94. 17
      src/Avalonia.Base/Input/FocusManager.cs
  95. 4
      src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs
  96. 4
      src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs
  97. 33
      src/Avalonia.Base/Input/Gestures.cs
  98. 39
      src/Avalonia.Base/Input/IInputElement.cs
  99. 2
      src/Avalonia.Base/Input/IMainMenu.cs
  100. 17
      src/Avalonia.Base/Input/InputExtensions.cs

18
.editorconfig

@ -64,7 +64,7 @@ dotnet_naming_symbols.static_fields.applicable_kinds = field
dotnet_naming_symbols.static_fields.required_modifiers = static dotnet_naming_symbols.static_fields.required_modifiers = static
dotnet_naming_style.static_prefix_style.required_prefix = s_ dotnet_naming_style.static_prefix_style.required_prefix = s_
dotnet_naming_style.static_prefix_style.capitalization = camel_case dotnet_naming_style.static_prefix_style.capitalization = camel_case
# internal and private fields should be _camelCase # internal and private fields should be _camelCase
dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion
@ -137,14 +137,30 @@ space_within_single_line_array_initializer_braces = true
#Net Analyzer #Net Analyzer
dotnet_analyzer_diagnostic.category-Performance.severity = none #error - Uncomment when all violations are fixed. dotnet_analyzer_diagnostic.category-Performance.severity = none #error - Uncomment when all violations are fixed.
# CS1591: Missing XML comment for publicly visible type or member
dotnet_diagnostic.CS1591.severity = suggestion
# CA1304: Specify CultureInfo
dotnet_diagnostic.CA1304.severity = warning
# CA1802: Use literals where appropriate # CA1802: Use literals where appropriate
dotnet_diagnostic.CA1802.severity = warning dotnet_diagnostic.CA1802.severity = warning
# CA1820: Test for empty strings using string length # CA1820: Test for empty strings using string length
dotnet_diagnostic.CA1820.severity = warning dotnet_diagnostic.CA1820.severity = warning
# CA1821: Remove empty finalizers # CA1821: Remove empty finalizers
dotnet_diagnostic.CA1821.severity = warning dotnet_diagnostic.CA1821.severity = warning
# CA1822: Mark members as static
dotnet_diagnostic.CA1822.severity = suggestion
dotnet_code_quality.CA1822.api_surface = private, internal
# CA1825: Avoid zero-length array allocations # CA1825: Avoid zero-length array allocations
dotnet_diagnostic.CA1825.severity = warning dotnet_diagnostic.CA1825.severity = warning
# CA1826: Use property instead of Linq Enumerable method
dotnet_diagnostic.CA1826.severity = suggestion
# CA1827: Do not use Count/LongCount when Any can be used
dotnet_diagnostic.CA1827.severity = warning
# CA1828: Do not use CountAsync/LongCountAsync when AnyAsync can be used
dotnet_diagnostic.CA1828.severity = warning
# CA1829: Use Length/Count property instead of Enumerable.Count method
dotnet_diagnostic.CA1829.severity = warning
#CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters #CA1847: Use string.Contains(char) instead of string.Contains(string) with single characters
dotnet_diagnostic.CA1847.severity = warning dotnet_diagnostic.CA1847.severity = warning

6
Avalonia.sln

@ -40,6 +40,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shared", "Shared", "{A689DE
.editorconfig = .editorconfig .editorconfig = .editorconfig
src\Shared\IsExternalInit.cs = src\Shared\IsExternalInit.cs src\Shared\IsExternalInit.cs = src\Shared\IsExternalInit.cs
src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs src\Shared\ModuleInitializer.cs = src\Shared\ModuleInitializer.cs
src\Shared\NullableAttributes.cs = src\Shared\NullableAttributes.cs
src\Shared\SourceGeneratorAttributes.cs = src\Shared\SourceGeneratorAttributes.cs src\Shared\SourceGeneratorAttributes.cs = src\Shared\SourceGeneratorAttributes.cs
src\Avalonia.Base\Compatibility\StringCompatibilityExtensions.cs = src\Avalonia.Base\Compatibility\StringCompatibilityExtensions.cs src\Avalonia.Base\Compatibility\StringCompatibilityExtensions.cs = src\Avalonia.Base\Compatibility\StringCompatibilityExtensions.cs
EndProjectSection EndProjectSection
@ -119,6 +120,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1
build\SourceLink.props = build\SourceLink.props build\SourceLink.props = build\SourceLink.props
build\System.Drawing.Common.props = build\System.Drawing.Common.props build\System.Drawing.Common.props = build\System.Drawing.Common.props
build\System.Memory.props = build\System.Memory.props build\System.Memory.props = build\System.Memory.props
build\TrimmingEnable.props = build\TrimmingEnable.props
build\UnitTests.NetFX.props = build\UnitTests.NetFX.props build\UnitTests.NetFX.props = build\UnitTests.NetFX.props
build\XUnit.props = build\XUnit.props build\XUnit.props = build\XUnit.props
EndProjectSection EndProjectSection
@ -222,9 +224,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.iOS", "sample
EndProject EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.Desktop", "samples\MobileSandbox.Desktop\MobileSandbox.Desktop.csproj", "{62D392C9-81CF-487F-92E8-598B2AF3FDCE}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MobileSandbox.Desktop", "samples\MobileSandbox.Desktop\MobileSandbox.Desktop.csproj", "{62D392C9-81CF-487F-92E8-598B2AF3FDCE}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Browser", "src\Browser\Avalonia.Browser\Avalonia.Browser.csproj", "{4A39637C-9338-4925-A4DB-D072E292EC78}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Browser", "src\Browser\Avalonia.Browser\Avalonia.Browser.csproj", "{4A39637C-9338-4925-A4DB-D072E292EC78}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.Browser.Blazor", "src\Browser\Avalonia.Browser.Blazor\Avalonia.Browser.Blazor.csproj", "{47F8530C-F19B-4B1A-B4D6-EB231522AE5D}" Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Browser.Blazor", "src\Browser\Avalonia.Browser.Blazor\Avalonia.Browser.Blazor.csproj", "{47F8530C-F19B-4B1A-B4D6-EB231522AE5D}"
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Browser", "samples\ControlCatalog.Browser\ControlCatalog.Browser.csproj", "{15B93A4C-1B46-43F6-B534-7B25B6E99932}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Browser", "samples\ControlCatalog.Browser\ControlCatalog.Browser.csproj", "{15B93A4C-1B46-43F6-B534-7B25B6E99932}"
EndProject EndProject

2
build/SharedVersion.props

@ -7,7 +7,7 @@
<PackageProjectUrl>https://avaloniaui.net</PackageProjectUrl> <PackageProjectUrl>https://avaloniaui.net</PackageProjectUrl>
<RepositoryUrl>https://github.com/AvaloniaUI/Avalonia/</RepositoryUrl> <RepositoryUrl>https://github.com/AvaloniaUI/Avalonia/</RepositoryUrl>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>CS1591</NoWarn> <NoWarn>$(NoWarn);CS1591</NoWarn>
<LangVersion>preview</LangVersion> <LangVersion>preview</LangVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>Icon.png</PackageIcon> <PackageIcon>Icon.png</PackageIcon>

16
build/TrimmingEnable.props

@ -0,0 +1,16 @@
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
<TrimmerSingleWarn>false</TrimmerSingleWarn>
<IsTrimmable>true</IsTrimmable>
</PropertyGroup>
<!-- Remove check for the AOT when we get rid of dependencies with reflection -->
<PropertyGroup Condition="'$(TargetFramework)' != 'netstandard2.0' and '$(PublishAot)' != 'true'">
<ILLinkTreatWarningsAsErrors>true</ILLinkTreatWarningsAsErrors>
<!-- Trim warnings -->
<WarningsAsErrors>$(WarningsAsErrors);IL2000;IL2001;IL2002;IL2003;IL2004;IL2005;IL2006;IL2007;IL2008;IL2009;IL2010;IL2011;IL2012;IL2013;IL2014;IL2015;IL2016;IL2017;IL2018;IL2019;IL2020;IL2021;IL2022;IL2023;IL2024;IL2025;IL2026;IL2027;IL2028;IL2029;IL2030;IL2031;IL2032;IL2033;IL2034;IL2035;IL2036;IL2037;IL2038;IL2039;IL2040;IL2041;IL2042;IL2043;IL2044;IL2045;IL2046;IL2047;IL2048;IL2049;IL2050;IL2051;IL2052;IL2053;IL2054;IL2055;IL2056;IL2057;IL2058;IL2059;IL2060;IL2061;IL2062;IL2063;IL2064;IL2065;IL2066;IL2067;IL2068;IL2069;IL2070;IL2071;IL2072;IL2073;IL2074;IL2075;IL2076;IL2077;IL2078;IL2079;IL2080;IL2081;IL2082;IL2083;IL2084;IL2085;IL2086;IL2087;IL2088;IL2089;IL2090;IL2091;IL2092;IL2093;IL2094;IL2095;IL2096;IL2097;IL2098;IL2099;IL2100;IL2101;IL2102;IL2103;IL2104;IL2105;IL2106;IL2107;IL2108;IL2109;IL2110;IL2111;IL2112;IL2113;IL2114;IL2115;IL2116;IL2117;IL2118;IL2119;IL2120;IL2121;IL2122;IL2123;IL2124;IL2125;IL2126;IL2127;IL2128;IL2129;IL2130;IL2131;IL2132;IL2133;IL2134;IL2135;IL2136;IL2137;IL2138;IL2139;IL2140;IL2141;IL2142;IL2143;IL2144;IL2145;IL2146;IL2147;IL2148;IL2149;IL2150;IL2151;IL2152;IL2153;IL2154;IL2155;IL2156;IL2157</WarningsAsErrors>
<!-- NativeAOT warnings -->
<WarningsAsErrors>$(WarningsAsErrors);IL3050;IL3051;IL3052;IL3053;IL3054;IL3055;IL3056</WarningsAsErrors>
</PropertyGroup>
</Project>

9
native/Avalonia.Native/src/OSX/WindowImpl.mm

@ -137,7 +137,11 @@ void WindowImpl::BringToFront()
for(auto iterator = _children.begin(); iterator != _children.end(); iterator++) for(auto iterator = _children.begin(); iterator != _children.end(); iterator++)
{ {
(*iterator)->BringToFront(); auto window = (*iterator)->Window;
// #9565: Only bring window to front if it's on the currently active space
if ([window isOnActiveSpace])
(*iterator)->BringToFront();
} }
} }
} }
@ -161,6 +165,9 @@ void WindowImpl::StartStateTransition() {
void WindowImpl::EndStateTransition() { void WindowImpl::EndStateTransition() {
_transitioningWindowState = false; _transitioningWindowState = false;
// Ensure correct order of child windows after fullscreen transition.
BringToFront();
} }
SystemDecorations WindowImpl::Decorations() { SystemDecorations WindowImpl::Decorations() {

1
nukebuild/Build.cs

@ -80,7 +80,6 @@ partial class Build : NukeBuild
if (Parameters.IsRunningOnAzure) if (Parameters.IsRunningOnAzure)
c.AddProperty("JavaSdkDirectory", GetVariable<string>("JAVA_HOME_11_X64")); c.AddProperty("JavaSdkDirectory", GetVariable<string>("JAVA_HOME_11_X64"));
c.AddProperty("PackageVersion", Parameters.Version) c.AddProperty("PackageVersion", Parameters.Version)
.AddProperty("iOSRoslynPathHackRequired", true)
.SetConfiguration(Parameters.Configuration) .SetConfiguration(Parameters.Configuration)
.SetVerbosity(DotNetVerbosity.Minimal); .SetVerbosity(DotNetVerbosity.Minimal);
return c; return c;

2
nukebuild/_build.csproj

@ -4,7 +4,7 @@
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath> <AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<RootNamespace></RootNamespace> <RootNamespace></RootNamespace>
<IsPackable>False</IsPackable> <IsPackable>False</IsPackable>
<NoWarn>CS0649;CS0169;SYSLIB0011</NoWarn> <NoWarn>$(NoWarn);CS0649;CS0169;SYSLIB0011</NoWarn>
<NukeTelemetryVersion>1</NukeTelemetryVersion> <NukeTelemetryVersion>1</NukeTelemetryVersion>
<TargetFramework>net7.0</TargetFramework> <TargetFramework>net7.0</TargetFramework>
</PropertyGroup> </PropertyGroup>

19
packages/Avalonia/AvaloniaBuildTasks.targets

@ -30,7 +30,9 @@
/> />
<Target Name="AddAvaloniaResources" BeforeTargets="ResolveReferences"> <Target Name="AddAvaloniaResources"
BeforeTargets="ResolveReferences"
Condition="('@(AvaloniaResource->Count())' &gt; 0) or ('@(AvaloniaXaml->Count())' &gt; 0)">
<PropertyGroup> <PropertyGroup>
<AvaloniaResourcesTemporaryFilePath Condition="'$(AvaloniaResourcesTemporaryFilePath)' == ''">$(IntermediateOutputPath)/Avalonia/resources</AvaloniaResourcesTemporaryFilePath> <AvaloniaResourcesTemporaryFilePath Condition="'$(AvaloniaResourcesTemporaryFilePath)' == ''">$(IntermediateOutputPath)/Avalonia/resources</AvaloniaResourcesTemporaryFilePath>
</PropertyGroup> </PropertyGroup>
@ -62,7 +64,9 @@
BeforeTargets="CoreCompile;CoreResGen" BeforeTargets="CoreCompile;CoreResGen"
Inputs="@(AvaloniaResource);@(AvaloniaXaml);@(CustomAdditionalGenerateAvaloniaResourcesInputs);$(MSBuildAllProjects)" Inputs="@(AvaloniaResource);@(AvaloniaXaml);@(CustomAdditionalGenerateAvaloniaResourcesInputs);$(MSBuildAllProjects)"
Outputs="$(AvaloniaResourcesTemporaryFilePath)" Outputs="$(AvaloniaResourcesTemporaryFilePath)"
DependsOnTargets="$(BuildAvaloniaResourcesDependsOn)"> DependsOnTargets="$(BuildAvaloniaResourcesDependsOn)"
Condition="('@(AvaloniaResource->Count())' &gt; 0) or ('@(AvaloniaXaml->Count())' &gt; 0)"
>
<ItemGroup> <ItemGroup>
<AvaloniaResource Include="@(AvaloniaXaml)"/> <AvaloniaResource Include="@(AvaloniaXaml)"/>
</ItemGroup> </ItemGroup>
@ -74,14 +78,19 @@
ReportImportance="$(AvaloniaXamlReportImportance)"/> ReportImportance="$(AvaloniaXamlReportImportance)"/>
<Exec <Exec
Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'" Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'"
Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:GenerateAvaloniaResources /p:_AvaloniaForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:TargetFramework=$(TargetFramework) /p:BuildProjectReferences=false"/> Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:GenerateAvaloniaResources /p:_AvaloniaForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:TargetFramework=$(TargetFramework) /p:RuntimeIdentifier=$(RuntimeIdentifier) /p:BuildProjectReferences=false"/>
</Target> </Target>
<Target <Target
Name="CompileAvaloniaXaml" Name="CompileAvaloniaXaml"
AfterTargets="AfterCompile" AfterTargets="AfterCompile"
Condition="Exists('@(IntermediateAssembly)') And $(DesignTimeBuild) != true And $(EnableAvaloniaXamlCompilation) != false" Condition="
(('@(AvaloniaResource->Count())' &gt; 0)
or ('@(AvaloniaXaml->Count())' &gt; 0))
and Exists('@(IntermediateAssembly)')
And $(DesignTimeBuild) != true
And $(EnableAvaloniaXamlCompilation) != false"
> >
<PropertyGroup> <PropertyGroup>
<AvaloniaXamlReferencesTemporaryFilePath Condition="'$(AvaloniaXamlReferencesTemporaryFilePath)' == ''">$(IntermediateOutputPath)/Avalonia/references</AvaloniaXamlReferencesTemporaryFilePath> <AvaloniaXamlReferencesTemporaryFilePath Condition="'$(AvaloniaXamlReferencesTemporaryFilePath)' == ''">$(IntermediateOutputPath)/Avalonia/references</AvaloniaXamlReferencesTemporaryFilePath>
@ -112,7 +121,7 @@
/> />
<Exec <Exec
Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'" Condition="'$(_AvaloniaUseExternalMSBuild)' == 'true'"
Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:CompileAvaloniaXaml /p:_AvaloniaForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:TargetFramework=$(TargetFramework) /p:BuildProjectReferences=false"/> Command="dotnet msbuild /nodereuse:false $(MSBuildProjectFile) /t:CompileAvaloniaXaml /p:_AvaloniaForceInternalMSBuild=true /p:Configuration=$(Configuration) /p:TargetFramework=$(TargetFramework) /p:RuntimeIdentifier=$(RuntimeIdentifier) /p:BuildProjectReferences=false"/>
</Target> </Target>

14
samples/BindingDemo/App.xaml

@ -2,8 +2,14 @@
xmlns="https://github.com/avaloniaui" xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="BindingDemo.App"> x:Class="BindingDemo.App">
<Application.Styles> <Application.Resources>
<FluentTheme /> <ResourceDictionary>
<StyleInclude Source="avares://Avalonia.Themes.Simple/Accents/BaseLight.xaml"/> <ResourceDictionary.MergedDictionaries>
</Application.Styles> <ResourceInclude Source="avares://Avalonia.Themes.Simple/Accents/BaseLight.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application> </Application>

9
samples/ControlCatalog.NetCore/Program.cs

@ -99,6 +99,15 @@ namespace ControlCatalog.NetCore
SilenceConsole(); SilenceConsole();
return builder.StartLinuxDrm(args, scaling: GetScaling()); return builder.StartLinuxDrm(args, scaling: GetScaling());
} }
else if (args.Contains("--dxgi"))
{
builder.With(new Win32PlatformOptions()
{
UseLowLatencyDxgiSwapChain = true,
UseWindowsUIComposition = false
});
return builder.StartWithClassicDesktopLifetime(args);
}
else else
return builder.StartWithClassicDesktopLifetime(args); return builder.StartWithClassicDesktopLifetime(args);
} }

11
samples/ControlCatalog.NetCore/Properties/launchSettings.json

@ -0,0 +1,11 @@
{
"profiles": {
"ControlCatalog.NetCore": {
"commandName": "Project"
},
"Dxgi": {
"commandName": "Project",
"commandLineArgs": "--dxgi"
}
}
}

10
samples/ControlCatalog/ControlCatalog.csproj

@ -33,4 +33,14 @@
</ItemGroup> </ItemGroup>
<Import Project="..\..\build\BuildTargets.targets" /> <Import Project="..\..\build\BuildTargets.targets" />
<ItemGroup>
<None Remove="Pages\CustomDrawing.xaml" />
</ItemGroup>
<ItemGroup>
<AvaloniaResource Update="Pages\CustomDrawing.xaml">
<Generator></Generator>
</AvaloniaResource>
</ItemGroup>
</Project> </Project>

29
samples/ControlCatalog/Converter/DegToRadConverter.cs

@ -0,0 +1,29 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Avalonia.Data.Converters;
namespace ControlCatalog.Converter
{
public class DegToRadConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is double rad)
{
return rad * 180.0d / Math.PI;
}
return 0.0d;
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if (value is double deg)
{
return deg / 180.0d * Math.PI;
}
return 0.0d;
}
}
}

3
samples/ControlCatalog/MainView.xaml

@ -66,6 +66,9 @@
<TabItem Header="Cursor" ScrollViewer.VerticalScrollBarVisibility="Disabled"> <TabItem Header="Cursor" ScrollViewer.VerticalScrollBarVisibility="Disabled">
<pages:CursorPage /> <pages:CursorPage />
</TabItem> </TabItem>
<TabItem Header="Custom Drawing" ScrollViewer.VerticalScrollBarVisibility="Disabled">
<pages:CustomDrawing/>
</TabItem>
<TabItem Header="DataGrid" <TabItem Header="DataGrid"
ScrollViewer.HorizontalScrollBarVisibility="Disabled" ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Disabled"> ScrollViewer.VerticalScrollBarVisibility="Disabled">

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

@ -14,7 +14,26 @@ namespace ControlCatalog.Pages
{ {
public class AutoCompleteBoxPage : UserControl public class AutoCompleteBoxPage : UserControl
{ {
private StateData[] BuildAllStates() public class StateData
{
public string Name { get; private set; }
public string Abbreviation { get; private set; }
public string Capital { get; private set; }
public StateData(string name, string abbreviatoin, string capital)
{
Name = name;
Abbreviation = abbreviatoin;
Capital = capital;
}
public override string ToString()
{
return Name;
}
}
private static StateData[] BuildAllStates()
{ {
return new StateData[] return new StateData[]
{ {
@ -72,7 +91,7 @@ namespace ControlCatalog.Pages
} }
public StateData[] States { get; private set; } public StateData[] States { get; private set; }
private LinkedList<string>[] BuildAllSentences() private static LinkedList<string>[] BuildAllSentences()
{ {
return new string[] return new string[]
{ {
@ -124,7 +143,7 @@ namespace ControlCatalog.Pages
.OfType<AutoCompleteBox>(); .OfType<AutoCompleteBox>();
} }
private bool StringContains(string str, string? query) 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; return str.IndexOf(query, StringComparison.OrdinalIgnoreCase) >= 0;

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

@ -25,7 +25,7 @@ public partial class CompositionPage : UserControl
this.Get<ItemsControl>("Items").Items = CreateColorItems(); this.Get<ItemsControl>("Items").Items = CreateColorItems();
} }
private List<CompositionPageColorItem> CreateColorItems() private static List<CompositionPageColorItem> CreateColorItems()
{ {
var list = new List<CompositionPageColorItem>(); var list = new List<CompositionPageColorItem>();

107
samples/ControlCatalog/Pages/CustomDrawing.xaml

@ -0,0 +1,107 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="using:ControlCatalog.Pages"
xmlns:converters="using:ControlCatalog.Converter"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="ControlCatalog.Pages.CustomDrawing">
<UserControl.Resources>
<converters:DegToRadConverter x:Key="DegToRadConverter"/>
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition/>
</Grid.RowDefinitions>
<StackPanel Orientation="Vertical">
<TextBlock Text="Translation" HorizontalAlignment="Center"/>
<Grid ColumnDefinitions="*,*"
RowDefinitions="Auto,Auto"
>
<TextBlock Text="Horizontal"/>
<TextBlock Text="Vertical" Grid.Column="1"/>
<TextBox IsEnabled="False"
Text="{Binding ElementName=CustomDrawingControl,
Path=ViewportCenterX,
Mode=OneWay,
StringFormat=\{0:g4\}}"
Grid.Row="1"
/>
<TextBox IsEnabled="False"
Text="{Binding ElementName=CustomDrawingControl,
Path=ViewportCenterY,
Mode=OneWay,
StringFormat=\{0:g4\}}"
Grid.Row="1" Grid.Column="1"
/>
</Grid>
</StackPanel>
<StackPanel Orientation="Vertical" Grid.Column="1"
>
<TextBlock Text="Rotation" HorizontalAlignment="Center"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"
>
<Button Content="➖" Width="40" Height="40"
VerticalContentAlignment="Center"
VerticalAlignment="Center"
Click="RotateMinus"
/>
<TextBox IsEnabled="False"
Text="{Binding ElementName=CustomDrawingControl,
Path=Rotation,
Converter={StaticResource DegToRadConverter},
Mode=OneWay,
StringFormat=\{0:g4\}}"
Grid.Row="1" Grid.Column="1"
/>
<Button Content="➕" Width="40" Height="40"
VerticalContentAlignment="Center"
VerticalAlignment="Center"
Click="RotatePlus"
/>
</StackPanel>
</StackPanel>
<StackPanel Orientation="Vertical" Grid.Column="2"
>
<TextBlock Text="Scale" HorizontalAlignment="Center"/>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"
>
<Button Content="➖" Width="40" Height="40"
VerticalContentAlignment="Center"
VerticalAlignment="Center"
Click="ZoomOut"
/>
<TextBox IsEnabled="False"
Text="{Binding ElementName=CustomDrawingControl,
Path=Scale,
Mode=OneWay,
StringFormat=\{0:g4\}}"
Grid.Row="1" Grid.Column="1"
/>
<Button Content="➕" Width="40" Height="40"
VerticalContentAlignment="Center"
VerticalAlignment="Center"
Click="ZoomIn"
/>
</StackPanel>
</StackPanel>
<Grid Grid.Row="1" Grid.ColumnSpan="3" ClipToBounds="True">
<local:CustomDrawingExampleControl
x:Name="CustomDrawingControl"
/>
</Grid>
</Grid>
</UserControl>

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

@ -0,0 +1,67 @@
using System;
using Avalonia.Controls;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public partial class CustomDrawing : UserControl
{
public CustomDrawing()
{
InitializeComponent();
}
private CustomDrawingExampleControl? _customControl;
public CustomDrawingExampleControl CustomDrawingControl
{
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;
}
private void RotatePlus(object? sender, RoutedEventArgs e)
{
if (_customControl is null)
return;
_customControl.Rotation += Math.PI / 20.0d;
}
private void ZoomIn(object? sender, RoutedEventArgs e)
{
if (_customControl is null)
return;
_customControl.Scale *= 1.2d;
}
private void ZoomOut(object? sender, RoutedEventArgs e)
{
if (_customControl is null)
return;
_customControl.Scale /= 1.2d;
}
}
}

215
samples/ControlCatalog/Pages/CustomDrawingExampleControl.cs

@ -0,0 +1,215 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Input;
using Avalonia.Threading;
using Avalonia.Controls.Shapes;
namespace ControlCatalog.Pages
{
public class CustomDrawingExampleControl : Control
{
private Point _cursorPoint;
public StyledProperty<double> ScaleProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(Scale), 1.0d);
public double Scale { get => GetValue(ScaleProperty); set => SetValue(ScaleProperty, value); }
public StyledProperty<double> RotationProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(Rotation));
/// <summary>
/// Rotation, measured in Radians!
/// </summary>
public double Rotation
{
get => GetValue(RotationProperty);
set
{
double valueToUse = value % (Math.PI * 2);
SetValue(RotationProperty, valueToUse);
}
}
public StyledProperty<double> ViewportCenterYProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(ViewportCenterY), 0.0d);
public double ViewportCenterY { get => GetValue(ViewportCenterYProperty); set => SetValue(ViewportCenterYProperty, value); }
public StyledProperty<double> ViewportCenterXProperty = AvaloniaProperty.Register<CustomDrawingExampleControl, double>(nameof(ViewportCenterX), 0.0d);
public double ViewportCenterX { get => GetValue(ViewportCenterXProperty); set => SetValue(ViewportCenterXProperty, value); }
private IPen _pen;
private System.Diagnostics.Stopwatch _timeKeeper = System.Diagnostics.Stopwatch.StartNew();
private bool _isPointerCaptured = false;
public CustomDrawingExampleControl()
{
_pen = new Pen(new SolidColorBrush(Colors.Black), lineCap: PenLineCap.Round);
var _arc = new ArcSegment()
{
IsLargeArc = false,
Point = new Point(0, 0),
RotationAngle = 0,
Size = new Size(25, 25),
SweepDirection = SweepDirection.Clockwise,
};
StreamGeometry sg = new StreamGeometry();
var cntx = sg.Open();
cntx.BeginFigure(new Point(-25.0d, -10.0d), false);
cntx.ArcTo(new Point(25.0d, -10.0d), new Size(10.0d, 10.0d), 0.0d, false, SweepDirection.Clockwise);
cntx.EndFigure(true);
_smileGeometry = sg.Clone();
}
private Geometry _smileGeometry;
protected override void OnPointerMoved(PointerEventArgs e)
{
base.OnPointerMoved(e);
Point previousPoint = _cursorPoint;
_cursorPoint = e.GetPosition(this);
if (_isPointerCaptured)
{
Point oldWorldPoint = UIPointToWorldPoint(previousPoint, ViewportCenterX, ViewportCenterY, Scale, Rotation);
Point newWorldPoint = UIPointToWorldPoint(_cursorPoint, ViewportCenterX, ViewportCenterY, Scale, Rotation);
Vector diff = newWorldPoint - oldWorldPoint;
ViewportCenterX -= diff.X;
ViewportCenterY -= diff.Y;
}
}
protected override void OnPointerPressed(PointerPressedEventArgs e)
{
e.Handled = true;
e.Pointer.Capture(this);
_isPointerCaptured = true;
base.OnPointerPressed(e);
}
protected override void OnPointerWheelChanged(PointerWheelEventArgs e)
{
base.OnPointerWheelChanged(e);
var oldScale = Scale;
Scale *= (1.0d + e.Delta.Y / 12.0d);
Point oldWorldPoint = UIPointToWorldPoint(_cursorPoint, ViewportCenterX, ViewportCenterY, oldScale, Rotation);
Point newWorldPoint = UIPointToWorldPoint(_cursorPoint, ViewportCenterX, ViewportCenterY, Scale, Rotation);
Vector diff = newWorldPoint - oldWorldPoint;
ViewportCenterX -= diff.X;
ViewportCenterY -= diff.Y;
}
protected override void OnPointerReleased(PointerReleasedEventArgs e)
{
e.Pointer.Capture(null);
_isPointerCaptured = false;
base.OnPointerReleased(e);
}
public override void Render(DrawingContext context)
{
var localBounds = new Rect(new Size(this.Bounds.Width, this.Bounds.Height));
var clip = context.PushClip(this.Bounds);
context.DrawRectangle(Brushes.White, _pen, localBounds, 1.0d);
var halfMax = Math.Max(this.Bounds.Width / 2.0d, this.Bounds.Height / 2.0d) * Math.Sqrt(2.0d);
var halfMin = Math.Min(this.Bounds.Width / 2.0d, this.Bounds.Height / 2.0d) / 1.3d;
var halfWidth = this.Bounds.Width / 2.0d;
var halfHeight = this.Bounds.Height / 2.0d;
// 0,0 refers to the top-left of the control now. It is not prime time to draw gui stuff because it'll be under the world
var translateModifier = context.PushPreTransform(Avalonia.Matrix.CreateTranslation(new Avalonia.Vector(halfWidth, halfHeight)));
// now 0,0 refers to the ViewportCenter(X,Y).
var rotationMatrix = Avalonia.Matrix.CreateRotation(Rotation);
var rotationModifier = context.PushPreTransform(rotationMatrix);
// everything is rotated but not scaled
var scaleModifier = context.PushPreTransform(Avalonia.Matrix.CreateScale(Scale, -Scale));
var mapPositionModifier = context.PushPreTransform(Matrix.CreateTranslation(new Vector(-ViewportCenterX, -ViewportCenterY)));
// now everything is rotated and scaled, and at the right position, now we're drawing strictly in world coordinates
context.DrawEllipse(Brushes.White, _pen, new Point(0.0d, 0.0d), 50.0d, 50.0d);
context.DrawLine(_pen, new Point(-25.0d, -5.0d), new Point(-25.0d, 15.0d));
context.DrawLine(_pen, new Point(25.0d, -5.0d), new Point(25.0d, 15.0d));
context.DrawGeometry(null, _pen, _smileGeometry);
Point cursorInWorldPoint = UIPointToWorldPoint(_cursorPoint, ViewportCenterX, ViewportCenterY, Scale, Rotation);
context.DrawEllipse(Brushes.Gray, _pen, cursorInWorldPoint, 20.0d, 20.0d);
for (int i = 0; i < 10; i++)
{
double orbitRadius = i * 100 + 200;
var orbitInput = ((_timeKeeper.Elapsed.TotalMilliseconds + 987654d) / orbitRadius) / 10.0d;
if (i % 3 == 0)
orbitInput *= -1;
Point orbitPosition = new Point(Math.Sin(orbitInput) * orbitRadius, Math.Cos(orbitInput) * orbitRadius);
context.DrawEllipse(Brushes.Gray, _pen, orbitPosition, 20.0d, 20.0d);
}
// end drawing the world
mapPositionModifier.Dispose();
scaleModifier.Dispose();
rotationModifier.Dispose();
translateModifier.Dispose();
// this is prime time to draw gui stuff
context.DrawLine(_pen, _cursorPoint + new Vector(-20, 0), _cursorPoint + new Vector(20, 0));
context.DrawLine(_pen, _cursorPoint + new Vector(0, -20), _cursorPoint + new Vector(0, 20));
clip.Dispose();
// oh and draw again when you can, no rush, right?
Dispatcher.UIThread.Post(InvalidateVisual, DispatcherPriority.Background);
}
private Point UIPointToWorldPoint(Point inPoint, double viewportCenterX, double viewportCenterY, double scale, double rotation)
{
Point workingPoint = new Point(inPoint.X, -inPoint.Y);
workingPoint += new Vector(-this.Bounds.Width / 2.0d, this.Bounds.Height / 2.0d);
workingPoint /= scale;
workingPoint = Matrix.CreateRotation(rotation).Transform(workingPoint);
workingPoint += new Vector(viewportCenterX, viewportCenterY);
return workingPoint;
}
private Point WorldPointToUIPoint(Point inPoint, double viewportCenterX, double viewportCenterY, double scale, double rotation)
{
Point workingPoint = new Point(inPoint.X, inPoint.Y);
workingPoint -= new Vector(viewportCenterX, viewportCenterY);
// undo rotation
workingPoint = Matrix.CreateRotation(-rotation).Transform(workingPoint);
workingPoint *= scale;
workingPoint -= new Vector(-this.Bounds.Width / 2.0d, this.Bounds.Height / 2.0d);
workingPoint = new Point(workingPoint.X, -workingPoint.Y);
return workingPoint;
}
}
}

18
samples/ControlCatalog/Pages/ExpanderPage.xaml

@ -52,6 +52,24 @@
</StackPanel> </StackPanel>
</Expander> </Expander>
<CheckBox IsChecked="{Binding Rounded}">Rounded</CheckBox> <CheckBox IsChecked="{Binding Rounded}">Rounded</CheckBox>
<Expander x:Name="CollapsingDisabledExpander"
Header="Collapsing Disabled"
IsExpanded="True"
ExpandDirection="Down"
CornerRadius="{Binding CornerRadius}">
<StackPanel>
<TextBlock>Expanded content</TextBlock>
</StackPanel>
</Expander>
<Expander x:Name="ExpandingDisabledExpander"
Header="Expanding Disabled"
IsExpanded="False"
ExpandDirection="Down"
CornerRadius="{Binding CornerRadius}">
<StackPanel>
<TextBlock>Expanded content</TextBlock>
</StackPanel>
</Expander>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
</UserControl> </UserControl>

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

@ -10,6 +10,12 @@ namespace ControlCatalog.Pages
{ {
this.InitializeComponent(); this.InitializeComponent();
DataContext = new ExpanderPageViewModel(); DataContext = new ExpanderPageViewModel();
var CollapsingDisabledExpander = this.Get<Expander>("CollapsingDisabledExpander");
var ExpandingDisabledExpander = this.Get<Expander>("ExpandingDisabledExpander");
CollapsingDisabledExpander.Collapsing += (s, e) => { e.Handled = true; };
ExpandingDisabledExpander.Expanding += (s, e) => { e.Handled = true; };
} }
private void InitializeComponent() private void InitializeComponent()

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

@ -58,7 +58,7 @@ namespace ControlCatalog.Pages
} }
} }
private PixelRect GetCropRect(int index) private static PixelRect GetCropRect(int index)
{ {
var bitmapWidth = 640; var bitmapWidth = 640;
var bitmapHeight = 426; var bitmapHeight = 426;

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

@ -247,7 +247,7 @@ namespace ControlCatalog.Pages
} }
private void CheckError(GlInterface gl) private static void CheckError(GlInterface gl)
{ {
int err; int err;
while ((err = gl.GetError()) != GL_NO_ERROR) while ((err = gl.GetError()) != GL_NO_ERROR)

3
samples/ControlCatalog/Pages/ScreenPage.cs

@ -66,6 +66,7 @@ namespace ControlCatalog.Pages
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 40)); context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 40));
formattedText = CreateFormattedText($"IsPrimary: {screen.IsPrimary}"); formattedText = CreateFormattedText($"IsPrimary: {screen.IsPrimary}");
context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 60)); context.DrawText(formattedText, boundsRect.Position.WithY(boundsRect.Size.Height + 60));
formattedText = formattedText =
@ -77,7 +78,7 @@ namespace ControlCatalog.Pages
context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10f, w.Bounds.Width / 10, w.Bounds.Height / 10)); context.DrawRectangle(p, new Rect(w.Position.X / 10f + Math.Abs(_leftMost), w.Position.Y / 10f, w.Bounds.Width / 10, w.Bounds.Height / 10));
} }
private FormattedText CreateFormattedText(string textToFormat) private static FormattedText CreateFormattedText(string textToFormat)
{ {
return new FormattedText(textToFormat, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, return new FormattedText(textToFormat, CultureInfo.CurrentCulture, FlowDirection.LeftToRight,
Typeface.Default, 12, Brushes.Green); Typeface.Default, 12, Brushes.Green);

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

@ -49,7 +49,7 @@ namespace ControlCatalog.Pages
AvaloniaXamlLoader.Load(this); AvaloniaXamlLoader.Load(this);
} }
private IBitmap LoadBitmap(string uri) private static IBitmap LoadBitmap(string uri)
{ {
var assets = AvaloniaLocator.Current!.GetService<IAssetLoader>()!; var assets = AvaloniaLocator.Current!.GetService<IAssetLoader>()!;
return new Bitmap(assets.Open(new Uri(uri))); return new Bitmap(assets.Open(new Uri(uri)));

6
samples/ControlCatalog/ViewModels/TransitioningContentControlPageViewModel.cs

@ -293,10 +293,10 @@ namespace ControlCatalog.ViewModels
/// <remarks> /// <remarks>
/// Any one of the parameters may be null, but not both. /// Any one of the parameters may be null, but not both.
/// </remarks> /// </remarks>
private static IVisual GetVisualParent(IVisual? from, IVisual? to) private static Visual GetVisualParent(Visual? from, Visual? to)
{ {
var p1 = (from ?? to)!.VisualParent; var p1 = (from ?? to)!.GetVisualParent();
var p2 = (to ?? from)!.VisualParent; var p2 = (to ?? from)!.GetVisualParent();
if (p1 != null && p2 != null && p1 != p2) if (p1 != null && p2 != null && p1 != p2)
{ {

3
samples/Directory.Build.props

@ -4,4 +4,7 @@
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)..\src\tools\Avalonia.Designer.HostApp\bin\Debug\netcoreapp2.0\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath> <AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)..\src\tools\Avalonia.Designer.HostApp\bin\Debug\netcoreapp2.0\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
</PropertyGroup> </PropertyGroup>
<Import Project="..\build\SharedVersion.props" /> <Import Project="..\build\SharedVersion.props" />
<PropertyGroup>
<EnableNETAnalyzers>false</EnableNETAnalyzers>
</PropertyGroup>
</Project> </Project>

15
samples/IntegrationTestApp/MainWindow.axaml

@ -17,11 +17,15 @@
</NativeMenuItem> </NativeMenuItem>
<NativeMenuItem Header="View"> <NativeMenuItem Header="View">
<NativeMenu/> <NativeMenu/>
</NativeMenuItem> </NativeMenuItem>
</NativeMenu> </NativeMenu>
</NativeMenu.Menu> </NativeMenu.Menu>
<DockPanel> <DockPanel>
<NativeMenuBar DockPanel.Dock="Top"/> <NativeMenuBar DockPanel.Dock="Top"/>
<StackPanel DockPanel.Dock="Bottom" Margin="4" Orientation="Horizontal">
<TextBlock Margin="0,0,4,0">WindowState:</TextBlock>
<TextBlock Name="MainWindowState" Text="{Binding WindowState}"/>
</StackPanel>
<TabControl TabStripPlacement="Left" Name="MainTabs"> <TabControl TabStripPlacement="Left" Name="MainTabs">
<TabItem Header="Automation"> <TabItem Header="Automation">
@ -129,13 +133,14 @@
<ComboBoxItem>CenterOwner</ComboBoxItem> <ComboBoxItem>CenterOwner</ComboBoxItem>
</ComboBox> </ComboBox>
<ComboBox Name="ShowWindowState" SelectedIndex="0"> <ComboBox Name="ShowWindowState" SelectedIndex="0">
<ComboBoxItem>Normal</ComboBoxItem> <ComboBoxItem Name="ShowWindowStateNormal">Normal</ComboBoxItem>
<ComboBoxItem>Minimized</ComboBoxItem> <ComboBoxItem Name="ShowWindowStateMinimized">Minimized</ComboBoxItem>
<ComboBoxItem>Maximized</ComboBoxItem> <ComboBoxItem Name="ShowWindowStateMaximized">Maximized</ComboBoxItem>
<ComboBoxItem>FullScreen</ComboBoxItem> <ComboBoxItem Name="ShowWindowStateFullScreen">FullScreen</ComboBoxItem>
</ComboBox> </ComboBox>
<Button Name="ShowWindow">Show Window</Button> <Button Name="ShowWindow">Show Window</Button>
<Button Name="SendToBack">Send to Back</Button> <Button Name="SendToBack">Send to Back</Button>
<Button Name="EnterFullscreen">Enter Fullscreen</Button>
<Button Name="ExitFullscreen">Exit Fullscreen</Button> <Button Name="ExitFullscreen">Exit Fullscreen</Button>
<Button Name="RestoreAll">Restore All</Button> <Button Name="RestoreAll">Restore All</Button>
</StackPanel> </StackPanel>

3
samples/IntegrationTestApp/MainWindow.axaml.cs

@ -1,3 +1,4 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Avalonia; using Avalonia;
@ -178,6 +179,8 @@ namespace IntegrationTestApp
ShowWindow(); ShowWindow();
if (source?.Name == "SendToBack") if (source?.Name == "SendToBack")
SendToBack(); SendToBack();
if (source?.Name == "EnterFullscreen")
WindowState = WindowState.FullScreen;
if (source?.Name == "ExitFullscreen") if (source?.Name == "ExitFullscreen")
WindowState = WindowState.Normal; WindowState = WindowState.Normal;
if (source?.Name == "RestoreAll") if (source?.Name == "RestoreAll")

8
samples/IntegrationTestApp/ShowWindowTest.axaml

@ -27,10 +27,10 @@
<Label Grid.Column="0" Grid.Row="7">WindowState</Label> <Label Grid.Column="0" Grid.Row="7">WindowState</Label>
<ComboBox Name="WindowState" Grid.Column="1" Grid.Row="7" SelectedIndex="{Binding WindowState}"> <ComboBox Name="WindowState" Grid.Column="1" Grid.Row="7" SelectedIndex="{Binding WindowState}">
<ComboBoxItem>Normal</ComboBoxItem> <ComboBoxItem Name="WindowStateNormal">Normal</ComboBoxItem>
<ComboBoxItem>Minimized</ComboBoxItem> <ComboBoxItem Name="WindowStateMinimized">Minimized</ComboBoxItem>
<ComboBoxItem>Maximized</ComboBoxItem> <ComboBoxItem Name="WindowStateMaximized">Maximized</ComboBoxItem>
<ComboBoxItem>FullScreen</ComboBoxItem> <ComboBoxItem Name="WindowStateFullScreen">FullScreen</ComboBoxItem>
</ComboBox> </ComboBox>
<Label Grid.Column="0" Grid.Row="8">Order (mac)</Label> <Label Grid.Column="0" Grid.Row="8">Order (mac)</Label>

1
src/Android/Avalonia.Android/Avalonia.Android.csproj

@ -16,4 +16,5 @@
<ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" /> <ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
</ItemGroup> </ItemGroup>
<Import Project="..\..\..\build\DevAnalyzers.props" /> <Import Project="..\..\..\build\DevAnalyzers.props" />
<Import Project="..\..\..\build\TrimmingEnable.props" />
</Project> </Project>

2
src/Android/Avalonia.Android/Platform/SkiaPlatform/TopLevelImpl.cs

@ -109,7 +109,7 @@ namespace Avalonia.Android.Platform.SkiaPlatform
? new CompositingRenderer(root, AndroidPlatform.Compositor) ? new CompositingRenderer(root, AndroidPlatform.Compositor)
: AndroidPlatform.Options.UseDeferredRendering : AndroidPlatform.Options.UseDeferredRendering
? new DeferredRenderer(root, AvaloniaLocator.Current.GetRequiredService<IRenderLoop>()) { RenderOnlyOnRenderThread = true } ? new DeferredRenderer(root, AvaloniaLocator.Current.GetRequiredService<IRenderLoop>()) { RenderOnlyOnRenderThread = true }
: new ImmediateRenderer(root); : new ImmediateRenderer((Visual)root);
public virtual void Hide() public virtual void Hide()
{ {

2
src/Android/Avalonia.Android/Platform/Specific/Helpers/AndroidKeyboardEventsHelper.cs

@ -30,7 +30,7 @@ namespace Avalonia.Android.Platform.Specific.Helpers
return DispatchKeyEventInternal(e, out callBase); return DispatchKeyEventInternal(e, out callBase);
} }
string UnicodeTextInput(KeyEvent keyEvent) static string UnicodeTextInput(KeyEvent keyEvent)
{ {
return keyEvent.Action == KeyEventActions.Multiple return keyEvent.Action == KeyEventActions.Multiple
&& keyEvent.RepeatCount == 0 && keyEvent.RepeatCount == 0

2
src/Android/Avalonia.Android/Platform/Storage/AndroidStorageItem.cs

@ -178,7 +178,7 @@ internal sealed class AndroidStorageFile : AndroidStorageItem, IStorageBookmarkF
return false; return false;
} }
private Stream? GetVirtualFileStream(Context context, AndroidUri uri, bool isOutput) private static Stream? GetVirtualFileStream(Context context, AndroidUri uri, bool isOutput)
{ {
var mimeTypes = context.ContentResolver?.GetStreamTypes(uri, FilePickerFileTypes.All.MimeTypes![0]); var mimeTypes = context.ContentResolver?.GetStreamTypes(uri, FilePickerFileTypes.All.MimeTypes![0]);
if (mimeTypes?.Length >= 1) if (mimeTypes?.Length >= 1)

2
src/Avalonia.Base/Animation/Animation.cs

@ -202,9 +202,7 @@ namespace Avalonia.Animation
/// <param name="setter">The animation setter.</param> /// <param name="setter">The animation setter.</param>
/// <param name="value">The property animator value.</param> /// <param name="value">The property animator value.</param>
public static void SetAnimator(IAnimationSetter setter, public static void SetAnimator(IAnimationSetter setter,
#if NET6_0_OR_GREATER
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicMethods)] [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor | DynamicallyAccessedMemberTypes.PublicMethods)]
#endif
Type value) Type value)
{ {
s_animators[setter] = (value, () => (IAnimator)Activator.CreateInstance(value)!); s_animators[setter] = (value, () => (IAnimator)Activator.CreateInstance(value)!);

4
src/Avalonia.Base/Animation/AnimatorKeyFrame.cs

@ -1,5 +1,6 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Animation.Animators; using Avalonia.Animation.Animators;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Reactive; using Avalonia.Reactive;
@ -66,7 +67,8 @@ namespace Avalonia.Animation
} }
} }
public T GetTypedValue<T>() [RequiresUnreferencedCode(TrimmingMessages.TypeConvertionRequiresUnreferencedCodeMessage)]
public T GetTypedValue<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T>()
{ {
var typeConv = TypeDescriptor.GetConverter(typeof(T)); var typeConv = TypeDescriptor.GetConverter(typeof(T));

2
src/Avalonia.Base/Animation/Animators/GradientBrushAnimator.cs

@ -72,7 +72,7 @@ namespace Avalonia.Animation.Animators
return control.Bind((AvaloniaProperty<IBrush?>)Property, instance, BindingPriority.Animation); return control.Bind((AvaloniaProperty<IBrush?>)Property, instance, BindingPriority.Animation);
} }
private IReadOnlyList<ImmutableGradientStop> InterpolateStops(double progress, IReadOnlyList<IGradientStop> oldValue, IReadOnlyList<IGradientStop> newValue) private static IReadOnlyList<ImmutableGradientStop> InterpolateStops(double progress, IReadOnlyList<IGradientStop> oldValue, IReadOnlyList<IGradientStop> newValue)
{ {
var resultCount = Math.Max(oldValue.Count, newValue.Count); var resultCount = Math.Max(oldValue.Count, newValue.Count);
var stops = new ImmutableGradientStop[resultCount]; var stops = new ImmutableGradientStop[resultCount];

2
src/Avalonia.Base/Animation/CrossFade.cs

@ -10,7 +10,7 @@ using Avalonia.VisualTree;
namespace Avalonia.Animation namespace Avalonia.Animation
{ {
/// <summary> /// <summary>
/// Defines a cross-fade animation between two <see cref="IVisual"/>s. /// Defines a cross-fade animation between two <see cref="Visual"/>s.
/// </summary> /// </summary>
public class CrossFade : IPageTransition public class CrossFade : IPageTransition
{ {

2
src/Avalonia.Base/Animation/KeySpline.cs

@ -196,7 +196,7 @@ namespace Avalonia.Animation
/// </summary> /// </summary>
/// <param name="value"></param> /// <param name="value"></param>
/// <returns></returns> /// <returns></returns>
private bool IsValidXValue(double value) private static bool IsValidXValue(double value)
{ {
return value >= 0.0 && value <= 1.0; return value >= 0.0 && value <= 1.0;
} }

2
src/Avalonia.Base/Animation/PageSlide.cs

@ -157,7 +157,7 @@ namespace Avalonia.Animation
/// <remarks> /// <remarks>
/// Any one of the parameters may be null, but not both. /// Any one of the parameters may be null, but not both.
/// </remarks> /// </remarks>
protected static IVisual GetVisualParent(IVisual? from, IVisual? to) protected static Visual GetVisualParent(Visual? from, Visual? to)
{ {
var p1 = (from ?? to)!.VisualParent; var p1 = (from ?? to)!.VisualParent;
var p2 = (to ?? from)!.VisualParent; var p2 = (to ?? from)!.VisualParent;

2
src/Avalonia.Base/AttachedProperty.cs

@ -34,7 +34,7 @@ namespace Avalonia
/// </summary> /// </summary>
/// <typeparam name="TOwner">The owner type.</typeparam> /// <typeparam name="TOwner">The owner type.</typeparam>
/// <returns>The property.</returns> /// <returns>The property.</returns>
public new AttachedProperty<TValue> AddOwner<TOwner>() where TOwner : IAvaloniaObject public new AttachedProperty<TValue> AddOwner<TOwner>() where TOwner : AvaloniaObject
{ {
AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), this); AvaloniaPropertyRegistry.Instance.Register(typeof(TOwner), this);
return this; return this;

10
src/Avalonia.Base/Avalonia.Base.csproj

@ -19,6 +19,7 @@
<Import Project="..\..\build\System.Memory.props" /> <Import Project="..\..\build\System.Memory.props" />
<Import Project="..\..\build\ApiDiff.props" /> <Import Project="..\..\build\ApiDiff.props" />
<Import Project="..\..\build\NullableEnable.props" /> <Import Project="..\..\build\NullableEnable.props" />
<Import Project="..\..\build\TrimmingEnable.props" />
<Import Project="..\..\build\DevAnalyzers.props" /> <Import Project="..\..\build\DevAnalyzers.props" />
<Import Project="..\..\build\SourceGenerators.props" /> <Import Project="..\..\build\SourceGenerators.props" />
<ItemGroup> <ItemGroup>
@ -30,6 +31,8 @@
<InternalsVisibleTo Include="Avalonia.Base.UnitTests, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Base.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Benchmarks, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Benchmarks, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Controls, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Controls, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Markup, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Markup.Xaml, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Controls.ColorPicker, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Controls.ColorPicker, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Controls.DataGrid, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Controls.DataGrid, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Controls.UnitTests, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Controls.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
@ -41,6 +44,7 @@
<InternalsVisibleTo Include="Avalonia.Skia.UnitTests, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Skia.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.UnitTests, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.UnitTests, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Benchmarks, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Benchmarks, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.X11, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Win32, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Win32, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Dialogs, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Dialogs, PublicKey=$(AvaloniaPublicKey)" />
<InternalsVisibleTo Include="Avalonia.Diagnostics, PublicKey=$(AvaloniaPublicKey)" /> <InternalsVisibleTo Include="Avalonia.Diagnostics, PublicKey=$(AvaloniaPublicKey)" />
@ -48,14 +52,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup Label="Build dependency"> <ItemGroup Label="Build dependency">
<ProjectReference Include="$(MSBuildThisFileDirectory)\..\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj" <ProjectReference Include="$(MSBuildThisFileDirectory)\..\Avalonia.Build.Tasks\Avalonia.Build.Tasks.csproj" SetTargetFramework="TargetFramework=netstandard2.0" ReferenceOutputAssembly="false" SkipGetTargetFrameworkProperties="true" />
SetTargetFramework="TargetFramework=netstandard2.0"
ReferenceOutputAssembly="false"
SkipGetTargetFrameworkProperties="true" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Folder Include="Compatibility\" />
<Folder Include="Rendering\Composition\Utils" /> <Folder Include="Rendering\Composition\Utils" />
</ItemGroup> </ItemGroup>
</Project> </Project>

2
src/Avalonia.Base/AvaloniaObject.cs

@ -16,7 +16,7 @@ namespace Avalonia
/// <remarks> /// <remarks>
/// This class is analogous to DependencyObject in WPF. /// This class is analogous to DependencyObject in WPF.
/// </remarks> /// </remarks>
public class AvaloniaObject : IAvaloniaObject, IAvaloniaObjectDebug, INotifyPropertyChanged public class AvaloniaObject : IAvaloniaObjectDebug, INotifyPropertyChanged
{ {
private readonly ValueStore _values; private readonly ValueStore _values;
private AvaloniaObject? _inheritanceParent; private AvaloniaObject? _inheritanceParent;

100
src/Avalonia.Base/AvaloniaObjectExtensions.cs

@ -36,7 +36,7 @@ namespace Avalonia
/// <remarks> /// <remarks>
/// The subscription to <paramref name="o"/> is created using a weak reference. /// The subscription to <paramref name="o"/> is created using a weak reference.
/// </remarks> /// </remarks>
public static IObservable<object?> GetObservable(this IAvaloniaObject o, AvaloniaProperty property) public static IObservable<object?> GetObservable(this AvaloniaObject o, AvaloniaProperty property)
{ {
return new AvaloniaPropertyObservable<object?>( return new AvaloniaPropertyObservable<object?>(
o ?? throw new ArgumentNullException(nameof(o)), o ?? throw new ArgumentNullException(nameof(o)),
@ -56,7 +56,7 @@ namespace Avalonia
/// <remarks> /// <remarks>
/// The subscription to <paramref name="o"/> is created using a weak reference. /// The subscription to <paramref name="o"/> is created using a weak reference.
/// </remarks> /// </remarks>
public static IObservable<T> GetObservable<T>(this IAvaloniaObject o, AvaloniaProperty<T> property) public static IObservable<T> GetObservable<T>(this AvaloniaObject o, AvaloniaProperty<T> property)
{ {
return new AvaloniaPropertyObservable<T>( return new AvaloniaPropertyObservable<T>(
o ?? throw new ArgumentNullException(nameof(o)), o ?? throw new ArgumentNullException(nameof(o)),
@ -76,7 +76,7 @@ namespace Avalonia
/// The subscription to <paramref name="o"/> is created using a weak reference. /// The subscription to <paramref name="o"/> is created using a weak reference.
/// </remarks> /// </remarks>
public static IObservable<BindingValue<object?>> GetBindingObservable( public static IObservable<BindingValue<object?>> GetBindingObservable(
this IAvaloniaObject o, this AvaloniaObject o,
AvaloniaProperty property) AvaloniaProperty property)
{ {
return new AvaloniaPropertyBindingObservable<object?>( return new AvaloniaPropertyBindingObservable<object?>(
@ -98,7 +98,7 @@ namespace Avalonia
/// The subscription to <paramref name="o"/> is created using a weak reference. /// The subscription to <paramref name="o"/> is created using a weak reference.
/// </remarks> /// </remarks>
public static IObservable<BindingValue<T>> GetBindingObservable<T>( public static IObservable<BindingValue<T>> GetBindingObservable<T>(
this IAvaloniaObject o, this AvaloniaObject o,
AvaloniaProperty<T> property) AvaloniaProperty<T> property)
{ {
return new AvaloniaPropertyBindingObservable<T>( return new AvaloniaPropertyBindingObservable<T>(
@ -115,11 +115,11 @@ namespace Avalonia
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
/// <returns> /// <returns>
/// An observable which when subscribed pushes the property changed event args /// An observable which when subscribed pushes the property changed event args
/// each time a <see cref="IAvaloniaObject.PropertyChanged"/> event is raised /// each time a <see cref="AvaloniaObject.PropertyChanged"/> event is raised
/// for the specified property. /// for the specified property.
/// </returns> /// </returns>
public static IObservable<AvaloniaPropertyChangedEventArgs> GetPropertyChangedObservable( public static IObservable<AvaloniaPropertyChangedEventArgs> GetPropertyChangedObservable(
this IAvaloniaObject o, this AvaloniaObject o,
AvaloniaProperty property) AvaloniaProperty property)
{ {
return new AvaloniaPropertyChangedObservable( return new AvaloniaPropertyChangedObservable(
@ -140,7 +140,7 @@ namespace Avalonia
/// property. /// property.
/// </returns> /// </returns>
public static ISubject<object?> GetSubject( public static ISubject<object?> GetSubject(
this IAvaloniaObject o, this AvaloniaObject o,
AvaloniaProperty property, AvaloniaProperty property,
BindingPriority priority = BindingPriority.LocalValue) BindingPriority priority = BindingPriority.LocalValue)
{ {
@ -163,7 +163,7 @@ namespace Avalonia
/// property. /// property.
/// </returns> /// </returns>
public static ISubject<T> GetSubject<T>( public static ISubject<T> GetSubject<T>(
this IAvaloniaObject o, this AvaloniaObject o,
AvaloniaProperty<T> property, AvaloniaProperty<T> property,
BindingPriority priority = BindingPriority.LocalValue) BindingPriority priority = BindingPriority.LocalValue)
{ {
@ -185,7 +185,7 @@ namespace Avalonia
/// property. /// property.
/// </returns> /// </returns>
public static ISubject<BindingValue<object?>> GetBindingSubject( public static ISubject<BindingValue<object?>> GetBindingSubject(
this IAvaloniaObject o, this AvaloniaObject o,
AvaloniaProperty property, AvaloniaProperty property,
BindingPriority priority = BindingPriority.LocalValue) BindingPriority priority = BindingPriority.LocalValue)
{ {
@ -214,7 +214,7 @@ namespace Avalonia
/// property. /// property.
/// </returns> /// </returns>
public static ISubject<BindingValue<T>> GetBindingSubject<T>( public static ISubject<BindingValue<T>> GetBindingSubject<T>(
this IAvaloniaObject o, this AvaloniaObject o,
AvaloniaProperty<T> property, AvaloniaProperty<T> property,
BindingPriority priority = BindingPriority.LocalValue) BindingPriority priority = BindingPriority.LocalValue)
{ {
@ -241,7 +241,7 @@ namespace Avalonia
/// A disposable which can be used to terminate the binding. /// A disposable which can be used to terminate the binding.
/// </returns> /// </returns>
public static IDisposable Bind<T>( public static IDisposable Bind<T>(
this IAvaloniaObject target, this AvaloniaObject target,
AvaloniaProperty<T> property, AvaloniaProperty<T> property,
IObservable<BindingValue<T>> source, IObservable<BindingValue<T>> source,
BindingPriority priority = BindingPriority.LocalValue) BindingPriority priority = BindingPriority.LocalValue)
@ -250,17 +250,12 @@ namespace Avalonia
property = property ?? throw new ArgumentNullException(nameof(property)); property = property ?? throw new ArgumentNullException(nameof(property));
source = source ?? throw new ArgumentNullException(nameof(source)); source = source ?? throw new ArgumentNullException(nameof(source));
if (target is AvaloniaObject ao) return property switch
{ {
return property switch StyledPropertyBase<T> styled => target.Bind(styled, source, priority),
{ DirectPropertyBase<T> direct => target.Bind(direct, source),
StyledPropertyBase<T> styled => ao.Bind(styled, source, priority), _ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
DirectPropertyBase<T> direct => ao.Bind(direct, source), };
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
};
}
throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
} }
/// <summary> /// <summary>
@ -274,22 +269,17 @@ namespace Avalonia
/// A disposable which can be used to terminate the binding. /// A disposable which can be used to terminate the binding.
/// </returns> /// </returns>
public static IDisposable Bind<T>( public static IDisposable Bind<T>(
this IAvaloniaObject target, this AvaloniaObject target,
AvaloniaProperty<T> property, AvaloniaProperty<T> property,
IObservable<T> source, IObservable<T> source,
BindingPriority priority = BindingPriority.LocalValue) BindingPriority priority = BindingPriority.LocalValue)
{ {
if (target is AvaloniaObject ao) return property switch
{ {
return property switch StyledPropertyBase<T> styled => target.Bind(styled, source, priority),
{ DirectPropertyBase<T> direct => target.Bind(direct, source),
StyledPropertyBase<T> styled => ao.Bind(styled, source, priority), _ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
DirectPropertyBase<T> direct => ao.Bind(direct, source), };
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type."),
};
}
throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
} }
/// <summary> /// <summary>
@ -306,7 +296,7 @@ namespace Avalonia
/// </param> /// </param>
/// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns> /// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
public static IDisposable Bind( public static IDisposable Bind(
this IAvaloniaObject target, this AvaloniaObject target,
AvaloniaProperty property, AvaloniaProperty property,
IBinding binding, IBinding binding,
object? anchor = null) object? anchor = null)
@ -340,23 +330,17 @@ namespace Avalonia
/// <param name="target">The object.</param> /// <param name="target">The object.</param>
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
/// <returns>The value.</returns> /// <returns>The value.</returns>
public static T GetValue<T>(this IAvaloniaObject target, AvaloniaProperty<T> property) public static T GetValue<T>(this AvaloniaObject target, AvaloniaProperty<T> property)
{ {
target = target ?? throw new ArgumentNullException(nameof(target)); target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property)); property = property ?? throw new ArgumentNullException(nameof(property));
if (target is AvaloniaObject ao) return property switch
{ {
return property switch StyledPropertyBase<T> styled => target.GetValue(styled),
{ DirectPropertyBase<T> direct => target.GetValue(direct),
StyledPropertyBase<T> styled => ao.GetValue(styled), _ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
DirectPropertyBase<T> direct => ao.GetValue(direct), };
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
};
}
throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
} }
/// <summary> /// <summary>
@ -372,15 +356,13 @@ namespace Avalonia
/// For direct properties returns the current value of the property. /// For direct properties returns the current value of the property.
/// </remarks> /// </remarks>
public static object? GetBaseValue( public static object? GetBaseValue(
this IAvaloniaObject target, this AvaloniaObject target,
AvaloniaProperty property) AvaloniaProperty property)
{ {
target = target ?? throw new ArgumentNullException(nameof(target)); target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property)); property = property ?? throw new ArgumentNullException(nameof(property));
if (target is AvaloniaObject ao) return property.RouteGetBaseValue(target);
return property.RouteGetBaseValue(ao);
throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
} }
/// <summary> /// <summary>
@ -396,24 +378,18 @@ namespace Avalonia
/// For direct properties returns the current value of the property. /// For direct properties returns the current value of the property.
/// </remarks> /// </remarks>
public static Optional<T> GetBaseValue<T>( public static Optional<T> GetBaseValue<T>(
this IAvaloniaObject target, this AvaloniaObject target,
AvaloniaProperty<T> property) AvaloniaProperty<T> property)
{ {
target = target ?? throw new ArgumentNullException(nameof(target)); target = target ?? throw new ArgumentNullException(nameof(target));
property = property ?? throw new ArgumentNullException(nameof(property)); property = property ?? throw new ArgumentNullException(nameof(property));
if (target is AvaloniaObject ao) return property switch
{ {
return property switch StyledPropertyBase<T> styled => target.GetBaseValue(styled),
{ DirectPropertyBase<T> direct => target.GetValue(direct),
StyledPropertyBase<T> styled => ao.GetBaseValue(styled), _ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
DirectPropertyBase<T> direct => ao.GetValue(direct), };
_ => throw new NotSupportedException("Unsupported AvaloniaProperty type.")
};
}
throw new NotSupportedException("Custom implementations of IAvaloniaObject not supported.");
} }
/// <summary> /// <summary>
@ -474,7 +450,7 @@ namespace Avalonia
} }
public InstancedBinding? Initiate( public InstancedBinding? Initiate(
IAvaloniaObject target, AvaloniaObject target,
AvaloniaProperty? targetProperty, AvaloniaProperty? targetProperty,
object? anchor = null, object? anchor = null,
bool enableDataValidation = false) bool enableDataValidation = false)

24
src/Avalonia.Base/AvaloniaProperty.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Data.Core; using Avalonia.Data.Core;
using Avalonia.PropertyStore; using Avalonia.PropertyStore;
@ -38,7 +39,7 @@ namespace Avalonia
Type valueType, Type valueType,
Type ownerType, Type ownerType,
AvaloniaPropertyMetadata metadata, AvaloniaPropertyMetadata metadata,
Action<IAvaloniaObject, bool>? notifying = null) Action<AvaloniaObject, bool>? notifying = null)
{ {
_ = name ?? throw new ArgumentNullException(nameof(name)); _ = name ?? throw new ArgumentNullException(nameof(name));
@ -145,7 +146,7 @@ namespace Avalonia
/// will be true before the property change notifications are sent and false afterwards. This /// will be true before the property change notifications are sent and false afterwards. This
/// callback is intended to support Control.IsDataContextChanging. /// callback is intended to support Control.IsDataContextChanging.
/// </remarks> /// </remarks>
public Action<IAvaloniaObject, bool>? Notifying { get; } public Action<AvaloniaObject, bool>? Notifying { get; }
/// <summary> /// <summary>
/// Gets the integer ID that represents this property. /// Gets the integer ID that represents this property.
@ -238,9 +239,9 @@ namespace Avalonia
bool inherits = false, bool inherits = false,
BindingMode defaultBindingMode = BindingMode.OneWay, BindingMode defaultBindingMode = BindingMode.OneWay,
Func<TValue, bool>? validate = null, Func<TValue, bool>? validate = null,
Func<IAvaloniaObject, TValue, TValue>? coerce = null, Func<AvaloniaObject, TValue, TValue>? coerce = null,
Action<IAvaloniaObject, bool>? notifying = null) Action<AvaloniaObject, bool>? notifying = null)
where TOwner : IAvaloniaObject where TOwner : AvaloniaObject
{ {
_ = name ?? throw new ArgumentNullException(nameof(name)); _ = name ?? throw new ArgumentNullException(nameof(name));
@ -279,8 +280,8 @@ namespace Avalonia
bool inherits = false, bool inherits = false,
BindingMode defaultBindingMode = BindingMode.OneWay, BindingMode defaultBindingMode = BindingMode.OneWay,
Func<TValue, bool>? validate = null, Func<TValue, bool>? validate = null,
Func<IAvaloniaObject, TValue, TValue>? coerce = null) Func<AvaloniaObject, TValue, TValue>? coerce = null)
where THost : IAvaloniaObject where THost : AvaloniaObject
{ {
_ = name ?? throw new ArgumentNullException(nameof(name)); _ = name ?? throw new ArgumentNullException(nameof(name));
@ -316,8 +317,8 @@ namespace Avalonia
bool inherits = false, bool inherits = false,
BindingMode defaultBindingMode = BindingMode.OneWay, BindingMode defaultBindingMode = BindingMode.OneWay,
Func<TValue, bool>? validate = null, Func<TValue, bool>? validate = null,
Func<IAvaloniaObject, TValue, TValue>? coerce = null) Func<AvaloniaObject, TValue, TValue>? coerce = null)
where THost : IAvaloniaObject where THost : AvaloniaObject
{ {
_ = name ?? throw new ArgumentNullException(nameof(name)); _ = name ?? throw new ArgumentNullException(nameof(name));
@ -354,7 +355,7 @@ namespace Avalonia
TValue unsetValue = default!, TValue unsetValue = default!,
BindingMode defaultBindingMode = BindingMode.OneWay, BindingMode defaultBindingMode = BindingMode.OneWay,
bool enableDataValidation = false) bool enableDataValidation = false)
where TOwner : IAvaloniaObject where TOwner : AvaloniaObject
{ {
_ = name ?? throw new ArgumentNullException(nameof(name)); _ = name ?? throw new ArgumentNullException(nameof(name));
_ = getter ?? throw new ArgumentNullException(nameof(getter)); _ = getter ?? throw new ArgumentNullException(nameof(getter));
@ -415,7 +416,7 @@ namespace Avalonia
/// <returns> /// <returns>
/// The property metadata. /// The property metadata.
/// </returns> /// </returns>
public AvaloniaPropertyMetadata GetMetadata<T>() where T : IAvaloniaObject public AvaloniaPropertyMetadata GetMetadata<T>() where T : AvaloniaObject
{ {
return GetMetadata(typeof(T)); return GetMetadata(typeof(T));
} }
@ -442,6 +443,7 @@ namespace Avalonia
/// </summary> /// </summary>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
/// <returns>True if the value is valid, otherwise false.</returns> /// <returns>True if the value is valid, otherwise false.</returns>
[RequiresUnreferencedCode(TrimmingMessages.ImplicitTypeConvertionRequiresUnreferencedCodeMessage)]
public bool IsValidValue(object? value) public bool IsValidValue(object? value)
{ {
return TypeUtilities.TryConvertImplicit(PropertyType, value, out _); return TypeUtilities.TryConvertImplicit(PropertyType, value, out _);

6
src/Avalonia.Base/AvaloniaPropertyChangedEventArgs.cs

@ -9,7 +9,7 @@ namespace Avalonia
public abstract class AvaloniaPropertyChangedEventArgs : EventArgs public abstract class AvaloniaPropertyChangedEventArgs : EventArgs
{ {
public AvaloniaPropertyChangedEventArgs( public AvaloniaPropertyChangedEventArgs(
IAvaloniaObject sender, AvaloniaObject sender,
BindingPriority priority) BindingPriority priority)
{ {
Sender = sender; Sender = sender;
@ -18,7 +18,7 @@ namespace Avalonia
} }
internal AvaloniaPropertyChangedEventArgs( internal AvaloniaPropertyChangedEventArgs(
IAvaloniaObject sender, AvaloniaObject sender,
BindingPriority priority, BindingPriority priority,
bool isEffectiveValueChange) bool isEffectiveValueChange)
{ {
@ -31,7 +31,7 @@ namespace Avalonia
/// Gets the <see cref="AvaloniaObject"/> that the property changed on. /// Gets the <see cref="AvaloniaObject"/> that the property changed on.
/// </summary> /// </summary>
/// <value>The sender object.</value> /// <value>The sender object.</value>
public IAvaloniaObject Sender { get; } public AvaloniaObject Sender { get; }
/// <summary> /// <summary>
/// Gets the property that changed. /// Gets the property that changed.

4
src/Avalonia.Base/AvaloniaPropertyChangedEventArgs`1.cs

@ -16,7 +16,7 @@ namespace Avalonia
/// <param name="newValue">The new value of the property.</param> /// <param name="newValue">The new value of the property.</param>
/// <param name="priority">The priority of the binding that produced the value.</param> /// <param name="priority">The priority of the binding that produced the value.</param>
public AvaloniaPropertyChangedEventArgs( public AvaloniaPropertyChangedEventArgs(
IAvaloniaObject sender, AvaloniaObject sender,
AvaloniaProperty<T> property, AvaloniaProperty<T> property,
Optional<T> oldValue, Optional<T> oldValue,
BindingValue<T> newValue, BindingValue<T> newValue,
@ -26,7 +26,7 @@ namespace Avalonia
} }
internal AvaloniaPropertyChangedEventArgs( internal AvaloniaPropertyChangedEventArgs(
IAvaloniaObject sender, AvaloniaObject sender,
AvaloniaProperty<T> property, AvaloniaProperty<T> property,
Optional<T> oldValue, Optional<T> oldValue,
BindingValue<T> newValue, BindingValue<T> newValue,

14
src/Avalonia.Base/AvaloniaPropertyRegistry.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
namespace Avalonia namespace Avalonia
@ -42,6 +43,7 @@ namespace Avalonia
/// </summary> /// </summary>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <returns>A collection of <see cref="AvaloniaProperty"/> definitions.</returns> /// <returns>A collection of <see cref="AvaloniaProperty"/> definitions.</returns>
[UnconditionalSuppressMessage("Trimming", "IL2059", Justification = "If type was trimmed out, no properties were referenced")]
public IReadOnlyList<AvaloniaProperty> GetRegistered(Type type) public IReadOnlyList<AvaloniaProperty> GetRegistered(Type type)
{ {
_ = type ?? throw new ArgumentNullException(nameof(type)); _ = type ?? throw new ArgumentNullException(nameof(type));
@ -189,7 +191,7 @@ namespace Avalonia
/// </summary> /// </summary>
/// <param name="o">The object.</param> /// <param name="o">The object.</param>
/// <returns>A collection of <see cref="AvaloniaProperty"/> definitions.</returns> /// <returns>A collection of <see cref="AvaloniaProperty"/> definitions.</returns>
public IReadOnlyList<AvaloniaProperty> GetRegistered(IAvaloniaObject o) public IReadOnlyList<AvaloniaProperty> GetRegistered(AvaloniaObject o)
{ {
_ = o ?? throw new ArgumentNullException(nameof(o)); _ = o ?? throw new ArgumentNullException(nameof(o));
@ -205,7 +207,7 @@ namespace Avalonia
/// The registered. /// The registered.
/// </returns> /// </returns>
public DirectPropertyBase<T> GetRegisteredDirect<T>( public DirectPropertyBase<T> GetRegisteredDirect<T>(
IAvaloniaObject o, AvaloniaObject o,
DirectPropertyBase<T> property) DirectPropertyBase<T> property)
{ {
return FindRegisteredDirect(o, property) ?? return FindRegisteredDirect(o, property) ??
@ -260,7 +262,7 @@ namespace Avalonia
/// <exception cref="InvalidOperationException"> /// <exception cref="InvalidOperationException">
/// The property name contains a '.'. /// The property name contains a '.'.
/// </exception> /// </exception>
public AvaloniaProperty? FindRegistered(IAvaloniaObject o, string name) public AvaloniaProperty? FindRegistered(AvaloniaObject o, string name)
{ {
_ = o ?? throw new ArgumentNullException(nameof(o)); _ = o ?? throw new ArgumentNullException(nameof(o));
_ = name ?? throw new ArgumentNullException(nameof(name)); _ = name ?? throw new ArgumentNullException(nameof(name));
@ -277,7 +279,7 @@ namespace Avalonia
/// The registered property or null if no matching property found. /// The registered property or null if no matching property found.
/// </returns> /// </returns>
public DirectPropertyBase<T>? FindRegisteredDirect<T>( public DirectPropertyBase<T>? FindRegisteredDirect<T>(
IAvaloniaObject o, AvaloniaObject o,
DirectPropertyBase<T> property) DirectPropertyBase<T> property)
{ {
if (property.Owner == o.GetType()) if (property.Owner == o.GetType())
@ -362,7 +364,7 @@ namespace Avalonia
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
/// <remarks> /// <remarks>
/// You won't usually want to call this method directly, instead use the /// You won't usually want to call this method directly, instead use the
/// <see cref="AvaloniaProperty.Register{TOwner, TValue}(string, TValue, bool, Data.BindingMode, Func{TValue, bool}, Func{IAvaloniaObject, TValue, TValue}, Action{IAvaloniaObject, bool})"/> /// <see cref="AvaloniaProperty.Register{TOwner, TValue}(string, TValue, bool, Data.BindingMode, Func{TValue, bool}, Func{AvaloniaObject, TValue, TValue}, Action{AvaloniaObject, bool})"/>
/// method. /// method.
/// </remarks> /// </remarks>
public void Register(Type type, AvaloniaProperty property) public void Register(Type type, AvaloniaProperty property)
@ -413,7 +415,7 @@ namespace Avalonia
/// <param name="property">The property.</param> /// <param name="property">The property.</param>
/// <remarks> /// <remarks>
/// You won't usually want to call this method directly, instead use the /// You won't usually want to call this method directly, instead use the
/// <see cref="AvaloniaProperty.RegisterAttached{THost, TValue}(string, Type, TValue, bool, Data.BindingMode, Func{TValue, bool}, Func{IAvaloniaObject, TValue, TValue})"/> /// <see cref="AvaloniaProperty.RegisterAttached{THost, TValue}(string, Type, TValue, bool, Data.BindingMode, Func{TValue, bool}, Func{AvaloniaObject, TValue, TValue})"/>
/// method. /// method.
/// </remarks> /// </remarks>
public void RegisterAttached(Type type, AvaloniaProperty property) public void RegisterAttached(Type type, AvaloniaProperty property)

4
src/Avalonia.Base/AvaloniaProperty`1.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Reactive.Subjects; using System.Reactive.Subjects;
using Avalonia.Data; using Avalonia.Data;
using Avalonia.Utilities; using Avalonia.Utilities;
@ -24,7 +25,7 @@ namespace Avalonia
string name, string name,
Type ownerType, Type ownerType,
AvaloniaPropertyMetadata metadata, AvaloniaPropertyMetadata metadata,
Action<IAvaloniaObject, bool>? notifying = null) Action<AvaloniaObject, bool>? notifying = null)
: base(name, typeof(TValue), ownerType, metadata, notifying) : base(name, typeof(TValue), ownerType, metadata, notifying)
{ {
_changed = new Subject<AvaloniaPropertyChangedEventArgs<TValue>>(); _changed = new Subject<AvaloniaPropertyChangedEventArgs<TValue>>();
@ -67,6 +68,7 @@ namespace Avalonia
protected override IObservable<AvaloniaPropertyChangedEventArgs> GetChanged() => Changed; protected override IObservable<AvaloniaPropertyChangedEventArgs> GetChanged() => Changed;
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ImplicitTypeConvertionSupressWarningMessage)]
protected BindingValue<object?> TryConvert(object? value) protected BindingValue<object?> TryConvert(object? value)
{ {
if (value == UnsetValue) if (value == UnsetValue)

4
src/Avalonia.Base/ClassBindingManager.cs

@ -9,7 +9,7 @@ namespace Avalonia
private static readonly Dictionary<string, AvaloniaProperty> s_RegisteredProperties = private static readonly Dictionary<string, AvaloniaProperty> s_RegisteredProperties =
new Dictionary<string, AvaloniaProperty>(); new Dictionary<string, AvaloniaProperty>();
public static IDisposable Bind(IStyledElement target, string className, IBinding source, object anchor) public static IDisposable Bind(StyledElement target, string className, IBinding source, object anchor)
{ {
if (!s_RegisteredProperties.TryGetValue(className, out var prop)) if (!s_RegisteredProperties.TryGetValue(className, out var prop))
s_RegisteredProperties[className] = prop = RegisterClassProxyProperty(className); s_RegisteredProperties[className] = prop = RegisterClassProxyProperty(className);
@ -21,7 +21,7 @@ namespace Avalonia
var prop = AvaloniaProperty.Register<StyledElement, bool>("__AvaloniaReserved::Classes::" + className); var prop = AvaloniaProperty.Register<StyledElement, bool>("__AvaloniaReserved::Classes::" + className);
prop.Changed.Subscribe(args => prop.Changed.Subscribe(args =>
{ {
var classes = ((IStyledElement)args.Sender).Classes; var classes = ((StyledElement)args.Sender).Classes;
classes.Set(className, args.NewValue.GetValueOrDefault()); classes.Set(className, args.NewValue.GetValueOrDefault());
}); });

4
src/Avalonia.Base/Collections/AvaloniaListConverter.cs

@ -1,5 +1,6 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using Avalonia.Utilities; using Avalonia.Utilities;
@ -8,7 +9,8 @@ namespace Avalonia.Collections
/// <summary> /// <summary>
/// Creates an <see cref="AvaloniaList{T}"/> from a string representation. /// Creates an <see cref="AvaloniaList{T}"/> from a string representation.
/// </summary> /// </summary>
public class AvaloniaListConverter<T> : TypeConverter [RequiresUnreferencedCode(TrimmingMessages.TypeConvertionRequiresUnreferencedCodeMessage)]
public class AvaloniaListConverter<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] T> : TypeConverter
{ {
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType) public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
{ {

121
src/Avalonia.Base/Compatibility/TrimmingAttributes.cs

@ -0,0 +1,121 @@
#pragma warning disable MA0048 // File name must match type name
// https://github.com/dotnet/runtime/tree/main/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
namespace System.Diagnostics.CodeAnalysis
{
#nullable enable
#if !NET6_0_OR_GREATER
[AttributeUsage(
AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter |
AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method |
AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct,
Inherited = false)]
internal sealed class DynamicallyAccessedMembersAttribute : Attribute
{
public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes)
{
MemberTypes = memberTypes;
}
public DynamicallyAccessedMemberTypes MemberTypes { get; }
}
[Flags]
internal enum DynamicallyAccessedMemberTypes
{
None = 0,
PublicParameterlessConstructor = 0x0001,
PublicConstructors = 0x0002 | PublicParameterlessConstructor,
NonPublicConstructors = 0x0004,
PublicMethods = 0x0008,
NonPublicMethods = 0x0010,
PublicFields = 0x0020,
NonPublicFields = 0x0040,
PublicNestedTypes = 0x0080,
NonPublicNestedTypes = 0x0100,
PublicProperties = 0x0200,
NonPublicProperties = 0x0400,
PublicEvents = 0x0800,
NonPublicEvents = 0x1000,
Interfaces = 0x2000,
All = ~None
}
[AttributeUsage(
AttributeTargets.Constructor | AttributeTargets.Field | AttributeTargets.Method,
AllowMultiple = true, Inherited = false)]
internal sealed class DynamicDependencyAttribute : Attribute
{
public DynamicDependencyAttribute(string memberSignature)
{
MemberSignature = memberSignature;
}
public DynamicDependencyAttribute(string memberSignature, Type type)
{
MemberSignature = memberSignature;
Type = type;
}
public DynamicDependencyAttribute(string memberSignature, string typeName, string assemblyName)
{
MemberSignature = memberSignature;
TypeName = typeName;
AssemblyName = assemblyName;
}
public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, Type type)
{
MemberTypes = memberTypes;
Type = type;
}
public DynamicDependencyAttribute(DynamicallyAccessedMemberTypes memberTypes, string typeName, string assemblyName)
{
MemberTypes = memberTypes;
TypeName = typeName;
AssemblyName = assemblyName;
}
public string? MemberSignature { get; }
public DynamicallyAccessedMemberTypes MemberTypes { get; }
public Type? Type { get; }
public string? TypeName { get; }
public string? AssemblyName { get; }
public string? Condition { get; set; }
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)]
internal sealed class RequiresUnreferencedCodeAttribute : Attribute
{
public RequiresUnreferencedCodeAttribute(string message)
{
Message = message;
}
public string Message { get; }
public string? Url { get; set; }
}
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
internal sealed class UnconditionalSuppressMessageAttribute : Attribute
{
public UnconditionalSuppressMessageAttribute(string category, string checkId)
{
Category = category;
CheckId = checkId;
}
public string Category { get; }
public string CheckId { get; }
public string? Scope { get; set; }
public string? Target { get; set; }
public string? MessageId { get; set; }
public string? Justification { get; set; }
}
#endif
}

6
src/Avalonia.Base/Controls/Classes.cs

@ -6,7 +6,7 @@ using Avalonia.Utilities;
namespace Avalonia.Controls namespace Avalonia.Controls
{ {
/// <summary> /// <summary>
/// Holds a collection of style classes for an <see cref="IStyledElement"/>. /// Holds a collection of style classes for an <see cref="StyledElement"/>.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// Similar to CSS, each control may have any number of styling classes applied. /// Similar to CSS, each control may have any number of styling classes applied.
@ -37,7 +37,7 @@ namespace Avalonia.Controls
/// <param name="items">The initial items.</param> /// <param name="items">The initial items.</param>
public Classes(params string[] items) public Classes(params string[] items)
: base(items) : base(items)
{ {
} }
/// <summary> /// <summary>
@ -320,7 +320,7 @@ namespace Avalonia.Controls
listener.Changed(); listener.Changed();
} }
private void ThrowIfPseudoclass(string name, string operation) private static void ThrowIfPseudoclass(string name, string operation)
{ {
if (name.StartsWith(":")) if (name.StartsWith(":"))
{ {

2
src/Avalonia.Base/Controls/ISetInheritanceParent.cs

@ -17,6 +17,6 @@ namespace Avalonia.Controls
/// Sets the control's inheritance parent. /// Sets the control's inheritance parent.
/// </summary> /// </summary>
/// <param name="parent">The parent.</param> /// <param name="parent">The parent.</param>
void SetParent(IAvaloniaObject? parent); void SetParent(AvaloniaObject? parent);
} }
} }

3
src/Avalonia.Base/Controls/ResourceNodeExtensions.cs

@ -2,6 +2,7 @@
using Avalonia.Data.Converters; using Avalonia.Data.Converters;
using Avalonia.LogicalTree; using Avalonia.LogicalTree;
using Avalonia.Reactive; using Avalonia.Reactive;
using Avalonia.Styling;
#nullable enable #nullable enable
@ -49,7 +50,7 @@ namespace Avalonia.Controls
return true; return true;
} }
current = (current as IStyledElement)?.StylingParent as IResourceNode; current = (current as IStyleHost)?.StylingParent as IResourceNode;
} }
value = null; value = null;

2
src/Avalonia.Base/CornerRadius.cs

@ -100,7 +100,7 @@ namespace Avalonia
public override string ToString() public override string ToString()
{ {
return $"{TopLeft},{TopRight},{BottomRight},{BottomLeft}"; return FormattableString.Invariant($"{TopLeft},{TopRight},{BottomRight},{BottomLeft}");
} }
public static CornerRadius Parse(string s) public static CornerRadius Parse(string s)

4
src/Avalonia.Base/Data/BindingOperations.cs

@ -9,7 +9,7 @@ namespace Avalonia.Data
public static readonly object DoNothing = new DoNothingType(); public static readonly object DoNothing = new DoNothingType();
/// <summary> /// <summary>
/// Applies an <see cref="InstancedBinding"/> a property on an <see cref="IAvaloniaObject"/>. /// Applies an <see cref="InstancedBinding"/> a property on an <see cref="AvaloniaObject"/>.
/// </summary> /// </summary>
/// <param name="target">The target object.</param> /// <param name="target">The target object.</param>
/// <param name="property">The property to bind.</param> /// <param name="property">The property to bind.</param>
@ -22,7 +22,7 @@ namespace Avalonia.Data
/// </param> /// </param>
/// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns> /// <returns>An <see cref="IDisposable"/> which can be used to cancel the binding.</returns>
public static IDisposable Apply( public static IDisposable Apply(
IAvaloniaObject target, AvaloniaObject target,
AvaloniaProperty property, AvaloniaProperty property,
InstancedBinding binding, InstancedBinding binding,
object? anchor) object? anchor)

3
src/Avalonia.Base/Data/BindingValue.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Avalonia.Utilities; using Avalonia.Utilities;
namespace Avalonia.Data namespace Avalonia.Data
@ -236,6 +237,7 @@ namespace Avalonia.Data
/// </summary> /// </summary>
/// <param name="value">The untyped value.</param> /// <param name="value">The untyped value.</param>
/// <returns>The typed binding value.</returns> /// <returns>The typed binding value.</returns>
[RequiresUnreferencedCode(TrimmingMessages.ImplicitTypeConvertionRequiresUnreferencedCodeMessage)]
public static BindingValue<T> FromUntyped(object? value) public static BindingValue<T> FromUntyped(object? value)
{ {
return FromUntyped(value, typeof(T)); return FromUntyped(value, typeof(T));
@ -249,6 +251,7 @@ namespace Avalonia.Data
/// <param name="value">The untyped value.</param> /// <param name="value">The untyped value.</param>
/// <param name="targetType">The runtime target type.</param> /// <param name="targetType">The runtime target type.</param>
/// <returns>The typed binding value.</returns> /// <returns>The typed binding value.</returns>
[RequiresUnreferencedCode(TrimmingMessages.ImplicitTypeConvertionRequiresUnreferencedCodeMessage)]
public static BindingValue<T> FromUntyped(object? value, Type targetType) public static BindingValue<T> FromUntyped(object? value, Type targetType)
{ {
if (value == AvaloniaProperty.UnsetValue) if (value == AvaloniaProperty.UnsetValue)

2
src/Avalonia.Base/Data/Converters/DefaultValueConverter.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using System.Windows.Input; using System.Windows.Input;
using Avalonia.Utilities; using Avalonia.Utilities;
@ -9,6 +10,7 @@ namespace Avalonia.Data.Converters
/// Provides a default set of value conversions for bindings that do not specify a value /// Provides a default set of value conversions for bindings that do not specify a value
/// converter. /// converter.
/// </summary> /// </summary>
[RequiresUnreferencedCode(TrimmingMessages.TypeConvertionRequiresUnreferencedCodeMessage)]
public class DefaultValueConverter : IValueConverter public class DefaultValueConverter : IValueConverter
{ {
/// <summary> /// <summary>

2
src/Avalonia.Base/Data/Converters/MethodToCommandConverter.cs

@ -1,5 +1,6 @@
using System; using System;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
@ -9,6 +10,7 @@ using Avalonia.Utilities;
namespace Avalonia.Data.Converters namespace Avalonia.Data.Converters
{ {
[RequiresUnreferencedCode(TrimmingMessages.ReflectionBindingRequiresUnreferencedCodeMessage)]
class MethodToCommandConverter : ICommand class MethodToCommandConverter : ICommand
{ {
readonly static Func<object?, bool> AlwaysEnabled = (_) => true; readonly static Func<object?, bool> AlwaysEnabled = (_) => true;

4
src/Avalonia.Base/Data/Core/AvaloniaPropertyAccessorNode.cs

@ -24,7 +24,7 @@ namespace Avalonia.Data.Core
{ {
try try
{ {
if (Target.TryGetTarget(out var target) && target is IAvaloniaObject obj) if (Target.TryGetTarget(out var target) && target is AvaloniaObject obj)
{ {
obj.SetValue(_property, value, priority); obj.SetValue(_property, value, priority);
return true; return true;
@ -39,7 +39,7 @@ namespace Avalonia.Data.Core
protected override void StartListeningCore(WeakReference<object?> reference) protected override void StartListeningCore(WeakReference<object?> reference)
{ {
if (reference.TryGetTarget(out var target) && target is IAvaloniaObject obj) if (reference.TryGetTarget(out var target) && target is AvaloniaObject obj)
{ {
_subscription = new AvaloniaPropertyObservable<object?>(obj, _property).Subscribe(ValueChanged); _subscription = new AvaloniaPropertyObservable<object?>(obj, _property).Subscribe(ValueChanged);
} }

2
src/Avalonia.Base/Data/Core/BindingExpression.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization; using System.Globalization;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Reactive.Subjects; using System.Reactive.Subjects;
@ -13,6 +14,7 @@ namespace Avalonia.Data.Core
/// Binds to an expression on an object using a type value converter to convert the values /// Binds to an expression on an object using a type value converter to convert the values
/// that are sent and received. /// that are sent and received.
/// </summary> /// </summary>
[RequiresUnreferencedCode(TrimmingMessages.TypeConvertionRequiresUnreferencedCodeMessage)]
public class BindingExpression : LightweightObservableBase<object?>, ISubject<object?>, IDescription public class BindingExpression : LightweightObservableBase<object?>, ISubject<object?>, IDescription
{ {
private readonly ExpressionObserver _inner; private readonly ExpressionObserver _inner;

2
src/Avalonia.Base/Data/Core/ExpressionNode.cs

@ -159,7 +159,7 @@ namespace Avalonia.Data.Core
_listening = false; _listening = false;
} }
private BindingNotification TargetNullNotification() private static BindingNotification TargetNullNotification()
{ {
return new BindingNotification( return new BindingNotification(
new MarkupBindingChainException("Null value"), new MarkupBindingChainException("Null value"),

5
src/Avalonia.Base/Data/Core/ExpressionObserver.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reactive; using System.Reactive;
using System.Reactive.Linq; using System.Reactive.Linq;
@ -126,6 +127,7 @@ namespace Avalonia.Data.Core
/// <param name="description"> /// <param name="description">
/// A description of the expression. If null, <paramref name="expression"/>'s string representation will be used. /// A description of the expression. If null, <paramref name="expression"/>'s string representation will be used.
/// </param> /// </param>
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ExpressionSafeSupressWarningMessage)]
public static ExpressionObserver Create<T, U>( public static ExpressionObserver Create<T, U>(
T? root, T? root,
Expression<Func<T, U>> expression, Expression<Func<T, U>> expression,
@ -144,6 +146,7 @@ namespace Avalonia.Data.Core
/// <param name="description"> /// <param name="description">
/// A description of the expression. If null, <paramref name="expression"/>'s string representation will be used. /// A description of the expression. If null, <paramref name="expression"/>'s string representation will be used.
/// </param> /// </param>
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ExpressionSafeSupressWarningMessage)]
public static ExpressionObserver Create<T, U>( public static ExpressionObserver Create<T, U>(
IObservable<T> rootObservable, IObservable<T> rootObservable,
Expression<Func<T, U>> expression, Expression<Func<T, U>> expression,
@ -168,6 +171,7 @@ namespace Avalonia.Data.Core
/// <param name="description"> /// <param name="description">
/// A description of the expression. If null, <paramref name="expression"/>'s string representation will be used. /// A description of the expression. If null, <paramref name="expression"/>'s string representation will be used.
/// </param> /// </param>
[UnconditionalSuppressMessage("Trimming", "IL2026", Justification = TrimmingMessages.ExpressionSafeSupressWarningMessage)]
public static ExpressionObserver Create<T, U>( public static ExpressionObserver Create<T, U>(
Func<T> rootGetter, Func<T> rootGetter,
Expression<Func<T, U>> expression, Expression<Func<T, U>> expression,
@ -283,6 +287,7 @@ namespace Avalonia.Data.Core
} }
} }
[RequiresUnreferencedCode(TrimmingMessages.ExpressionNodeRequiresUnreferencedCodeMessage)]
private static ExpressionNode Parse(LambdaExpression expression, bool enableDataValidation) private static ExpressionNode Parse(LambdaExpression expression, bool enableDataValidation)
{ {
return ExpressionTreeParser.Parse(expression, enableDataValidation); return ExpressionTreeParser.Parse(expression, enableDataValidation);

4
src/Avalonia.Base/Data/Core/Parsers/ExpressionTreeParser.cs

@ -1,10 +1,12 @@
using System.Linq; using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
namespace Avalonia.Data.Core.Parsers namespace Avalonia.Data.Core.Parsers
{ {
static class ExpressionTreeParser static class ExpressionTreeParser
{ {
[RequiresUnreferencedCode(TrimmingMessages.ExpressionNodeRequiresUnreferencedCodeMessage)]
public static ExpressionNode Parse(Expression expr, bool enableDataValidation) public static ExpressionNode Parse(Expression expr, bool enableDataValidation)
{ {
var visitor = new ExpressionVisitorNodeBuilder(enableDataValidation); var visitor = new ExpressionVisitorNodeBuilder(enableDataValidation);

6
src/Avalonia.Base/Data/Core/Parsers/ExpressionVisitorNodeBuilder.cs

@ -1,11 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
namespace Avalonia.Data.Core.Parsers namespace Avalonia.Data.Core.Parsers
{ {
[RequiresUnreferencedCode(TrimmingMessages.ExpressionNodeRequiresUnreferencedCodeMessage)]
class ExpressionVisitorNodeBuilder : ExpressionVisitor class ExpressionVisitorNodeBuilder : ExpressionVisitor
{ {
private const string MultiDimensionalArrayGetterMethodName = "Get"; private const string MultiDimensionalArrayGetterMethodName = "Get";
@ -81,7 +83,7 @@ namespace Avalonia.Data.Core.Parsers
return node; return node;
} }
private T GetArgumentExpressionValue<T>(Expression expr) private static T GetArgumentExpressionValue<T>(Expression expr)
{ {
try try
{ {
@ -193,7 +195,7 @@ namespace Avalonia.Data.Core.Parsers
throw new ExpressionParseException(0, $"Invalid method call in binding expression: '{node.Method.DeclaringType!.AssemblyQualifiedName}.{node.Method.Name}'."); throw new ExpressionParseException(0, $"Invalid method call in binding expression: '{node.Method.DeclaringType!.AssemblyQualifiedName}.{node.Method.Name}'.");
} }
private PropertyInfo? TryGetPropertyFromMethod(MethodInfo method) private static PropertyInfo? TryGetPropertyFromMethod(MethodInfo method)
{ {
var type = method.DeclaringType; var type = method.DeclaringType;
return type?.GetRuntimeProperties().FirstOrDefault(prop => prop.GetMethod == method); return type?.GetRuntimeProperties().FirstOrDefault(prop => prop.GetMethod == method);

3
src/Avalonia.Base/Data/Core/Plugins/AvaloniaPropertyAccessorPlugin.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.ExceptionServices; using System.Runtime.ExceptionServices;
using Avalonia.Utilities; using Avalonia.Utilities;
@ -10,6 +11,7 @@ namespace Avalonia.Data.Core.Plugins
public class AvaloniaPropertyAccessorPlugin : IPropertyAccessorPlugin public class AvaloniaPropertyAccessorPlugin : IPropertyAccessorPlugin
{ {
/// <inheritdoc/> /// <inheritdoc/>
[RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)]
public bool Match(object obj, string propertyName) public bool Match(object obj, string propertyName)
{ {
if (obj is AvaloniaObject o) if (obj is AvaloniaObject o)
@ -29,6 +31,7 @@ namespace Avalonia.Data.Core.Plugins
/// An <see cref="IPropertyAccessor"/> interface through which future interactions with the /// An <see cref="IPropertyAccessor"/> interface through which future interactions with the
/// property will be made. /// property will be made.
/// </returns> /// </returns>
[RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)]
public IPropertyAccessor? Start(WeakReference<object?> reference, string propertyName) public IPropertyAccessor? Start(WeakReference<object?> reference, string propertyName)
{ {
_ = reference ?? throw new ArgumentNullException(nameof(reference)); _ = reference ?? throw new ArgumentNullException(nameof(reference));

6
src/Avalonia.Base/Data/Core/Plugins/DataAnnotationsValidationPlugin.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@ -12,6 +13,7 @@ namespace Avalonia.Data.Core.Plugins
public class DataAnnotationsValidationPlugin : IDataValidationPlugin public class DataAnnotationsValidationPlugin : IDataValidationPlugin
{ {
/// <inheritdoc/> /// <inheritdoc/>
[RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)]
public bool Match(WeakReference<object?> reference, string memberName) public bool Match(WeakReference<object?> reference, string memberName)
{ {
reference.TryGetTarget(out var target); reference.TryGetTarget(out var target);
@ -24,11 +26,13 @@ namespace Avalonia.Data.Core.Plugins
} }
/// <inheritdoc/> /// <inheritdoc/>
[RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)]
public IPropertyAccessor Start(WeakReference<object?> reference, string name, IPropertyAccessor inner) public IPropertyAccessor Start(WeakReference<object?> reference, string name, IPropertyAccessor inner)
{ {
return new Accessor(reference, name, inner); return new Accessor(reference, name, inner);
} }
[RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)]
private sealed class Accessor : DataValidationBase private sealed class Accessor : DataValidationBase
{ {
private readonly ValidationContext? _context; private readonly ValidationContext? _context;
@ -63,7 +67,7 @@ namespace Avalonia.Data.Core.Plugins
} }
} }
private Exception CreateException(IList<ValidationResult> errors) private static Exception CreateException(IList<ValidationResult> errors)
{ {
if (errors.Count == 1) if (errors.Count == 1)
{ {

3
src/Avalonia.Base/Data/Core/Plugins/ExceptionValidationPlugin.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Reflection; using System.Reflection;
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
@ -9,9 +10,11 @@ namespace Avalonia.Data.Core.Plugins
public class ExceptionValidationPlugin : IDataValidationPlugin public class ExceptionValidationPlugin : IDataValidationPlugin
{ {
/// <inheritdoc/> /// <inheritdoc/>
[RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)]
public bool Match(WeakReference<object?> reference, string memberName) => true; public bool Match(WeakReference<object?> reference, string memberName) => true;
/// <inheritdoc/> /// <inheritdoc/>
[RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)]
public IPropertyAccessor Start(WeakReference<object?> reference, string name, IPropertyAccessor inner) public IPropertyAccessor Start(WeakReference<object?> reference, string name, IPropertyAccessor inner)
{ {
return new Validator(reference, name, inner); return new Validator(reference, name, inner);

3
src/Avalonia.Base/Data/Core/Plugins/IDataValidationPlugin.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
@ -13,6 +14,7 @@ namespace Avalonia.Data.Core.Plugins
/// <param name="reference">A weak reference to the object.</param> /// <param name="reference">A weak reference to the object.</param>
/// <param name="memberName">The name of the member to validate.</param> /// <param name="memberName">The name of the member to validate.</param>
/// <returns>True if the plugin can handle the object; otherwise false.</returns> /// <returns>True if the plugin can handle the object; otherwise false.</returns>
[RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)]
bool Match(WeakReference<object?> reference, string memberName); bool Match(WeakReference<object?> reference, string memberName);
/// <summary> /// <summary>
@ -25,6 +27,7 @@ namespace Avalonia.Data.Core.Plugins
/// An <see cref="IPropertyAccessor"/> interface through which future interactions with the /// An <see cref="IPropertyAccessor"/> interface through which future interactions with the
/// property will be made. /// property will be made.
/// </returns> /// </returns>
[RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)]
IPropertyAccessor Start(WeakReference<object?> reference, IPropertyAccessor Start(WeakReference<object?> reference,
string propertyName, string propertyName,
IPropertyAccessor inner); IPropertyAccessor inner);

3
src/Avalonia.Base/Data/Core/Plugins/IPropertyAccessorPlugin.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
@ -14,6 +15,7 @@ namespace Avalonia.Data.Core.Plugins
/// <param name="obj">The object.</param> /// <param name="obj">The object.</param>
/// <param name="propertyName">The property name.</param> /// <param name="propertyName">The property name.</param>
/// <returns>True if the plugin can handle the property on the object; otherwise false.</returns> /// <returns>True if the plugin can handle the property on the object; otherwise false.</returns>
[RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)]
bool Match(object obj, string propertyName); bool Match(object obj, string propertyName);
/// <summary> /// <summary>
@ -25,6 +27,7 @@ namespace Avalonia.Data.Core.Plugins
/// An <see cref="IPropertyAccessor"/> interface through which future interactions with the /// An <see cref="IPropertyAccessor"/> interface through which future interactions with the
/// property will be made. /// property will be made.
/// </returns> /// </returns>
[RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)]
IPropertyAccessor? Start(WeakReference<object?> reference, IPropertyAccessor? Start(WeakReference<object?> reference,
string propertyName); string propertyName);
} }

3
src/Avalonia.Base/Data/Core/Plugins/IStreamPlugin.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
namespace Avalonia.Data.Core.Plugins namespace Avalonia.Data.Core.Plugins
{ {
@ -12,6 +13,7 @@ namespace Avalonia.Data.Core.Plugins
/// </summary> /// </summary>
/// <param name="reference">A weak reference to the value.</param> /// <param name="reference">A weak reference to the value.</param>
/// <returns>True if the plugin can handle the value; otherwise false.</returns> /// <returns>True if the plugin can handle the value; otherwise false.</returns>
[RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)]
bool Match(WeakReference<object?> reference); bool Match(WeakReference<object?> reference);
/// <summary> /// <summary>
@ -21,6 +23,7 @@ namespace Avalonia.Data.Core.Plugins
/// <returns> /// <returns>
/// An observable that produces the output for the value. /// An observable that produces the output for the value.
/// </returns> /// </returns>
[RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)]
IObservable<object?> Start(WeakReference<object?> reference); IObservable<object?> Start(WeakReference<object?> reference);
} }
} }

5
src/Avalonia.Base/Data/Core/Plugins/IndeiValidationPlugin.cs

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using Avalonia.Utilities; using Avalonia.Utilities;
@ -18,6 +19,7 @@ namespace Avalonia.Data.Core.Plugins
); );
/// <inheritdoc/> /// <inheritdoc/>
[RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)]
public bool Match(WeakReference<object?> reference, string memberName) public bool Match(WeakReference<object?> reference, string memberName)
{ {
reference.TryGetTarget(out var target); reference.TryGetTarget(out var target);
@ -26,6 +28,7 @@ namespace Avalonia.Data.Core.Plugins
} }
/// <inheritdoc/> /// <inheritdoc/>
[RequiresUnreferencedCode(TrimmingMessages.DataValidationPluginRequiresUnreferencedCodeMessage)]
public IPropertyAccessor Start(WeakReference<object?> reference, string name, IPropertyAccessor accessor) public IPropertyAccessor Start(WeakReference<object?> reference, string name, IPropertyAccessor accessor)
{ {
return new Validator(reference, name, accessor); return new Validator(reference, name, accessor);
@ -108,7 +111,7 @@ namespace Avalonia.Data.Core.Plugins
return target; return target;
} }
private Exception GenerateException(IList<object> errors) private static Exception GenerateException(IList<object> errors)
{ {
if (errors.Count == 1) if (errors.Count == 1)
{ {

10
src/Avalonia.Base/Data/Core/Plugins/InpcPropertyAccessorPlugin.cs

@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized;
using System.ComponentModel; using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
using System.Reflection; using System.Reflection;
using Avalonia.Utilities; using Avalonia.Utilities;
@ -17,6 +17,7 @@ namespace Avalonia.Data.Core.Plugins
new Dictionary<(Type, string), PropertyInfo?>(); new Dictionary<(Type, string), PropertyInfo?>();
/// <inheritdoc/> /// <inheritdoc/>
[RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)]
public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj, propertyName) != null; public bool Match(object obj, string propertyName) => GetFirstPropertyWithName(obj, propertyName) != null;
/// <summary> /// <summary>
@ -28,6 +29,7 @@ namespace Avalonia.Data.Core.Plugins
/// An <see cref="IPropertyAccessor"/> interface through which future interactions with the /// An <see cref="IPropertyAccessor"/> interface through which future interactions with the
/// property will be made. /// property will be made.
/// </returns> /// </returns>
[RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)]
public IPropertyAccessor? Start(WeakReference<object?> reference, string propertyName) public IPropertyAccessor? Start(WeakReference<object?> reference, string propertyName)
{ {
_ = reference ?? throw new ArgumentNullException(nameof(reference)); _ = reference ?? throw new ArgumentNullException(nameof(reference));
@ -52,7 +54,8 @@ namespace Avalonia.Data.Core.Plugins
private const BindingFlags PropertyBindingFlags = private const BindingFlags PropertyBindingFlags =
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance; BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance;
[RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)]
private PropertyInfo? GetFirstPropertyWithName(object instance, string propertyName) private PropertyInfo? GetFirstPropertyWithName(object instance, string propertyName)
{ {
if (instance is IReflectableType reflectableType && instance is not Type) if (instance is IReflectableType reflectableType && instance is not Type)
@ -70,7 +73,8 @@ namespace Avalonia.Data.Core.Plugins
return propertyInfo; return propertyInfo;
} }
private PropertyInfo? TryFindAndCacheProperty(Type type, string propertyName) private PropertyInfo? TryFindAndCacheProperty(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties | DynamicallyAccessedMemberTypes.NonPublicProperties)] Type type, string propertyName)
{ {
PropertyInfo? found = null; PropertyInfo? found = null;

9
src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
@ -10,8 +11,10 @@ namespace Avalonia.Data.Core.Plugins
private readonly Dictionary<(Type, string), MethodInfo?> _methodLookup = private readonly Dictionary<(Type, string), MethodInfo?> _methodLookup =
new Dictionary<(Type, string), MethodInfo?>(); new Dictionary<(Type, string), MethodInfo?>();
[RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)]
public bool Match(object obj, string methodName) => GetFirstMethodWithName(obj.GetType(), methodName) != null; public bool Match(object obj, string methodName) => GetFirstMethodWithName(obj.GetType(), methodName) != null;
[RequiresUnreferencedCode(TrimmingMessages.PropertyAccessorsRequiresUnreferencedCodeMessage)]
public IPropertyAccessor? Start(WeakReference<object?> reference, string methodName) public IPropertyAccessor? Start(WeakReference<object?> reference, string methodName)
{ {
_ = reference ?? throw new ArgumentNullException(nameof(reference)); _ = reference ?? throw new ArgumentNullException(nameof(reference));
@ -34,7 +37,8 @@ namespace Avalonia.Data.Core.Plugins
} }
} }
private MethodInfo? GetFirstMethodWithName(Type type, string methodName) private MethodInfo? GetFirstMethodWithName(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type type, string methodName)
{ {
var key = (type, methodName); var key = (type, methodName);
@ -46,7 +50,8 @@ namespace Avalonia.Data.Core.Plugins
return methodInfo; return methodInfo;
} }
private MethodInfo? TryFindAndCacheMethod(Type type, string methodName) private MethodInfo? TryFindAndCacheMethod(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods)] Type type, string methodName)
{ {
MethodInfo? found = null; MethodInfo? found = null;

5
src/Avalonia.Base/Data/Core/Plugins/ObservableStreamPlugin.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq; using System.Linq;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Reflection; using System.Reflection;
@ -8,6 +9,7 @@ namespace Avalonia.Data.Core.Plugins
/// <summary> /// <summary>
/// Handles binding to <see cref="IObservable{T}"/>s for the '^' stream binding operator. /// Handles binding to <see cref="IObservable{T}"/>s for the '^' stream binding operator.
/// </summary> /// </summary>
[UnconditionalSuppressMessage("Trimming", "IL3050", Justification = TrimmingMessages.IgnoreNativeAotSupressWarningMessage)]
public class ObservableStreamPlugin : IStreamPlugin public class ObservableStreamPlugin : IStreamPlugin
{ {
static MethodInfo? observableSelect; static MethodInfo? observableSelect;
@ -17,6 +19,7 @@ namespace Avalonia.Data.Core.Plugins
/// </summary> /// </summary>
/// <param name="reference">A weak reference to the value.</param> /// <param name="reference">A weak reference to the value.</param>
/// <returns>True if the plugin can handle the value; otherwise false.</returns> /// <returns>True if the plugin can handle the value; otherwise false.</returns>
[RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)]
public virtual bool Match(WeakReference<object?> reference) public virtual bool Match(WeakReference<object?> reference)
{ {
reference.TryGetTarget(out var target); reference.TryGetTarget(out var target);
@ -33,6 +36,7 @@ namespace Avalonia.Data.Core.Plugins
/// <returns> /// <returns>
/// An observable that produces the output for the value. /// An observable that produces the output for the value.
/// </returns> /// </returns>
[RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)]
public virtual IObservable<object?> Start(WeakReference<object?> reference) public virtual IObservable<object?> Start(WeakReference<object?> reference)
{ {
if (!reference.TryGetTarget(out var target) || target is null) if (!reference.TryGetTarget(out var target) || target is null)
@ -65,6 +69,7 @@ namespace Avalonia.Data.Core.Plugins
new object[] { target, box })!; new object[] { target, box })!;
} }
[RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)]
private static MethodInfo GetObservableSelect(Type source) private static MethodInfo GetObservableSelect(Type source)
{ {
return GetObservableSelect().MakeGenericMethod(source, typeof(object)); return GetObservableSelect().MakeGenericMethod(source, typeof(object));

9
src/Avalonia.Base/Data/Core/Plugins/TaskStreamPlugin.cs

@ -1,4 +1,5 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Reactive.Linq; using System.Reactive.Linq;
using System.Reactive.Subjects; using System.Reactive.Subjects;
using System.Reflection; using System.Reflection;
@ -9,6 +10,7 @@ namespace Avalonia.Data.Core.Plugins
/// <summary> /// <summary>
/// Handles binding to <see cref="Task"/>s for the '^' stream binding operator. /// Handles binding to <see cref="Task"/>s for the '^' stream binding operator.
/// </summary> /// </summary>
[UnconditionalSuppressMessage("Trimming", "IL3050", Justification = TrimmingMessages.IgnoreNativeAotSupressWarningMessage)]
public class TaskStreamPlugin : IStreamPlugin public class TaskStreamPlugin : IStreamPlugin
{ {
/// <summary> /// <summary>
@ -16,12 +18,13 @@ namespace Avalonia.Data.Core.Plugins
/// </summary> /// </summary>
/// <param name="reference">A weak reference to the value.</param> /// <param name="reference">A weak reference to the value.</param>
/// <returns>True if the plugin can handle the value; otherwise false.</returns> /// <returns>True if the plugin can handle the value; otherwise false.</returns>
[RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)]
public virtual bool Match(WeakReference<object?> reference) public virtual bool Match(WeakReference<object?> reference)
{ {
reference.TryGetTarget(out var target); reference.TryGetTarget(out var target);
return target is Task; return target is Task;
} }
/// <summary> /// <summary>
/// Starts producing output based on the specified value. /// Starts producing output based on the specified value.
@ -30,6 +33,7 @@ namespace Avalonia.Data.Core.Plugins
/// <returns> /// <returns>
/// An observable that produces the output for the value. /// An observable that produces the output for the value.
/// </returns> /// </returns>
[RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)]
public virtual IObservable<object?> Start(WeakReference<object?> reference) public virtual IObservable<object?> Start(WeakReference<object?> reference)
{ {
reference.TryGetTarget(out var target); reference.TryGetTarget(out var target);
@ -59,7 +63,8 @@ namespace Avalonia.Data.Core.Plugins
return Observable.Empty<object?>(); return Observable.Empty<object?>();
} }
private IObservable<object?> HandleCompleted(Task task) [RequiresUnreferencedCode(TrimmingMessages.StreamPluginRequiresUnreferencedCodeMessage)]
private static IObservable<object?> HandleCompleted(Task task)
{ {
var resultProperty = task.GetType().GetRuntimeProperty("Result"); var resultProperty = task.GetType().GetRuntimeProperty("Result");

3
src/Avalonia.Base/Data/Core/PropertyAccessorNode.cs

@ -1,9 +1,10 @@
using System; using System;
using System.Diagnostics; using System.Diagnostics.CodeAnalysis;
using Avalonia.Data.Core.Plugins; using Avalonia.Data.Core.Plugins;
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
[RequiresUnreferencedCode(TrimmingMessages.ExpressionNodeRequiresUnreferencedCodeMessage)]
public class PropertyAccessorNode : SettableNode public class PropertyAccessorNode : SettableNode
{ {
private readonly bool _enableValidation; private readonly bool _enableValidation;

2
src/Avalonia.Base/Data/Core/StreamNode.cs

@ -1,9 +1,11 @@
using System; using System;
using System.Diagnostics.CodeAnalysis;
using System.Reactive.Linq; using System.Reactive.Linq;
using Avalonia.Data.Core.Plugins; using Avalonia.Data.Core.Plugins;
namespace Avalonia.Data.Core namespace Avalonia.Data.Core
{ {
[RequiresUnreferencedCode(TrimmingMessages.ExpressionNodeRequiresUnreferencedCodeMessage)]
public class StreamNode : ExpressionNode public class StreamNode : ExpressionNode
{ {
private IStreamPlugin? _customPlugin = null; private IStreamPlugin? _customPlugin = null;

2
src/Avalonia.Base/Data/IBinding.cs

@ -24,7 +24,7 @@ namespace Avalonia.Data
/// A <see cref="InstancedBinding"/> or null if the binding could not be resolved. /// A <see cref="InstancedBinding"/> or null if the binding could not be resolved.
/// </returns> /// </returns>
InstancedBinding? Initiate( InstancedBinding? Initiate(
IAvaloniaObject target, AvaloniaObject target,
AvaloniaProperty? targetProperty, AvaloniaProperty? targetProperty,
object? anchor = null, object? anchor = null,
bool enableDataValidation = false); bool enableDataValidation = false);

6
src/Avalonia.Base/Data/IndexerBinding.cs

@ -3,7 +3,7 @@
public class IndexerBinding : IBinding public class IndexerBinding : IBinding
{ {
public IndexerBinding( public IndexerBinding(
IAvaloniaObject source, AvaloniaObject source,
AvaloniaProperty property, AvaloniaProperty property,
BindingMode mode) BindingMode mode)
{ {
@ -12,12 +12,12 @@
Mode = mode; Mode = mode;
} }
private IAvaloniaObject Source { get; } private AvaloniaObject Source { get; }
public AvaloniaProperty Property { get; } public AvaloniaProperty Property { get; }
private BindingMode Mode { get; } private BindingMode Mode { get; }
public InstancedBinding? Initiate( public InstancedBinding? Initiate(
IAvaloniaObject target, AvaloniaObject target,
AvaloniaProperty? targetProperty, AvaloniaProperty? targetProperty,
object? anchor = null, object? anchor = null,
bool enableDataValidation = false) bool enableDataValidation = false)

2
src/Avalonia.Base/Data/InstancedBinding.cs

@ -9,7 +9,7 @@ namespace Avalonia.Data
/// <remarks> /// <remarks>
/// Whereas an <see cref="IBinding"/> holds a description of a binding such as "Bind to the X /// Whereas an <see cref="IBinding"/> holds a description of a binding such as "Bind to the X
/// property on a control's DataContext"; this class represents a binding that has been /// property on a control's DataContext"; this class represents a binding that has been
/// *instanced* by calling <see cref="IBinding.Initiate(IAvaloniaObject, AvaloniaProperty, object, bool)"/> /// *instanced* by calling <see cref="IBinding.Initiate(AvaloniaObject, AvaloniaProperty, object, bool)"/>
/// on a target object. /// on a target object.
/// </remarks> /// </remarks>
public class InstancedBinding public class InstancedBinding

2
src/Avalonia.Base/Diagnostics/IAvaloniaObjectDebug.cs

@ -8,7 +8,7 @@ namespace Avalonia.Diagnostics
public interface IAvaloniaObjectDebug public interface IAvaloniaObjectDebug
{ {
/// <summary> /// <summary>
/// Gets the subscriber list for the <see cref="IAvaloniaObject.PropertyChanged"/> /// Gets the subscriber list for the <see cref="AvaloniaObject.PropertyChanged"/>
/// event. /// event.
/// </summary> /// </summary>
/// <returns> /// <returns>

30
src/Avalonia.Base/Diagnostics/TrimmingMessages.cs

@ -0,0 +1,30 @@
namespace Avalonia;
internal static class TrimmingMessages
{
public const string ImplicitTypeConvertionSupressWarningMessage = "Implicit convertion methods might be removed by the linker. We don't have a reliable way to prevent it, except converting everything in compile time when possible.";
public const string ImplicitTypeConvertionRequiresUnreferencedCodeMessage = "Implicit convertion methods are required for type conversion.";
public const string TypeConvertionSupressWarningMessage = "Convertion methods might be removed by the linker. We don't have a reliable way to prevent it, except converting everything in compile time when possible.";
public const string TypeConvertionRequiresUnreferencedCodeMessage = "Convertion methods are required for type conversion, including op_Implicit, op_Explicit, Parse and TypeConverter.";
public const string ReflectionBindingRequiresUnreferencedCodeMessage = "BindingExpression and ReflectionBinding heavily use reflection. Consider using CompiledBindings instead.";
public const string ReflectionBindingSupressWarningMessage = "BindingExpression and ReflectionBinding internal heavily use reflection.";
public const string CompiledBindingSafeSupressWarningMessage = "CompiledBinding preserves members used in the expression tree.";
public const string ExpressionNodeRequiresUnreferencedCodeMessage = "ExpressionNode might require unreferenced code.";
public const string ExpressionSafeSupressWarningMessage = "Typed Expressions preserves members used in the expression tree.";
public const string SelectorsParseRequiresUnreferencedCodeMessage = "Selectors runtime parser might require unreferenced code. Consider using stronly typed selectors factory with 'new Style(s => s.OfType<Button>())' syntax.";
public const string PropertyAccessorsRequiresUnreferencedCodeMessage = "PropertyAccessors might require unreferenced code.";
public const string DataValidationPluginRequiresUnreferencedCodeMessage = "DataValidationPlugin might require unreferenced code.";
public const string StreamPluginRequiresUnreferencedCodeMessage = "StreamPlugin might require unreferenced code.";
public const string StyleResourceIncludeRequiresUnreferenceCodeMessage = "StyleInclude and ResourceInclude use AvaloniaXamlLoader.Load which dynamically loads referenced assembly with Avalonia resources. Note, StyleInclude and ResourceInclude defined in XAML are resolved compile time and are safe with trimming and AOT.";
public const string AvaloniaXamlLoaderRequiresUnreferenceCodeMessage = "AvaloniaXamlLoader.Load(uri, baseUri) dynamically loads referenced assembly with Avalonia resources.";
public const string XamlTypeResolvedRequiresUnreferenceCodeMessage = "XamlTypeResolver might require unreferenced code.";
public const string IgnoreNativeAotSupressWarningMessage = "This method is not supported by NativeAOT.";
}

10
src/Avalonia.Base/DirectProperty.cs

@ -15,7 +15,7 @@ namespace Avalonia
/// allows the avalonia property system to read and write the current value. /// allows the avalonia property system to read and write the current value.
/// </remarks> /// </remarks>
public class DirectProperty<TOwner, TValue> : DirectPropertyBase<TValue>, IDirectPropertyAccessor public class DirectProperty<TOwner, TValue> : DirectPropertyBase<TValue>, IDirectPropertyAccessor
where TOwner : IAvaloniaObject where TOwner : AvaloniaObject
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="DirectProperty{TOwner, TValue}"/> class. /// Initializes a new instance of the <see cref="DirectProperty{TOwner, TValue}"/> class.
@ -151,13 +151,13 @@ namespace Avalonia
} }
/// <inheritdoc/> /// <inheritdoc/>
internal override TValue InvokeGetter(IAvaloniaObject instance) internal override TValue InvokeGetter(AvaloniaObject instance)
{ {
return Getter((TOwner)instance); return Getter((TOwner)instance);
} }
/// <inheritdoc/> /// <inheritdoc/>
internal override void InvokeSetter(IAvaloniaObject instance, BindingValue<TValue> value) internal override void InvokeSetter(AvaloniaObject instance, BindingValue<TValue> value)
{ {
if (Setter == null) if (Setter == null)
{ {
@ -171,13 +171,13 @@ namespace Avalonia
} }
/// <inheritdoc/> /// <inheritdoc/>
object? IDirectPropertyAccessor.GetValue(IAvaloniaObject instance) object? IDirectPropertyAccessor.GetValue(AvaloniaObject instance)
{ {
return Getter((TOwner)instance); return Getter((TOwner)instance);
} }
/// <inheritdoc/> /// <inheritdoc/>
void IDirectPropertyAccessor.SetValue(IAvaloniaObject instance, object? value) void IDirectPropertyAccessor.SetValue(AvaloniaObject instance, object? value)
{ {
if (Setter == null) if (Setter == null)
{ {

6
src/Avalonia.Base/DirectPropertyBase.cs

@ -54,14 +54,14 @@ namespace Avalonia
/// </summary> /// </summary>
/// <param name="instance">The instance.</param> /// <param name="instance">The instance.</param>
/// <returns>The property value.</returns> /// <returns>The property value.</returns>
internal abstract TValue InvokeGetter(IAvaloniaObject instance); internal abstract TValue InvokeGetter(AvaloniaObject instance);
/// <summary> /// <summary>
/// Sets the value of the property on the instance. /// Sets the value of the property on the instance.
/// </summary> /// </summary>
/// <param name="instance">The instance.</param> /// <param name="instance">The instance.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
internal abstract void InvokeSetter(IAvaloniaObject instance, BindingValue<TValue> value); internal abstract void InvokeSetter(AvaloniaObject instance, BindingValue<TValue> value);
/// <summary> /// <summary>
/// Gets the unset value for the property on the specified type. /// Gets the unset value for the property on the specified type.
@ -91,7 +91,7 @@ namespace Avalonia
/// </summary> /// </summary>
/// <typeparam name="T">The type.</typeparam> /// <typeparam name="T">The type.</typeparam>
/// <param name="metadata">The metadata.</param> /// <param name="metadata">The metadata.</param>
public void OverrideMetadata<T>(DirectPropertyMetadata<TValue> metadata) where T : IAvaloniaObject public void OverrideMetadata<T>(DirectPropertyMetadata<TValue> metadata) where T : AvaloniaObject
{ {
base.OverrideMetadata(typeof(T), metadata); base.OverrideMetadata(typeof(T), metadata);
} }

79
src/Avalonia.Base/IAvaloniaObject.cs

@ -1,79 +0,0 @@
using System;
using Avalonia.Data;
using Avalonia.Metadata;
namespace Avalonia
{
/// <summary>
/// Interface for getting/setting <see cref="AvaloniaProperty"/> values on an object.
/// </summary>
[NotClientImplementable]
public interface IAvaloniaObject
{
/// <summary>
/// Raised when a <see cref="AvaloniaProperty"/> value changes on this object.
/// </summary>
event EventHandler<AvaloniaPropertyChangedEventArgs>? PropertyChanged;
/// <summary>
/// Clears an <see cref="AvaloniaProperty"/>'s local value.
/// </summary>
/// <param name="property">The property.</param>
void ClearValue(AvaloniaProperty property);
/// <summary>
/// Gets a <see cref="AvaloniaProperty"/> value.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>The value.</returns>
object? GetValue(AvaloniaProperty property);
/// <summary>
/// Checks whether a <see cref="AvaloniaProperty"/> is animating.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>True if the property is animating, otherwise false.</returns>
bool IsAnimating(AvaloniaProperty property);
/// <summary>
/// Checks whether a <see cref="AvaloniaProperty"/> is set on this object.
/// </summary>
/// <param name="property">The property.</param>
/// <returns>True if the property is set, otherwise false.</returns>
bool IsSet(AvaloniaProperty property);
/// <summary>
/// Sets a <see cref="AvaloniaProperty"/> value.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="value">The value.</param>
/// <param name="priority">The priority of the value.</param>
/// <returns>
/// An <see cref="IDisposable"/> if setting the property can be undone, otherwise null.
/// </returns>
IDisposable? SetValue(
AvaloniaProperty property,
object? value,
BindingPriority priority = BindingPriority.LocalValue);
/// <summary>
/// Binds a <see cref="AvaloniaProperty"/> to an observable.
/// </summary>
/// <param name="property">The property.</param>
/// <param name="source">The observable.</param>
/// <param name="priority">The priority of the binding.</param>
/// <returns>
/// A disposable which can be used to terminate the binding.
/// </returns>
IDisposable Bind(
AvaloniaProperty property,
IObservable<object?> source,
BindingPriority priority = BindingPriority.LocalValue);
/// <summary>
/// Coerces the specified <see cref="AvaloniaProperty"/>.
/// </summary>
/// <param name="property">The property.</param>
void CoerceValue(AvaloniaProperty property);
}
}

2
src/Avalonia.Base/IDataContextProvider.cs

@ -6,7 +6,7 @@ namespace Avalonia
/// Defines an element with a data context that can be used for binding. /// Defines an element with a data context that can be used for binding.
/// </summary> /// </summary>
[NotClientImplementable] [NotClientImplementable]
public interface IDataContextProvider : IAvaloniaObject public interface IDataContextProvider
{ {
/// <summary> /// <summary>
/// Gets or sets the element's data context. /// Gets or sets the element's data context.

4
src/Avalonia.Base/IDirectPropertyAccessor.cs

@ -24,13 +24,13 @@ namespace Avalonia
/// </summary> /// </summary>
/// <param name="instance">The instance.</param> /// <param name="instance">The instance.</param>
/// <returns>The property value.</returns> /// <returns>The property value.</returns>
object? GetValue(IAvaloniaObject instance); object? GetValue(AvaloniaObject instance);
/// <summary> /// <summary>
/// Sets the value of the property on the instance. /// Sets the value of the property on the instance.
/// </summary> /// </summary>
/// <param name="instance">The instance.</param> /// <param name="instance">The instance.</param>
/// <param name="value">The value.</param> /// <param name="value">The value.</param>
void SetValue(IAvaloniaObject instance, object? value); void SetValue(AvaloniaObject instance, object? value);
} }
} }

39
src/Avalonia.Base/IStyledElement.cs

@ -1,39 +0,0 @@
using System;
using System.ComponentModel;
using Avalonia.Controls;
using Avalonia.LogicalTree;
using Avalonia.Metadata;
using Avalonia.Styling;
namespace Avalonia
{
[NotClientImplementable]
public interface IStyledElement :
IStyleable,
IStyleHost,
ILogical,
IResourceHost,
IDataContextProvider,
ISupportInitialize
{
/// <summary>
/// Occurs when the control has finished initialization.
/// </summary>
event EventHandler? Initialized;
/// <summary>
/// Gets a value that indicates whether the element has finished initialization.
/// </summary>
bool IsInitialized { get; }
/// <summary>
/// Gets or sets the control's styling classes.
/// </summary>
new Classes Classes { get; set; }
/// <summary>
/// Gets the control's logical parent.
/// </summary>
IStyledElement? Parent { get; }
}
}

20
src/Avalonia.Base/Input/AccessKeyHandler.cs

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.VisualTree; using Avalonia.VisualTree;
@ -23,7 +24,7 @@ namespace Avalonia.Input
/// <summary> /// <summary>
/// The registered access keys. /// The registered access keys.
/// </summary> /// </summary>
private readonly List<Tuple<string, IInputElement>> _registered = new List<Tuple<string, IInputElement>>(); private readonly List<(string AccessKey, IInputElement Element)> _registered = new();
/// <summary> /// <summary>
/// The window to which the handler belongs. /// The window to which the handler belongs.
@ -108,12 +109,12 @@ namespace Avalonia.Input
{ {
var existing = _registered.FirstOrDefault(x => x.Item2 == element); var existing = _registered.FirstOrDefault(x => x.Item2 == element);
if (existing != null) if (existing != default)
{ {
_registered.Remove(existing); _registered.Remove(existing);
} }
_registered.Add(Tuple.Create(accessKey.ToString().ToUpper(), element)); _registered.Add((accessKey.ToString().ToUpperInvariant(), element));
} }
/// <summary> /// <summary>
@ -143,7 +144,7 @@ namespace Avalonia.Input
{ {
// TODO: Use FocusScopes to store the current element and restore it when context menu is closed. // TODO: Use FocusScopes to store the current element and restore it when context menu is closed.
// Save currently focused input element. // Save currently focused input element.
_restoreFocusElement = FocusManager.Instance?.Current; _restoreFocusElement = FocusManager.Instance?.Current;
// When Alt is pressed without a main menu, or with a closed main menu, show // When Alt is pressed without a main menu, or with a closed main menu, show
// access key markers in the window (i.e. "_File"). // access key markers in the window (i.e. "_File").
@ -180,21 +181,22 @@ namespace Avalonia.Input
{ {
// If any other key is pressed with the Alt key held down, or the main menu is open, // If any other key is pressed with the Alt key held down, or the main menu is open,
// find all controls who have registered that access key. // find all controls who have registered that access key.
var text = e.Key.ToString().ToUpper(); var text = e.Key.ToString();
var matches = _registered var matches = _registered
.Where(x => x.Item1 == text && x.Item2.IsEffectivelyVisible) .Where(x => string.Equals(x.AccessKey, text, StringComparison.OrdinalIgnoreCase)
.Select(x => x.Item2); && x.Element.IsEffectivelyVisible)
.Select(x => x.Element);
// If the menu is open, only match controls in the menu's visual tree. // If the menu is open, only match controls in the menu's visual tree.
if (menuIsOpen) if (menuIsOpen)
{ {
matches = matches.Where(x => x is not null && MainMenu!.IsVisualAncestorOf(x)); matches = matches.Where(x => x is not null && ((Visual)MainMenu!).IsVisualAncestorOf((Visual)x));
} }
var match = matches.FirstOrDefault(); var match = matches.FirstOrDefault();
// If there was a match, raise the AccessKeyPressed event on it. // If there was a match, raise the AccessKeyPressed event on it.
if (match != null) if (match is not null)
{ {
match.RaiseEvent(new RoutedEventArgs(AccessKeyPressedEvent)); match.RaiseEvent(new RoutedEventArgs(AccessKeyPressedEvent));
e.Handled = true; e.Handled = true;

9
src/Avalonia.Base/Input/DragDropDevice.cs

@ -11,20 +11,21 @@ namespace Avalonia.Input
private Interactive? _lastTarget = null; private Interactive? _lastTarget = null;
private Interactive? GetTarget(IInputRoot root, Point local) private static Interactive? GetTarget(IInputRoot root, Point local)
{ {
var target = root.InputHitTest(local)?.GetSelfAndVisualAncestors()?.OfType<Interactive>()?.FirstOrDefault(); var hit = root.InputHitTest(local) as Visual;
var target = hit?.GetSelfAndVisualAncestors()?.OfType<Interactive>()?.FirstOrDefault();
if (target != null && DragDrop.GetAllowDrop(target)) if (target != null && DragDrop.GetAllowDrop(target))
return target; return target;
return null; return null;
} }
private DragDropEffects RaiseDragEvent(Interactive? target, IInputRoot inputRoot, Point point, RoutedEvent<DragEventArgs> routedEvent, DragDropEffects operation, IDataObject data, KeyModifiers modifiers) private static DragDropEffects RaiseDragEvent(Interactive? target, IInputRoot inputRoot, Point point, RoutedEvent<DragEventArgs> routedEvent, DragDropEffects operation, IDataObject data, KeyModifiers modifiers)
{ {
if (target == null) if (target == null)
return DragDropEffects.None; return DragDropEffects.None;
var p = inputRoot.TranslatePoint(point, target); var p = ((Visual)inputRoot).TranslatePoint(point, target);
if (!p.HasValue) if (!p.HasValue)
return DragDropEffects.None; return DragDropEffects.None;

2
src/Avalonia.Base/Input/DragEventArgs.cs

@ -15,7 +15,7 @@ namespace Avalonia.Input
public KeyModifiers KeyModifiers { get; private set; } public KeyModifiers KeyModifiers { get; private set; }
public Point GetPosition(IVisual relativeTo) public Point GetPosition(Visual relativeTo)
{ {
var point = new Point(0, 0); var point = new Point(0, 0);

17
src/Avalonia.Base/Input/FocusManager.cs

@ -185,7 +185,7 @@ namespace Avalonia.Input
/// </summary> /// </summary>
/// <param name="e">The element.</param> /// <param name="e">The element.</param>
/// <returns>True if the element can be focused.</returns> /// <returns>True if the element can be focused.</returns>
private static bool CanFocus(IInputElement e) => e.Focusable && e.IsEffectivelyEnabled && e.IsVisible; private static bool CanFocus(IInputElement e) => e.Focusable && e.IsEffectivelyEnabled && IsVisible(e);
/// <summary> /// <summary>
/// Gets the focus scope ancestors of the specified control, traversing popups. /// Gets the focus scope ancestors of the specified control, traversing popups.
@ -198,14 +198,15 @@ namespace Avalonia.Input
while (c != null) while (c != null)
{ {
var scope = c as IFocusScope; if (c is IFocusScope scope &&
c is Visual v &&
if (scope != null && c.VisualRoot?.IsVisible == true) v.VisualRoot is Visual root &&
root.IsVisible)
{ {
yield return scope; yield return scope;
} }
c = c.GetVisualParent<IInputElement>() ?? c = (c as Visual)?.GetVisualParent<IInputElement>() ??
((c as IHostedVisualTreeRoot)?.Host as IInputElement); ((c as IHostedVisualTreeRoot)?.Host as IInputElement);
} }
} }
@ -221,11 +222,11 @@ namespace Avalonia.Input
return; return;
var ev = (PointerPressedEventArgs)e; var ev = (PointerPressedEventArgs)e;
var visual = (IVisual)sender; var visual = (Visual)sender;
if (sender == e.Source && ev.GetCurrentPoint(visual).Properties.IsLeftButtonPressed) if (sender == e.Source && ev.GetCurrentPoint(visual).Properties.IsLeftButtonPressed)
{ {
IVisual? element = ev.Pointer?.Captured ?? e.Source as IInputElement; Visual? element = ev.Pointer?.Captured as Visual ?? e.Source as Visual;
while (element != null) while (element != null)
{ {
@ -240,5 +241,7 @@ namespace Avalonia.Input
} }
} }
} }
private static bool IsVisible(IInputElement e) => (e as Visual)?.IsVisible ?? true;
} }
} }

4
src/Avalonia.Base/Input/GestureRecognizers/GestureRecognizerCollection.cs

@ -35,8 +35,8 @@ namespace Avalonia.Input.GestureRecognizers
if (_inputElement is ILogical logicalParent && recognizer is ISetLogicalParent logical) if (_inputElement is ILogical logicalParent && recognizer is ISetLogicalParent logical)
{ {
logical.SetParent(logicalParent); logical.SetParent(logicalParent);
if (recognizer is IStyleable styleableRecognizer if (recognizer is StyledElement styleableRecognizer
&& _inputElement is IStyleable styleableParent) && _inputElement is StyledElement styleableParent)
styleableRecognizer.Bind(StyledElement.TemplatedParentProperty, styleableRecognizer.Bind(StyledElement.TemplatedParentProperty,
styleableParent.GetObservable(StyledElement.TemplatedParentProperty)); styleableParent.GetObservable(StyledElement.TemplatedParentProperty));
} }

4
src/Avalonia.Base/Input/GestureRecognizers/ScrollGestureRecognizer.cs

@ -72,7 +72,7 @@ namespace Avalonia.Input.GestureRecognizers
EndGesture(); EndGesture();
_tracking = e.Pointer; _tracking = e.Pointer;
_gestureId = ScrollGestureEventArgs.GetNextFreeId(); _gestureId = ScrollGestureEventArgs.GetNextFreeId();
_trackedRootPoint = e.GetPosition(_target); _trackedRootPoint = e.GetPosition((Visual?)_target);
} }
} }
@ -86,7 +86,7 @@ namespace Avalonia.Input.GestureRecognizers
{ {
if (e.Pointer == _tracking) if (e.Pointer == _tracking)
{ {
var rootPoint = e.GetPosition(_target); var rootPoint = e.GetPosition((Visual?)_target);
if (!_scrolling) if (!_scrolling)
{ {
if (CanHorizontallyScroll && Math.Abs(_trackedRootPoint.X - rootPoint.X) > ScrollStartDistance) if (CanHorizontallyScroll && Math.Abs(_trackedRootPoint.X - rootPoint.X) > ScrollStartDistance)

33
src/Avalonia.Base/Input/Gestures.cs

@ -43,7 +43,7 @@ namespace Avalonia.Input
RoutedEvent.Register<PointerDeltaEventArgs>( RoutedEvent.Register<PointerDeltaEventArgs>(
"PointerSwipeGesture", RoutingStrategies.Bubble, typeof(Gestures)); "PointerSwipeGesture", RoutingStrategies.Bubble, typeof(Gestures));
private static readonly WeakReference<IInteractive?> s_lastPress = new WeakReference<IInteractive?>(null); private static readonly WeakReference<object?> s_lastPress = new WeakReference<object?>(null);
private static Point s_lastPressPoint; private static Point s_lastPressPoint;
static Gestures() static Gestures()
@ -52,32 +52,32 @@ namespace Avalonia.Input
InputElement.PointerReleasedEvent.RouteFinished.Subscribe(PointerReleased); InputElement.PointerReleasedEvent.RouteFinished.Subscribe(PointerReleased);
} }
public static void AddTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler) public static void AddTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
{ {
element.AddHandler(TappedEvent, handler); element.AddHandler(TappedEvent, handler);
} }
public static void AddDoubleTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler) public static void AddDoubleTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
{ {
element.AddHandler(DoubleTappedEvent, handler); element.AddHandler(DoubleTappedEvent, handler);
} }
public static void AddRightTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler) public static void AddRightTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
{ {
element.AddHandler(RightTappedEvent, handler); element.AddHandler(RightTappedEvent, handler);
} }
public static void RemoveTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler) public static void RemoveTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
{ {
element.RemoveHandler(TappedEvent, handler); element.RemoveHandler(TappedEvent, handler);
} }
public static void RemoveDoubleTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler) public static void RemoveDoubleTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
{ {
element.RemoveHandler(DoubleTappedEvent, handler); element.RemoveHandler(DoubleTappedEvent, handler);
} }
public static void RemoveRightTappedHandler(IInteractive element, EventHandler<RoutedEventArgs> handler) public static void RemoveRightTappedHandler(Interactive element, EventHandler<RoutedEventArgs> handler)
{ {
element.RemoveHandler(RightTappedEvent, handler); element.RemoveHandler(RightTappedEvent, handler);
} }
@ -92,20 +92,22 @@ namespace Avalonia.Input
if (ev.Route == RoutingStrategies.Bubble) if (ev.Route == RoutingStrategies.Bubble)
{ {
var e = (PointerPressedEventArgs)ev; var e = (PointerPressedEventArgs)ev;
var visual = (IVisual)ev.Source; var visual = (Visual)ev.Source;
if (e.ClickCount % 2 == 1) if (e.ClickCount % 2 == 1)
{ {
s_isDoubleTapped = false; s_isDoubleTapped = false;
s_lastPress.SetTarget(ev.Source); s_lastPress.SetTarget(ev.Source);
s_lastPressPoint = e.GetPosition((IVisual)ev.Source); s_lastPressPoint = e.GetPosition((Visual)ev.Source);
} }
else if (e.ClickCount % 2 == 0 && e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed) else if (e.ClickCount % 2 == 0 && e.GetCurrentPoint(visual).Properties.IsLeftButtonPressed)
{ {
if (s_lastPress.TryGetTarget(out var target) && target == e.Source) if (s_lastPress.TryGetTarget(out var target) &&
target == e.Source &&
e.Source is Interactive i)
{ {
s_isDoubleTapped = true; s_isDoubleTapped = true;
e.Source.RaiseEvent(new TappedEventArgs(DoubleTappedEvent, e)); i.RaiseEvent(new TappedEventArgs(DoubleTappedEvent, e));
} }
} }
} }
@ -119,9 +121,10 @@ namespace Avalonia.Input
if (s_lastPress.TryGetTarget(out var target) && if (s_lastPress.TryGetTarget(out var target) &&
target == e.Source && target == e.Source &&
e.InitialPressMouseButton is MouseButton.Left or MouseButton.Right) e.InitialPressMouseButton is MouseButton.Left or MouseButton.Right &&
e.Source is Interactive i)
{ {
var point = e.GetCurrentPoint((IVisual)target); var point = e.GetCurrentPoint((Visual)target);
var settings = AvaloniaLocator.Current.GetService<IPlatformSettings>(); var settings = AvaloniaLocator.Current.GetService<IPlatformSettings>();
var tapSize = settings?.GetTapSize(point.Pointer.Type) ?? new Size(4, 4); var tapSize = settings?.GetTapSize(point.Pointer.Type) ?? new Size(4, 4);
var tapRect = new Rect(s_lastPressPoint, new Size()) var tapRect = new Rect(s_lastPressPoint, new Size())
@ -131,13 +134,13 @@ namespace Avalonia.Input
{ {
if (e.InitialPressMouseButton == MouseButton.Right) if (e.InitialPressMouseButton == MouseButton.Right)
{ {
e.Source.RaiseEvent(new TappedEventArgs(RightTappedEvent, e)); i.RaiseEvent(new TappedEventArgs(RightTappedEvent, e));
} }
//s_isDoubleTapped needed here to prevent invoking Tapped event when DoubleTapped is called. //s_isDoubleTapped needed here to prevent invoking Tapped event when DoubleTapped is called.
//This behaviour matches UWP behaviour. //This behaviour matches UWP behaviour.
else if (s_isDoubleTapped == false) else if (s_isDoubleTapped == false)
{ {
e.Source.RaiseEvent(new TappedEventArgs(TappedEvent, e)); i.RaiseEvent(new TappedEventArgs(TappedEvent, e));
} }
} }
} }

39
src/Avalonia.Base/Input/IInputElement.cs

@ -2,9 +2,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Metadata; using Avalonia.Metadata;
using Avalonia.VisualTree;
#nullable enable
namespace Avalonia.Input namespace Avalonia.Input
{ {
@ -12,7 +9,7 @@ namespace Avalonia.Input
/// Defines input-related functionality for a control. /// Defines input-related functionality for a control.
/// </summary> /// </summary>
[NotClientImplementable] [NotClientImplementable]
public interface IInputElement : IInteractive, IVisual public interface IInputElement
{ {
/// <summary> /// <summary>
/// Occurs when the control receives focus. /// Occurs when the control receives focus.
@ -93,7 +90,12 @@ namespace Avalonia.Input
/// <see cref="IsEnabled"/> value of this control and its parent controls. /// <see cref="IsEnabled"/> value of this control and its parent controls.
/// </remarks> /// </remarks>
bool IsEffectivelyEnabled { get; } bool IsEffectivelyEnabled { get; }
/// <summary>
/// Gets a value indicating whether this control and all its parents are visible.
/// </summary>
bool IsEffectivelyVisible { get; }
/// <summary> /// <summary>
/// Gets a value indicating whether keyboard focus is anywhere within the element or its visual tree child elements. /// Gets a value indicating whether keyboard focus is anywhere within the element or its visual tree child elements.
/// </summary> /// </summary>
@ -123,5 +125,32 @@ namespace Avalonia.Input
/// Gets the key bindings for the element. /// Gets the key bindings for the element.
/// </summary> /// </summary>
List<KeyBinding> KeyBindings { get; } List<KeyBinding> KeyBindings { get; }
/// <summary>
/// Adds a handler for the specified routed event.
/// </summary>
/// <param name="routedEvent">The routed event.</param>
/// <param name="handler">The handler.</param>
/// <param name="routes">The routing strategies to listen to.</param>
/// <param name="handledEventsToo">Whether handled events should also be listened for.</param>
/// <returns>A disposable that terminates the event subscription.</returns>
void AddHandler(
RoutedEvent routedEvent,
Delegate handler,
RoutingStrategies routes = RoutingStrategies.Direct | RoutingStrategies.Bubble,
bool handledEventsToo = false);
/// <summary>
/// Removes a handler for the specified routed event.
/// </summary>
/// <param name="routedEvent">The routed event.</param>
/// <param name="handler">The handler.</param>
void RemoveHandler(RoutedEvent routedEvent, Delegate handler);
/// <summary>
/// Raises a routed event.
/// </summary>
/// <param name="e">The event args.</param>
void RaiseEvent(RoutedEventArgs e);
} }
} }

2
src/Avalonia.Base/Input/IMainMenu.cs

@ -9,7 +9,7 @@ namespace Avalonia.Input
/// Defines the interface for a window's main menu. /// Defines the interface for a window's main menu.
/// </summary> /// </summary>
[NotClientImplementable] [NotClientImplementable]
public interface IMainMenu : IVisual public interface IMainMenu
{ {
/// <summary> /// <summary>
/// Gets a value indicating whether the menu is open. /// Gets a value indicating whether the menu is open.

17
src/Avalonia.Base/Input/InputExtensions.cs

@ -12,7 +12,7 @@ namespace Avalonia.Input
/// </summary> /// </summary>
public static class InputExtensions public static class InputExtensions
{ {
private static readonly Func<IVisual, bool> s_hitTestDelegate = IsHitTestVisible; private static readonly Func<Visual, bool> s_hitTestDelegate = IsHitTestVisible;
/// <summary> /// <summary>
/// Returns the active input elements at a point on an <see cref="IInputElement"/>. /// Returns the active input elements at a point on an <see cref="IInputElement"/>.
@ -26,7 +26,8 @@ namespace Avalonia.Input
{ {
element = element ?? throw new ArgumentNullException(nameof(element)); element = element ?? throw new ArgumentNullException(nameof(element));
return element.GetVisualsAt(p, s_hitTestDelegate).Cast<IInputElement>(); return (element as Visual)?.GetVisualsAt(p, s_hitTestDelegate).Cast<IInputElement>() ??
Enumerable.Empty<IInputElement>();
} }
/// <summary> /// <summary>
@ -39,7 +40,7 @@ namespace Avalonia.Input
{ {
element = element ?? throw new ArgumentNullException(nameof(element)); element = element ?? throw new ArgumentNullException(nameof(element));
return element.GetVisualAt(p, s_hitTestDelegate) as IInputElement; return (element as Visual)?.GetVisualAt(p, s_hitTestDelegate) as IInputElement;
} }
/// <summary> /// <summary>
@ -55,22 +56,22 @@ namespace Avalonia.Input
public static IInputElement? InputHitTest( public static IInputElement? InputHitTest(
this IInputElement element, this IInputElement element,
Point p, Point p,
Func<IVisual, bool> filter) Func<Visual, bool> filter)
{ {
element = element ?? throw new ArgumentNullException(nameof(element)); element = element ?? throw new ArgumentNullException(nameof(element));
filter = filter ?? throw new ArgumentNullException(nameof(filter)); filter = filter ?? throw new ArgumentNullException(nameof(filter));
return element.GetVisualAt(p, x => s_hitTestDelegate(x) && filter(x)) as IInputElement; return (element as Visual)?.GetVisualAt(p, x => s_hitTestDelegate(x) && filter(x)) as IInputElement;
} }
private static bool IsHitTestVisible(IVisual visual) private static bool IsHitTestVisible(Visual visual)
{ {
var element = visual as IInputElement; var element = visual as IInputElement;
return element != null && return element != null &&
element.IsVisible && visual.IsVisible &&
element.IsHitTestVisible && element.IsHitTestVisible &&
element.IsEffectivelyEnabled && element.IsEffectivelyEnabled &&
element.IsAttachedToVisualTree; visual.IsAttachedToVisualTree;
} }
} }
} }

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

Loading…
Cancel
Save