Skip to content

Feature/blazorwebview wpf composition control opt in#34869

Open
mobiletonster wants to merge 5 commits intodotnet:net11.0from
mobiletonster:feature/blazorwebview-wpf-composition-control-opt-in
Open

Feature/blazorwebview wpf composition control opt in#34869
mobiletonster wants to merge 5 commits intodotnet:net11.0from
mobiletonster:feature/blazorwebview-wpf-composition-control-opt-in

Conversation

@mobiletonster
Copy link
Copy Markdown

Description of Change

This change introduces an opt‑out mechanism for using WebView2CompositionControl in WPF BlazorWebView scenarios.
While WebView2CompositionControl resolves the WPF airspace issue, it introduces measurable rendering overhead that
negatively impacts Razor component performance.

For apps that do not require XAML‑over‑WebView layering, developers can now disable the composition control and fall
back to the standard WebView2, resulting in significantly improved performance on WPF.

This feature also addresses feedback from the original WebView2CompositionControl introduction
(PR #31777: #31777)
and community requests asking for an opt‑in/opt‑out toggle
(Issue #28063: #28063).


Issues Fixed / Related

This PR enables opting out of WebView2CompositionControl (added in .NET 10.0 to address WPF airspace issues) in
favor of the standard WebView2 when airspace layering is unnecessary and lower rendering overhead is preferred.


Summary of Changes

  • Added UseCompositionControl dependency property (default: true) on BlazorWebView, preserving existing
    behavior.
  • Updated _webview and the WebView property to use IWebView2, implemented by both WebView2 and
    WebView2CompositionControl.
  • CreateWebViewTemplate() now selects the correct control type at initialization; the property‑changed callback
    updates the template if changed before the control is added to the visual tree.
  • Added an InvalidOperationException if UseCompositionControl is modified after the underlying WebView has been
    created.
  • Updated WebView2WebViewManager and BlazorWebViewInitializedEventArgs shared source to use the WPF IWebView2
    alias.
  • Updated PublicAPI.Unshipped.txt with *REMOVED* entries for the old WebView2CompositionControl return types
    and the new IWebView2 and UseCompositionControl public API surface.

PR Review Feedback Addressed

  • Missing *REMOVED* entries in PublicAPI.Unshipped.txt — Added *REMOVED* entries for both
    BlazorWebViewInitializedEventArgs.WebView and BlazorWebView.WebView to satisfy the API analyzer.
  • Unsafe cast in OnApplyTemplate() — Replaced the direct (IWebView2) cast with an is not pattern match that
    throws a descriptive InvalidOperationException if the template child is missing or the wrong type.
  • Unit tests — No WPF-specific test project exists in this repository; the existing DeviceTests project targets
    the MAUI handler layer and is not appropriate for standalone WPF control tests. A dedicated WPF test project would be
    a disproportionate investment for this change and has been deferred.
  • WebView return type / source compatibility — The change from WebView2CompositionControl! to IWebView2! is
    intentional and documented. The proposed alternative (keeping WebView as WebView2CompositionControl? and adding a
    WebViewBase property) would itself be a breaking change (nullable return) and would produce a confusing two-property
    API. Callers relying on WebView2CompositionControl-specific members can cast explicitly.

Notes

This is not a bug fix; it is a feature/performance enhancement.

Copilot AI review requested due to automatic review settings April 8, 2026 00:06
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 8, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34869

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34869"

@dotnet-policy-service dotnet-policy-service Bot added the community ✨ Community Contribution label Apr 8, 2026
@dotnet-policy-service
Copy link
Copy Markdown
Contributor

Hey there @@mobiletonster! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a WPF-specific toggle to allow opting out of WebView2CompositionControl for BlazorWebView, enabling better performance in scenarios that don’t need XAML-over-WebView layering, while preserving the current default behavior.

Changes:

  • Added UseCompositionControl (default true) to select between WebView2CompositionControl and standard WebView2.
  • Updated the exposed WebView surface (and related initialization plumbing) to use a common WPF IWebView2 type.
  • Updated WPF PublicAPI tracking with new entries and *REMOVED* markers for the prior return types.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.

File Description
src/BlazorWebView/src/Wpf/BlazorWebView.cs Adds UseCompositionControl DP, selects the appropriate WPF WebView2 control via template creation, and updates WebView to return IWebView2.
src/BlazorWebView/src/SharedSource/WebView2WebViewManager.cs Switches the WPF WebView2 alias to IWebView2 to support either underlying control.
src/BlazorWebView/src/SharedSource/BlazorWebViewInitializedEventArgs.cs Updates the WPF WebView event arg type alias to IWebView2.
src/BlazorWebView/src/Wpf/PublicAPI.Unshipped.txt Records new API surface and removals for the changed WebView-returning members.

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented Apr 26, 2026

Code Review — PR #34869

Multi-model review (Claude Opus, GPT-5.4, Claude Sonnet 4.5)

Independent Assessment

What this changes: Adds a UseCompositionControl WPF dependency property (default: true) to BlazorWebView that lets developers opt out of WebView2CompositionControl in favor of the standard WebView2 for better rendering performance when WPF airspace layering is not needed. Internally, the _webview field and public WebView property are widened from the concrete WebView2CompositionControl type to the IWebView2 interface (which both controls implement).

Inferred motivation: WebView2CompositionControl was introduced in .NET 10 GA (PR #31777) to fix the WPF airspace issue, but it introduces measurable rendering overhead. Users who don't need XAML-over-WebView layering want to opt out.

Reconciliation with PR Narrative

Author claims: Feature enhancement, not a bug fix. Breaking change to WebView return type is intentional. No tests due to lack of WPF test infrastructure.

Agreement: The code matches the description accurately. The approach is sound — using IWebView2 as the common type is the cleanest path. The PR description is thorough and addresses prior review feedback.

Findings

⚠️ Warning — Branch target may be incorrect (needs maintainer guidance)

This PR targets net10.0, but .NET 10 GA shipped in November 2025 (commit e101f0d9ac marked the API as shipped Nov 3, 2025). The WebView2CompositionControl return types on BlazorWebView.WebView and BlazorWebViewInitializedEventArgs.WebView are in PublicAPI.Shipped.txt, making this a breaking change to shipped GA API.

Per repository conventions, new features with API changes should target the highest netN.0 feature branch (net11.0). This PR adds new public API surface (UseCompositionControl, UseCompositionControlProperty) and changes shipped return types — both are feature-branch work, not servicing.

Recommendation: A MAUI maintainer should confirm whether this should retarget to net11.0.

⚠️ Warning — Breaking API change to WebView property (binary + source)

BlazorWebView.WebView changes from WebView2CompositionControl!IWebView2! and BlazorWebViewInitializedEventArgs.WebView from WebView2CompositionControlIWebView2.

  • Binary breaking: Callers compiled against .NET 10 GA will get MissingMethodException at runtime
  • Source breaking: Code explicitly typed as WebView2CompositionControl (e.g., WebView2CompositionControl ctrl = blazorWebView.WebView;) won't compile

The *REMOVED* entries in PublicAPI.Unshipped.txt correctly track this against the shipped API — the Public API analyzer approach is sound. However, since these were shipped in .NET 10 GA, this needs explicit maintainer approval for the breaking change, with migration guidance in release notes.

Multi-model agreement: all 3 models flagged this. All agreed it's acceptable for a pre-GA change, but the shipped status in PublicAPI.Shipped.txt raises the bar.

💡 Suggestion — Missing newline at end of PublicAPI.Unshipped.txt

src/BlazorWebView/src/Wpf/PublicAPI.Unshipped.txt doesn't end with a newline. This will trigger git diff warnings and may cause analyzer issues.

Multi-model agreement: all 3 models flagged this.

💡 Suggestion — (_webview as IDisposable)?.Dispose() cast — verify necessity

In DisposeAsyncCore(), the dispose changed from _webview?.Dispose() to (_webview as IDisposable)?.Dispose(). If IWebView2 extends IDisposable, the cast is unnecessary. If it doesn't, the cast is correct and defensive. Either way it works, but a brief comment explaining the cast would help future readers.

💡 Suggestion — Binding to UseCompositionControl could throw unexpectedly

If a consumer data-binds UseCompositionControl and the bound value changes after the WebView is created, the property-changed callback throws InvalidOperationException. The XML docs correctly warn about this, but consider logging a warning instead of throwing for the "value didn't actually change" case (i.e., setting true when it's already true after initialization).

Devil's Advocate

Challenge to my findings: The branch target concern may be outdated if the team is still accepting feature work on net10.0 for a point release. MAUI has historically included features in servicing releases. A maintainer should weigh in.

Challenge to my approval: I can't verify runtime behavior of the WPF template swapping or IWebView2 interface contract without running the code on Windows. The WebView2 SDK's IWebView2 interface surface is an external dependency — if it changes or lacks expected members, this will break at runtime.

What I didn't verify: No WPF test infrastructure exists in this repo, so I can't empirically verify the UseCompositionControl=false path works correctly. The approach is sound in theory, but manual testing on WPF is essential before merge.

CI Status

CI checks show fail for maui-pr, maui-pr-devicetests, and maui-pr-uitests, but the failure links reference PR #34764 (a different PR), suggesting these may be base-branch failures rather than failures caused by this PR. Build Analysis is pending.

Verdict: NEEDS_DISCUSSION

Confidence: medium
Summary: The implementation is clean, well-documented, and follows correct patterns for WPF dependency properties. The core concern is that this makes a breaking change to shipped .NET 10 GA public API and targets a servicing branch (net10.0) rather than the feature branch (net11.0). A MAUI maintainer should confirm the branch target and approve the breaking API change. The code itself is well-structured — if the branch and breaking change are approved, the implementation is ready.

Copy link
Copy Markdown
Contributor

@kubaflo kubaflo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please review the comment?

@mobiletonster
Copy link
Copy Markdown
Author

mobiletonster commented Apr 27, 2026 via email

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented Apr 27, 2026

@mobiletonster no worries, it might be a bit confusing at first :) So the main branch is the latest maui release (currently net10), net10 branch is an old branch we had for net10 pre-releases. MauiBot asked you to target this pr to net11 branch. I've just done it;

@kubaflo kubaflo changed the base branch from net10.0 to net11.0 April 27, 2026 18:21
@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented Apr 27, 2026

@mobiletonster I've also resolved conflicts :)

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented Apr 27, 2026

Code Review — PR #34869 (Re-review post-retarget)

Previous Finding Status

# Finding Status
1 ⚠️ Branch target net10.0 — breaking change to shipped GA API Fixed — retargeted to net11.0 (feature branch). API changes are now for .NET 11, not servicing.
2 ⚠️ Breaking API change (WebView return type) Acceptable — in net11.0 this is a new-major-version API change, not a servicing break. *REMOVED* entries properly track the .NET 10 → 11 transition.
3 💡 Missing newline at EOF in PublicAPI.Unshipped.txt ⚠️ Still present\ No newline at end of file
4 💡 (_webview as IDisposable)?.Dispose() cast ℹ️ Non-blocking
5 💡 Binding to UseCompositionControl edge case ℹ️ Non-blocking, documented

Merge Conflict Resolution

Conflicts resolved by accepting net11.0 versions for 10 infrastructure files (NuGet.config, global.json, Version.Details.xml, etc.). None of the PR's BlazorWebView changes were affected — the core diff is identical to the pre-merge version.

CI Status

CI just started after the merge push. Build Analysis pending, maui-pr not yet triggered. No results to assess yet.

Verdict: LGTM

Confidence: high
Summary: The retarget from net10.0net11.0 resolves the primary concern from the initial review. The API change is now properly in the feature branch for .NET 11. Implementation is clean, well-documented, and follows correct WPF patterns. Only remaining nit is the missing EOF newline in PublicAPI.Unshipped.txt. Ready for maintainer approval.

mobiletonster and others added 5 commits April 27, 2026 20:29
Allows opting out of the WebView2CompositionControl (introduced in
net10.0 to fix WPF airspace issues) in favor of the standard WebView2
control when airspace layering is not required and lower rendering
overhead is preferred.

- Add UseCompositionControl dependency property (default: true) to
  BlazorWebView, preserving existing behavior by default
- Type _webview and the WebView property as IWebView2, the interface
  implemented by both WebView2 and WebView2CompositionControl
- CreateWebViewTemplate() selects the correct control type at init time;
  the property-changed callback swaps the template if set before the
  control is added to the visual tree
- Throw InvalidOperationException if UseCompositionControl is changed
  after the underlying WebView2 has already been created
- Update WebView2WebViewManager and BlazorWebViewInitializedEventArgs
  shared source to use IWebView2 for the WPF type alias
- Update PublicAPI.Shipped.txt to reflect the IWebView2 return types
  and new UseCompositionControl public API
…Unshipped.txt

I misunderstood how this was used. This should correct it.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Enhanced error reporting when WebView2 template child is missing
or of the wrong type. Updated public API to return IWebView2
instead of WebView2CompositionControl for greater abstraction.
@kubaflo kubaflo force-pushed the feature/blazorwebview-wpf-composition-control-opt-in branch from c612109 to 543def2 Compare April 27, 2026 18:30
@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented Apr 27, 2026

@jfversluis the pr looks good! The only question is if we want it in net 11

@mobiletonster
Copy link
Copy Markdown
Author

mobiletonster commented Apr 27, 2026 via email

Copy link
Copy Markdown
Member

@mattleibow mattleibow left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue with IWebView is that it is a very weird type - not a view type - so all existing code will have to cast to one or the other.

Not helpful, I know, but maybe we need to rather have 2 handlers and when a flag/switch is set at compile time the app can swap a handler. Like we do with the collection views on ios.

But maybe this is fine if usage is lower?

@kubaflo
Copy link
Copy Markdown
Contributor

kubaflo commented Apr 30, 2026

I'd say it the usage is not that popular so we might keep the pr as it is

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community ✨ Community Contribution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants