Browse Source

Merge remote-tracking branch 'origin/master' into child-window-property2

pull/7622/head
Dan Walmsley 5 years ago
parent
commit
2e96893717
  1. 9
      Avalonia.sln
  2. 1
      Directory.Build.props
  3. 9
      azure-pipelines.yml
  4. 16
      build/AndroidWorkarounds.props
  5. 2
      build/SharedVersion.props
  6. 11
      dirs.proj
  7. 1
      global.json
  8. 5
      nukebuild/Build.cs
  9. 19
      samples/ControlCatalog.Android/Assets/AboutAssets.txt
  10. 183
      samples/ControlCatalog.Android/ControlCatalog.Android.csproj
  11. 5
      samples/ControlCatalog.Android/MainActivity.cs
  12. 7
      samples/ControlCatalog.Android/Properties/AndroidManifest.xml
  13. 30
      samples/ControlCatalog.Android/Properties/AssemblyInfo.cs
  14. 101
      samples/ControlCatalog.Android/Resources/Resource.Designer.cs
  15. 2
      samples/ControlCatalog.Android/Resources/values/styles.xml
  16. 2
      samples/ControlCatalog.Android/SplashActivity.cs
  17. 14
      samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj
  18. 7
      samples/ControlCatalog.NetCore/rd.xml
  19. 1
      samples/ControlCatalog.Web/ControlCatalog.Web.csproj
  20. 3
      samples/ControlCatalog/MainView.xaml
  21. 23
      samples/ControlCatalog/Pages/ClipboardPage.xaml
  22. 77
      samples/ControlCatalog/Pages/ClipboardPage.xaml.cs
  23. 8
      samples/ControlCatalog/Pages/DragAndDropPage.xaml
  24. 4
      samples/ControlCatalog/Pages/DragAndDropPage.xaml.cs
  25. 2
      samples/ControlCatalog/Pages/ItemsRepeaterPage.xaml.cs
  26. 4
      src/Android/Avalonia.Android/AndroidInputMethod.cs
  27. 9
      src/Android/Avalonia.Android/AndroidPlatform.cs
  28. 14
      src/Android/Avalonia.Android/AppBuilder.cs
  29. 17
      src/Android/Avalonia.Android/Avalonia.Android.csproj
  30. 36
      src/Android/Avalonia.Android/AvaloniaActivity.cs
  31. 11
      src/Android/Avalonia.Android/AvaloniaViewModel.cs
  32. 50
      src/Android/Avalonia.Android/Resources/AboutResources.txt
  33. 6
      src/Android/Avalonia.Android/Resources/Values/Strings.xml
  34. 4
      src/Android/Avalonia.Android/SoftKeyboardListener.cs
  35. 19
      src/Android/Avalonia.AndroidTestApplication/Assets/AboutAssets.txt
  36. 172
      src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj
  37. 2
      src/Android/Avalonia.AndroidTestApplication/MainActivity.cs
  38. 6
      src/Android/Avalonia.AndroidTestApplication/Properties/AndroidManifest.xml
  39. 30
      src/Android/Avalonia.AndroidTestApplication/Properties/AssemblyInfo.cs
  40. 79
      src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs
  41. 11
      src/Android/Avalonia.AndroidTestApplication/app.config
  42. 33
      src/Avalonia.Base/Data/Core/Plugins/MethodAccessorPlugin.cs
  43. 6
      src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs
  44. 3
      src/Avalonia.Controls/Calendar/CalendarDatePicker.cs
  45. 20
      src/Avalonia.Controls/DateTimePickers/DatePicker.cs
  46. 5
      src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs
  47. 23
      src/Avalonia.Controls/DateTimePickers/TimePicker.cs
  48. 5
      src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs
  49. 20
      src/Avalonia.Controls/Primitives/SelectingItemsControl.cs
  50. 37
      src/Avalonia.Controls/Primitives/TextSearch.cs
  51. 96
      src/Avalonia.Controls/TransitioningContentControl.cs
  52. 8
      src/Avalonia.Input/MouseDevice.cs
  53. 53
      src/Avalonia.MicroCom/CallbackBase.cs
  54. 15
      src/Avalonia.MicroCom/MicroComRuntime.cs
  55. 6
      src/Avalonia.MicroCom/MicroComShadow.cs
  56. 2
      src/Avalonia.Native/AvaloniaNativeApplicationPlatform.cs
  57. 2
      src/Avalonia.Native/AvaloniaNativeDragSource.cs
  58. 2
      src/Avalonia.Native/AvaloniaNativePlatform.cs
  59. 49
      src/Avalonia.Native/CallbackBase.cs
  60. 2
      src/Avalonia.Native/DeferredFramebuffer.cs
  61. 2
      src/Avalonia.Native/IAvnMenu.cs
  62. 2
      src/Avalonia.Native/MenuActionCallback.cs
  63. 4
      src/Avalonia.Native/PlatformThreadingInterface.cs
  64. 2
      src/Avalonia.Native/PredicateCallback.cs
  65. 2
      src/Avalonia.Native/SystemDialogs.cs
  66. 2
      src/Avalonia.Native/WindowImplBase.cs
  67. 2
      src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs
  68. 1
      src/Avalonia.ReactiveUI/ReactiveUserControl.cs
  69. 1
      src/Avalonia.ReactiveUI/TransitioningContentControl.cs
  70. 20
      src/Avalonia.Themes.Default/Controls/TransitioningContentControl.xaml
  71. 1
      src/Avalonia.Themes.Default/DefaultTheme.xaml
  72. 1
      src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml
  73. 20
      src/Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml
  74. 6
      src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs
  75. 2
      src/Avalonia.Visuals/Animation/PageSlide.cs
  76. 5
      src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs
  77. 4
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompilerConfiguration.cs
  78. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs
  79. 4
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs
  80. 6
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs
  81. 202
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs
  82. 116
      src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlTrampolineBuilder.cs
  83. 2
      src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github
  84. 2
      src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj
  85. 156
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CommandAccessorPlugin.cs
  86. 48
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs
  87. 74
      src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/MethodAccessorPlugin.cs
  88. 71
      src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs
  89. 1
      src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj
  90. 18
      src/Windows/Avalonia.Win32/ClipboardFormats.cs
  91. 13
      src/Windows/Avalonia.Win32/ClipboardImpl.cs
  92. 219
      src/Windows/Avalonia.Win32/DataObject.cs
  93. 21
      src/Windows/Avalonia.Win32/DragSource.cs
  94. 84
      src/Windows/Avalonia.Win32/Interop/UnmanagedMethods.cs
  95. 4
      src/Windows/Avalonia.Win32/OleContext.cs
  96. 60
      src/Windows/Avalonia.Win32/OleDataObject.cs
  97. 7
      src/Windows/Avalonia.Win32/OleDragSource.cs
  98. 125
      src/Windows/Avalonia.Win32/OleDropTarget.cs
  99. 123
      src/Windows/Avalonia.Win32/Win32Com/win32.idl
  100. 1
      src/Windows/Avalonia.Win32/WindowImpl.cs

9
Avalonia.sln

@ -77,7 +77,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Android", "Android", "{7CF9
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Android", "src\Android\Avalonia.Android\Avalonia.Android.csproj", "{7B92AF71-6287-4693-9DCB-BD5B6E927E23}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Avalonia.AndroidTestApplication", "src\Android\Avalonia.AndroidTestApplication\Avalonia.AndroidTestApplication.csproj", "{FF69B927-C545-49AE-8E16-3D14D621AA12}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.AndroidTestApplication", "src\Android\Avalonia.AndroidTestApplication\Avalonia.AndroidTestApplication.csproj", "{FF69B927-C545-49AE-8E16-3D14D621AA12}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "iOS", "iOS", "{0CB0B92E-6CFF-4240-80A5-CCAFE75D91E1}"
EndProject
@ -95,7 +95,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog", "samples\C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Desktop", "samples\ControlCatalog.Desktop\ControlCatalog.Desktop.csproj", "{2B888490-D14A-4BCA-AB4B-48676FA93C9B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{57E0455D-D565-44BB-B069-EE1AA20F8337}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.iOS", "samples\ControlCatalog.iOS\ControlCatalog.iOS.csproj", "{57E0455D-D565-44BB-B069-EE1AA20F8337}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.DesignerSupport.Tests", "tests\Avalonia.DesignerSupport.Tests\Avalonia.DesignerSupport.Tests.csproj", "{52F55355-D120-42AC-8116-8410A7D602FA}"
EndProject
@ -107,7 +107,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Interop", "Interop", "{A0CC
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RenderDemo", "samples\RenderDemo\RenderDemo.csproj", "{F1FDC5B0-4654-416F-AE69-E3E9BBD87801}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ControlCatalog.Android", "samples\ControlCatalog.Android\ControlCatalog.Android.csproj", "{29132311-1848-4FD6-AE0C-4FF841151BD3}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.Android", "samples\ControlCatalog.Android\ControlCatalog.Android.csproj", "{29132311-1848-4FD6-AE0C-4FF841151BD3}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Avalonia.Skia", "src\Skia\Avalonia.Skia\Avalonia.Skia.csproj", "{7D2D3083-71DD-4CC9-8907-39A0D86FB322}"
EndProject
@ -115,7 +115,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ControlCatalog.NetCore", "s
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{F3AC8BC1-27F5-4255-9AFC-04ABFD11683A}"
ProjectSection(SolutionItems) = preProject
build\AndroidWorkarounds.props = build\AndroidWorkarounds.props
build\ApiDiff.props = build\ApiDiff.props
build\Base.props = build\Base.props
build\Binding.props = build\Binding.props
@ -1106,7 +1105,7 @@ Global
{57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhone.Build.0 = AppStore|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhoneSimulator.ActiveCfg = AppStore|iPhoneSimulator
{57E0455D-D565-44BB-B069-EE1AA20F8337}.AppStore|iPhoneSimulator.Build.0 = AppStore|iPhoneSimulator
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|Any CPU.ActiveCfg = Debug|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhone.ActiveCfg = Debug|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhone.Build.0 = Debug|iPhone
{57E0455D-D565-44BB-B069-EE1AA20F8337}.Debug|iPhoneSimulator.ActiveCfg = Debug|iPhoneSimulator

1
Directory.Build.props

@ -4,5 +4,6 @@
<AvaloniaPreviewerNetCoreToolPath>$(MSBuildThisFileDirectory)\src\tools\Avalonia.Designer.HostApp\bin\$(Configuration)\netcoreapp2.0\Avalonia.Designer.HostApp.dll</AvaloniaPreviewerNetCoreToolPath>
<!-- https://github.com/dotnet/msbuild/issues/2661 -->
<AddSyntheticProjectReferencesForSolutionDependencies>false</AddSyntheticProjectReferencesForSolutionDependencies>
<MSBuildEnableWorkloadResolver>false</MSBuildEnableWorkloadResolver>
</PropertyGroup>
</Project>

9
azure-pipelines.yml

@ -1,6 +1,3 @@
variables:
MSBuildEnableWorkloadResolver: 'false'
jobs:
- job: GetPRNumber
@ -146,6 +143,12 @@ jobs:
inputs:
version: 6.0.100
- task: CmdLine@2
displayName: 'Install Workloads'
inputs:
script: |
dotnet workload install android
- task: CmdLine@2
displayName: 'Install Nuke'
inputs:

16
build/AndroidWorkarounds.props

@ -1,16 +0,0 @@
<Project>
<ItemGroup Condition="'$(AndroidApplication)' == 'true'">
<!-- WORKAROUND: The packages below are transitively referenced by System.Memory,
but Xamarin.Android applications need the newest versions directly referenced for the linker. -->
<PackageReference Include="System.Runtime.CompilerServices.Unsafe" Version="4.6.0" />
<PackageReference Include="System.Buffers" Version="4.5.0" />
</ItemGroup>
<Target Name="_RemoveNonExistingResgenFile" BeforeTargets="CoreCompile" Condition="'$(_SdkSetAndroidResgenFile)' == 'true' And '$(AndroidResgenFile)' != '' And !Exists('$(AndroidResgenFile)')">
<ItemGroup>
<Compile Remove="$(AndroidResgenFile)"/>
</ItemGroup>
</Target>
<PropertyGroup>
<DesignTimeBuild>false</DesignTimeBuild>
</PropertyGroup>
</Project>

2
build/SharedVersion.props

@ -11,7 +11,7 @@
<LangVersion>latest</LangVersion>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageIcon>Icon.png</PackageIcon>
<PackageDescription>Avalonia is a WPF/UWP-inspired cross-platform XAML-based UI framework providing a flexible styling system and supporting a wide range of Operating Systems such as Windows (.NET Framework, .NET Core), Linux (via Xorg), MacOS and with experimental support for Android and iOS.</PackageDescription>
<PackageDescription>Avalonia is a cross-platform UI framework for .NET providing a flexible styling system and supporting a wide range of Operating Systems such as Windows, Linux, MacOS and with experimental support for Android, iOS and WebAssembly.</PackageDescription>
<PackageTags>avalonia;avaloniaui;mvvm;rx;reactive extensions;android;ios;mac;forms;wpf;net;netstandard;net461;uwp;xamarin</PackageTags>
<PackageReleaseNotes>https://github.com/AvaloniaUI/Avalonia/releases</PackageReleaseNotes>
<RepositoryType>git</RepositoryType>

11
dirs.proj

@ -1,5 +1,7 @@
<Project Sdk="Microsoft.Build.Traversal">
<ItemGroup>
<!-- Build Avalonia.Build.Tasks first because everything depends on it -->
<ProjectReference Include="src/Avalonia.Build.Tasks/Avalonia.Build.Tasks.csproj" />
<ProjectReference Include="src/**/*.*proj" />
<ProjectReference Include="samples/**/*.*proj" />
<ProjectReference Include="tests/**/*.*proj" />
@ -8,11 +10,8 @@
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml/PortableXaml/**/*.*proj" />
<ProjectReference Remove="src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github/**/*.*proj" />
<ProjectReference Remove="tests/Avalonia.ReactiveUI.Events.UnitTests/Avalonia.ReactiveUI.Events.UnitTests.csproj" />
</ItemGroup>
<!--<ItemGroup Condition="!Exists('$(MSBuildExtensionsPath)\Xamarin\Android')">-->
<ItemGroup>
<ProjectReference Remove="src/Android/**/*.*proj" />
<ProjectReference Remove="samples/ControlCatalog.Android/ControlCatalog.Android.csproj" />
<ProjectReference Remove="src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj" />
</ItemGroup>
<ItemGroup Condition="!Exists('$(MSBuildExtensionsPath)\Xamarin\iOS')">
<ProjectReference Remove="src/iOS/**/*.*proj" />
@ -23,6 +22,10 @@
<ProjectReference Remove="samples/interop/**/*.*proj" />
<ProjectReference Remove="samples/ControlCatalog.Desktop/*.*proj" />
</ItemGroup>
<!-- Build android projects only on Windows, where we have installed android workload -->
<ItemGroup Condition="!$([MSBuild]::IsOsPlatform('Windows'))">
<ProjectReference Remove="src/Android/**/*.*proj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SlnGen" Version="2.0.40" PrivateAssets="all" />

1
global.json

@ -4,6 +4,7 @@
},
"msbuild-sdks": {
"Microsoft.Build.Traversal": "1.0.43",
"Xamarin.Legacy.Sdk": "0.1.2-alpha6",
"MSBuild.Sdk.Extras": "3.0.22",
"AggregatePackage.NuGet.Sdk" : "0.1.12"
}

5
nukebuild/Build.cs

@ -87,7 +87,8 @@ partial class Build : NukeBuild
Console.WriteLine(preamble);
Process.Start(new ProcessStartInfo(command, args) {UseShellExecute = false}).WaitForExit();
}
ExecWait("dotnet version:", "dotnet", "--version");
ExecWait("dotnet version:", "dotnet", "--info");
ExecWait("dotnet workloads:", "dotnet", "workload list");
}
IReadOnlyCollection<Output> MsBuildCommon(
@ -99,7 +100,7 @@ partial class Build : NukeBuild
// This is required for VS2019 image on Azure Pipelines
.When(Parameters.IsRunningOnWindows &&
Parameters.IsRunningOnAzure, _ => _
.AddProperty("JavaSdkDirectory", GetVariable<string>("JAVA_HOME_8_X64")))
.AddProperty("JavaSdkDirectory", GetVariable<string>("JAVA_HOME_11_X64")))
.AddProperty("PackageVersion", Parameters.Version)
.AddProperty("iOSRoslynPathHackRequired", true)
.SetProcessToolPath(MsBuildExe.Value)

19
samples/ControlCatalog.Android/Assets/AboutAssets.txt

@ -1,19 +0,0 @@
Any raw assets you want to be deployed with your application can be placed in
this directory (and child directories) and given a Build Action of "AndroidAsset".
These files will be deployed with your package and will be accessible using Android's
AssetManager, like this:
public class ReadAsset : Activity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
InputStream input = Assets.Open ("my_asset.txt");
}
}
Additionally, some Android functions will automatically load asset files:
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");

183
samples/ControlCatalog.Android/ControlCatalog.Android.csproj

@ -1,165 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{29132311-1848-4FD6-AE0C-4FF841151BD3}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ControlCatalog.Android</RootNamespace>
<AssemblyName>ControlCatalog.Android</AssemblyName>
<FileAlignment>512</FileAlignment>
<AndroidApplication>true</AndroidApplication>
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v11.0</TargetFrameworkVersion>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<TargetFramework>net6.0-android</TargetFramework>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<ApplicationId>com.Avalonia.ControlCatalog</ApplicationId>
<ApplicationVersion>1</ApplicationVersion>
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<AndroidPackageFormat>apk</AndroidPackageFormat>
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>True</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
<AndroidLinkMode>None</AndroidLinkMode>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
<AndroidSupportedAbis>armeabi-v7a;x86;x86_64</AndroidSupportedAbis>
<Debugger>Xamarin</Debugger>
<AndroidEnableMultiDex>False</AndroidEnableMultiDex>
<AotAssemblies>False</AotAssemblies>
<EnableLLVM>False</EnableLLVM>
<EnableProguard>False</EnableProguard>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
<AndroidLinkMode>Full</AndroidLinkMode>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
<AndroidSupportedAbis>armeabi-v7a,x86;x86_64</AndroidSupportedAbis>
<Debugger>Xamarin</Debugger>
<AotAssemblies>False</AotAssemblies>
<EnableLLVM>False</EnableLLVM>
<AndroidEnableMultiDex>False</AndroidEnableMultiDex>
<EnableProguard>False</EnableProguard>
<DebugSymbols>False</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Android" />
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="MainActivity.cs" />
<Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SplashActivity.cs" />
</ItemGroup>
<ItemGroup>
<None Include="Resources\AboutResources.txt" />
<None Include="Assets\AboutAssets.txt" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\splash_screen.xml" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\values\colors.xml" />
<AndroidResource Include="Resources\values\styles.xml" />
<None Remove="Assets\AboutAssets.txt" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="..\..\build\Assets\Icon.png">
<Link>Resources\drawable\Icon.png</Link>
</AndroidResource>
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release' and '$(TF_BUILD)' == ''">
<DebugSymbols>True</DebugSymbols>
<RunAOTCompilation>False</RunAOTCompilation>
<EnableLLVM>True</EnableLLVM>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<EmbedAssembliesIntoApk>False</EmbedAssembliesIntoApk>
<RunAOTCompilation>False</RunAOTCompilation>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
</PropertyGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.3" />
<PackageReference Include="Xamarin.AndroidX.Lifecycle.ViewModel" Version="2.3.1.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Android\Avalonia.Android\Avalonia.Android.csproj">
<Project>{7B92AF71-6287-4693-9DCB-BD5B6E927E23}</Project>
<Name>Avalonia.Android</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Animation\Avalonia.Animation.csproj">
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
<Name>Avalonia.Animation</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Base\Avalonia.Base.csproj">
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project>
<Name>Avalonia.Base</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Controls\Avalonia.Controls.csproj">
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
<Name>Avalonia.Controls</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj">
<Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project>
<Name>Avalonia.Diagnostics</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Input\Avalonia.Input.csproj">
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
<Name>Avalonia.Input</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj">
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project>
<Name>Avalonia.Interactivity</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Layout\Avalonia.Layout.csproj">
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
<Name>Avalonia.Layout</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Themes.Fluent\Avalonia.Themes.Fluent.csproj">
<Project>{c42d2fc1-a531-4ed4-84b9-89aec7c962fc}</Project>
<Name>Avalonia.Themes.Fluent</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Visuals\Avalonia.Visuals.csproj">
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
<Name>Avalonia.Visuals</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Styling\Avalonia.Styling.csproj">
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
<Name>Avalonia.Styling</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj">
<Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project>
<Name>Avalonia.Themes.Default</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj">
<Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project>
<Name>Avalonia.Markup.Xaml</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Markup\Avalonia.Markup\Avalonia.Markup.csproj">
<Project>{6417e941-21bc-467b-a771-0de389353ce6}</Project>
<Name>Avalonia.Markup</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Skia\Avalonia.Skia\Avalonia.Skia.csproj">
<Project>{7d2d3083-71dd-4cc9-8907-39a0d86fb322}</Project>
<Name>Avalonia.Skia</Name>
</ProjectReference>
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj">
<Project>{d0a739b9-3c68-4ba6-a328-41606954b6bd}</Project>
<Name>ControlCatalog</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\Android\Avalonia.Android\Avalonia.Android.csproj" />
<ProjectReference Include="..\ControlCatalog\ControlCatalog.csproj" />
</ItemGroup>
<Import Project="..\..\build\Rx.props" />
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<Import Project="..\..\build\AndroidWorkarounds.props" />
<Import Project="..\..\build\LegacyProject.targets" />
</Project>

5
samples/ControlCatalog.Android/MainActivity.cs

@ -5,10 +5,10 @@ using Avalonia.Android;
namespace ControlCatalog.Android
{
[Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance)]
[Activity(Label = "ControlCatalog.Android", Theme = "@style/MyTheme.NoActionBar", Icon = "@drawable/icon", LaunchMode = LaunchMode.SingleInstance, ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize)]
public class MainActivity : AvaloniaActivity
{
protected override void OnCreate(Bundle savedInstanceState)
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
@ -16,4 +16,3 @@ namespace ControlCatalog.Android
}
}
}

7
samples/ControlCatalog.Android/Properties/AndroidManifest.xml

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ControlCatalog.Android" android:versionCode="1" android:versionName="1.0" android:installLocation="auto">
<uses-sdk android:targetSdkVersion="30" />
<application android:label="ControlCatalog.Android"></application>
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="auto">
<application android:label="ControlCatalog.Android" android:icon="@drawable/Icon"></application>
</manifest>

30
samples/ControlCatalog.Android/Properties/AssemblyInfo.cs

@ -1,30 +0,0 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Android.App;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("ControlCatalog.Android")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("ControlCatalog.Android")]
[assembly: AssemblyCopyright("Copyright © 2016")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

101
samples/ControlCatalog.Android/Resources/Resource.Designer.cs

@ -1,101 +0,0 @@
#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[assembly: global::Android.Runtime.ResourceDesignerAttribute("ControlCatalog.Android.Resource", IsApplication=true)]
namespace ControlCatalog.Android
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")]
public partial class Resource
{
static Resource()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
public static void UpdateIdValues()
{
}
public partial class Attribute
{
static Attribute()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Attribute()
{
}
}
public partial class Color
{
// aapt resource value: 0x7F010000
public const int splash_background = 2130771968;
static Color()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Color()
{
}
}
public partial class Drawable
{
// aapt resource value: 0x7F020000
public const int Icon = 2130837504;
// aapt resource value: 0x7F020001
public const int splash_screen = 2130837505;
static Drawable()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Drawable()
{
}
}
public partial class Style
{
// aapt resource value: 0x7F030000
public const int MyTheme = 2130903040;
// aapt resource value: 0x7F030001
public const int MyTheme_NoActionBar = 2130903041;
// aapt resource value: 0x7F030002
public const int MyTheme_Splash = 2130903042;
static Style()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Style()
{
}
}
}
}
#pragma warning restore 1591

2
samples/ControlCatalog.Android/Resources/values/styles.xml

@ -4,7 +4,7 @@
<style name="MyTheme">
</style>
<style name="MyTheme.NoActionBar">
<style name="MyTheme.NoActionBar" parent="@style/Theme.AppCompat.NoActionBar">
<item name="android:windowActionBar">false</item>
<item name="android:windowNoTitle">true</item>
</style>

2
samples/ControlCatalog.Android/SplashActivity.cs

@ -10,7 +10,7 @@ namespace ControlCatalog.Android
[Activity(Theme = "@style/MyTheme.Splash", MainLauncher = true, NoHistory = true)]
public class SplashActivity : Activity
{
protected override void OnCreate(Bundle savedInstanceState)
protected override void OnCreate(Bundle? savedInstanceState)
{
base.OnCreate(savedInstanceState);
}

14
samples/ControlCatalog.NetCore/ControlCatalog.NetCore.csproj

@ -6,6 +6,12 @@
<TargetLatestRuntimePatch>true</TargetLatestRuntimePatch>
</PropertyGroup>
<PropertyGroup Condition="'$(RunAotCompilation)' == 'true'">
<IlcTrimMetadata>true</IlcTrimMetadata>
<RestoreAdditionalProjectSources>https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet7/nuget/v3/index.json</RestoreAdditionalProjectSources>
<NativeAotCompilerVersion>7.0.0-*</NativeAotCompilerVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Avalonia.Headless.Vnc\Avalonia.Headless.Vnc.csproj" />
<ProjectReference Include="..\..\src\Avalonia.Dialogs\Avalonia.Dialogs.csproj" />
@ -15,6 +21,14 @@
<PackageReference Include="Avalonia.Angle.Windows.Natives" Version="2.1.0.2020091801" />
</ItemGroup>
<ItemGroup Condition="'$(RunAotCompilation)' == 'true'">
<PackageReference Include="Microsoft.DotNet.ILCompiler" Version="$(NativeAotCompilerVersion)" />
<!-- Cross-compilation for Windows x64-arm64 and Linux x64-arm64 -->
<PackageReference Condition="'$(RuntimeIdentifier)'=='win-arm64'" Include="runtime.win-x64.Microsoft.DotNet.ILCompiler" Version="$(NativeAotCompilerVersion)" />
<PackageReference Condition="'$(RuntimeIdentifier)'=='linux-arm64'" Include="runtime.linux-x64.Microsoft.DotNet.ILCompiler" Version="$(NativeAotCompilerVersion)" />
<RdXmlFile Include="rd.xml" />
</ItemGroup>
<PropertyGroup>
<!-- For Microsoft.CodeAnalysis -->
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>

7
samples/ControlCatalog.NetCore/rd.xml

@ -0,0 +1,7 @@
<Directives>
<Application>
<Assembly Name="ControlCatalog" Dynamic="Required All"></Assembly>
<Assembly Name="Avalonia.Themes.Default" Dynamic="Required All"></Assembly>
<Assembly Name="Avalonia.Themes.Fluent" Dynamic="Required All"></Assembly>
</Application>
</Directives>

1
samples/ControlCatalog.Web/ControlCatalog.Web.csproj

@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<MSBuildEnableWorkloadResolver>false</MSBuildEnableWorkloadResolver>
<Nullable>enable</Nullable>
<WasmBuildNative>True</WasmBuildNative>
</PropertyGroup>

3
samples/ControlCatalog/MainView.xaml

@ -40,6 +40,9 @@
<TabItem Header="CheckBox">
<pages:CheckBoxPage />
</TabItem>
<TabItem Header="Clipboard">
<pages:ClipboardPage />
</TabItem>
<TabItem Header="ComboBox">
<pages:ComboBoxPage />
</TabItem>

23
samples/ControlCatalog/Pages/ClipboardPage.xaml

@ -0,0 +1,23 @@
<UserControl x:Class="ControlCatalog.Pages.ClipboardPage"
xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<StackPanel Orientation="Vertical" Spacing="4">
<TextBlock Classes="h2">Example of ClipboardPage capabilities</TextBlock>
<Button Click="CopyText" Content="Copy text to clipboard" />
<Button Click="PasteText" Content="Paste text from clipboard" />
<Button Click="CopyTextDataObject" Content="Copy text to clipboard (data object)" />
<Button Click="PasteTextDataObject" Content="Paste text from clipboard (data object)" />
<Button Click="CopyFilesDataObject" Content="Copy files to clipboard (data object)" />
<Button Click="PasteFilesDataObject" Content="Paste files from clipboard (data object)" />
<Button Click="GetFormats" Content="Get clipboard formats" />
<Button Click="Clear" Content="Clear clipboard" />
<TextBox x:Name="ClipboardContent"
MinHeight="100"
AcceptsReturn="True"
Watermark="Text to copy of file names per line" />
</StackPanel>
</UserControl>

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

@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using Avalonia;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Markup.Xaml;
namespace ControlCatalog.Pages
{
public partial class ClipboardPage : UserControl
{
public ClipboardPage()
{
InitializeComponent();
}
private TextBox ClipboardContent => this.Get<TextBox>("ClipboardContent");
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private async void CopyText(object sender, RoutedEventArgs args)
{
await Application.Current.Clipboard.SetTextAsync(ClipboardContent.Text);
}
private async void PasteText(object sender, RoutedEventArgs args)
{
ClipboardContent.Text = await Application.Current.Clipboard.GetTextAsync();
}
private async void CopyTextDataObject(object sender, RoutedEventArgs args)
{
var dataObject = new DataObject();
dataObject.Set(DataFormats.Text, ClipboardContent.Text ?? string.Empty);
await Application.Current.Clipboard.SetDataObjectAsync(dataObject);
}
private async void PasteTextDataObject(object sender, RoutedEventArgs args)
{
ClipboardContent.Text = await Application.Current.Clipboard.GetDataAsync(DataFormats.Text) as string ?? string.Empty;
}
private async void CopyFilesDataObject(object sender, RoutedEventArgs args)
{
var files = ClipboardContent.Text.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries);
if (files.Length == 0)
{
return;
}
var dataObject = new DataObject();
dataObject.Set(DataFormats.FileNames, files);
await Application.Current.Clipboard.SetDataObjectAsync(dataObject);
}
private async void PasteFilesDataObject(object sender, RoutedEventArgs args)
{
var fiels = await Application.Current.Clipboard.GetDataAsync(DataFormats.FileNames) as IEnumerable<string>;
ClipboardContent.Text = fiels != null ? string.Join(Environment.NewLine, fiels) : string.Empty;
}
private async void GetFormats(object sender, RoutedEventArgs args)
{
var formats = await Application.Current.Clipboard.GetFormatsAsync();
ClipboardContent.Text = string.Join(Environment.NewLine, formats);
}
private async void Clear(object sender, RoutedEventArgs args)
{
await Application.Current.Clipboard.ClearAsync();
}
}
}

8
samples/ControlCatalog/Pages/DragAndDropPage.xaml

@ -11,7 +11,13 @@
Padding="16"
BorderBrush="{DynamicResource SystemAccentColor}"
BorderThickness="2">
<TextBlock Name="DragStateText" TextWrapping="Wrap">Drag Me</TextBlock>
<TextBlock Name="DragStateText" TextWrapping="Wrap">Drag Me (text)</TextBlock>
</Border>
<Border Name="DragMeFiles"
Padding="16"
BorderBrush="{DynamicResource SystemAccentColor}"
BorderThickness="2">
<TextBlock Name="DragStateFiles" TextWrapping="Wrap">Drag Me (files)</TextBlock>
</Border>
<Border Name="DragMeCustom"
Padding="16"

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

@ -2,11 +2,8 @@
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
namespace ControlCatalog.Pages
{
@ -24,6 +21,7 @@ namespace ControlCatalog.Pages
$"Text was dragged {++textCount} times"), DragDropEffects.Copy | DragDropEffects.Move | DragDropEffects.Link);
SetupDnd("Custom", d => d.Set(CustomFormat, "Test123"), DragDropEffects.Move);
SetupDnd("Files", d => d.Set(DataFormats.FileNames, new[] { Assembly.GetEntryAssembly()?.GetModules().FirstOrDefault()?.FullyQualifiedName }), DragDropEffects.Copy);
}
void SetupDnd(string suffix, Action<DataObject> factory, DragDropEffects effects)

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

@ -111,7 +111,7 @@ namespace ControlCatalog.Pages
private void ScrollTo(int index)
{
System.Diagnostics.Debug.WriteLine("Scroll to " + index);
var layoutManager = ((Window)this.GetVisualRoot()).LayoutManager;
var layoutManager = ((TopLevel)VisualRoot).LayoutManager;
var element = _repeater.GetOrCreateElement(index);
layoutManager.ExecuteLayoutPass();
element.BringIntoView();

4
src/Android/Avalonia.Android/AndroidInputMethod.cs

@ -25,7 +25,7 @@ namespace Avalonia.Android
_host.Focusable = true;
_host.FocusableInTouchMode = true;
_host.ViewTreeObserver.AddOnGlobalLayoutListener(new SoftKeyboardListner(_host));
_host.ViewTreeObserver.AddOnGlobalLayoutListener(new SoftKeyboardListener(_host));
}
public void Reset()
@ -83,6 +83,8 @@ namespace Avalonia.Android
if (options.Multiline)
outAttrs.InputType |= global::Android.Text.InputTypes.TextFlagMultiLine;
outAttrs.ImeOptions |= ImeFlags.NoFullscreen | ImeFlags.NoExtractUi;
});
//_inputElement.PointerReleased += RestoreSoftKeyboard;

9
src/Android/Avalonia.Android/AndroidPlatform.cs

@ -10,7 +10,6 @@ using Avalonia.Input.Platform;
using Avalonia.OpenGL.Egl;
using Avalonia.Platform;
using Avalonia.Rendering;
using Avalonia.PlatformSupport;
using Avalonia.Skia;
namespace Avalonia
@ -20,9 +19,9 @@ namespace Avalonia
public static T UseAndroid<T>(this T builder) where T : AppBuilderBase<T>, new()
{
var options = AvaloniaLocator.Current.GetService<AndroidPlatformOptions>() ?? new AndroidPlatformOptions();
builder.UseWindowingSubsystem(() => AndroidPlatform.Initialize(builder.ApplicationType, options), "Android");
builder.UseSkia();
return builder;
return builder
.UseWindowingSubsystem(() => AndroidPlatform.Initialize(options), "Android")
.UseSkia();
}
}
}
@ -44,7 +43,7 @@ namespace Avalonia.Android
public TimeSpan DoubleClickTime => TimeSpan.FromMilliseconds(500);
public static void Initialize(Type appType, AndroidPlatformOptions options)
public static void Initialize(AndroidPlatformOptions options)
{
Options = options;

14
src/Android/Avalonia.Android/AppBuilder.cs

@ -1,14 +0,0 @@
using Avalonia.Controls;
using Avalonia.PlatformSupport;
namespace Avalonia
{
public sealed class AppBuilder : AppBuilderBase<AppBuilder>
{
public AppBuilder() : base(new StandardRuntimePlatform(),
builder => StandardRuntimePlatformServices.Register(builder.Instance?.GetType()?.Assembly))
{
}
}
}

17
src/Android/Avalonia.Android/Avalonia.Android.csproj

@ -1,15 +1,18 @@
<Project Sdk="MSBuild.Sdk.Extras">
<Project Sdk="Xamarin.Legacy.Sdk">
<PropertyGroup>
<TargetFramework>monoandroid11.0</TargetFramework>
<TargetFrameworks>net6.0-android;monoandroid11.0</TargetFrameworks>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
<DebugType>portable</DebugType>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\..\Avalonia.PlatformSupport\Avalonia.PlatformSupport.csproj">
<SetTargetFramework>TargetFramework=netstandard2.0</SetTargetFramework>
</ProjectReference>
<PackageReference Include="Xamarin.AndroidX.AppCompat" Version="1.3.1.3" />
<PackageReference Include="Xamarin.AndroidX.Lifecycle.ViewModel" Version="2.3.1.3" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Avalonia.Base\Avalonia.Base.csproj" />
<ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj" />
</ItemGroup>
<Import Project="..\..\..\build\Rx.props" />
<Import Project="..\..\..\build\AndroidWorkarounds.props" />
</Project>

36
src/Android/Avalonia.Android/AvaloniaActivity.cs

@ -1,35 +1,55 @@
using Android.App;
using Android.OS;
using Android.Views;
using Android.Content.PM;
using AndroidX.AppCompat.App;
using Android.Content.Res;
using AndroidX.Lifecycle;
namespace Avalonia.Android
{
public abstract class AvaloniaActivity : Activity
public abstract class AvaloniaActivity : AppCompatActivity
{
internal AvaloniaView View;
object _content;
internal AvaloniaViewModel _viewModel;
protected override void OnCreate(Bundle savedInstanceState)
{
View = new AvaloniaView(this);
if (_content != null)
View.Content = _content;
SetContentView(View);
_viewModel = new ViewModelProvider(this).Get(Java.Lang.Class.FromType(typeof(AvaloniaViewModel))) as AvaloniaViewModel;
if (_viewModel.Content != null)
{
View.Content = _viewModel.Content;
}
base.OnCreate(savedInstanceState);
}
public object Content
{
get
{
return _content;
return _viewModel.Content;
}
set
{
_content = value;
_viewModel.Content = value;
if (View != null)
View.Content = value;
}
}
public override void OnConfigurationChanged(Configuration newConfig)
{
base.OnConfigurationChanged(newConfig);
}
protected override void OnDestroy()
{
View.Content = null;
base.OnDestroy();
}
}
}

11
src/Android/Avalonia.Android/AvaloniaViewModel.cs

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Avalonia.Android
{
internal class AvaloniaViewModel : AndroidX.Lifecycle.ViewModel
{
public object Content { get; set; }
}
}

50
src/Android/Avalonia.Android/Resources/AboutResources.txt

@ -1,50 +0,0 @@
Images, layout descriptions, binary blobs and string dictionaries can be included
in your application as resource files. Various Android APIs are designed to
operate on the resource IDs instead of dealing with images, strings or binary blobs
directly.
For example, a sample Android app that contains a user interface layout (main.xml),
an internationalization string table (strings.xml) and some icons (drawable-XXX/icon.png)
would keep its resources in the "Resources" directory of the application:
Resources/
drawable-hdpi/
icon.png
drawable-ldpi/
icon.png
drawable-mdpi/
icon.png
layout/
main.xml
values/
strings.xml
In order to get the build system to recognize Android resources, set the build action to
"AndroidResource". The native Android APIs do not operate directly with filenames, but
instead operate on resource IDs. When you compile an Android application that uses resources,
the build system will package the resources for distribution and generate a class called
"Resource" that contains the tokens for each one of the resources included. For example,
for the above Resources layout, this is what the Resource class would expose:
public class Resource {
public class drawable {
public const int icon = 0x123;
}
public class layout {
public const int main = 0x456;
}
public class strings {
public const int first_string = 0xabc;
public const int second_string = 0xbcd;
}
}
You would then use R.drawable.icon to reference the drawable/icon.png file, or Resource.layout.main
to reference the layout/main.xml file, or Resource.strings.first_string to reference the first
string in the dictionary file values/strings.xml.

6
src/Android/Avalonia.Android/Resources/Values/Strings.xml

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="Hello">Hello World, Click Me!</string>
<string name="ApplicationName">$projectname$</string>
</resources>

4
src/Android/Avalonia.Android/SoftKeyboardListner.cs → src/Android/Avalonia.Android/SoftKeyboardListener.cs

@ -9,7 +9,7 @@ using Avalonia.Input;
namespace Avalonia.Android
{
class SoftKeyboardListner : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
class SoftKeyboardListener : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
{
private const int DefaultKeyboardHeightDP = 100;
private static readonly int EstimatedKeyboardDP = DefaultKeyboardHeightDP + (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop ? 48 : 0);
@ -17,7 +17,7 @@ namespace Avalonia.Android
private readonly View _host;
private bool _wasKeyboard;
public SoftKeyboardListner(View view)
public SoftKeyboardListener(View view)
{
_host = view;
}

19
src/Android/Avalonia.AndroidTestApplication/Assets/AboutAssets.txt

@ -1,19 +0,0 @@
Any raw assets you want to be deployed with your application can be placed in
this directory (and child directories) and given a Build Action of "AndroidAsset".
These files will be deployed with you package and will be accessible using Android's
AssetManager, like this:
public class ReadAsset : Activity
{
protected override void OnCreate (Bundle bundle)
{
base.OnCreate (bundle);
InputStream input = Assets.Open ("my_asset.txt");
}
}
Additionally, some Android functions will automatically load asset files:
Typeface tf = Typeface.CreateFromAsset (Context.Assets, "fonts/samplefont.ttf");

172
src/Android/Avalonia.AndroidTestApplication/Avalonia.AndroidTestApplication.csproj

@ -1,153 +1,37 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{FF69B927-C545-49AE-8E16-3D14D621AA12}</ProjectGuid>
<ProjectTypeGuids>{EFBA0AD7-5A72-4C68-AF49-83D382785DCF};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Avalonia.AndroidTestApplication</RootNamespace>
<AssemblyName>Avalonia.AndroidTestApplication</AssemblyName>
<FileAlignment>512</FileAlignment>
<AndroidApplication>true</AndroidApplication>
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>False</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v11.0</TargetFrameworkVersion>
<AndroidManifest>Properties\AndroidManifest.xml</AndroidManifest>
<TargetFramework>net6.0-android</TargetFramework>
<SupportedOSPlatformVersion>21</SupportedOSPlatformVersion>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
<ApplicationId>com.Avalonia.AndroidTestApplication</ApplicationId>
<ApplicationVersion>1</ApplicationVersion>
<ApplicationDisplayVersion>1.0</ApplicationDisplayVersion>
<AndroidPackageFormat>apk</AndroidPackageFormat>
<MSBuildEnableWorkloadResolver>true</MSBuildEnableWorkloadResolver>
<DebugType>portable</DebugType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PropertyGroup Condition="'$(Configuration)'=='Release' and '$(TF_BUILD)' == ''">
<DebugSymbols>True</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>True</AndroidUseSharedRuntime>
<AndroidLinkMode>None</AndroidLinkMode>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
<AndroidSupportedAbis>armeabi-v7a;x86</AndroidSupportedAbis>
<Debugger>Xamarin</Debugger>
<AndroidEnableMultiDex>False</AndroidEnableMultiDex>
<DevInstrumentationEnabled>True</DevInstrumentationEnabled>
<AotAssemblies>False</AotAssemblies>
<EnableLLVM>False</EnableLLVM>
<EnableProguard>False</EnableProguard>
<RunAOTCompilation>True</RunAOTCompilation>
<EnableLLVM>True</EnableLLVM>
<!--<AndroidEnableProfiledAot>True</AndroidEnableProfiledAot>-->
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<AndroidUseSharedRuntime>False</AndroidUseSharedRuntime>
<AndroidLinkMode>Full</AndroidLinkMode>
<EmbedAssembliesIntoApk>True</EmbedAssembliesIntoApk>
<BundleAssemblies>False</BundleAssemblies>
<AndroidCreatePackagePerAbi>False</AndroidCreatePackagePerAbi>
<AndroidSupportedAbis>armeabi-v7a,x86</AndroidSupportedAbis>
<Debugger>Xamarin</Debugger>
<AotAssemblies>False</AotAssemblies>
<EnableLLVM>False</EnableLLVM>
<AndroidEnableMultiDex>False</AndroidEnableMultiDex>
<EnableProguard>False</EnableProguard>
<DevInstrumentationEnabled>False</DevInstrumentationEnabled>
<DebugSymbols>False</DebugSymbols>
</PropertyGroup>
<ItemGroup>
<Reference Include="Mono.Android" />
<Reference Include="mscorlib" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="MainActivity.cs" />
<Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="Resources\AboutResources.txt" />
<None Include="Assets\AboutAssets.txt" />
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\values\Strings.xml">
<SubType>Designer</SubType>
</AndroidResource>
</ItemGroup>
<ItemGroup>
<AndroidResource Include="Resources\drawable\Icon.png" />
</ItemGroup>
<ItemGroup>
<None Include="Properties\AndroidManifest.xml" />
<None Remove="Assets\AboutAssets.txt" />
</ItemGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
<BundleAssemblies>True</BundleAssemblies>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<BundleAssemblies>True</BundleAssemblies>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\Android\Avalonia.Android\Avalonia.Android.csproj">
<Project>{7b92af71-6287-4693-9dcb-bd5b6e927e23}</Project>
<Name>Avalonia.Android</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Markup\Avalonia.Markup.Xaml\Avalonia.Markup.Xaml.csproj">
<Project>{3e53a01a-b331-47f3-b828-4a5717e77a24}</Project>
<Name>Avalonia.Markup.Xaml</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Animation\Avalonia.Animation.csproj">
<Project>{d211e587-d8bc-45b9-95a4-f297c8fa5200}</Project>
<Name>Avalonia.Animation</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Base\Avalonia.Base.csproj">
<Project>{b09b78d8-9b26-48b0-9149-d64a2f120f3f}</Project>
<Name>Avalonia.Base</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Controls\Avalonia.Controls.csproj">
<Project>{d2221c82-4a25-4583-9b43-d791e3f6820c}</Project>
<Name>Avalonia.Controls</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Diagnostics\Avalonia.Diagnostics.csproj">
<Project>{7062ae20-5dcc-4442-9645-8195bdece63e}</Project>
<Name>Avalonia.Diagnostics</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Input\Avalonia.Input.csproj">
<Project>{62024b2d-53eb-4638-b26b-85eeaa54866e}</Project>
<Name>Avalonia.Input</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Interactivity\Avalonia.Interactivity.csproj">
<Project>{6b0ed19d-a08b-461c-a9d9-a9ee40b0c06b}</Project>
<Name>Avalonia.Interactivity</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Layout\Avalonia.Layout.csproj">
<Project>{42472427-4774-4c81-8aff-9f27b8e31721}</Project>
<Name>Avalonia.Layout</Name>
</ProjectReference>
<ProjectReference Include="..\..\Avalonia.Visuals\Avalonia.Visuals.csproj">
<Project>{eb582467-6abb-43a1-b052-e981ba910e3a}</Project>
<Name>Avalonia.Visuals</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Styling\Avalonia.Styling.csproj">
<Project>{f1baa01a-f176-4c6a-b39d-5b40bb1b148f}</Project>
<Name>Avalonia.Styling</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\src\Avalonia.Themes.Default\Avalonia.Themes.Default.csproj">
<Project>{3e10a5fa-e8da-48b1-ad44-6a5b6cb7750f}</Project>
<Name>Avalonia.Themes.Default</Name>
</ProjectReference>
<ProjectReference Include="..\..\Skia\Avalonia.Skia\Avalonia.Skia.csproj">
<Project>{7d2d3083-71dd-4cc9-8907-39a0d86fb322}</Project>
<Name>Avalonia.Skia</Name>
</ProjectReference>
<ProjectReference Include="..\..\..\packages\Avalonia\Avalonia.csproj" />
<ProjectReference Include="..\Avalonia.Android\Avalonia.Android.csproj" />
</ItemGroup>
<Import Project="$(MSBuildExtensionsPath)\Xamarin\Android\Xamarin.Android.CSharp.targets" />
<Import Project="..\..\..\build\Base.props" />
<Import Project="..\..\..\build\Rx.props" />
<Import Project="..\..\..\build\System.Memory.props" />
<Import Project="..\..\..\build\AndroidWorkarounds.props" />
<Import Project="..\..\..\build\LegacyProject.targets" />
</Project>

2
src/Android/Avalonia.AndroidTestApplication/MainActivity.cs

@ -14,6 +14,8 @@ namespace Avalonia.AndroidTestApplication
[Activity(Label = "Main",
MainLauncher = true,
Icon = "@drawable/icon",
Theme = "@style/Theme.AppCompat.NoActionBar",
ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.ScreenSize,
LaunchMode = LaunchMode.SingleInstance/*,
ScreenOrientation = ScreenOrientation.Landscape*/)]
public class MainBaseActivity : AvaloniaActivity

6
src/Android/Avalonia.AndroidTestApplication/Properties/AndroidManifest.xml

@ -1,6 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="Avalonia.AndroidTestApplication" android:versionCode="1" android:versionName="1.0" android:installLocation="auto">
<uses-sdk android:targetSdkVersion="30" />
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application android:label="Avalonia.AndroidTestApplication" android:icon="@drawable/Icon" android:hardwareAccelerated="true"></application>
<uses-permission android:name="android.permission.INTERNET" />
</manifest>
</manifest>

30
src/Android/Avalonia.AndroidTestApplication/Properties/AssemblyInfo.cs

@ -1,30 +0,0 @@
using System.Reflection;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Avalonia.AndroidTestApplication")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Avalonia.AndroidTestApplication")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

79
src/Android/Avalonia.AndroidTestApplication/Resources/Resource.Designer.cs

@ -1,79 +0,0 @@
#pragma warning disable 1591
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
[assembly: global::Android.Runtime.ResourceDesignerAttribute("Avalonia.AndroidTestApplication.Resource", IsApplication=true)]
namespace Avalonia.AndroidTestApplication
{
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Android.Build.Tasks", "12.1.99.62")]
public partial class Resource
{
static Resource()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
public static void UpdateIdValues()
{
}
public partial class Attribute
{
static Attribute()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Attribute()
{
}
}
public partial class Drawable
{
// aapt resource value: 0x7F010000
public const int Icon = 2130771968;
static Drawable()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private Drawable()
{
}
}
public partial class String
{
// aapt resource value: 0x7F020000
public const int ApplicationName = 2130837504;
// aapt resource value: 0x7F020001
public const int Hello = 2130837505;
static String()
{
global::Android.Runtime.ResourceIdManager.UpdateIdValues();
}
private String()
{
}
}
}
}
#pragma warning restore 1591

11
src/Android/Avalonia.AndroidTestApplication/app.config

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="System.Runtime.InteropServices.WindowsRuntime" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.1.0" newVersion="4.0.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

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

@ -22,19 +22,9 @@ namespace Avalonia.Data.Core.Plugins
var method = GetFirstMethodWithName(instance.GetType(), methodName);
if (method != null)
if (method is not null)
{
var parameters = method.GetParameters();
if (parameters.Length + (method.ReturnType == typeof(void) ? 0 : 1) > 8)
{
var exception = new ArgumentException(
"Cannot create a binding accessor for a method with more than 8 parameters or more than 7 parameters if it has a non-void return type.",
nameof(methodName));
return new PropertyError(new BindingNotification(exception, BindingErrorType.Error));
}
return new Accessor(reference, method, parameters);
return new Accessor(reference, method);
}
else
{
@ -82,18 +72,20 @@ namespace Avalonia.Data.Core.Plugins
private sealed class Accessor : PropertyAccessorBase
{
public Accessor(WeakReference<object?> reference, MethodInfo method, ParameterInfo[] parameters)
public Accessor(WeakReference<object?> reference, MethodInfo method)
{
_ = reference ?? throw new ArgumentNullException(nameof(reference));
_ = method ?? throw new ArgumentNullException(nameof(method));
var returnType = method.ReturnType;
bool hasReturn = returnType != typeof(void);
var signatureTypeCount = (hasReturn ? 1 : 0) + parameters.Length;
var parameters = method.GetParameters();
var signatureTypeCount = parameters.Length + 1;
var paramTypes = new Type[signatureTypeCount];
for (var i = 0; i < parameters.Length; i++)
{
ParameterInfo parameter = parameters[i];
@ -101,16 +93,9 @@ namespace Avalonia.Data.Core.Plugins
paramTypes[i] = parameter.ParameterType;
}
if (hasReturn)
{
paramTypes[paramTypes.Length - 1] = returnType;
paramTypes[paramTypes.Length - 1] = returnType;
PropertyType = Expression.GetFuncType(paramTypes);
}
else
{
PropertyType = Expression.GetActionType(paramTypes);
}
PropertyType = Expression.GetDelegateType(paramTypes);
if (method.IsStatic)
{

6
src/Avalonia.Build.Tasks/XamlCompilerTaskExecutor.cs

@ -124,6 +124,9 @@ namespace Avalonia.Build.Tasks
var indexerAccessorClosure = new TypeDefinition("CompiledAvaloniaXaml", "!IndexerAccessorFactoryClosure",
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
asm.MainModule.Types.Add(indexerAccessorClosure);
var trampolineBuilder = new TypeDefinition("CompiledAvaloniaXaml", "XamlIlTrampolines",
TypeAttributes.Class, asm.MainModule.TypeSystem.Object);
asm.MainModule.Types.Add(trampolineBuilder);
var (xamlLanguage , emitConfig) = AvaloniaXamlIlLanguage.Configure(typeSystem);
var compilerConfig = new AvaloniaXamlIlCompilerConfiguration(typeSystem,
@ -133,6 +136,7 @@ namespace Avalonia.Build.Tasks
AvaloniaXamlIlLanguage.CustomValueConverter,
new XamlIlClrPropertyInfoEmitter(typeSystem.CreateTypeBuilder(clrPropertiesDef)),
new XamlIlPropertyInfoAccessorFactoryEmitter(typeSystem.CreateTypeBuilder(indexerAccessorClosure)),
new XamlIlTrampolineBuilder(typeSystem.CreateTypeBuilder(trampolineBuilder)),
new DeterministicIdGenerator());
@ -255,6 +259,8 @@ namespace Avalonia.Build.Tasks
true),
(closureName, closureBaseType) =>
populateBuilder.DefineSubType(closureBaseType, closureName, false),
(closureName, returnType, parameterTypes) =>
populateBuilder.DefineDelegateSubType(closureName, false, returnType, parameterTypes),
res.Uri, res
);

3
src/Avalonia.Controls/Calendar/CalendarDatePicker.cs

@ -186,7 +186,8 @@ namespace Avalonia.Controls
nameof(SelectedDate),
o => o.SelectedDate,
(o, v) => o.SelectedDate = v,
enableDataValidation: true);
enableDataValidation: true,
defaultBindingMode:BindingMode.TwoWay);
public static readonly StyledProperty<CalendarDatePickerFormat> SelectedDateFormatProperty =
AvaloniaProperty.Register<CalendarDatePicker, CalendarDatePickerFormat>(

20
src/Avalonia.Controls/DateTimePickers/DatePicker.cs

@ -4,6 +4,7 @@ using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Interactivity;
using Avalonia.Layout;
using System;
using System.Collections.Generic;
using System.Globalization;
@ -398,18 +399,27 @@ namespace Avalonia.Controls
private void OnFlyoutButtonClicked(object? sender, RoutedEventArgs e)
{
if (_presenter == null)
throw new InvalidOperationException("No DatePickerPresenter found");
throw new InvalidOperationException("No DatePickerPresenter found.");
if (_popup == null)
throw new InvalidOperationException("No Popup found.");
_presenter.Date = SelectedDate ?? DateTimeOffset.Now;
_popup!.IsOpen = true;
_popup.PlacementMode = PlacementMode.AnchorAndGravity;
_popup.PlacementAnchor = Primitives.PopupPositioning.PopupAnchor.Bottom;
_popup.PlacementGravity = Primitives.PopupPositioning.PopupGravity.Bottom;
_popup.PlacementConstraintAdjustment = Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY;
_popup.IsOpen = true;
// Overlay popup hosts won't get measured until the next layout pass, but we need the
// template to be applied to `_presenter` now. Detect this case and force a layout pass.
if (!_presenter.IsMeasureValid)
(VisualRoot as ILayoutRoot)?.LayoutManager?.ExecuteInitialLayoutPass();
var deltaY = _presenter.GetOffsetForPopup();
// The extra 5 px I think is related to default popup placement behavior
_popup!.Host!.ConfigurePosition(_popup.PlacementTarget!, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5),
Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);
_popup.VerticalOffset = deltaY + 5;
}
protected virtual void OnSelectedDateChanged(object? sender, DatePickerSelectedValueChangedEventArgs e)

5
src/Avalonia.Controls/DateTimePickers/DatePickerPresenter.cs

@ -537,8 +537,11 @@ namespace Avalonia.Controls
internal double GetOffsetForPopup()
{
if (_monthSelector is null)
return 0;
var acceptDismissButtonHeight = _acceptButton != null ? _acceptButton.Bounds.Height : 41;
return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_monthSelector!.ItemHeight / 2);
return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_monthSelector.ItemHeight / 2);
}
}
}

23
src/Avalonia.Controls/DateTimePickers/TimePicker.cs

@ -3,6 +3,7 @@ using Avalonia.Controls.Primitives;
using Avalonia.Controls.Shapes;
using Avalonia.Controls.Templates;
using Avalonia.Data;
using Avalonia.Layout;
using System;
using System.Globalization;
@ -254,16 +255,28 @@ namespace Avalonia.Controls
private void OnFlyoutButtonClicked(object? sender, Interactivity.RoutedEventArgs e)
{
_presenter!.Time = SelectedTime ?? DateTime.Now.TimeOfDay;
if (_presenter == null)
throw new InvalidOperationException("No DatePickerPresenter found.");
if (_popup == null)
throw new InvalidOperationException("No Popup found.");
_popup!.IsOpen = true;
_presenter.Time = SelectedTime ?? DateTime.Now.TimeOfDay;
_popup.PlacementMode = PlacementMode.AnchorAndGravity;
_popup.PlacementAnchor = Primitives.PopupPositioning.PopupAnchor.Bottom;
_popup.PlacementGravity = Primitives.PopupPositioning.PopupGravity.Bottom;
_popup.PlacementConstraintAdjustment = Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY;
_popup.IsOpen = true;
// Overlay popup hosts won't get measured until the next layout pass, but we need the
// template to be applied to `_presenter` now. Detect this case and force a layout pass.
if (!_presenter.IsMeasureValid)
(VisualRoot as ILayoutRoot)?.LayoutManager?.ExecuteInitialLayoutPass();
var deltaY = _presenter.GetOffsetForPopup();
// The extra 5 px I think is related to default popup placement behavior
_popup.Host!.ConfigurePosition(_popup.PlacementTarget!, PlacementMode.AnchorAndGravity, new Point(0, deltaY + 5),
Primitives.PopupPositioning.PopupAnchor.Bottom, Primitives.PopupPositioning.PopupGravity.Bottom,
Primitives.PopupPositioning.PopupPositionerConstraintAdjustment.SlideY);
_popup.VerticalOffset = deltaY + 5;
}
private void OnDismissPicker(object? sender, EventArgs e)

5
src/Avalonia.Controls/DateTimePickers/TimePickerPresenter.cs

@ -256,8 +256,11 @@ namespace Avalonia.Controls
internal double GetOffsetForPopup()
{
if (_hourSelector is null)
return 0;
var acceptDismissButtonHeight = _acceptButton != null ? _acceptButton.Bounds.Height : 41;
return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_hourSelector!.ItemHeight / 2);
return -(MaxHeight - acceptDismissButtonHeight) / 2 - (_hourSelector.ItemHeight / 2);
}
}
}

20
src/Avalonia.Controls/Primitives/SelectingItemsControl.cs

@ -531,11 +531,23 @@ namespace Avalonia.Controls.Primitives
_textSearchTerm += e.Text;
bool match(ItemContainerInfo info) =>
info.ContainerControl is IContentControl control &&
control.Content?.ToString()?.StartsWith(_textSearchTerm, StringComparison.OrdinalIgnoreCase) == true;
bool Match(ItemContainerInfo info)
{
if (info.ContainerControl.IsSet(TextSearch.TextProperty))
{
var searchText = info.ContainerControl.GetValue(TextSearch.TextProperty);
var info = ItemContainerGenerator?.Containers.FirstOrDefault(match);
if (searchText?.StartsWith(_textSearchTerm, StringComparison.OrdinalIgnoreCase) == true)
{
return true;
}
}
return info.ContainerControl is IContentControl control &&
control.Content?.ToString()?.StartsWith(_textSearchTerm, StringComparison.OrdinalIgnoreCase) == true;
}
var info = ItemContainerGenerator?.Containers.FirstOrDefault(Match);
if (info != null)
{

37
src/Avalonia.Controls/Primitives/TextSearch.cs

@ -0,0 +1,37 @@
using Avalonia.Interactivity;
namespace Avalonia.Controls.Primitives
{
/// <summary>
/// Allows to customize text searching in <see cref="SelectingItemsControl"/>.
/// </summary>
public static class TextSearch
{
/// <summary>
/// Defines the Text attached property.
/// This text will be considered during text search in <see cref="SelectingItemsControl"/> (such as <see cref="ComboBox"/>)
/// </summary>
public static readonly AttachedProperty<string> TextProperty
= AvaloniaProperty.RegisterAttached<Interactive, string>("Text", typeof(TextSearch));
/// <summary>
/// Sets the <see cref="TextProperty"/> for a control.
/// </summary>
/// <param name="control">The control</param>
/// <param name="text">The search text to set</param>
public static void SetText(Control control, string text)
{
control.SetValue(TextProperty, text);
}
/// <summary>
/// Gets the <see cref="TextProperty"/> of a control.
/// </summary>
/// <param name="control">The control</param>
/// <returns>The property value</returns>
public static string GetText(Control control)
{
return control.GetValue(TextProperty);
}
}
}

96
src/Avalonia.Controls/TransitioningContentControl.cs

@ -0,0 +1,96 @@
using System;
using System.Threading;
using Avalonia.Animation;
using Avalonia.Controls.Templates;
using Avalonia.Threading;
namespace Avalonia.Controls;
/// <summary>
/// Displays <see cref="ContentControl.Content"/> according to a <see cref="FuncDataTemplate"/>.
/// Uses <see cref="PageTransition"/> to move between the old and new content values.
/// </summary>
public class TransitioningContentControl : ContentControl
{
private CancellationTokenSource? _lastTransitionCts;
private object? _currentContent;
/// <summary>
/// Defines the <see cref="PageTransition"/> property.
/// </summary>
public static readonly StyledProperty<IPageTransition?> PageTransitionProperty =
AvaloniaProperty.Register<TransitioningContentControl, IPageTransition?>(nameof(PageTransition),
new CrossFade(TimeSpan.FromSeconds(0.125)));
/// <summary>
/// Defines the <see cref="CurrentContent"/> property.
/// </summary>
public static readonly DirectProperty<TransitioningContentControl, object?> CurrentContentProperty =
AvaloniaProperty.RegisterDirect<TransitioningContentControl, object?>(nameof(CurrentContent),
o => o.CurrentContent);
/// <summary>
/// Gets or sets the animation played when content appears and disappears.
/// </summary>
public IPageTransition? PageTransition
{
get => GetValue(PageTransitionProperty);
set => SetValue(PageTransitionProperty, value);
}
/// <summary>
/// Gets the content currently displayed on the screen.
/// </summary>
public object? CurrentContent
{
get => _currentContent;
private set => SetAndRaise(CurrentContentProperty, ref _currentContent, value);
}
protected override void OnAttachedToVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnAttachedToVisualTree(e);
Dispatcher.UIThread.Post(() => UpdateContentWithTransition(Content));
}
protected override void OnDetachedFromVisualTree(VisualTreeAttachmentEventArgs e)
{
base.OnDetachedFromVisualTree(e);
_lastTransitionCts?.Cancel();
}
protected override void OnPropertyChanged<T>(AvaloniaPropertyChangedEventArgs<T> change)
{
base.OnPropertyChanged(change);
if (change.Property == ContentProperty)
{
Dispatcher.UIThread.Post(() => UpdateContentWithTransition(Content));
}
}
/// <summary>
/// Updates the content with transitions.
/// </summary>
/// <param name="content">New content to set.</param>
private async void UpdateContentWithTransition(object? content)
{
if (VisualRoot is null)
{
return;
}
_lastTransitionCts?.Cancel();
_lastTransitionCts = new CancellationTokenSource();
if (PageTransition != null)
await PageTransition.Start(this, null, true, _lastTransitionCts.Token);
CurrentContent = content;
if (PageTransition != null)
await PageTransition.Start(null, this, true, _lastTransitionCts.Token);
}
}

8
src/Avalonia.Input/MouseDevice.cs

@ -5,6 +5,7 @@ using System.Reactive.Linq;
using Avalonia.Input.Raw;
using Avalonia.Interactivity;
using Avalonia.Platform;
using Avalonia.Utilities;
using Avalonia.VisualTree;
namespace Avalonia.Input
@ -334,6 +335,13 @@ namespace Avalonia.Input
var hit = HitTest(root, p);
var source = GetSource(hit);
// KeyModifiers.Shift should scroll in horizontal direction. This does not work on every platform.
// If Shift-Key is pressed and X is close to 0 we swap the Vector.
if (inputModifiers == KeyModifiers.Shift && MathUtilities.IsZero(delta.X))
{
delta = new Vector(delta.Y, delta.X);
}
if (source is not null)
{
var e = new PointerWheelEventArgs(source, _pointer, root, p, timestamp, props, inputModifiers, delta);

53
src/Avalonia.MicroCom/CallbackBase.cs

@ -0,0 +1,53 @@
namespace Avalonia.MicroCom
{
public abstract class CallbackBase : IUnknown, IMicroComShadowContainer
{
private readonly object _lock = new object();
private bool _referencedFromManaged = true;
private bool _referencedFromNative = false;
private bool _destroyed;
public bool IsDestroyed => _destroyed;
protected virtual void Destroyed()
{
}
public void Dispose()
{
lock (_lock)
{
_referencedFromManaged = false;
DestroyIfNeeded();
}
}
void DestroyIfNeeded()
{
if(_destroyed)
return;
if (_referencedFromManaged == false && _referencedFromNative == false)
{
_destroyed = true;
Destroyed();
}
}
public MicroComShadow Shadow { get; set; }
public void OnReferencedFromNative()
{
lock (_lock)
_referencedFromNative = true;
}
public void OnUnreferencedFromNative()
{
lock (_lock)
{
_referencedFromNative = false;
DestroyIfNeeded();
}
}
}
}

15
src/Avalonia.MicroCom/MicroComRuntime.cs

@ -12,6 +12,8 @@ namespace Avalonia.MicroCom
new ConcurrentDictionary<Type, Func<IntPtr, bool, object>>();
private static ConcurrentDictionary<Type, Guid> _guids = new ConcurrentDictionary<Type, Guid>();
private static ConcurrentDictionary<Guid, Type> _guidsToTypes = new ConcurrentDictionary<Guid, Type>();
internal static readonly Guid ManagedObjectInterfaceGuid = Guid.Parse("cd7687c0-a9c2-4563-b08e-a399df50c633");
static MicroComRuntime()
{
@ -82,6 +84,19 @@ namespace Avalonia.MicroCom
return shadow.Target;
}
public static bool IsComWrapper(IUnknown obj) => obj is MicroComProxyBase;
public static object TryUnwrapManagedObject(IUnknown obj)
{
if (obj is not MicroComProxyBase proxy)
return null;
if (proxy.QueryInterface(ManagedObjectInterfaceGuid, out _) != 0)
return null;
// Successful QueryInterface always increments ref counter
proxy.Release();
return GetObjectFromCcw(proxy.NativePointer);
}
public static bool TryGetTypeForGuid(Guid guid, out Type t) => _guidsToTypes.TryGetValue(guid, out t);
public static bool GetVtableFor(Type type, out IntPtr ptr) => _vtables.TryGetValue(type, out ptr);

6
src/Avalonia.MicroCom/MicroComShadow.cs

@ -23,6 +23,12 @@ namespace Avalonia.MicroCom
{
if (MicroComRuntime.TryGetTypeForGuid(*guid, out var type))
return QueryInterface(type, ppv);
else if (*guid == MicroComRuntime.ManagedObjectInterfaceGuid)
{
ccw->RefCount++;
*ppv = ccw;
return 0;
}
else
return unchecked((int)0x80004002u);
}

2
src/Avalonia.Native/AvaloniaNativeApplicationPlatform.cs

@ -6,7 +6,7 @@ using Avalonia.Platform;
namespace Avalonia.Native
{
internal class AvaloniaNativeApplicationPlatform : CallbackBase, IAvnApplicationEvents, IPlatformLifetimeEventsImpl
internal class AvaloniaNativeApplicationPlatform : NativeCallbackBase, IAvnApplicationEvents, IPlatformLifetimeEventsImpl
{
public event EventHandler<ShutdownRequestedEventArgs> ShutdownRequested;

2
src/Avalonia.Native/AvaloniaNativeDragSource.cs

@ -30,7 +30,7 @@ namespace Avalonia.Native
return visual.VisualRoot as TopLevel;
}
class DndCallback : CallbackBase, IAvnDndResultCallback
class DndCallback : NativeCallbackBase, IAvnDndResultCallback
{
private TaskCompletionSource<DragDropEffects> _tcs;

2
src/Avalonia.Native/AvaloniaNativePlatform.cs

@ -79,7 +79,7 @@ namespace Avalonia.Native
_factory = factory;
}
class GCHandleDeallocator : CallbackBase, IAvnGCHandleDeallocatorCallback
class GCHandleDeallocator : NativeCallbackBase, IAvnGCHandleDeallocatorCallback
{
public void FreeGCHandle(IntPtr handle)
{

49
src/Avalonia.Native/CallbackBase.cs

@ -5,19 +5,8 @@ using Avalonia.Platform;
namespace Avalonia.Native
{
public class CallbackBase : IUnknown, IMicroComShadowContainer, IMicroComExceptionCallback
public abstract class NativeCallbackBase : CallbackBase, IMicroComExceptionCallback
{
private readonly object _lock = new object();
private bool _referencedFromManaged = true;
private bool _referencedFromNative = false;
private bool _destroyed;
protected virtual void Destroyed()
{
}
public void RaiseException(Exception e)
{
if (AvaloniaLocator.Current.GetService<IPlatformThreadingInterface>() is PlatformThreadingInterface threadingInterface)
@ -27,41 +16,5 @@ namespace Avalonia.Native
threadingInterface.DispatchException(ExceptionDispatchInfo.Capture(e));
}
}
public void Dispose()
{
lock (_lock)
{
_referencedFromManaged = false;
DestroyIfNeeded();
}
}
void DestroyIfNeeded()
{
if(_destroyed)
return;
if (_referencedFromManaged == false && _referencedFromNative == false)
{
_destroyed = true;
Destroyed();
}
}
public MicroComShadow Shadow { get; set; }
public void OnReferencedFromNative()
{
lock (_lock)
_referencedFromNative = true;
}
public void OnUnreferencedFromNative()
{
lock (_lock)
{
_referencedFromNative = false;
DestroyIfNeeded();
}
}
}
}

2
src/Avalonia.Native/DeferredFramebuffer.cs

@ -27,7 +27,7 @@ namespace Avalonia.Native
public Vector Dpi { get; set; }
public PixelFormat Format { get; set; }
class Disposer : CallbackBase
class Disposer : NativeCallbackBase
{
private IntPtr _ptr;

2
src/Avalonia.Native/IAvnMenu.cs

@ -7,7 +7,7 @@ using Avalonia.Platform.Interop;
namespace Avalonia.Native.Interop
{
class MenuEvents : CallbackBase, IAvnMenuEvents
class MenuEvents : NativeCallbackBase, IAvnMenuEvents
{
private IAvnMenu _parent;

2
src/Avalonia.Native/MenuActionCallback.cs

@ -3,7 +3,7 @@ using Avalonia.Native.Interop;
namespace Avalonia.Native
{
public class MenuActionCallback : CallbackBase, IAvnActionCallback
public class MenuActionCallback : NativeCallbackBase, IAvnActionCallback
{
private Action _action;

4
src/Avalonia.Native/PlatformThreadingInterface.cs

@ -9,7 +9,7 @@ namespace Avalonia.Native
{
internal class PlatformThreadingInterface : IPlatformThreadingInterface
{
class TimerCallback : CallbackBase, IAvnActionCallback
class TimerCallback : NativeCallbackBase, IAvnActionCallback
{
readonly Action _tick;
@ -24,7 +24,7 @@ namespace Avalonia.Native
}
}
class SignaledCallback : CallbackBase, IAvnSignaledCallback
class SignaledCallback : NativeCallbackBase, IAvnSignaledCallback
{
readonly PlatformThreadingInterface _parent;

2
src/Avalonia.Native/PredicateCallback.cs

@ -3,7 +3,7 @@ using Avalonia.Native.Interop;
namespace Avalonia.Native
{
public class PredicateCallback : CallbackBase, IAvnPredicateCallback
public class PredicateCallback : NativeCallbackBase, IAvnPredicateCallback
{
private Func<bool> _predicate;

2
src/Avalonia.Native/SystemDialogs.cs

@ -62,7 +62,7 @@ namespace Avalonia.Native
}
}
internal unsafe class SystemDialogEvents : CallbackBase, IAvnSystemDialogEvents
internal unsafe class SystemDialogEvents : NativeCallbackBase, IAvnSystemDialogEvents
{
private TaskCompletionSource<string[]> _tcs;

2
src/Avalonia.Native/WindowImplBase.cs

@ -151,7 +151,7 @@ namespace Avalonia.Native
public IMouseDevice MouseDevice => _mouse;
public abstract IPopupImpl CreatePopup();
protected unsafe class WindowBaseEvents : CallbackBase, IAvnWindowBaseEvents
protected unsafe class WindowBaseEvents : NativeCallbackBase, IAvnWindowBaseEvents
{
private readonly WindowBaseImpl _parent;

2
src/Avalonia.PlatformSupport/StandardRuntimePlatform.cs

@ -201,7 +201,7 @@ namespace Avalonia.PlatformSupport
#if NETCOREAPP
IsCoreClr = true,
#elif NETFRAMEWORK
IsDotNetFramework = false,
IsDotNetFramework = true,
#endif
IsDesktop = os == OperatingSystemType.Linux || os == OperatingSystemType.OSX || os == OperatingSystemType.WinNT,
IsMono = os == OperatingSystemType.Android || os == OperatingSystemType.iOS || os == OperatingSystemType.Browser,

1
src/Avalonia.ReactiveUI/ReactiveUserControl.cs

@ -48,6 +48,7 @@ namespace Avalonia.ReactiveUI
protected override void OnDataContextChanged(EventArgs e)
{
base.OnDataContextChanged(e);
ViewModel = DataContext as TViewModel;
}

1
src/Avalonia.ReactiveUI/TransitioningContentControl.cs

@ -10,6 +10,7 @@ namespace Avalonia.ReactiveUI
/// <summary>
/// A ContentControl that animates the transition when its content is changed.
/// </summary>
[Obsolete("Use TransitioningContentControl in Avalonia.Controls namespace")]
public class TransitioningContentControl : ContentControl, IStyleable
{
/// <summary>

20
src/Avalonia.Themes.Default/Controls/TransitioningContentControl.xaml

@ -0,0 +1,20 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="TransitioningContentControl">
<!-- Set Defaults -->
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding CurrentContent}"
Padding="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</ControlTemplate>
</Setter>
</Style>
</Styles>

1
src/Avalonia.Themes.Default/DefaultTheme.xaml

@ -38,6 +38,7 @@
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/TabItem.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/TextBox.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/ToggleButton.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/TransitioningContentControl.xaml" />
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/Expander.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/TitleBar.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Default/Controls/TreeView.xaml"/>

1
src/Avalonia.Themes.Fluent/Controls/FluentControls.xaml

@ -37,6 +37,7 @@
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/TabItem.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/TextBox.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/ToggleButton.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml" />
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/Expander.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/TitleBar.xaml"/>
<StyleInclude Source="avares://Avalonia.Themes.Fluent/Controls/TreeView.xaml"/>

20
src/Avalonia.Themes.Fluent/Controls/TransitioningContentControl.xaml

@ -0,0 +1,20 @@
<Styles xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style Selector="TransitioningContentControl">
<!-- Set Defaults -->
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{TemplateBinding BorderThickness}"
CornerRadius="{TemplateBinding CornerRadius}"
ContentTemplate="{TemplateBinding ContentTemplate}"
Content="{TemplateBinding CurrentContent}"
Padding="{TemplateBinding Padding}"
VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"
HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}"/>
</ControlTemplate>
</Setter>
</Style>
</Styles>

6
src/Avalonia.Visuals/Animation/Animators/SolidColorBrushAnimator.cs

@ -12,6 +12,8 @@ namespace Avalonia.Animation.Animators
/// </summary>
public class ISolidColorBrushAnimator : Animator<ISolidColorBrush?>
{
private static readonly DoubleAnimator s_doubleAnimator = new DoubleAnimator();
public override ISolidColorBrush? Interpolate(double progress, ISolidColorBrush? oldValue, ISolidColorBrush? newValue)
{
if (oldValue is null || newValue is null)
@ -19,7 +21,9 @@ namespace Avalonia.Animation.Animators
return progress >= 0.5 ? newValue : oldValue;
}
return new ImmutableSolidColorBrush(ColorAnimator.InterpolateCore(progress, oldValue.Color, newValue.Color));
return new ImmutableSolidColorBrush(
ColorAnimator.InterpolateCore(progress, oldValue.Color, newValue.Color),
s_doubleAnimator.Interpolate(progress, oldValue.Opacity, newValue.Opacity));
}
public override IDisposable BindAnimation(Animatable control, IObservable<ISolidColorBrush?> instance)

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

@ -79,6 +79,7 @@ namespace Avalonia.Animation
var animation = new Animation
{
Easing = SlideOutEasing,
FillMode = FillMode.Forward,
Children =
{
new KeyFrame
@ -109,6 +110,7 @@ namespace Avalonia.Animation
to.IsVisible = true;
var animation = new Animation
{
FillMode = FillMode.Forward,
Easing = SlideInEasing,
Children =
{

5
src/Markup/Avalonia.Markup.Xaml.Loader/AvaloniaXamlIlRuntimeCompiler.cs

@ -177,11 +177,13 @@ namespace Avalonia.Markup.Xaml.XamlIl
var tb = _sreBuilder.DefineType("Builder_" + Guid.NewGuid().ToString("N") + "_" + uri);
var clrPropertyBuilder = tb.DefineNestedType("ClrProperties_" + Guid.NewGuid().ToString("N"));
var indexerClosureType = _sreBuilder.DefineType("IndexerClosure_" + Guid.NewGuid().ToString("N"));
var trampolineBuilder = _sreBuilder.DefineType("Trampolines_" + Guid.NewGuid().ToString("N"));
var compiler = new AvaloniaXamlIlCompiler(new AvaloniaXamlIlCompilerConfiguration(_sreTypeSystem, asm,
_sreMappings, _sreXmlns, AvaloniaXamlIlLanguage.CustomValueConverter,
new XamlIlClrPropertyInfoEmitter(_sreTypeSystem.CreateTypeBuilder(clrPropertyBuilder)),
new XamlIlPropertyInfoAccessorFactoryEmitter(_sreTypeSystem.CreateTypeBuilder(indexerClosureType))),
new XamlIlPropertyInfoAccessorFactoryEmitter(_sreTypeSystem.CreateTypeBuilder(indexerClosureType)),
new XamlIlTrampolineBuilder(_sreTypeSystem.CreateTypeBuilder(trampolineBuilder))),
_sreEmitMappings,
_sreContextType) { EnableIlVerification = true };
@ -196,6 +198,7 @@ namespace Avalonia.Markup.Xaml.XamlIl
compiler.ParseAndCompile(xaml, uri?.ToString(), null, _sreTypeSystem.CreateTypeBuilder(tb), overrideType);
var created = tb.CreateTypeInfo();
clrPropertyBuilder.CreateTypeInfo();
trampolineBuilder.CreateTypeInfo();
return LoadOrPopulate(created, rootInstance);
}

4
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlCompilerConfiguration.cs

@ -7,6 +7,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
public XamlIlClrPropertyInfoEmitter ClrPropertyEmitter { get; }
public XamlIlPropertyInfoAccessorFactoryEmitter AccessorFactoryEmitter { get; }
public XamlIlTrampolineBuilder TrampolineBuilder { get; }
public AvaloniaXamlIlCompilerConfiguration(IXamlTypeSystem typeSystem,
IXamlAssembly defaultAssembly,
@ -15,13 +16,16 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
XamlValueConverter customValueConverter,
XamlIlClrPropertyInfoEmitter clrPropertyEmitter,
XamlIlPropertyInfoAccessorFactoryEmitter accessorFactoryEmitter,
XamlIlTrampolineBuilder trampolineBuilder,
IXamlIdentifierGenerator identifierGenerator = null)
: base(typeSystem, defaultAssembly, typeMappings, xmlnsMappings, customValueConverter, identifierGenerator)
{
ClrPropertyEmitter = clrPropertyEmitter;
AccessorFactoryEmitter = accessorFactoryEmitter;
TrampolineBuilder = trampolineBuilder;
AddExtra(ClrPropertyEmitter);
AddExtra(AccessorFactoryEmitter);
AddExtra(TrampolineBuilder);
}
}
}

2
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/AvaloniaXamlIlLanguageParseIntrinsics.cs

@ -201,7 +201,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
if (type.Equals(types.Classes))
{
var classes = text.Split(' ');
var classNodes = classes.Select(c => new XamlAstTextNode(node, c, types.XamlIlTypes.String)).ToArray();
var classNodes = classes.Select(c => new XamlAstTextNode(node, c, type: types.XamlIlTypes.String)).ToArray();
result = new AvaloniaXamlIlAvaloniaListConstantAstNode(node, types, types.Classes, types.XamlIlTypes.String, classNodes);
return true;

4
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlSelectorTransformer.cs

@ -77,7 +77,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
throw new XamlParseException($"Cannot find '{property.Property}' on '{type}", node);
if (!XamlTransformHelpers.TryGetCorrectlyTypedValue(context,
new XamlAstTextNode(node, property.Value, context.Configuration.WellKnownTypes.String),
new XamlAstTextNode(node, property.Value, type: context.Configuration.WellKnownTypes.String),
targetProperty.PropertyType, out var typedValue))
throw new XamlParseException(
$"Cannot convert '{property.Value}' to '{targetProperty.PropertyType.GetFqn()}",
@ -118,7 +118,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
.GetAvaloniaPropertyType(targetPropertyField, context.GetAvaloniaTypes(), node);
if (!XamlTransformHelpers.TryGetCorrectlyTypedValue(context,
new XamlAstTextNode(node, attachedProperty.Value, context.Configuration.WellKnownTypes.String),
new XamlAstTextNode(node, attachedProperty.Value, type: context.Configuration.WellKnownTypes.String),
targetPropertyType, out var typedValue))
throw new XamlParseException(
$"Cannot convert '{attachedProperty.Value}' to '{targetPropertyType.GetFqn()}",

6
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/Transformers/AvaloniaXamlIlWellKnownTypes.cs

@ -20,10 +20,12 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlMethod AvaloniaObjectBindMethod { get; }
public IXamlMethod AvaloniaObjectSetValueMethod { get; }
public IXamlType IDisposable { get; }
public IXamlType ICommand { get; }
public XamlTypeWellKnownTypes XamlIlTypes { get; }
public XamlLanguageTypeMappings XamlIlMappings { get; }
public IXamlType Transitions { get; }
public IXamlType AssignBindingAttribute { get; }
public IXamlType DependsOnAttribute { get; }
public IXamlType UnsetValueType { get; }
public IXamlType StyledElement { get; }
public IXamlType IStyledElement { get; }
@ -85,6 +87,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
public IXamlType IBrush { get; }
public IXamlType ImmutableSolidColorBrush { get; }
public IXamlConstructor ImmutableSolidColorBrushConstructorColor { get; }
public IXamlType TypeUtilities { get; }
public AvaloniaXamlIlWellKnownTypes(TransformerConfiguration cfg)
{
@ -98,8 +101,10 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
BindingPriority = cfg.TypeSystem.GetType("Avalonia.Data.BindingPriority");
IBinding = cfg.TypeSystem.GetType("Avalonia.Data.IBinding");
IDisposable = cfg.TypeSystem.GetType("System.IDisposable");
ICommand = cfg.TypeSystem.GetType("System.Windows.Input.ICommand");
Transitions = cfg.TypeSystem.GetType("Avalonia.Animation.Transitions");
AssignBindingAttribute = cfg.TypeSystem.GetType("Avalonia.Data.AssignBindingAttribute");
DependsOnAttribute = cfg.TypeSystem.GetType("Avalonia.Metadata.DependsOnAttribute");
AvaloniaObjectBindMethod = AvaloniaObjectExtensions.FindMethod("Bind", IDisposable, false, IAvaloniaObject,
AvaloniaProperty,
IBinding, cfg.WellKnownTypes.Object);
@ -187,6 +192,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers
IBrush = cfg.TypeSystem.GetType("Avalonia.Media.IBrush");
ImmutableSolidColorBrush = cfg.TypeSystem.GetType("Avalonia.Media.Immutable.ImmutableSolidColorBrush");
ImmutableSolidColorBrushConstructorColor = ImmutableSolidColorBrush.GetConstructor(new List<IXamlType> { UInt });
TypeUtilities = cfg.TypeSystem.GetType("Avalonia.Utilities.TypeUtilities");
}
}

202
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlBindingPathHelper.cs

@ -14,7 +14,7 @@ using XamlX.Emit;
using XamlX.IL;
using Avalonia.Utilities;
using XamlIlEmitContext = XamlX.Emit.XamlEmitContext<XamlX.IL.IXamlILEmitter, XamlX.IL.XamlILNodeEmitResult>;
using XamlIlEmitContext = XamlX.Emit.XamlEmitContextWithLocals<XamlX.IL.IXamlILEmitter, XamlX.IL.XamlILNodeEmitResult>;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
@ -32,6 +32,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
selfType,
bindingPath.Path);
transformed = TransformForTargetTyping(transformed, context);
bindingResultType = transformed.BindingResultType;
binding.Arguments[0] = transformed;
}
@ -54,6 +56,8 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
selfType,
bindingPathNode.Path);
transformed = TransformForTargetTyping(transformed, context);
bindingResultType = transformed.BindingResultType;
bindingPathAssignment.Values[0] = transformed;
}
@ -66,7 +70,32 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
return bindingResultType;
}
private static IXamlIlBindingPathNode TransformBindingPath(AstTransformationContext context, IXamlLineInfo lineInfo, Func<IXamlType> startTypeResolver, IXamlType selfType, IEnumerable<BindingExpressionGrammar.INode> bindingExpression)
private static XamlIlBindingPathNode TransformForTargetTyping(XamlIlBindingPathNode transformed, AstTransformationContext context)
{
if (context.ParentNodes().OfType<XamlPropertyAssignmentNode>().FirstOrDefault().Property.Getter.ReturnType == context.GetAvaloniaTypes().ICommand
&& transformed.Elements[transformed.Elements.Count - 1] is XamlIlClrMethodPathElementNode method)
{
IXamlMethod executeMethod = method.Method;
IXamlMethod canExecuteMethod = executeMethod.DeclaringType.FindMethod(new FindMethodMethodSignature($"Can{executeMethod.Name}", context.Configuration.WellKnownTypes.Boolean, context.Configuration.WellKnownTypes.Object));
List<string> dependsOnProperties = new();
if (canExecuteMethod is not null)
{
foreach (var attr in canExecuteMethod.CustomAttributes)
{
if (attr.Type == context.GetAvaloniaTypes().DependsOnAttribute)
{
dependsOnProperties.Add((string)attr.Parameters[0]);
}
}
}
transformed.Elements.RemoveAt(transformed.Elements.Count - 1);
transformed.Elements.Add(new XamlIlClrMethodAsCommandPathElementNode(context.GetAvaloniaTypes().ICommand, executeMethod, canExecuteMethod, dependsOnProperties));
}
return transformed;
}
private static XamlIlBindingPathNode TransformBindingPath(AstTransformationContext context, IXamlLineInfo lineInfo, Func<IXamlType> startTypeResolver, IXamlType selfType, IEnumerable<BindingExpressionGrammar.INode> bindingExpression)
{
List<IXamlIlBindingPathElementNode> transformNodes = new List<IXamlIlBindingPathElementNode>();
List<IXamlIlBindingPathElementNode> nodes = new List<IXamlIlBindingPathElementNode>();
@ -126,16 +155,18 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
nodes.Add(new XamlIlAvaloniaPropertyPropertyPathElementNode(avaloniaPropertyFieldMaybe,
XamlIlAvaloniaPropertyHelper.GetAvaloniaPropertyType(avaloniaPropertyFieldMaybe, context.GetAvaloniaTypes(), lineInfo)));
}
else
else if (GetAllDefinedProperties(targetType).FirstOrDefault(p => p.Name == propName.PropertyName) is IXamlProperty clrProperty)
{
var clrProperty = GetAllDefinedProperties(targetType).FirstOrDefault(p => p.Name == propName.PropertyName);
if (clrProperty is null)
{
throw new XamlX.XamlParseException($"Unable to resolve property of name '{propName.PropertyName}' on type '{targetType}'.", lineInfo);
}
nodes.Add(new XamlIlClrPropertyPathElementNode(clrProperty));
}
else if (GetAllDefinedMethods(targetType).FirstOrDefault(m => m.Name == propName.PropertyName) is IXamlMethod method)
{
nodes.Add(new XamlIlClrMethodPathElementNode(method, context.Configuration.WellKnownTypes.Delegate));
}
else
{
throw new XamlX.XamlParseException($"Unable to resolve property or method of name '{propName.PropertyName}' on type '{targetType}'.", lineInfo);
}
break;
}
case BindingExpressionGrammar.IndexerNode indexer:
@ -284,6 +315,17 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
}
static IEnumerable<IXamlMethod> GetAllDefinedMethods(IXamlType type)
{
foreach (var t in TraverseTypeHierarchy(type))
{
foreach (var m in t.Methods)
{
yield return m;
}
}
}
static IEnumerable<IXamlType> TraverseTypeHierarchy(IXamlType type)
{
if (type.IsInterface)
@ -538,6 +580,131 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
public IXamlType Type => _property.Getter?.ReturnType ?? _property.Setter?.Parameters[0];
}
class XamlIlClrMethodPathElementNode : IXamlIlBindingPathElementNode
{
public XamlIlClrMethodPathElementNode(IXamlMethod method, IXamlType systemDelegateType)
{
Method = method;
Type = systemDelegateType;
}
public IXamlMethod Method { get; }
public IXamlType Type { get; }
public void Emit(XamlIlEmitContext context, IXamlILEmitter codeGen)
{
IXamlTypeBuilder<IXamlILEmitter> newDelegateTypeBuilder = null;
IXamlType specificDelegateType;
if (Method.ReturnType == context.Configuration.WellKnownTypes.Void && Method.Parameters.Count == 0)
{
specificDelegateType = context.Configuration.TypeSystem
.GetType("System.Action");
}
else if (Method.ReturnType == context.Configuration.WellKnownTypes.Void && Method.Parameters.Count <= 16)
{
specificDelegateType = context.Configuration.TypeSystem
.GetType($"System.Action`{Method.Parameters.Count}")
.MakeGenericType(Method.Parameters);
}
else if (Method.Parameters.Count <= 16)
{
List<IXamlType> genericParameters = new();
genericParameters.AddRange(Method.Parameters);
genericParameters.Add(Method.ReturnType);
specificDelegateType = context.Configuration.TypeSystem
.GetType($"System.Func`{Method.Parameters.Count + 1}")
.MakeGenericType(genericParameters);
}
else
{
// In this case, we need to emit our own delegate type.
string delegateTypeName = context.Configuration.IdentifierGenerator.GenerateIdentifierPart();
specificDelegateType = newDelegateTypeBuilder = context.DefineDelegateSubType(delegateTypeName, Method.ReturnType, Method.Parameters);
}
codeGen
.Ldtoken(Method)
.Ldtoken(specificDelegateType)
.EmitCall(context.GetAvaloniaTypes()
.CompiledBindingPathBuilder.FindMethod(m => m.Name == "Method"));
newDelegateTypeBuilder?.CreateType();
}
}
class XamlIlClrMethodAsCommandPathElementNode : IXamlIlBindingPathElementNode
{
private readonly IXamlMethod _executeMethod;
private readonly IXamlMethod _canExecuteMethod;
private readonly IReadOnlyList<string> _dependsOnProperties;
public XamlIlClrMethodAsCommandPathElementNode(IXamlType iCommandType, IXamlMethod executeMethod, IXamlMethod canExecuteMethod, IReadOnlyList<string> dependsOnProperties)
{
Type = iCommandType;
_executeMethod = executeMethod;
_canExecuteMethod = canExecuteMethod;
_dependsOnProperties = dependsOnProperties;
}
public IXamlType Type { get; }
public void Emit(XamlIlEmitContext context, IXamlILEmitter codeGen)
{
var trampolineBuilder = context.Configuration.GetExtra<XamlIlTrampolineBuilder>();
var objectType = context.Configuration.WellKnownTypes.Object;
codeGen
.Ldstr(_executeMethod.Name)
.Ldnull()
.Ldftn(trampolineBuilder.EmitCommandExecuteTrampoline(context, _executeMethod))
.Newobj(context.Configuration.TypeSystem.GetType("System.Action`2")
.MakeGenericType(objectType, objectType)
.GetConstructor(new() { objectType, context.Configuration.TypeSystem.GetType("System.IntPtr") }));
if (_canExecuteMethod is null)
{
codeGen.Ldnull();
}
else
{
codeGen
.Ldnull()
.Ldftn(trampolineBuilder.EmitCommandCanExecuteTrampoline(context, _canExecuteMethod))
.Newobj(context.Configuration.TypeSystem.GetType("System.Func`3")
.MakeGenericType(objectType, objectType, context.Configuration.WellKnownTypes.Boolean)
.GetConstructor(new() { objectType, context.Configuration.TypeSystem.GetType("System.IntPtr") }));
}
if (_dependsOnProperties is { Count:> 1 })
{
using var dependsOnPropertiesArray = context.GetLocalOfType(context.Configuration.WellKnownTypes.String.MakeArrayType(1));
codeGen
.Ldc_I4(_dependsOnProperties.Count)
.Newarr(context.Configuration.WellKnownTypes.String)
.Stloc(dependsOnPropertiesArray.Local);
for (int i = 0; i < _dependsOnProperties.Count; i++)
{
codeGen
.Ldloc(dependsOnPropertiesArray.Local)
.Ldc_I4(i)
.Ldstr(_dependsOnProperties[i])
.Stelem_ref();
}
codeGen.Ldloc(dependsOnPropertiesArray.Local);
}
else
{
codeGen.Ldnull();
}
codeGen
.EmitCall(context.GetAvaloniaTypes()
.CompiledBindingPathBuilder.FindMethod(m => m.Name == "Command"));
}
}
class XamlIlClrIndexerPathElementNode : IXamlIlBindingPathElementNode
{
private readonly IXamlProperty _property;
@ -660,10 +827,9 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
}
}
class XamlIlBindingPathNode : XamlAstNode, IXamlIlBindingPathNode, IXamlAstEmitableNode<IXamlILEmitter, XamlILNodeEmitResult>
class XamlIlBindingPathNode : XamlAstNode, IXamlIlBindingPathNode, IXamlAstLocalsEmitableNode<IXamlILEmitter, XamlILNodeEmitResult>
{
private readonly List<IXamlIlBindingPathElementNode> _transformElements;
private readonly List<IXamlIlBindingPathElementNode> _elements;
public XamlIlBindingPathNode(IXamlLineInfo lineInfo,
IXamlType bindingPathType,
@ -672,16 +838,18 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
Type = new XamlAstClrTypeReference(lineInfo, bindingPathType, false);
_transformElements = transformElements;
_elements = elements;
Elements = elements;
}
public IXamlType BindingResultType
=> _transformElements.Count > 0
? _transformElements[0].Type
: _elements[_elements.Count - 1].Type;
: Elements[Elements.Count - 1].Type;
public IXamlAstTypeReference Type { get; }
public List<IXamlIlBindingPathElementNode> Elements { get; }
public XamlILNodeEmitResult Emit(XamlIlEmitContext context, IXamlILEmitter codeGen)
{
var types = context.GetAvaloniaTypes();
@ -692,7 +860,7 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
transform.Emit(context, codeGen);
}
foreach (var element in _elements)
foreach (var element in Elements)
{
element.Emit(context, codeGen);
}
@ -710,11 +878,11 @@ namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
_transformElements[i] = (IXamlIlBindingPathElementNode)ast.Visit(visitor);
}
}
for (int i = 0; i < _elements.Count; i++)
for (int i = 0; i < Elements.Count; i++)
{
if (_elements[i] is IXamlAstNode ast)
if (Elements[i] is IXamlAstNode ast)
{
_elements[i] = (IXamlIlBindingPathElementNode)ast.Visit(visitor);
Elements[i] = (IXamlIlBindingPathElementNode)ast.Visit(visitor);
}
}
}

116
src/Markup/Avalonia.Markup.Xaml.Loader/CompilerExtensions/XamlIlTrampolineBuilder.cs

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using XamlX.Emit;
using XamlX.IL;
using XamlX.TypeSystem;
using Avalonia.Markup.Xaml.XamlIl.CompilerExtensions.Transformers;
using System.Reflection.Emit;
namespace Avalonia.Markup.Xaml.XamlIl.CompilerExtensions
{
internal class XamlIlTrampolineBuilder
{
private IXamlTypeBuilder<IXamlILEmitter> _builder;
private Dictionary<string, IXamlMethod> _trampolines = new();
public XamlIlTrampolineBuilder(IXamlTypeBuilder<IXamlILEmitter> builder)
{
_builder = builder;
}
public IXamlMethod EmitCommandExecuteTrampoline(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlMethod executeMethod)
{
Debug.Assert(!executeMethod.IsStatic);
string methodName = $"{executeMethod.DeclaringType.GetFqn()}+{executeMethod.Name}_{executeMethod.Parameters.Count}!CommandExecuteTrampoline";
if (_trampolines.TryGetValue(methodName, out var method))
{
return method;
}
var trampoline = _builder.DefineMethod(
context.Configuration.WellKnownTypes.Void,
new[] { context.Configuration.WellKnownTypes.Object, context.Configuration.WellKnownTypes.Object },
methodName,
true,
true,
false);
var gen = trampoline.Generator;
if (executeMethod.DeclaringType.IsValueType)
{
gen.Ldarg_0()
.Unbox(executeMethod.DeclaringType);
}
else
{
gen.Ldarg_0()
.Castclass(executeMethod.DeclaringType);
}
if (executeMethod.Parameters.Count != 0)
{
Debug.Assert(executeMethod.Parameters.Count == 1);
if (executeMethod.Parameters[0] != context.Configuration.WellKnownTypes.Object)
{
var convertedValue = gen.DefineLocal(context.Configuration.WellKnownTypes.Object);
gen.Ldtype(executeMethod.Parameters[0])
.Ldarg(1)
.EmitCall(context.Configuration.WellKnownTypes.CultureInfo.FindMethod(m => m.Name == "get_CurrentCulture"))
.Ldloca(convertedValue)
.EmitCall(
context.GetAvaloniaTypes().TypeUtilities.FindMethod(m => m.Name == "TryConvert"),
swallowResult: true)
.Ldloc(convertedValue)
.Unbox_Any(executeMethod.Parameters[0]);
}
else
{
gen.Ldarg(1);
}
}
gen.EmitCall(executeMethod, swallowResult: true);
gen.Ret();
_trampolines.Add(methodName, trampoline);
return trampoline;
}
public IXamlMethod EmitCommandCanExecuteTrampoline(XamlEmitContext<IXamlILEmitter, XamlILNodeEmitResult> context, IXamlMethod canExecuteMethod)
{
Debug.Assert(!canExecuteMethod.IsStatic);
Debug.Assert(canExecuteMethod.Parameters.Count == 1);
Debug.Assert(canExecuteMethod.ReturnType == context.Configuration.WellKnownTypes.Boolean);
string methodName = $"{canExecuteMethod.DeclaringType.GetFqn()}+{canExecuteMethod.Name}!CommandCanExecuteTrampoline";
if (_trampolines.TryGetValue(methodName, out var method))
{
return method;
}
var trampoline = _builder.DefineMethod(
context.Configuration.WellKnownTypes.Boolean,
new[] { context.Configuration.WellKnownTypes.Object, context.Configuration.WellKnownTypes.Object },
methodName,
true,
true,
false);
if (canExecuteMethod.DeclaringType.IsValueType)
{
trampoline.Generator
.Ldarg_0()
.Unbox(canExecuteMethod.DeclaringType);
}
else
{
trampoline.Generator
.Ldarg_0()
.Castclass(canExecuteMethod.DeclaringType);
}
trampoline.Generator
.Ldarg(1)
.Emit(OpCodes.Tailcall)
.EmitCall(canExecuteMethod)
.Ret();
_trampolines.Add(methodName, trampoline);
return trampoline;
}
}
}

2
src/Markup/Avalonia.Markup.Xaml.Loader/xamlil.github

@ -1 +1 @@
Subproject commit 8e20d65eb5f1efbae08e49b18f39bfdce32df7b3
Subproject commit daaac590e078967b78045f74c38ef046d00d8582

2
src/Markup/Avalonia.Markup.Xaml/Avalonia.Markup.Xaml.csproj

@ -18,8 +18,10 @@
<Compile Include="MarkupExtension.cs" />
<Compile Include="MarkupExtensions\CompiledBindingExtension.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\ArrayElementPlugin.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\CommandAccessorPlugin.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\CompiledBindingPath.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\FindVisualAncestorNode.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\MethodAccessorPlugin.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\ObservableStreamPlugin.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\PropertyInfoAccessorFactory.cs" />
<Compile Include="MarkupExtensions\CompiledBindings\PropertyInfoAccessorPlugin.cs" />

156
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CommandAccessorPlugin.cs

@ -0,0 +1,156 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;
using System.Text;
using System.Windows.Input;
using Avalonia.Data;
using Avalonia.Data.Core.Plugins;
using Avalonia.Utilities;
namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
{
internal class CommandAccessorPlugin : IPropertyAccessorPlugin
{
private readonly Action<object, object> _execute;
private readonly Func<object, object, bool> _canExecute;
private readonly ISet<string> _dependsOnProperties;
public CommandAccessorPlugin(Action<object, object> execute, Func<object, object, bool> canExecute, ISet<string> dependsOnProperties)
{
_execute = execute;
_canExecute = canExecute;
_dependsOnProperties = dependsOnProperties;
}
public bool Match(object obj, string propertyName)
{
throw new InvalidOperationException("The CommandAccessorPlugin does not support dynamic matching");
}
public IPropertyAccessor Start(WeakReference<object> reference, string propertyName)
{
return new CommandAccessor(reference, _execute, _canExecute, _dependsOnProperties);
}
private sealed class CommandAccessor : PropertyAccessorBase
{
private readonly WeakReference<object> _reference;
private Command _command;
private readonly ISet<string> _dependsOnProperties;
public CommandAccessor(WeakReference<object> reference, Action<object, object> execute, Func<object, object, bool> canExecute, ISet<string> dependsOnProperties)
{
Contract.Requires<ArgumentNullException>(reference != null);
_reference = reference;
_dependsOnProperties = dependsOnProperties;
_command = new Command(reference, execute, canExecute);
}
public override object Value => _reference.TryGetTarget(out var _) ? _command : null;
private void RaiseCanExecuteChanged()
{
_command.RaiseCanExecuteChanged();
}
private sealed class Command : ICommand
{
private readonly WeakReference<object> _target;
private readonly Action<object, object> _execute;
private readonly Func<object, object, bool> _canExecute;
public event EventHandler CanExecuteChanged;
public Command(WeakReference<object> target, Action<object, object> execute, Func<object, object, bool> canExecute)
{
_target = target;
_execute = execute;
_canExecute = canExecute;
}
public void RaiseCanExecuteChanged()
{
Threading.Dispatcher.UIThread.Post(() => CanExecuteChanged?.Invoke(this, EventArgs.Empty)
, Threading.DispatcherPriority.Input);
}
public bool CanExecute(object parameter)
{
if (_target.TryGetTarget(out var target))
{
if (_canExecute == null)
{
return true;
}
return _canExecute(target, parameter);
}
return false;
}
public void Execute(object parameter)
{
if (_target.TryGetTarget(out var target))
{
_execute(target, parameter);
}
}
}
public override Type PropertyType => typeof(ICommand);
public override bool SetValue(object value, BindingPriority priority)
{
return false;
}
void OnNotifyPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (string.IsNullOrEmpty(e.PropertyName) || _dependsOnProperties.Contains(e.PropertyName))
{
RaiseCanExecuteChanged();
}
}
protected override void SubscribeCore()
{
SendCurrentValue();
SubscribeToChanges();
}
protected override void UnsubscribeCore()
{
if (_dependsOnProperties is { Count: > 0 } && _reference.TryGetTarget(out var o) && o is INotifyPropertyChanged inpc)
{
WeakEventHandlerManager.Unsubscribe<PropertyChangedEventArgs, InpcPropertyAccessor>(
inpc,
nameof(INotifyPropertyChanged.PropertyChanged),
OnNotifyPropertyChanged);
}
}
private void SendCurrentValue()
{
try
{
var value = Value;
PublishValue(value);
}
catch { }
}
private void SubscribeToChanges()
{
if (_dependsOnProperties is { Count:>0 } && _reference.TryGetTarget(out var o) && o is INotifyPropertyChanged inpc)
{
WeakEventHandlerManager.Subscribe<INotifyPropertyChanged, PropertyChangedEventArgs, InpcPropertyAccessor>(
inpc,
nameof(INotifyPropertyChanged.PropertyChanged),
OnNotifyPropertyChanged);
}
}
}
}
}

48
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/CompiledBindingPath.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Avalonia.Controls;
using Avalonia.Data.Core;
using Avalonia.Data.Core.Plugins;
@ -36,6 +37,12 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
case PropertyElement prop:
node = new PropertyAccessorNode(prop.Property.Name, enableValidation, new PropertyInfoAccessorPlugin(prop.Property, prop.AccessorFactory));
break;
case MethodAsCommandElement methodAsCommand:
node = new PropertyAccessorNode(methodAsCommand.MethodName, enableValidation, new CommandAccessorPlugin(methodAsCommand.ExecuteMethod, methodAsCommand.CanExecuteMethod, methodAsCommand.DependsOnProperties));
break;
case MethodAsDelegateElement methodAsDelegate:
node = new PropertyAccessorNode(methodAsDelegate.Method.Name, enableValidation, new MethodAccessorPlugin(methodAsDelegate.Method, methodAsDelegate.DelegateType));
break;
case ArrayElementPathElement arr:
node = new PropertyAccessorNode(CommonPropertyNames.IndexerName, enableValidation, new ArrayElementPlugin(arr.Indices, arr.ElementType));
break;
@ -92,6 +99,18 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
return this;
}
public CompiledBindingPathBuilder Method(RuntimeMethodHandle handle, RuntimeTypeHandle delegateType)
{
_elements.Add(new MethodAsDelegateElement(handle, delegateType));
return this;
}
public CompiledBindingPathBuilder Command(string methodName, Action<object, object> executeHelper, Func<object, object, bool> canExecuteHelper, string[] dependsOnProperties)
{
_elements.Add(new MethodAsCommandElement(methodName, executeHelper, canExecuteHelper, dependsOnProperties ?? Array.Empty<string>()));
return this;
}
public CompiledBindingPathBuilder StreamTask<T>()
{
_elements.Add(new TaskStreamPathElement<T>());
@ -178,6 +197,35 @@ namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
=> _isFirstElement ? Property.Name : $".{Property.Name}";
}
internal class MethodAsDelegateElement : ICompiledBindingPathElement
{
public MethodAsDelegateElement(RuntimeMethodHandle method, RuntimeTypeHandle delegateType)
{
Method = (MethodInfo)MethodBase.GetMethodFromHandle(method);
DelegateType = Type.GetTypeFromHandle(delegateType);
}
public MethodInfo Method { get; }
public Type DelegateType { get; }
}
internal class MethodAsCommandElement : ICompiledBindingPathElement
{
public MethodAsCommandElement(string methodName, Action<object, object> executeHelper, Func<object, object, bool> canExecuteHelper, string[] dependsOnElements)
{
MethodName = methodName;
ExecuteMethod = executeHelper;
CanExecuteMethod = canExecuteHelper;
DependsOnProperties = new HashSet<string>(dependsOnElements);
}
public string MethodName { get; }
public Action<object, object> ExecuteMethod { get; }
public Func<object, object, bool> CanExecuteMethod { get; }
public HashSet<string> DependsOnProperties { get; }
}
internal interface IStronglyTypedStreamElement : ICompiledBindingPathElement
{
IStreamPlugin CreatePlugin();

74
src/Markup/Avalonia.Markup.Xaml/MarkupExtensions/CompiledBindings/MethodAccessorPlugin.cs

@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using Avalonia.Data;
using Avalonia.Data.Core.Plugins;
#nullable enable
namespace Avalonia.Markup.Xaml.MarkupExtensions.CompiledBindings
{
internal class MethodAccessorPlugin : IPropertyAccessorPlugin
{
private MethodInfo _method;
private readonly Type _delegateType;
public MethodAccessorPlugin(MethodInfo method, Type delegateType)
{
_method = method;
_delegateType = delegateType;
}
public bool Match(object obj, string propertyName)
{
throw new InvalidOperationException("The MethodAccessorPlugin does not support dynamic matching");
}
public IPropertyAccessor Start(WeakReference<object?> reference, string propertyName)
{
Debug.Assert(_method.Name == propertyName);
return new Accessor(reference, _method, _delegateType);
}
private sealed class Accessor : PropertyAccessorBase
{
public Accessor(WeakReference<object?> reference, MethodInfo method, Type delegateType)
{
_ = reference ?? throw new ArgumentNullException(nameof(reference));
_ = method ?? throw new ArgumentNullException(nameof(method));
PropertyType = delegateType;
if (method.IsStatic)
{
Value = method.CreateDelegate(PropertyType);
}
else if (reference.TryGetTarget(out var target))
{
Value = method.CreateDelegate(PropertyType, target);
}
}
public override Type? PropertyType { get; }
public override object? Value { get; }
public override bool SetValue(object? value, BindingPriority priority) => false;
protected override void SubscribeCore()
{
try
{
PublishValue(Value);
}
catch { }
}
protected override void UnsubscribeCore()
{
}
}
}
}

71
src/Skia/Avalonia.Skia/Helpers/DrawingContextHelper.cs

@ -1,10 +1,11 @@
using Avalonia.Platform;
using System;
using Avalonia.Platform;
using Avalonia.Rendering;
using SkiaSharp;
namespace Avalonia.Skia.Helpers
{
public class DrawingContextHelper
public static class DrawingContextHelper
{
/// <summary>
/// Wrap Skia canvas in drawing context so we can use Avalonia api to render to external skia canvas
@ -27,5 +28,71 @@ namespace Avalonia.Skia.Helpers
return new DrawingContextImpl(createInfo);
}
/// <summary>
/// Unsupported - Wraps a GPU Backed SkiaSurface in an Avalonia DrawingContext.
/// </summary>
[Obsolete]
public static ISkiaDrawingContextImpl WrapSkiaSurface(this SKSurface surface, GRContext grContext, Vector dpi, params IDisposable[] disposables)
{
var createInfo = new DrawingContextImpl.CreateInfo
{
GrContext = grContext,
Surface = surface,
Dpi = dpi,
DisableTextLcdRendering = false,
};
return new DrawingContextImpl(createInfo, disposables);
}
/// <summary>
/// Unsupported - Wraps a non-GPU Backed SkiaSurface in an Avalonia DrawingContext.
/// </summary>
[Obsolete]
public static ISkiaDrawingContextImpl WrapSkiaSurface(this SKSurface surface, Vector dpi, params IDisposable[] disposables)
{
var createInfo = new DrawingContextImpl.CreateInfo
{
Surface = surface,
Dpi = dpi,
DisableTextLcdRendering = false,
};
return new DrawingContextImpl(createInfo, disposables);
}
[Obsolete]
public static ISkiaDrawingContextImpl CreateDrawingContext(Size size, Vector dpi, GRContext grContext = null)
{
if (grContext is null)
{
var surface = SKSurface.Create(
new SKImageInfo(
(int)Math.Ceiling(size.Width),
(int)Math.Ceiling(size.Height),
SKImageInfo.PlatformColorType,
SKAlphaType.Premul));
return WrapSkiaSurface(surface, dpi, surface);
}
else
{
var surface = SKSurface.Create(grContext, false,
new SKImageInfo(
(int)Math.Ceiling(size.Width),
(int)Math.Ceiling(size.Height),
SKImageInfo.PlatformColorType,
SKAlphaType.Premul));
return WrapSkiaSurface(surface, grContext, dpi, surface);
}
}
[Obsolete]
public static void DrawTo(this ISkiaDrawingContextImpl source, ISkiaDrawingContextImpl destination, SKPaint paint = null)
{
destination.SkCanvas.DrawSurface(source.SkSurface, new SKPoint(0, 0), paint);
}
}
}

1
src/Web/Avalonia.Web.Blazor/Avalonia.Web.Blazor.csproj

@ -5,6 +5,7 @@
<ImplicitUsings>enable</ImplicitUsings>
<PackageId>Avalonia.Web.Blazor</PackageId>
<LangVersion>preview</LangVersion>
<MSBuildEnableWorkloadResolver>false</MSBuildEnableWorkloadResolver>
<StaticWebAssetsDisableProjectBuildPropsFileGeneration>true</StaticWebAssetsDisableProjectBuildPropsFileGeneration>
</PropertyGroup>

18
src/Windows/Avalonia.Win32/ClipboardFormats.cs

@ -14,11 +14,11 @@ namespace Avalonia.Win32
class ClipboardFormat
{
public short Format { get; private set; }
public ushort Format { get; private set; }
public string Name { get; private set; }
public short[] Synthesized { get; private set; }
public ushort[] Synthesized { get; private set; }
public ClipboardFormat(string name, short format, params short[] synthesized)
public ClipboardFormat(string name, ushort format, params ushort[] synthesized)
{
Format = format;
Name = name;
@ -28,12 +28,12 @@ namespace Avalonia.Win32
private static readonly List<ClipboardFormat> FormatList = new List<ClipboardFormat>()
{
new ClipboardFormat(DataFormats.Text, (short)UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT, (short)UnmanagedMethods.ClipboardFormat.CF_TEXT),
new ClipboardFormat(DataFormats.FileNames, (short)UnmanagedMethods.ClipboardFormat.CF_HDROP),
new ClipboardFormat(DataFormats.Text, (ushort)UnmanagedMethods.ClipboardFormat.CF_UNICODETEXT, (ushort)UnmanagedMethods.ClipboardFormat.CF_TEXT),
new ClipboardFormat(DataFormats.FileNames, (ushort)UnmanagedMethods.ClipboardFormat.CF_HDROP),
};
private static string QueryFormatName(short format)
private static string QueryFormatName(ushort format)
{
StringBuilder sb = new StringBuilder(MAX_FORMAT_NAME_LENGTH);
if (UnmanagedMethods.GetClipboardFormatName(format, sb, sb.Capacity) > 0)
@ -41,7 +41,7 @@ namespace Avalonia.Win32
return null;
}
public static string GetFormat(short format)
public static string GetFormat(ushort format)
{
lock (FormatList)
{
@ -58,7 +58,7 @@ namespace Avalonia.Win32
}
}
public static short GetFormat(string format)
public static ushort GetFormat(string format)
{
lock (FormatList)
{
@ -68,7 +68,7 @@ namespace Avalonia.Win32
int id = UnmanagedMethods.RegisterClipboardFormat(format);
if (id == 0)
throw new Win32Exception();
pd = new ClipboardFormat(format, (short)id);
pd = new ClipboardFormat(format, (ushort)id);
FormatList.Add(pd);
}
return pd.Format;

13
src/Windows/Avalonia.Win32/ClipboardImpl.cs

@ -78,12 +78,13 @@ namespace Avalonia.Win32
public async Task SetDataObjectAsync(IDataObject data)
{
Dispatcher.UIThread.VerifyAccess();
var wrapper = new DataObject(data);
using var wrapper = new DataObject(data);
var i = OleRetryCount;
while (true)
{
var hr = UnmanagedMethods.OleSetClipboard(wrapper);
var ptr = MicroCom.MicroComRuntime.GetNativeIntPtr<Win32Com.IDataObject>(wrapper);
var hr = UnmanagedMethods.OleSetClipboard(ptr);
if (hr == 0)
break;
@ -106,9 +107,9 @@ namespace Avalonia.Win32
if (hr == 0)
{
var wrapper = new OleDataObject(dataObject);
using var proxy = MicroCom.MicroComRuntime.CreateProxyFor<Win32Com.IDataObject>(dataObject, true);
using var wrapper = new OleDataObject(proxy);
var formats = wrapper.GetDataFormats().ToArray();
Marshal.ReleaseComObject(dataObject);
return formats;
}
@ -130,9 +131,9 @@ namespace Avalonia.Win32
if (hr == 0)
{
var wrapper = new OleDataObject(dataObject);
using var proxy = MicroCom.MicroComRuntime.CreateProxyFor<Win32Com.IDataObject>(dataObject, true);
using var wrapper = new OleDataObject(proxy);
var rv = wrapper.Get(format);
Marshal.ReleaseComObject(dataObject);
return rv;
}

219
src/Windows/Avalonia.Win32/DataObject.cs

@ -8,22 +8,25 @@ using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;
using System.Runtime.Serialization.Formatters.Binary;
using Avalonia.Input;
using Avalonia.MicroCom;
using Avalonia.Win32.Interop;
using FORMATETC = Avalonia.Win32.Interop.FORMATETC;
using IDataObject = Avalonia.Input.IDataObject;
namespace Avalonia.Win32
{
class DataObject : IDataObject, IOleDataObject
internal sealed class DataObject : CallbackBase, IDataObject, Win32Com.IDataObject
{
// Compatibility with WinForms + WPF...
internal static readonly byte[] SerializedObjectGUID = new Guid("FD9EA796-3B13-4370-A679-56106BB288FB").ToByteArray();
class FormatEnumerator : IEnumFORMATETC
class FormatEnumerator : CallbackBase, Win32Com.IEnumFORMATETC
{
private FORMATETC[] _formats;
private int _current;
private uint _current;
private FormatEnumerator(FORMATETC[] formats, int current)
private FormatEnumerator(FORMATETC[] formats, uint current)
{
_formats = formats;
_current = current;
@ -46,59 +49,70 @@ namespace Avalonia.Win32
return result;
}
public void Clone(out IEnumFORMATETC newEnum)
{
newEnum = new FormatEnumerator(_formats, _current);
}
public int Next(int celt, FORMATETC[] rgelt, int[] pceltFetched)
public unsafe uint Next(uint celt, FORMATETC* rgelt, uint* results)
{
if (rgelt == null)
return unchecked((int)UnmanagedMethods.HRESULT.E_INVALIDARG);
return (uint)UnmanagedMethods.HRESULT.E_INVALIDARG;
int i = 0;
uint i = 0;
while (i < celt && _current < _formats.Length)
{
rgelt[i] = _formats[_current];
_current++;
i++;
}
if (pceltFetched != null)
pceltFetched[0] = i;
if (i != celt)
return unchecked((int)UnmanagedMethods.HRESULT.S_FALSE);
return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
return (uint)UnmanagedMethods.HRESULT.S_FALSE;
// "results" parameter can be NULL if celt is 1.
if (celt != 1 || results != default)
*results = i;
return 0;
}
public uint Skip(uint celt)
{
_current += Math.Min(celt, int.MaxValue - _current);
if (_current >= _formats.Length)
return (uint)UnmanagedMethods.HRESULT.S_FALSE;
return 0;
}
public int Reset()
public void Reset()
{
_current = 0;
return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
}
public int Skip(int celt)
public Win32Com.IEnumFORMATETC Clone()
{
_current += Math.Min(celt, int.MaxValue - _current);
if (_current >= _formats.Length)
return unchecked((int)UnmanagedMethods.HRESULT.S_FALSE);
return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
return new FormatEnumerator(_formats, _current);
}
}
private const int DV_E_TYMED = unchecked((int)0x80040069);
private const int DV_E_DVASPECT = unchecked((int)0x8004006B);
private const int DV_E_FORMATETC = unchecked((int)0x80040064);
private const int OLE_E_ADVISENOTSUPPORTED = unchecked((int)0x80040003);
private const int STG_E_MEDIUMFULL = unchecked((int)0x80030070);
private const uint DV_E_TYMED = 0x80040069;
private const uint DV_E_DVASPECT = 0x8004006B;
private const uint DV_E_FORMATETC = 0x80040064;
private const uint OLE_E_ADVISENOTSUPPORTED = 0x80040003;
private const uint STG_E_MEDIUMFULL = 0x80030070;
private const int GMEM_ZEROINIT = 0x0040;
private const int GMEM_MOVEABLE = 0x0002;
IDataObject _wrapped;
private IDataObject _wrapped;
public DataObject(IDataObject wrapped)
{
if (wrapped == null)
{
throw new ArgumentNullException(nameof(wrapped));
}
if (_wrapped is DataObject || _wrapped is OleDataObject)
{
throw new InvalidOperationException();
}
_wrapped = wrapped;
}
@ -131,123 +145,123 @@ namespace Avalonia.Win32
#region IOleDataObject
int IOleDataObject.DAdvise(ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection)
unsafe int Win32Com.IDataObject.DAdvise(FORMATETC* pFormatetc, int advf, void* adviseSink)
{
if (_wrapped is IOleDataObject ole)
return ole.DAdvise(ref pFormatetc, advf, adviseSink, out connection);
connection = 0;
return OLE_E_ADVISENOTSUPPORTED;
if (_wrapped is Win32Com.IDataObject ole)
return ole.DAdvise(pFormatetc, advf, adviseSink);
return 0;
}
void IOleDataObject.DUnadvise(int connection)
void Win32Com.IDataObject.DUnadvise(int connection)
{
if (_wrapped is IOleDataObject ole)
if (_wrapped is Win32Com.IDataObject ole)
ole.DUnadvise(connection);
Marshal.ThrowExceptionForHR(OLE_E_ADVISENOTSUPPORTED);
throw new COMException(nameof(OLE_E_ADVISENOTSUPPORTED), unchecked((int)OLE_E_ADVISENOTSUPPORTED));
}
int IOleDataObject.EnumDAdvise(out IEnumSTATDATA enumAdvise)
unsafe void* Win32Com.IDataObject.EnumDAdvise()
{
if (_wrapped is IOleDataObject ole)
return ole.EnumDAdvise(out enumAdvise);
if (_wrapped is Win32Com.IDataObject ole)
return ole.EnumDAdvise();
enumAdvise = null;
return OLE_E_ADVISENOTSUPPORTED;
return null;
}
IEnumFORMATETC IOleDataObject.EnumFormatEtc(DATADIR direction)
Win32Com.IEnumFORMATETC Win32Com.IDataObject.EnumFormatEtc(int direction)
{
if (_wrapped is IOleDataObject ole)
if (_wrapped is Win32Com.IDataObject ole)
return ole.EnumFormatEtc(direction);
if (direction == DATADIR.DATADIR_GET)
if ((DATADIR)direction == DATADIR.DATADIR_GET)
return new FormatEnumerator(_wrapped);
throw new NotSupportedException();
throw new COMException(nameof(UnmanagedMethods.HRESULT.E_NOTIMPL), unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL));
}
int IOleDataObject.GetCanonicalFormatEtc(ref FORMATETC formatIn, out FORMATETC formatOut)
unsafe FORMATETC Win32Com.IDataObject.GetCanonicalFormatEtc(FORMATETC* formatIn)
{
if (_wrapped is IOleDataObject ole)
return ole.GetCanonicalFormatEtc(ref formatIn, out formatOut);
if (_wrapped is Win32Com.IDataObject ole)
return ole.GetCanonicalFormatEtc(formatIn);
formatOut = new FORMATETC();
var formatOut = new FORMATETC();
formatOut.ptd = IntPtr.Zero;
return unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL);
throw new COMException(nameof(UnmanagedMethods.HRESULT.E_NOTIMPL), unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL));
}
void IOleDataObject.GetData(ref FORMATETC format, out STGMEDIUM medium)
unsafe uint Win32Com.IDataObject.GetData(FORMATETC* format, Interop.STGMEDIUM* medium)
{
if (_wrapped is IOleDataObject ole)
if (_wrapped is Win32Com.IDataObject ole)
{
ole.GetData(ref format, out medium);
return;
return ole.GetData(format, medium);
}
if(!format.tymed.HasAllFlags(TYMED.TYMED_HGLOBAL))
Marshal.ThrowExceptionForHR(DV_E_TYMED);
if (format.dwAspect != DVASPECT.DVASPECT_CONTENT)
Marshal.ThrowExceptionForHR(DV_E_DVASPECT);
if (!format->tymed.HasAllFlags(TYMED.TYMED_HGLOBAL))
return DV_E_TYMED;
if (format->dwAspect != DVASPECT.DVASPECT_CONTENT)
return DV_E_DVASPECT;
string fmt = ClipboardFormats.GetFormat(format.cfFormat);
string fmt = ClipboardFormats.GetFormat(format->cfFormat);
if (string.IsNullOrEmpty(fmt) || !_wrapped.Contains(fmt))
Marshal.ThrowExceptionForHR(DV_E_FORMATETC);
return DV_E_FORMATETC;
medium = default(STGMEDIUM);
medium.tymed = TYMED.TYMED_HGLOBAL;
int result = WriteDataToHGlobal(fmt, ref medium.unionmember);
Marshal.ThrowExceptionForHR(result);
* medium = default(Interop.STGMEDIUM);
medium->tymed = TYMED.TYMED_HGLOBAL;
return WriteDataToHGlobal(fmt, ref medium->unionmember);
}
void IOleDataObject.GetDataHere(ref FORMATETC format, ref STGMEDIUM medium)
unsafe uint Win32Com.IDataObject.GetDataHere(FORMATETC* format, Interop.STGMEDIUM* medium)
{
if (_wrapped is IOleDataObject ole)
if (_wrapped is Win32Com.IDataObject ole)
{
ole.GetDataHere(ref format, ref medium);
return;
return ole.GetDataHere(format, medium);
}
if (medium.tymed != TYMED.TYMED_HGLOBAL || !format.tymed.HasAllFlags(TYMED.TYMED_HGLOBAL))
Marshal.ThrowExceptionForHR(DV_E_TYMED);
if (medium->tymed != TYMED.TYMED_HGLOBAL || !format->tymed.HasAllFlags(TYMED.TYMED_HGLOBAL))
return DV_E_TYMED;
if (format.dwAspect != DVASPECT.DVASPECT_CONTENT)
Marshal.ThrowExceptionForHR(DV_E_DVASPECT);
if (format->dwAspect != DVASPECT.DVASPECT_CONTENT)
return DV_E_DVASPECT;
string fmt = ClipboardFormats.GetFormat(format.cfFormat);
string fmt = ClipboardFormats.GetFormat(format->cfFormat);
if (string.IsNullOrEmpty(fmt) || !_wrapped.Contains(fmt))
Marshal.ThrowExceptionForHR(DV_E_FORMATETC);
return DV_E_FORMATETC;
if (medium.unionmember == IntPtr.Zero)
Marshal.ThrowExceptionForHR(STG_E_MEDIUMFULL);
if (medium->unionmember == IntPtr.Zero)
return STG_E_MEDIUMFULL;
int result = WriteDataToHGlobal(fmt, ref medium.unionmember);
Marshal.ThrowExceptionForHR(result);
return WriteDataToHGlobal(fmt, ref medium->unionmember);
}
int IOleDataObject.QueryGetData(ref FORMATETC format)
unsafe uint Win32Com.IDataObject.QueryGetData(FORMATETC* format)
{
if (_wrapped is IOleDataObject ole)
return ole.QueryGetData(ref format);
if (format.dwAspect != DVASPECT.DVASPECT_CONTENT)
if (_wrapped is Win32Com.IDataObject ole)
{
return ole.QueryGetData(format);
}
if (format->dwAspect != DVASPECT.DVASPECT_CONTENT)
return DV_E_DVASPECT;
if (!format.tymed.HasAllFlags(TYMED.TYMED_HGLOBAL))
if (!format->tymed.HasAllFlags(TYMED.TYMED_HGLOBAL))
return DV_E_TYMED;
string dataFormat = ClipboardFormats.GetFormat(format.cfFormat);
if (!string.IsNullOrEmpty(dataFormat) && _wrapped.Contains(dataFormat))
return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
return DV_E_FORMATETC;
var dataFormat = ClipboardFormats.GetFormat(format->cfFormat);
if (string.IsNullOrEmpty(dataFormat) || !_wrapped.Contains(dataFormat))
return DV_E_FORMATETC;
return 0;
}
void IOleDataObject.SetData(ref FORMATETC formatIn, ref STGMEDIUM medium, bool release)
unsafe uint Win32Com.IDataObject.SetData(FORMATETC* pformatetc, Interop.STGMEDIUM* pmedium, int fRelease)
{
if (_wrapped is IOleDataObject ole)
if (_wrapped is Win32Com.IDataObject ole)
{
ole.SetData(ref formatIn, ref medium, release);
return;
return ole.SetData(pformatetc, pmedium, fRelease);
}
Marshal.ThrowExceptionForHR(unchecked((int)UnmanagedMethods.HRESULT.E_NOTIMPL));
return (uint)UnmanagedMethods.HRESULT.E_NOTIMPL;
}
private int WriteDataToHGlobal(string dataFormat, ref IntPtr hGlobal)
private uint WriteDataToHGlobal(string dataFormat, ref IntPtr hGlobal)
{
object data = _wrapped.Get(dataFormat);
if (dataFormat == DataFormats.Text || data is string)
@ -288,7 +302,7 @@ namespace Avalonia.Win32
}
}
private unsafe int WriteBytesToHGlobal(ref IntPtr hGlobal, ReadOnlySpan<byte> data)
private unsafe uint WriteBytesToHGlobal(ref IntPtr hGlobal, ReadOnlySpan<byte> data)
{
int required = data.Length;
if (hGlobal == IntPtr.Zero)
@ -312,7 +326,7 @@ namespace Avalonia.Win32
}
}
private int WriteFileListToHGlobal(ref IntPtr hGlobal, IEnumerable<string> files)
private uint WriteFileListToHGlobal(ref IntPtr hGlobal, IEnumerable<string> files)
{
if (!files?.Any() ?? false)
return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
@ -344,7 +358,7 @@ namespace Avalonia.Win32
}
}
private int WriteStringToHGlobal(ref IntPtr hGlobal, string data)
private uint WriteStringToHGlobal(ref IntPtr hGlobal, string data)
{
int required = (data.Length + 1) * sizeof(char);
if (hGlobal == IntPtr.Zero)
@ -367,6 +381,15 @@ namespace Avalonia.Win32
}
}
protected override void Destroyed()
{
ReleaseWrapped();
}
public void ReleaseWrapped()
{
_wrapped = null;
}
#endregion
}
}

21
src/Windows/Avalonia.Win32/DragSource.cs

@ -8,17 +8,26 @@ namespace Avalonia.Win32
{
class DragSource : IPlatformDragSource
{
public Task<DragDropEffects> DoDragDrop(PointerEventArgs triggerEvent,
public unsafe Task<DragDropEffects> DoDragDrop(PointerEventArgs triggerEvent,
IDataObject data, DragDropEffects allowedEffects)
{
Dispatcher.UIThread.VerifyAccess();
triggerEvent.Pointer.Capture(null);
OleDragSource src = new OleDragSource();
DataObject dataObject = new DataObject(data);
int allowed = (int)OleDropTarget.ConvertDropEffect(allowedEffects);
using var dataObject = new DataObject(data);
using var src = new OleDragSource();
var allowed = OleDropTarget.ConvertDropEffect(allowedEffects);
var objPtr = MicroCom.MicroComRuntime.GetNativeIntPtr<Win32Com.IDataObject>(dataObject);
var srcPtr = MicroCom.MicroComRuntime.GetNativeIntPtr<Win32Com.IDropSource>(src);
UnmanagedMethods.DoDragDrop(objPtr, srcPtr, (int)allowed, out var finalEffect);
// Force releasing of internal wrapper to avoid memory leak, if drop target keeps com reference.
dataObject.ReleaseWrapped();
UnmanagedMethods.DoDragDrop(dataObject, src, allowed, out var finalEffect);
return Task.FromResult(OleDropTarget.ConvertDropEffect((DropEffect)finalEffect));
return Task.FromResult(OleDropTarget.ConvertDropEffect((Win32Com.DropEffect)finalEffect));
}
}
}

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

@ -1277,10 +1277,10 @@ namespace Avalonia.Win32.Interop
public static extern IntPtr SetClipboardData(ClipboardFormat uFormat, IntPtr hMem);
[DllImport("ole32.dll", PreserveSig = false)]
public static extern int OleGetClipboard(out IOleDataObject dataObject);
public static extern int OleGetClipboard(out IntPtr dataObject);
[DllImport("ole32.dll", PreserveSig = true)]
public static extern int OleSetClipboard(IOleDataObject dataObject);
public static extern int OleSetClipboard(IntPtr dataObject);
[DllImport("kernel32.dll", ExactSpelling = true)]
public static extern IntPtr GlobalLock(IntPtr handle);
@ -1424,7 +1424,7 @@ namespace Avalonia.Win32.Interop
public static extern IntPtr CopyMemory(IntPtr dest, IntPtr src, UIntPtr count);
[DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern HRESULT RegisterDragDrop(IntPtr hwnd, IDropTarget target);
public static extern HRESULT RegisterDragDrop(IntPtr hwnd, IntPtr target);
[DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern HRESULT RevokeDragDrop(IntPtr hwnd);
@ -1448,7 +1448,7 @@ namespace Avalonia.Win32.Interop
public static extern int DragQueryFile(IntPtr hDrop, int iFile, StringBuilder lpszFile, int cch);
[DllImport("ole32.dll", CharSet = CharSet.Auto, ExactSpelling = true, PreserveSig = false)]
internal static extern void DoDragDrop(IOleDataObject dataObject, IDropSource dropSource, int allowedEffects, out int finalEffect);
internal static extern void DoDragDrop(IntPtr dataObject, IntPtr dropSource, int allowedEffects, [Out] out int finalEffect);
[DllImport("dwmapi.dll")]
public static extern int DwmExtendFrameIntoClientArea(IntPtr hwnd, ref MARGINS margins);
@ -2064,64 +2064,6 @@ namespace Avalonia.Win32.Interop
}
}
[Flags]
internal enum DropEffect : int
{
None = 0,
Copy = 1,
Move = 2,
Link = 4,
Scroll = -2147483648,
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("00000122-0000-0000-C000-000000000046")]
internal interface IDropTarget
{
[PreserveSig]
UnmanagedMethods.HRESULT DragEnter([MarshalAs(UnmanagedType.Interface)] [In] IOleDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect);
[PreserveSig]
UnmanagedMethods.HRESULT DragOver([MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect);
[PreserveSig]
UnmanagedMethods.HRESULT DragLeave();
[PreserveSig]
UnmanagedMethods.HRESULT Drop([MarshalAs(UnmanagedType.Interface)] [In] IOleDataObject pDataObj, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState, [MarshalAs(UnmanagedType.U8)] [In] long pt, [In] [Out] ref DropEffect pdwEffect);
}
[ComImport]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[Guid("00000121-0000-0000-C000-000000000046")]
internal interface IDropSource
{
[PreserveSig]
int QueryContinueDrag(int fEscapePressed, [MarshalAs(UnmanagedType.U4)] [In] int grfKeyState);
[PreserveSig]
int GiveFeedback([MarshalAs(UnmanagedType.U4)] [In] int dwEffect);
}
[ComImport]
[Guid("0000010E-0000-0000-C000-000000000046")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IOleDataObject
{
void GetData([In] ref FORMATETC format, out STGMEDIUM medium);
void GetDataHere([In] ref FORMATETC format, ref STGMEDIUM medium);
[PreserveSig]
int QueryGetData([In] ref FORMATETC format);
[PreserveSig]
int GetCanonicalFormatEtc([In] ref FORMATETC formatIn, out FORMATETC formatOut);
void SetData([In] ref FORMATETC formatIn, [In] ref STGMEDIUM medium, [MarshalAs(UnmanagedType.Bool)] bool release);
IEnumFORMATETC EnumFormatEtc(DATADIR direction);
[PreserveSig]
int DAdvise([In] ref FORMATETC pFormatetc, ADVF advf, IAdviseSink adviseSink, out int connection);
void DUnadvise(int connection);
[PreserveSig]
int EnumDAdvise(out IEnumSTATDATA enumAdvise);
}
[StructLayoutAttribute(LayoutKind.Sequential)]
internal struct _DROPFILES
{
@ -2132,6 +2074,24 @@ namespace Avalonia.Win32.Interop
public bool fWide;
}
[StructLayoutAttribute(LayoutKind.Sequential)]
internal struct STGMEDIUM
{
public TYMED tymed;
public IntPtr unionmember;
public IntPtr pUnkForRelease;
}
[StructLayoutAttribute(LayoutKind.Sequential)]
internal struct FORMATETC
{
public ushort cfFormat;
public IntPtr ptd;
public DVASPECT dwAspect;
public int lindex;
public TYMED tymed;
}
[Flags]
internal enum PixelFormatDescriptorFlags : uint
{

4
src/Windows/Avalonia.Win32/OleContext.cs

@ -4,6 +4,7 @@ using System.Threading;
using Avalonia.Platform;
using Avalonia.Threading;
using Avalonia.Win32.Interop;
using Avalonia.Win32.Win32Com;
namespace Avalonia.Win32
{
@ -46,7 +47,8 @@ namespace Avalonia.Win32
return false;
}
return UnmanagedMethods.RegisterDragDrop(hwnd.Handle, target) == UnmanagedMethods.HRESULT.S_OK;
var trgPtr = MicroCom.MicroComRuntime.GetNativeIntPtr(target);
return UnmanagedMethods.RegisterDragDrop(hwnd.Handle, trgPtr) == UnmanagedMethods.HRESULT.S_OK;
}
internal bool UnregisterDragDrop(IPlatformHandle hwnd)

60
src/Windows/Avalonia.Win32/OleDataObject.cs

@ -1,7 +1,6 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
@ -9,17 +8,20 @@ using System.Runtime.InteropServices.ComTypes;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using Avalonia.Input;
using Avalonia.MicroCom;
using Avalonia.Win32.Interop;
using IDataObject = Avalonia.Input.IDataObject;
namespace Avalonia.Win32
{
class OleDataObject : Avalonia.Input.IDataObject
internal class OleDataObject : IDataObject, IDisposable
{
private IOleDataObject _wrapped;
private readonly Win32Com.IDataObject _wrapped;
public OleDataObject(IOleDataObject wrapped)
public OleDataObject(Win32Com.IDataObject wrapped)
{
_wrapped = wrapped;
_wrapped = wrapped.CloneReference();
}
public bool Contains(string dataFormat)
@ -34,12 +36,12 @@ namespace Avalonia.Win32
public string GetText()
{
return GetDataFromOleHGLOBAL(DataFormats.Text, DVASPECT.DVASPECT_CONTENT) as string;
return (string)GetDataFromOleHGLOBAL(DataFormats.Text, DVASPECT.DVASPECT_CONTENT);
}
public IEnumerable<string> GetFileNames()
{
return GetDataFromOleHGLOBAL(DataFormats.FileNames, DVASPECT.DVASPECT_CONTENT) as IEnumerable<string>;
return (IEnumerable<string>)GetDataFromOleHGLOBAL(DataFormats.FileNames, DVASPECT.DVASPECT_CONTENT);
}
public object Get(string dataFormat)
@ -47,16 +49,17 @@ namespace Avalonia.Win32
return GetDataFromOleHGLOBAL(dataFormat, DVASPECT.DVASPECT_CONTENT);
}
private object GetDataFromOleHGLOBAL(string format, DVASPECT aspect)
private unsafe object GetDataFromOleHGLOBAL(string format, DVASPECT aspect)
{
FORMATETC formatEtc = new FORMATETC();
var formatEtc = new Interop.FORMATETC();
formatEtc.cfFormat = ClipboardFormats.GetFormat(format);
formatEtc.dwAspect = aspect;
formatEtc.lindex = -1;
formatEtc.tymed = TYMED.TYMED_HGLOBAL;
if (_wrapped.QueryGetData(ref formatEtc) == 0)
if (_wrapped.QueryGetData(&formatEtc) == 0)
{
_wrapped.GetData(ref formatEtc, out STGMEDIUM medium);
Interop.STGMEDIUM medium = default;
_ = _wrapped.GetData(&formatEtc, &medium);
try
{
if (medium.unionmember != IntPtr.Zero && medium.tymed == TYMED.TYMED_HGLOBAL)
@ -140,38 +143,53 @@ namespace Avalonia.Win32
}
}
private IEnumerable<string> GetDataFormatsCore()
private unsafe IEnumerable<string> GetDataFormatsCore()
{
var enumFormat = _wrapped.EnumFormatEtc(DATADIR.DATADIR_GET);
var formatsList = new List<string>();
var enumFormat = _wrapped.EnumFormatEtc((int)DATADIR.DATADIR_GET);
if (enumFormat != null)
{
enumFormat.Reset();
var formats = ArrayPool<FORMATETC>.Shared.Rent(1);
var fetched = ArrayPool<int>.Shared.Rent(1);
var formats = ArrayPool<Interop.FORMATETC>.Shared.Rent(1);
try
{
uint fetched = 0;
do
{
fetched[0] = 0;
if (enumFormat.Next(1, formats, fetched) == 0 && fetched[0] > 0)
fixed (Interop.FORMATETC* formatsPtr = formats)
{
// Read one item at a time.
// When "celt" parameter is 1, "pceltFetched" is ignored.
var res = enumFormat.Next(1, formatsPtr, &fetched);
if (res != 0)
{
break;
}
}
if (fetched > 0)
{
if (formats[0].ptd != IntPtr.Zero)
Marshal.FreeCoTaskMem(formats[0].ptd);
yield return ClipboardFormats.GetFormat(formats[0].cfFormat);
formatsList.Add(ClipboardFormats.GetFormat(formats[0].cfFormat));
}
}
while (fetched[0] > 0);
while (fetched > 0);
}
finally
{
ArrayPool<FORMATETC>.Shared.Return(formats);
ArrayPool<int>.Shared.Return(fetched);
ArrayPool<Interop.FORMATETC>.Shared.Return(formats);
}
}
return formatsList;
}
public void Dispose()
{
_wrapped.Dispose();
}
}
}

7
src/Windows/Avalonia.Win32/OleDragSource.cs

@ -1,9 +1,12 @@
using System.Linq;
using Avalonia.MicroCom;
using Avalonia.Win32.Interop;
using Avalonia.Win32.Win32Com;
namespace Avalonia.Win32
{
class OleDragSource : IDropSource
internal class OleDragSource : CallbackBase, IDropSource
{
private const int DRAGDROP_S_USEDEFAULTCURSORS = 0x00040102;
private const int DRAGDROP_S_DROP = 0x00040100;
@ -30,7 +33,7 @@ namespace Avalonia.Win32
return unchecked((int)UnmanagedMethods.HRESULT.S_OK);
}
public int GiveFeedback(int dwEffect)
public int GiveFeedback(DropEffect dwEffect)
{
return DRAGDROP_S_USEDEFAULTCURSORS;
}

125
src/Windows/Avalonia.Win32/OleDropTarget.cs

@ -1,12 +1,15 @@
using Avalonia.Input;
using System;
using Avalonia.Input;
using Avalonia.Input.Raw;
using Avalonia.MicroCom;
using Avalonia.Platform;
using Avalonia.Win32.Interop;
using IDataObject = Avalonia.Input.IDataObject;
using DropEffect = Avalonia.Win32.Win32Com.DropEffect;
namespace Avalonia.Win32
{
internal class OleDropTarget : IDropTarget
internal class OleDropTarget : CallbackBase, Win32Com.IDropTarget
{
private readonly IInputRoot _target;
private readonly ITopLevelImpl _tl;
@ -65,58 +68,51 @@ namespace Avalonia.Win32
return modifiers;
}
UnmanagedMethods.HRESULT IDropTarget.DragEnter(IOleDataObject pDataObj, int grfKeyState, long pt, ref DropEffect pdwEffect)
unsafe void Win32Com.IDropTarget.DragEnter(Win32Com.IDataObject pDataObj, int grfKeyState, UnmanagedMethods.POINT pt, DropEffect* pdwEffect)
{
var dispatch = _tl?.Input;
if (dispatch == null)
{
pdwEffect = DropEffect.None;
return UnmanagedMethods.HRESULT.S_OK;
*pdwEffect= (int)DropEffect.None;
}
_currentDrag = pDataObj as IDataObject;
if (_currentDrag == null)
_currentDrag = new OleDataObject(pDataObj);
SetDataObject(pDataObj);
var args = new RawDragEvent(
_dragDevice,
RawDragEventType.DragEnter,
_target,
GetDragLocation(pt),
_target,
GetDragLocation(pt),
_currentDrag,
ConvertDropEffect(pdwEffect),
ConvertDropEffect(*pdwEffect),
ConvertKeyState(grfKeyState)
);
dispatch(args);
pdwEffect = ConvertDropEffect(args.Effects);
return UnmanagedMethods.HRESULT.S_OK;
*pdwEffect = ConvertDropEffect(args.Effects);
}
UnmanagedMethods.HRESULT IDropTarget.DragOver(int grfKeyState, long pt, ref DropEffect pdwEffect)
unsafe void Win32Com.IDropTarget.DragOver(int grfKeyState, UnmanagedMethods.POINT pt, DropEffect* pdwEffect)
{
var dispatch = _tl?.Input;
if (dispatch == null)
{
pdwEffect = DropEffect.None;
return UnmanagedMethods.HRESULT.S_OK;
*pdwEffect = (int)DropEffect.None;
}
var args = new RawDragEvent(
_dragDevice,
RawDragEventType.DragOver,
_target,
GetDragLocation(pt),
_currentDrag,
ConvertDropEffect(pdwEffect),
_target,
GetDragLocation(pt),
_currentDrag,
ConvertDropEffect(*pdwEffect),
ConvertKeyState(grfKeyState)
);
dispatch(args);
pdwEffect = ConvertDropEffect(args.Effects);
return UnmanagedMethods.HRESULT.S_OK;
*pdwEffect = ConvertDropEffect(args.Effects);
}
UnmanagedMethods.HRESULT IDropTarget.DragLeave()
void Win32Com.IDropTarget.DragLeave()
{
try
{
@ -129,56 +125,91 @@ namespace Avalonia.Win32
DragDropEffects.None,
RawInputModifiers.None
));
return UnmanagedMethods.HRESULT.S_OK;
}
finally
{
_currentDrag = null;
ReleaseDataObject();
}
}
UnmanagedMethods.HRESULT IDropTarget.Drop(IOleDataObject pDataObj, int grfKeyState, long pt, ref DropEffect pdwEffect)
unsafe void Win32Com.IDropTarget.Drop(Win32Com.IDataObject pDataObj, int grfKeyState, UnmanagedMethods.POINT pt, DropEffect* pdwEffect)
{
try
{
var dispatch = _tl?.Input;
if (dispatch == null)
{
pdwEffect = DropEffect.None;
return UnmanagedMethods.HRESULT.S_OK;
*pdwEffect = (int)DropEffect.None;
}
_currentDrag = pDataObj as IDataObject;
if (_currentDrag == null)
_currentDrag= new OleDataObject(pDataObj);
SetDataObject(pDataObj);
var args = new RawDragEvent(
_dragDevice,
RawDragEventType.Drop,
_target,
GetDragLocation(pt),
_currentDrag,
ConvertDropEffect(pdwEffect),
_target,
GetDragLocation(pt),
_currentDrag,
ConvertDropEffect(*pdwEffect),
ConvertKeyState(grfKeyState)
);
dispatch(args);
pdwEffect = ConvertDropEffect(args.Effects);
return UnmanagedMethods.HRESULT.S_OK;
*pdwEffect = ConvertDropEffect(args.Effects);
}
finally
{
_currentDrag = null;
ReleaseDataObject();
}
}
private Point GetDragLocation(long dragPoint)
private void SetDataObject(Win32Com.IDataObject pDataObj)
{
int x = (int)dragPoint;
int y = (int)(dragPoint >> 32);
var newDrag = GetAvaloniaObjectFromCOM(pDataObj);
if (_currentDrag != newDrag)
{
ReleaseDataObject();
_currentDrag = newDrag;
}
}
var screenPt = new PixelPoint(x, y);
private void ReleaseDataObject()
{
// OleDataObject keeps COM reference, so it should be disposed.
if (_currentDrag is OleDataObject oleDragSource)
{
oleDragSource?.Dispose();
_currentDrag = null;
}
}
private Point GetDragLocation(UnmanagedMethods.POINT dragPoint)
{
var screenPt = new PixelPoint(dragPoint.X, dragPoint.Y);
return _target.PointToClient(screenPt);
}
protected override void Destroyed()
{
ReleaseDataObject();
}
public static unsafe IDataObject GetAvaloniaObjectFromCOM(Win32Com.IDataObject pDataObj)
{
if (pDataObj is null)
{
throw new ArgumentNullException(nameof(pDataObj));
}
if (pDataObj is IDataObject disposableDataObject)
{
return disposableDataObject;
}
var dataObject = MicroComRuntime.TryUnwrapManagedObject(pDataObj) as DataObject;
if (dataObject is not null)
{
return dataObject;
}
return new OleDataObject(pDataObj);
}
}
}

123
src/Windows/Avalonia.Win32/Win32Com/win32.idl

@ -27,6 +27,8 @@
@clr-map REFGUID System.Guid*
@clr-map REFIID System.Guid*
@clr-map WCHAR System.Char
@clr-map STGMEDIUM Avalonia.Win32.Interop.STGMEDIUM
@clr-map FORMATETC Avalonia.Win32.Interop.FORMATETC
[flags]
enum FILEOPENDIALOGOPTIONS
@ -53,6 +55,16 @@ enum FILEOPENDIALOGOPTIONS
FOS_DEFAULTNOMINIMODE = 0x20000000
}
[flags]
enum DropEffect
{
None = 0,
Copy = 1,
Move = 2,
Link = 4,
Scroll = -2147483648,
}
[
object,
uuid(43826d1e-e718-42ee-bc55-a1e261c37bfe),
@ -189,3 +201,114 @@ interface IFileOpenDialog : IFileDialog
HRESULT GetSelectedItems(
[out] IShellItemArray** ppsai);
}
[
object,
uuid(00000103-0000-0000-C000-000000000046),
pointer_default(unique)
]
interface IEnumFORMATETC : IUnknown
{
UINT32 Next(
[in] ULONG celt,
[out] FORMATETC* rgelt,
ULONG* pceltFetched);
UINT32 Skip(
[in] ULONG celt);
HRESULT Reset();
HRESULT Clone(
[out] IEnumFORMATETC** ppenum);
}
[
object,
uuid(0000010e-0000-0000-C000-000000000046),
pointer_default(unique)
]
interface IDataObject : IUnknown
{
UINT32 GetData(
[in, unique] FORMATETC* pformatetcIn,
[out] STGMEDIUM* pmedium);
UINT32 GetDataHere(
[in, unique] FORMATETC* pformatetc,
[in] STGMEDIUM* pmedium);
UINT32 QueryGetData(
[in, unique] FORMATETC* pformatetc);
HRESULT GetCanonicalFormatEtc(
[in, unique] FORMATETC* pformatectIn,
[out] FORMATETC* pformatetcOut);
UINT32 SetData(
[in, unique] FORMATETC* pformatetc,
[in, unique] STGMEDIUM* pmedium,
[in] BOOL fRelease);
HRESULT EnumFormatEtc(
[in] DWORD dwDirection,
[out] IEnumFORMATETC** ppenumFormatEtc);
HRESULT DAdvise(
[in] FORMATETC* pformatetc,
[in] DWORD advf,
[in, unique] void* pAdvSink,
[out] DWORD* pdwConnection);
HRESULT DUnadvise(
[in] DWORD dwConnection);
HRESULT EnumDAdvise(
[out] void** ppenumAdvise);
}
[
local,
object,
uuid(00000121-0000-0000-C000-000000000046)
]
interface IDropSource : IUnknown
{
INT32 QueryContinueDrag([in] BOOL fEscapePressed, [in] DWORD grfKeyState);
INT32 GiveFeedback([in] DropEffect dwEffect);
}
[
object,
uuid(00000122-0000-0000-C000-000000000046),
pointer_default(unique)
]
interface IDropTarget : IUnknown
{
HRESULT DragEnter
(
[in, unique] IDataObject* pDataObj,
[in] DWORD grfKeyState,
[in] POINT pt,
[in] DropEffect* pdwEffect
);
HRESULT DragOver
(
[in] DWORD grfKeyState,
[in] POINT pt,
[in] DropEffect* pdwEffect
);
HRESULT DragLeave();
HRESULT Drop
(
[in, unique] IDataObject* pDataObj,
[in] DWORD grfKeyState,
[in] POINT pt,
[in] DropEffect* pdwEffect
);
}

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

@ -529,6 +529,7 @@ namespace Avalonia.Win32
if (_dropTarget != null)
{
OleContext.Current?.UnregisterDragDrop(Handle);
_dropTarget.Dispose();
_dropTarget = null;
}

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

Loading…
Cancel
Save