Feature/blazorwebview wpf composition control opt in - #34869
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34869Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34869" |
There was a problem hiding this comment.
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(defaulttrue) to select betweenWebView2CompositionControland standardWebView2. - Updated the exposed
WebViewsurface (and related initialization plumbing) to use a common WPFIWebView2type. - 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. |
Code Review — PR #34869Multi-model review (Claude Opus, GPT-5.4, Claude Sonnet 4.5) Independent AssessmentWhat this changes: Adds a Inferred motivation: Reconciliation with PR NarrativeAuthor claims: Feature enhancement, not a bug fix. Breaking change to Agreement: The code matches the description accurately. The approach is sound — using Findings
|
kubaflo
left a comment
There was a problem hiding this comment.
Could you please review the comment?
|
So, I'm a bit confused about all of this. Initially, I put a PR up against master and the code bot said NO...put the PR up against the .NET 10.0 branch, so I did...now the bot says NO...don't put it there. I feel like the bot needs to get the story straight...lol.
Where should this PR point to? I can put it wherever makes sense.
From: Jakub Florkowski ***@***.***>
Sent: Sunday, April 26, 2026 8:08 AM
To: dotnet/maui ***@***.***>
Cc: Tony Spencer ***@***.***>; Mention ***@***.***>
Subject: Re: [dotnet/maui] Feature/blazorwebview wpf composition control opt in (PR #34869)
@kubaflo requested changes on this pull request.
Could you please review the comment?
-
Reply to this email directly, view it on GitHub<#34869 (review)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AA3SFVO5SGWUD27UZGJXUUD4XYJ3LAVCNFSM6AAAAACXQDD4VCVHI2DSMVQWIX3LMV43YUDVNRWFEZLROVSXG5CSMV3GSZLXHM2DCNZXGAYTCNJSHE>.
You are receiving this because you were mentioned.Message ID: ***@***.******@***.***>>
|
|
@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; |
|
@mobiletonster I've also resolved conflicts :) |
Code Review — PR #34869 (Re-review post-retarget)Previous Finding Status
Merge Conflict ResolutionConflicts resolved by accepting CI StatusCI just started after the merge push. Verdict: LGTMConfidence: high |
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.
c612109 to
543def2
Compare
|
@jfversluis the pr looks good! The only question is if we want it in net 11 |
|
Thanks! I appreciate your help. Do we want it in net11? I mean, we do…or sooner. We have just finished a big research project to determine our future direction for our product (currently a large WPF project) and have decided to move forward with razor components in BlazorWebView starting in WPF but then leveraging .Net Maui for a migration into a mobile version. We love having the composition control especially as we begin the migration journey and are willing to accept the performance hit while we migrate, but eventually want to switch sections to the more performant WebView2 (sans composition control) as soon as we can.
Thanks for considering this PR.
Tony Spencer
Get Outlook for iOS<https://aka.ms/o0ukef>
…________________________________
From: Jakub Florkowski ***@***.***>
Sent: Monday, April 27, 2026 12:31:53 PM
To: dotnet/maui ***@***.***>
Cc: Tony Spencer ***@***.***>; Mention ***@***.***>
Subject: Re: [dotnet/maui] Feature/blazorwebview wpf composition control opt in (PR #34869)
[https://avatars.githubusercontent.com/u/42434498?s=20&v=4]kubaflo left a comment (dotnet/maui#34869)<#34869 (comment)>
@jfversluis<https://github.com/jfversluis> the pr looks good! The only question is if we want it in net 11
—
Reply to this email directly, view it on GitHub<#34869 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AA3SFVOCNJHMPZI54PKVWE34X6RRTAVCNFSM6AAAAACXQDD4VCVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHM2DGMRZGUYDIOBWHE>.
You are receiving this because you were mentioned.Message ID: ***@***.***>
|
This comment has been minimized.
This comment has been minimized.
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 2 findings
See inline comments for details.
| @@ -1 +1,8 @@ | |||
| #nullable enable | |||
| *REMOVED*~Microsoft.AspNetCore.Components.WebView.BlazorWebViewInitializedEventArgs.WebView.get -> Microsoft.Web.WebView2.Wpf.WebView2CompositionControl | |||
| *REMOVED*Microsoft.AspNetCore.Components.WebView.Wpf.BlazorWebView.WebView.get -> Microsoft.Web.WebView2.Wpf.WebView2CompositionControl! | |||
There was a problem hiding this comment.
[major] Public API Surface — This removes the shipped BlazorWebView.WebView getter returning WebView2CompositionControl and replaces it with an IWebView2 return type. Return-type widening is still a metadata/signature break for existing compiled WPF consumers, and the same compatibility issue applies to BlazorWebViewInitializedEventArgs.WebView. Prefer an additive API shape that preserves the existing concrete WebView2CompositionControl-facing API for compatibility and adds a neutral IWebView2/active-control property for the new standard WebView2 option.
| /// the visual tree). Changing it after the underlying WebView2 has been created will throw an | ||
| /// <see cref="InvalidOperationException"/>. | ||
| /// </remarks> | ||
| public bool UseCompositionControl |
There was a problem hiding this comment.
[moderate] Regression Prevention and Test Coverage — This new public WPF behavior has no detected regression coverage. Please add targeted coverage for the default UseCompositionControl=true path, the false path creating a standard Microsoft.Web.WebView2.Wpf.WebView2, the initialized event/control property type exposed in each mode, and the documented late-change exception after the template child has been created.
MauiBot
left a comment
There was a problem hiding this comment.
AI Review Summary
@mobiletonster — new AI review results are available based on this last commit:
543def2. To request a fresh review after new comments or commits, comment/review rerun.
Review Sessions — click to expand
Gate — Test Before & After Fix
Gate Result: ⚠️ SKIPPED
No tests were detected in this PR.
Recommendation: Add tests to verify the fix using the write-tests-agent.
Pre-Flight — Context & Validation
Issue: #28063 - WPF BlazorWebView WebView2CompositionControl airspace/performance follow-up
PR: #34869 - Feature/blazorwebview wpf composition control opt in
Platforms Affected: Windows WPF BlazorWebView (WEBVIEW2_WPF)
Files Changed: 4 implementation/API, 0 test
Key Findings
- PR adds
UseCompositionControlto WPFBlazorWebView, defaulting totrueso existing airspace behavior remains the default while allowing standardWebView2opt-out for performance. - WPF WebView-related public/internal types are widened from
WebView2CompositionControltoIWebView2so both WPF WebView2 controls can flow through the existing manager and initialized event args. - No tests are included; gate was already skipped because no tests were detected in the PR.
- GitHub CLI is unauthenticated in this environment, so PR context came from local review branch and public GitHub API responses.
Code Review Summary
Verdict: NEEDS_DISCUSSION
Confidence: low
Errors: 0 | Warnings: 2 | Suggestions: 1
Key code review findings:
⚠️ Public API return-type break still needs explicit maintainer/API approval:BlazorWebView.WebViewandBlazorWebViewInitializedEventArgs.WebViewchange fromWebView2CompositionControltoIWebView2.⚠️ CI status is undetermined from this unauthenticated environment, so LGTM is not appropriate under the review rules.- 💡 Consider release-note/migration documentation for callers that need concrete
WebView2CompositionControlmembers and must cast.
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #34869 | Add UseCompositionControl DP, choose WebView2CompositionControl vs WebView2 in the template, and expose WPF webview as IWebView2. |
BlazorWebView.cs, shared WebView2 source, PublicAPI |
Original PR; default preserves existing behavior but changes public return type. |
Code Review — Deep Analysis
Code Review — PR #34869
Independent Assessment
What this changes: Adds a WPF BlazorWebView.UseCompositionControl dependency property, defaulting to true, to choose between WebView2CompositionControl and standard WebView2. The WPF internal/public webview type is widened to Microsoft.Web.WebView2.Wpf.IWebView2, and PublicAPI entries record the return-type change plus new property/API surface.
Inferred motivation: Preserve the airspace-fixing composition control by default while allowing WPF apps that do not need XAML-over-WebView layering to opt out for better rendering performance.
Reconciliation with PR Narrative
Author claims: This is a feature/performance enhancement, not a bug fix; it intentionally changes WPF WebView-related API from WebView2CompositionControl to IWebView2; tests are deferred because there is no dedicated WPF test project.
Agreement/disagreement: The code matches the narrative. UseCompositionControlProperty defaults to true (BlazorWebView.cs:93-97), the constructor creates the template from the current value (BlazorWebView.cs:127-129), the template selects the concrete WPF control (BlazorWebView.cs:248-253), and shared WebView2 plumbing now aliases WPF to IWebView2 (BlazorWebViewInitializedEventArgs.cs:8, WebView2WebViewManager.cs:31). The only remaining discussion point is API compatibility/approval for changing a shipped return type to an interface, plus the current red/undetermined CI state.
Prior Review Reconciliation
| Prior ❌ Error Finding | Source | Status | Evidence |
|---|---|---|---|
| No prior ❌ Error findings found. | N/A | N/A | Queried public PR issue comments and inline review comments; gh review surfaces were unavailable because the CLI is unauthenticated. Prior reviews found warnings/discussion only, primarily branch target/API break and CI validation. |
Notes:
- Earlier branch-target warning is fixed; the PR now targets
net11.0. - Earlier missing newline nit appears obsolete; the current
PublicAPI.Unshipped.txtcontent read from HEAD ends with newline. - Prior API design discussion about
IWebView2remains a maintainer/API review concern, not a concrete code defect.
Blast Radius Assessment
- Runs for all instances: Yes, for WPF
BlazorWebViewinstances. The constructor now always creates a template throughCreateWebViewTemplate(...), but the default remains composition control, preserving current runtime behavior unless consumers opt out. - Startup impact: Yes, this affects WPF
BlazorWebViewinitialization/template application. The default path still createsWebView2CompositionControl. - Static/shared state: No new static mutable/shared state. The new dependency property metadata is static, but instance state remains per-control.
CI Status
- Required-check result:
gh pr checks 34869 --repo dotnet/maui --requiredcould not run becauseghis unauthenticated (gh_exit=4). - Classification: Undetermined from this environment. Public check-run/comment context indicates CI was not fully validated or had failures at different points, but authenticated AzDO detail is unavailable.
- Action taken: Confidence capped to low and verdict cannot be LGTM.
Findings
❌ Error
No code-level ❌ Error findings found on the added/modified lines.
⚠️ Warning — Public API return-type break still needs explicit approval
BlazorWebView.WebView changes from WebView2CompositionControl! to IWebView2! (BlazorWebView.cs:135-144; PublicAPI entries at PublicAPI.Unshipped.txt:2-8), and BlazorWebViewInitializedEventArgs.WebView similarly changes to IWebView2 (BlazorWebViewInitializedEventArgs.cs:8, :30-32). This is intentional and correctly tracked, and targeting net11.0 makes it much more plausible than servicing, but consumers typed directly against WebView2CompositionControl will need casts or source changes. This should have explicit API/maintainer sign-off.
⚠️ Warning — CI is red/undetermined from this environment
The changed code is WPF BlazorWebView-only, but without authenticated required-check/AzDO details I cannot classify current CI state as passing or unrelated. Per review rules, this blocks LGTM.
💡 Suggestion — Consider documenting the IWebView2 cast expectation
The API docs correctly state the inner control may be either WebView2CompositionControl or WebView2Control (BlazorWebView.cs:135-142, :211-219). Given reviewer concern that IWebView2 is not the most ergonomic consumer type, release notes or migration docs should call out that callers needing concrete-specific members must cast based on UseCompositionControl.
Failure-Mode Probing
UseCompositionControl=falsebefore initialization: The property-changed callback updatesTemplatewhile_webviewis still null (BlazorWebView.cs:237-245), soOnApplyTemplatecreates/accepts the standardWebView2throughIWebView2.UseCompositionControlchanges after template/webview creation: The callback throwsInvalidOperationExceptiononce_webview != null(BlazorWebView.cs:239-243), matching the XML docs.- Custom template omits or misnames the child:
OnApplyTemplatenow throws a descriptiveInvalidOperationExceptioninstead of an invalid cast/null failure (BlazorWebView.cs:280-286). - Shared manager/event args with either concrete WPF control: Both WPF aliases now use
IWebView2(WebView2WebViewManager.cs:31,BlazorWebViewInitializedEventArgs.cs:8). - Disposal after widening to interface: The code disposes only when the interface instance is also
IDisposable(BlazorWebView.cs:450), avoiding a compile-time dependency onIWebView2inheritingIDisposable.
Verdict: NEEDS_DISCUSSION
Confidence: low
Summary: The implementation is clean and I found no concrete code defect. However, the change deliberately alters shipped public API shape and CI status is undetermined from this unauthenticated environment, so this needs maintainer/API sign-off and CI clarification before merge.
Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix-1 | Preserve existing concrete WPF WebView APIs and add a new neutral IWebView2 WebViewControl access path plus UseCompositionControl. |
✅ PASS — targeted WPF build succeeded | 4 files | Better API compatibility than PR because it avoids widening existing public return types; tradeoff is two access properties and a legacy property that throws in opt-out mode. |
| 2 | try-fix-2 | Add a separate standard-WebView2 WPF BlazorWebView control type while leaving existing BlazorWebView unchanged. |
❌ FAIL — rejected before implementation | 0 files | Too invasive: requires broad WPF control refactor and new public control API. |
| PR | PR #34869 | Add UseCompositionControl, select WebView2 control type in the template, and widen existing WPF WebView APIs to IWebView2. |
4 files | Clean and compact, but has deliberate public return-type break needing API approval. |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| maui-expert-reviewer | 1 | Yes | Additive compatibility API: keep existing concrete WebView APIs and add neutral IWebView2 access. Implemented as try-fix-1. |
| maui-expert-reviewer | 2 | Yes | Split standard WebView2 into a separate WPF control type. Rejected as too invasive for this PR. |
Exhausted: Yes — the remaining meaningful alternatives either match the PR's current bool/interface strategy, try-fix-1's additive compatibility strategy, or require the broad separate-control refactor rejected in try-fix-2.
Selected Fix: Candidate #1 — It passed the targeted WPF build and most directly addresses the main review concern (public API return-type break). However, because there are no PR tests and only targeted build validation was possible, this should be treated as a candidate for maintainer/API discussion rather than an automatically superior merge recommendation.
Report — Final Recommendation
Comparative Report — PR #34869
Candidates compared
| Rank | Candidate | Regression/build result | Assessment |
|---|---|---|---|
| 1 | pr-plus-reviewer |
PASS — targeted WPF build succeeded in sandbox after restore | Best candidate. It preserves the PR's opt-out behavior and default composition mode while applying the expert reviewer's major compatibility feedback by keeping existing concrete WPF APIs and adding a neutral IWebView2 access path. |
| 2 | try-fix-1 |
PASS — targeted WPF build succeeded | Technically the same additive compatibility strategy as pr-plus-reviewer, and therefore also stronger than the raw PR. Ranked below pr-plus-reviewer only because the requested Phase 1 candidate explicitly incorporates the expert review findings into the PR-fix line. |
| 3 | pr |
Gate skipped — no tests detected; CI unavailable/undetermined in this environment | Compact implementation and likely functionally correct, but it widens existing public WPF return types from WebView2CompositionControl to IWebView2, creating a source/binary compatibility concern that the expert reviewer flagged as major. |
| 4 | try-fix-2 |
FAIL — rejected before implementation | The separate-control design has good theoretical compatibility, but it is too invasive for this PR and would require broad WPF control refactoring and new API design approval. Per the ranking rule, failed candidates rank below passing candidates. |
Key comparison points
pr and pr-plus-reviewer both keep the requested user-facing behavior: WPF BlazorWebView uses WebView2CompositionControl by default and allows opting out to standard WebView2 for performance-sensitive scenarios where airspace layering is unnecessary.
The decisive difference is public API compatibility. The raw PR changes the existing WebView property return types to IWebView2, which is a breaking metadata/signature change for consumers compiled against WebView2CompositionControl. pr-plus-reviewer avoids that break by preserving the concrete APIs and adding WebViewControl for the selected active WPF control. This is more awkward than the raw PR because callers now have two access paths, and the legacy WebView property can throw if used after opting out, but that behavior is limited to callers who explicitly select the new standard-WebView2 mode.
Test coverage remains the main weakness for the winning candidate. The gate was skipped because no tests were detected in the PR, and no automated regression test candidate was available from STEP 5a. However, candidates with passing targeted builds and no known regression-test failure must rank above failed/rejected candidates, and pr-plus-reviewer addresses the only major code-review finding while preserving the PR's intended behavior.
Winner
pr-plus-reviewer wins. It is the best balance of behavior, compatibility, and implementation scope: it keeps the PR's opt-in performance improvement, avoids the major public API return-type break, and passed the targeted WPF build in the sandbox.
Future Action — review latest findings
No alternative fix was selected for this run. Review the session findings and CI results before merging.
|
🔍 AI-generated comment (drafted by GitHub Copilot CLI on behalf of @kubaflo) @jfversluis — this one needs an owner decision to unblock; it's been waiting since April. What it is: adds The open design question (from @mattleibow): to return either control, the public Two realistic paths: Caveats either way: narrow audience (WPF BlazorWebView + no airspace layering + perf-sensitive); no test coverage (no WPF test project exists); CI has never gone green here; perf claim is unquantified. ● Go/no-go for net11? And if go — accept the |
kubaflo
left a comment
There was a problem hiding this comment.
Could you please check the ai's suggestions?
I saw the AI's suggestion. I think the impact of this change (as is) will be very minor if any at all. 1) The WPF Blazor web view is probably not widely used today 2) the probability that someone is trying to access the webview2 control from the parent of the BlazorWebView control is relatively unlikely. If they are, the fix is a pretty simple cast from IWebView2 -> Concrete type. I feel like given the statical probability of these conditions; it doesn't rise to the level of requiring more code to work around it. However, if the humans believe otherwise, I think we could add the additional code for it. |
|
🚨 API change(s) detected @davidbritch FYI |
|
🚨 API change(s) detected @davidortinau FYI |
Description of Change
This change introduces an opt‑out mechanism for using
WebView2CompositionControlin WPF BlazorWebView scenarios.While
WebView2CompositionControlresolves the WPF airspace issue, it introduces measurable rendering overhead thatnegatively 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
WebView2CompositionControlintroduction(PR #31777: #31777)
and community requests asking for an opt‑in/opt‑out toggle
(Issue #28063: #28063).
Issues Fixed / Related
WebView2CompositionControlrequest and discussion:Allow use of new WebView2CompositionControl for WPF BlazorWebView #28063
Use the WebView2CompositionControl in Blazor WPF #31777
This PR enables opting out of
WebView2CompositionControl(added in .NET 10.0 to address WPF airspace issues) infavor of the standard
WebView2when airspace layering is unnecessary and lower rendering overhead is preferred.Summary of Changes
UseCompositionControldependency property (default:true) onBlazorWebView, preserving existingbehavior.
_webviewand theWebViewproperty to useIWebView2, implemented by bothWebView2andWebView2CompositionControl.CreateWebViewTemplate()now selects the correct control type at initialization; the property‑changed callbackupdates the template if changed before the control is added to the visual tree.
InvalidOperationExceptionifUseCompositionControlis modified after the underlying WebView has beencreated.
WebView2WebViewManagerandBlazorWebViewInitializedEventArgsshared source to use the WPFIWebView2alias.
*REMOVED*entries for the oldWebView2CompositionControlreturn typesand the new
IWebView2andUseCompositionControlpublic API surface.PR Review Feedback Addressed
*REMOVED*entries inPublicAPI.Unshipped.txt— Added*REMOVED*entries for bothBlazorWebViewInitializedEventArgs.WebViewandBlazorWebView.WebViewto satisfy the API analyzer.OnApplyTemplate()— Replaced the direct(IWebView2)cast with anis notpattern match thatthrows a descriptive
InvalidOperationExceptionif the template child is missing or the wrong type.DeviceTestsproject targetsthe 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.
WebViewreturn type / source compatibility — The change fromWebView2CompositionControl!toIWebView2!isintentional and documented. The proposed alternative (keeping
WebViewasWebView2CompositionControl?and adding aWebViewBaseproperty) would itself be a breaking change (nullable return) and would produce a confusing two-propertyAPI. Callers relying on
WebView2CompositionControl-specific members can cast explicitly.Notes
This is not a bug fix; it is a feature/performance enhancement.