+
-
+
CreateIslandAsync(Compositor compositor, Vector2 size)
+ {
+ ContainerVisual root = compositor.CreateContainerVisual();
+ root.Size = size;
+
+ SpriteVisual background = compositor.CreateSpriteVisual();
+ background.Size = size;
+
+ CompositionLinearGradientBrush backgroundBrush = compositor.CreateLinearGradientBrush();
+ backgroundBrush.StartPoint = Vector2.Zero;
+ backgroundBrush.EndPoint = Vector2.One;
+ backgroundBrush.ColorStops.Add(
+ compositor.CreateColorGradientStop(0, Color.FromArgb(255, 0, 99, 177)));
+ backgroundBrush.ColorStops.Add(
+ compositor.CreateColorGradientStop(1, Color.FromArgb(255, 80, 230, 210)));
+ background.Brush = backgroundBrush;
+ root.Children.InsertAtBottom(background);
+
+ float diameter = MathF.Max(48, MathF.Min(size.X, size.Y) * 0.42f);
+ SpriteVisual accent = compositor.CreateSpriteVisual();
+ accent.Size = new Vector2(diameter);
+ accent.Offset = new Vector3((size.X - diameter) / 2, (size.Y - diameter) / 2, 0);
+ accent.CenterPoint = new Vector3(diameter / 2, diameter / 2, 0);
+ accent.Brush = compositor.CreateColorBrush(Color.FromArgb(220, 255, 255, 255));
+ root.Children.InsertAtTop(accent);
+
+ ScalarKeyFrameAnimation rotation = compositor.CreateScalarKeyFrameAnimation();
+ rotation.InsertKeyFrame(0, 0);
+ rotation.InsertKeyFrame(1, 360);
+ rotation.Duration = TimeSpan.FromSeconds(8);
+ rotation.IterationBehavior = AnimationIterationBehavior.Forever;
+ accent.StartAnimation("RotationAngleInDegrees", rotation);
+
+ ContentIsland island = ContentIsland.CreateForSystemVisual(
+ DispatcherQueue.GetForCurrentThread(),
+ root);
+ return Task.FromResult(island);
+ }
+}
diff --git a/WinUIGallery/Samples/PullToRefresh/PullToRefreshPage.xaml.cs b/WinUIGallery/Samples/PullToRefresh/PullToRefreshPage.xaml.cs
index e2d7f9f59..9d9e9992c 100644
--- a/WinUIGallery/Samples/PullToRefresh/PullToRefreshPage.xaml.cs
+++ b/WinUIGallery/Samples/PullToRefresh/PullToRefreshPage.xaml.cs
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
-using Microsoft.UI.Composition;
using Microsoft.UI.Dispatching;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
@@ -13,6 +12,7 @@
using System.Collections.ObjectModel;
using Windows.Foundation;
using Windows.Foundation.Metadata;
+using Windows.UI.Composition;
using Windows.UI.ViewManagement;
using WinUIGallery.Helpers;
diff --git a/WinUIGallery/Samples/ScrollView/ScrollViewPage.xaml.cs b/WinUIGallery/Samples/ScrollView/ScrollViewPage.xaml.cs
index aa204c185..958c4bae8 100644
--- a/WinUIGallery/Samples/ScrollView/ScrollViewPage.xaml.cs
+++ b/WinUIGallery/Samples/ScrollView/ScrollViewPage.xaml.cs
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
-using Microsoft.UI.Composition;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;
@@ -9,6 +8,7 @@
using System.IO;
using System.Numerics;
using Windows.Globalization.NumberFormatting;
+using Windows.UI.Composition;
using WinUIGallery.Helpers;
namespace WinUIGallery.ControlPages;
diff --git a/WinUIGallery/Samples/WebView2/WebView2Page.xaml b/WinUIGallery/Samples/WebView2/WebView2Page.xaml
index 8bf3104c9..a154bbabc 100644
--- a/WinUIGallery/Samples/WebView2/WebView2Page.xaml
+++ b/WinUIGallery/Samples/WebView2/WebView2Page.xaml
@@ -18,29 +18,99 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">
-
-
-
-
-
-
-
-
- WebView2 is powered by the Chromium engine.
-
-
-
-
+
+
+
+
+
+
+
+
+
+ WebView2 is powered by the Chromium engine.
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/WinUIGallery/Samples/WebView2/WebView2Page.xaml.cs b/WinUIGallery/Samples/WebView2/WebView2Page.xaml.cs
index ac31ec625..c5637a4e9 100644
--- a/WinUIGallery/Samples/WebView2/WebView2Page.xaml.cs
+++ b/WinUIGallery/Samples/WebView2/WebView2Page.xaml.cs
@@ -1,14 +1,119 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
+using Microsoft.UI;
using Microsoft.UI.Xaml.Controls;
+using Microsoft.UI.Xaml.Hosting;
+using System;
+using System.Threading.Tasks;
+using Windows.UI;
+using Windows.UI.Composition;
namespace WinUIGallery.ControlPages;
public sealed partial class WebView2Page : Page
{
+ private const string TransparentTestHtml = """
+
+
+
+
+
+
+
+ Transparent HTML; the XAML gradient should remain visible.
+
+
+ """;
+
+ private bool _transparencyTestInitialized;
+
public WebView2Page()
{
- this.InitializeComponent();
+ InitializeComponent();
+ Loaded += WebView2Page_Loaded;
+ }
+
+ private async void WebView2Page_Loaded(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
+ {
+ if (!_transparencyTestInitialized)
+ {
+ await TransparentWebView2.EnsureCoreWebView2Async();
+ TransparentWebView2.NavigateToString(TransparentTestHtml);
+ _transparencyTestInitialized = true;
+ }
+
+ await RefreshTransparencyDiagnosticAsync();
+ }
+
+ private async void UseTransparentBackgroundToggle_Toggled(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
+ {
+ TransparentWebView2.DefaultBackgroundColor =
+ UseTransparentBackgroundToggle.IsOn ? Colors.Transparent : Colors.White;
+ await RefreshTransparencyDiagnosticAsync();
+ }
+
+ private async void RefreshTransparencyDiagnostic_Click(object sender, Microsoft.UI.Xaml.RoutedEventArgs e)
+ {
+ await RefreshTransparencyDiagnosticAsync();
+ }
+
+ private async Task RefreshTransparencyDiagnosticAsync()
+ {
+ if (!_transparencyTestInitialized)
+ {
+ return;
+ }
+
+ await Task.Delay(500);
+
+ Color requestedColor = TransparentWebView2.DefaultBackgroundColor;
+ Visual? childVisual = ElementCompositionPreview.GetElementChildVisual(TransparentWebView2);
+
+ if (childVisual is SpriteVisual spriteVisual &&
+ spriteVisual.Brush is CompositionColorBrush colorBrush)
+ {
+ Color hostColor = colorBrush.Color;
+ bool reproducesTransparencyBug = requestedColor.A == 0 && hostColor.A != 0;
+
+ TransparencyVerdictText.Text = reproducesTransparencyBug
+ ? "REPRODUCED: WebView2 requested transparency, but its host visual is opaque."
+ : requestedColor.A == 0
+ ? "NOT REPRODUCED: WebView2 and its host visual are transparent."
+ : "Opaque baseline enabled.";
+ TransparencyDetailsText.Text =
+ $"Requested background: {FormatColor(requestedColor)}; host visual brush: {FormatColor(hostColor)}.";
+ return;
+ }
+
+ TransparencyVerdictText.Text =
+ "INCONCLUSIVE: the WebView2 host visual or its color brush was not available.";
+ TransparencyDetailsText.Text =
+ $"Requested background: {FormatColor(requestedColor)}.";
+ }
+
+ private static string FormatColor(Color color)
+ {
+ return $"#{color.A:X2}{color.R:X2}{color.G:X2}{color.B:X2}";
}
}
diff --git a/WinUIGallery/Samples/WebView2/WebView2Transparency.txt b/WinUIGallery/Samples/WebView2/WebView2Transparency.txt
new file mode 100644
index 000000000..57bc3f855
--- /dev/null
+++ b/WinUIGallery/Samples/WebView2/WebView2Transparency.txt
@@ -0,0 +1,25 @@
+--- header
+Test a transparent WebView2 over XAML content
+--- xaml
+
+
+
+
+
+
+
+
+
+
+
+--- csharp
+await MyWebView2.EnsureCoreWebView2Async();
+MyWebView2.NavigateToString("""
+
+
+ Transparent HTML
+
+
+ """);
diff --git a/WinUIGallery/Samples/XamlCompInterop/XamlCompInteropPage.xaml.cs b/WinUIGallery/Samples/XamlCompInterop/XamlCompInteropPage.xaml.cs
index e737fd44d..44b7662e9 100644
--- a/WinUIGallery/Samples/XamlCompInterop/XamlCompInteropPage.xaml.cs
+++ b/WinUIGallery/Samples/XamlCompInterop/XamlCompInteropPage.xaml.cs
@@ -1,7 +1,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
-using Microsoft.UI.Composition;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Automation;
using Microsoft.UI.Xaml.Controls;
@@ -10,6 +9,7 @@
using System;
using System.Globalization;
using System.Numerics;
+using Windows.UI.Composition;
namespace WinUIGallery.ControlPages;
@@ -57,7 +57,7 @@ TimeSpan GetPeriod()
return TimeSpan.FromMilliseconds(PeriodSlider.Value);
}
- private void StartAnimationIfAPIPresent(UIElement sender, Microsoft.UI.Composition.CompositionAnimation animation)
+ private void StartAnimationIfAPIPresent(UIElement sender, CompositionAnimation animation)
{
(sender as UIElement).StartAnimation(animation);
}
diff --git a/WinUIGallery/WinUIGallery.csproj b/WinUIGallery/WinUIGallery.csproj
index aabb79cfb..c771cd84e 100644
--- a/WinUIGallery/WinUIGallery.csproj
+++ b/WinUIGallery/WinUIGallery.csproj
@@ -202,4 +202,70 @@
+
+
+ <_WinUIBaseAppManifest>$(MSBuildProjectDirectory)\app.manifest
+ <_WinUIRegistrationFile>$(NuGetPackageRoot)microsoft.windowsappsdk.winui\$(WinUIVersion)\buildTransitive\native\LiftedWinRTClassRegistrations.xml
+ <_WinUIManifestMergeScript>$(MSBuildProjectDirectory)\..\scripts\MergeWinUIAppManifest.ps1
+ <_WinUIAugmentedManifest>$(IntermediateOutputPath)WinUIAugmentedAppManifest.manifest
+ <_WinAppSDKResourceExtractionScript>$(MSBuildProjectDirectory)\..\scripts\ExtractAppxFile.ps1
+ <_WinAppSDKInsightsResource>$(IntermediateOutputPath)Microsoft.WindowsAppRuntime.Insights.Resource.dll
+ <_WinAppSDKRuntimeArchitecture Condition="'$(Platform)' == 'x86' or '$(Platform)' == 'Win32'">x86
+ <_WinAppSDKRuntimeArchitecture Condition="'$(Platform)' == 'x64'">x64
+ <_WinAppSDKRuntimeArchitecture Condition="'$(Platform)' == 'ARM64'">arm64
+
+
+
+
+
+
+
+
+
+
+ <_WinAppSDKRuntimeMsix Include="@(AppxPackageRegistration)"
+ Condition="'%(AppxPackageRegistration.WindowsAppSDK)' == 'true' and '%(AppxPackageRegistration.Architecture)' == '$(_WinAppSDKRuntimeArchitecture)'" />
+ <_WinAppSDKRuntimeMsix Include="$(MicrosoftWindowsAppSDKPackageDir)tools\MSIX\win10-$(_WinAppSDKRuntimeArchitecture)\$(WinAppSDKPackageName).msix"
+ Condition="'@(_WinAppSDKRuntimeMsix)' == '' and '$(WinAppSDKPackageName)' != ''" />
+ <_WinAppSDKRuntimeMsix Include="$(MicrosoftWindowsAppSDKPackageDir)tools\MSIX\win10-$(_WinAppSDKRuntimeArchitecture)\Microsoft.WindowsAppRuntime.*.msix"
+ Exclude="$(MicrosoftWindowsAppSDKPackageDir)tools\MSIX\win10-$(_WinAppSDKRuntimeArchitecture)\Microsoft.WindowsAppRuntime.DDLM.*.msix;$(MicrosoftWindowsAppSDKPackageDir)tools\MSIX\win10-$(_WinAppSDKRuntimeArchitecture)\Microsoft.WindowsAppRuntime.Main.*.msix;$(MicrosoftWindowsAppSDKPackageDir)tools\MSIX\win10-$(_WinAppSDKRuntimeArchitecture)\Microsoft.WindowsAppRuntime.Singleton.*.msix"
+ Condition="'@(_WinAppSDKRuntimeMsix)' == ''" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $(_WinUIAugmentedManifest)
+
+
+
diff --git a/scripts/ExtractAppxFile.ps1 b/scripts/ExtractAppxFile.ps1
new file mode 100644
index 000000000..12dd95dcf
--- /dev/null
+++ b/scripts/ExtractAppxFile.ps1
@@ -0,0 +1,56 @@
+[CmdletBinding()]
+param(
+ [Parameter(Mandatory = $true)]
+ [string]$Package,
+
+ [Parameter(Mandatory = $true)]
+ [string]$Entry,
+
+ [Parameter(Mandatory = $true)]
+ [string]$Out
+)
+
+$ErrorActionPreference = 'Stop'
+
+if (-not (Test-Path -LiteralPath $Package)) {
+ throw "App package does not exist: $Package"
+}
+
+Add-Type -AssemblyName System.IO.Compression.FileSystem
+
+$archive = [System.IO.Compression.ZipFile]::OpenRead($Package)
+try {
+ $packageEntry = $archive.GetEntry($Entry)
+ if ($null -eq $packageEntry) {
+ throw "App package '$Package' does not contain '$Entry'."
+ }
+
+ $outDirectory = Split-Path -Parent $Out
+ if (-not (Test-Path -LiteralPath $outDirectory)) {
+ New-Item -ItemType Directory -Path $outDirectory -Force | Out-Null
+ }
+
+ $temporaryOut = "$Out.$PID.tmp"
+ $source = $packageEntry.Open()
+ try {
+ $destination = [System.IO.File]::Create($temporaryOut)
+ try {
+ $source.CopyTo($destination)
+ }
+ finally {
+ $destination.Dispose()
+ }
+ }
+ finally {
+ $source.Dispose()
+ }
+
+ Move-Item -LiteralPath $temporaryOut -Destination $Out -Force
+ [System.IO.File]::SetLastWriteTimeUtc($Out, $packageEntry.LastWriteTime.UtcDateTime)
+}
+finally {
+ $archive.Dispose()
+ if ($null -ne $temporaryOut -and (Test-Path -LiteralPath $temporaryOut)) {
+ Remove-Item -LiteralPath $temporaryOut -Force
+ }
+}
diff --git a/scripts/MergeWinUIAppManifest.ps1 b/scripts/MergeWinUIAppManifest.ps1
new file mode 100644
index 000000000..9aeb3412c
--- /dev/null
+++ b/scripts/MergeWinUIAppManifest.ps1
@@ -0,0 +1,121 @@
+[CmdletBinding()]
+param(
+ [Parameter(Mandatory = $true)]
+ [string]$Base,
+
+ [Parameter(Mandatory = $true)]
+ [string]$Registrations,
+
+ [Parameter(Mandatory = $true)]
+ [string]$Out
+)
+
+$ErrorActionPreference = 'Stop'
+
+foreach ($path in @($Base, $Registrations)) {
+ if (-not (Test-Path -LiteralPath $path)) {
+ throw "Required manifest input does not exist: $path"
+ }
+}
+
+[xml]$baseDocument = [System.IO.File]::ReadAllText($Base)
+[xml]$registrationDocument = [System.IO.File]::ReadAllText($Registrations)
+
+$assembly = $baseDocument.DocumentElement
+if ($null -eq $assembly -or $assembly.LocalName -ne 'assembly') {
+ throw "Base manifest '$Base' does not contain an assembly root."
+}
+
+$appxNamespace = $registrationDocument.DocumentElement.NamespaceURI
+$namespaceManager = [System.Xml.XmlNamespaceManager]::new($registrationDocument.NameTable)
+$namespaceManager.AddNamespace('m', $appxNamespace)
+
+$serverNodes = $registrationDocument.SelectNodes(
+ '/m:Data/m:Extension/m:InProcessServer',
+ $namespaceManager)
+
+$requiredDlls = @(
+ 'Microsoft.UI.Xaml.dll',
+ 'Microsoft.UI.Xaml.Controls.dll',
+ 'Microsoft.UI.Xaml.Phone.dll',
+ 'WinUIEdit.dll'
+)
+
+$assemblyNamespace = 'urn:schemas-microsoft-com:asm.v3'
+$winrtNamespace = 'urn:schemas-microsoft-com:winrt.v1'
+$addedClasses = [System.Collections.Generic.HashSet[string]]::new(
+ [System.StringComparer]::Ordinal)
+$addedDlls = 0
+
+foreach ($requiredDll in $requiredDlls) {
+ $server = $serverNodes |
+ Where-Object {
+ $pathNode = $_.SelectSingleNode('./m:Path', $namespaceManager)
+ $null -ne $pathNode -and
+ $pathNode.InnerText.Equals(
+ $requiredDll,
+ [System.StringComparison]::OrdinalIgnoreCase)
+ } |
+ Select-Object -First 1
+
+ if ($null -eq $server) {
+ throw "Registration file '$Registrations' does not contain '$requiredDll'."
+ }
+
+ $fileElement = $baseDocument.CreateElement('asmv3', 'file', $assemblyNamespace)
+ $fileElement.SetAttribute('name', $requiredDll)
+
+ foreach ($classNode in $server.SelectNodes('./m:ActivatableClass', $namespaceManager)) {
+ $className = $classNode.GetAttribute('ActivatableClassId')
+ if ([string]::IsNullOrWhiteSpace($className)) {
+ continue
+ }
+
+ $threadingModel = $classNode.GetAttribute('ThreadingModel')
+ if ([string]::IsNullOrWhiteSpace($threadingModel)) {
+ $threadingModel = 'both'
+ }
+
+ $classElement = $baseDocument.CreateElement(
+ 'winrtv1',
+ 'activatableClass',
+ $winrtNamespace)
+ $classElement.SetAttribute('name', $className)
+ $classElement.SetAttribute('threadingModel', $threadingModel.ToLowerInvariant())
+ [void]$fileElement.AppendChild($classElement)
+ [void]$addedClasses.Add($className)
+ }
+
+ [void]$assembly.AppendChild($fileElement)
+ $addedDlls++
+}
+
+foreach ($requiredClass in @(
+ 'Microsoft.UI.Xaml.Media.Animation.EntranceNavigationTransitionInfo',
+ 'Microsoft.UI.Xaml.Media.RevealBrush',
+ 'Microsoft.UI.Text.TextConstants',
+ 'Microsoft.UI.Xaml.XamlTypeInfo.XamlControlsXamlMetaDataProvider'
+)) {
+ if (-not $addedClasses.Contains($requiredClass)) {
+ throw "Augmented manifest is missing required class '$requiredClass'."
+ }
+}
+
+$outDirectory = Split-Path -Parent $Out
+if (-not (Test-Path -LiteralPath $outDirectory)) {
+ New-Item -ItemType Directory -Path $outDirectory -Force | Out-Null
+}
+
+$settings = [System.Xml.XmlWriterSettings]::new()
+$settings.Encoding = [System.Text.UTF8Encoding]::new($false)
+$settings.Indent = $true
+
+$writer = [System.Xml.XmlWriter]::Create($Out, $settings)
+try {
+ $baseDocument.Save($writer)
+}
+finally {
+ $writer.Dispose()
+}
+
+Write-Host "MergeWinUIAppManifest: added $($addedClasses.Count) classes across $addedDlls DLLs."