Fixed BindingContext of the Window TitleBar is not being passed on to its child content.#30080
Conversation
|
Hey there @@NirmalKumarYuvaraj! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
There was a problem hiding this comment.
Pull Request Overview
This PR fixes the issue where the BindingContext of the Window TitleBar was not propagated to its child content by adding an override for OnBindingContextChanged in the TitleBar class and updating the property change handlers.
- Propagated BindingContext changes to Content, LeadingContent, and TrailingContent.
- Added test cases in TestCases.Shared.Tests and updated the host sample in TestCases.HostApp to validate the fix.
- Updated PublicAPI files to include the new override method.
Reviewed Changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue24831.cs | Added a test case to verify BindingContext propagation for the TitleBar. |
| src/Controls/tests/TestCases.HostApp/Issues/Issue24831.cs | Implemented a sample demonstrating the TitleBar BindingContext propagation fix. |
| src/Controls/src/Core/TitleBar/TitleBar.cs | Overrode OnBindingContextChanged and adjusted OnLeadingChanged, OnContentChanged, and OnTrailingContentChanged to propagate the BindingContext. |
| PublicAPI Unshipped Files | Declared the new override in the public API for multiple target frameworks. |
|
@NirmalKumarYuvaraj Could you rebase to fix the conflicts? |
fe27d8d to
3690fe2
Compare
@jsuarezruiz , I have rebased. |
Sorry, but requires rebase again :( |
3690fe2 to
048d802
Compare
Rebased |
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
|
|
kubaflo
left a comment
There was a problem hiding this comment.
Could you please resolve conflicts?
dc298c1 to
aeb72fb
Compare
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 30080Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 30080" |
|
@kubaflo , I have resolved the conflicts. Please let me know if you have any concerns. |
kubaflo
left a comment
There was a problem hiding this comment.
Could you please review the AI's summary?
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
…otnet#34548) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description Adds a [gh-aw (GitHub Agentic Workflows)](https://github.github.com/gh-aw/introduction/overview/) workflow that automatically evaluates test quality on PRs using the `evaluate-pr-tests` skill. ### What it does When a PR adds or modifies test files, this workflow: 1. **Checks out the PR branch** (including fork PRs) in a pre-agent step 2. **Runs the `evaluate-pr-tests` skill** via Copilot CLI in a sandboxed container 3. **Posts the evaluation report** as a PR comment using gh-aw safe-outputs ### Triggers | Trigger | When | Fork PR support | |---------|------|-----------------| | `pull_request` | Automatic on test file changes (`src/**/tests/**`) | ❌ Blocked by `pre_activation` gate | | `workflow_dispatch` | Manual — enter PR number | ✅ Works for all PRs | | `issue_comment` (`/evaluate-tests`) | Comment on PR |⚠️ Same-repo only (see Known Limitations) | ### Security model | Layer | Implementation | |-------|---------------| | **gh-aw sandbox** | Agent runs in container with scrubbed credentials, network firewall | | **Safe outputs** | Max 1 PR comment per run, content-limited | | **Checkout without execution** | `steps:` checks out PR code but never executes workspace scripts | | **Base branch restoration** | `.github/skills/`, `.github/instructions/`, `.github/copilot-instructions.md` restored from base branch after checkout | | **Fork PR activation gate** | `pull_request` events blocked for forks via `head.repo.id == repository_id` | | **Pinned actions** | SHA-pinned `actions/checkout`, `actions/github-script`, etc. | | **Minimal permissions** | Each job declares only what it needs | | **Concurrency** | One evaluation per PR, cancels in-progress | | **Threat detection** | gh-aw built-in threat detection analyzes agent output | ### Files added/modified - `.github/workflows/copilot-evaluate-tests.md` — gh-aw workflow source - `.github/workflows/copilot-evaluate-tests.lock.yml` — Compiled workflow (auto-generated by `gh aw compile`) - `.github/skills/evaluate-pr-tests/scripts/Gather-TestContext.ps1` — Test context gathering script (binary-safe file download, path traversal protection) - `.github/instructions/gh-aw-workflows.instructions.md` — Copilot instructions for gh-aw development ### Known Limitations **Fork PR evaluation via `/evaluate-tests` comment is not supported in v1.** The gh-aw platform inserts a `checkout_pr_branch.cjs` step after all user steps, which may overwrite base-branch skill files restored for fork PRs. This is a known gh-aw platform limitation — user steps always run before platform-generated steps, with no way to insert steps after. **Workaround:** Use `workflow_dispatch` (Actions UI → "Run workflow" → enter PR number) to evaluate fork PRs. This trigger bypasses the platform checkout step entirely and works correctly. **Related upstream issues:** - [github/gh-aw#18481](github/gh-aw#18481) — "Using gh-aw in forks of repositories" - [github/gh-aw#18518](github/gh-aw#18518) — Fork detection and warning in `gh aw init` - [github/gh-aw#18520](github/gh-aw#18520) — Fork context hint in failure messages - [github/gh-aw#18521](github/gh-aw#18521) — Fork support documentation ### Fixes - Fixes dotnet#34602 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Jakub Florkowski <kubaflo123@gmail.com>
## Summary Enables the copilot-evaluate-tests gh-aw workflow to run on fork PRs by adding `forks: ["*"]` to the `pull_request` trigger and removing the fork guard from `Checkout-GhAwPr.ps1`. ## Changes 1. **copilot-evaluate-tests.md**: Added `forks: ["*"]` to opt out of gh-aw auto-injected fork activation guard. Scoped `Checkout-GhAwPr.ps1` step to `workflow_dispatch` only (redundant for other triggers since platform handles checkout). 2. **copilot-evaluate-tests.lock.yml**: Recompiled via `gh aw compile` — fork guard removed from activation `if:` conditions. 3. **Checkout-GhAwPr.ps1**: Removed the `isCrossRepository` fork guard. Updated header docs and restore comments to accurately describe behavior for all trigger×fork combinations (including corrected step ordering). 4. **gh-aw-workflows.instructions.md**: Updated all stale references to the removed fork guard. Documented `forks: ["*"]` opt-in, clarified residual risk model for fork PRs, and updated troubleshooting table. ## Security Model Fork PRs are safe because: - Agent runs in **sandboxed container** with all credentials scrubbed - Output limited to **1 comment** via `safe-outputs: add-comment: max: 1` - Agent **prompt comes from base branch** (`runtime-import`) — forks cannot alter instructions - Pre-flight check catches missing `SKILL.md` if fork isn't rebased on `main` - No workspace code is executed with `GITHUB_TOKEN` (checkout without execution) ## Testing - ✅ `workflow_dispatch` tested against fork PR dotnet#34621 - ✅ Lock.yml statically verified — fork guard removed from `if:` conditions - ⏳ `pull_request` trigger on fork PRs can only be verified post-merge (GitHub Actions reads lock.yml from default branch) --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
aeb72fb to
5d89b17
Compare
|
@kubaflo , The Last AI summary is incomplete. Gate failed due to baseline snap missing. I have attached the baseline snap for windows. |
🤖 AI Summary📊 Expand Full Review —
|
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #30080 | Override OnBindingContextChanged() + update On*Changed handlers to propagate/clear BindingContext via SetInheritedBindingContext |
✅ PASSED (Windows Gate) | TitleBar.cs |
Canonical MAUI pattern; PR already merged |
🔧 Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix (claude-opus-4.6) | AddLogicalChild/RemoveLogicalChild in property handlers + SetChildInheritedBindingContext override |
✅ PASS | 2 files | Framework propagates automatically via logical child mechanism; overrides internal method |
| 2 | try-fix (claude-sonnet-4.6) | SetBinding/RemoveBinding on child BindingContextProperty with TitleBar as explicit source |
✅ PASS | 2 files | Binding infra handles propagation; unconventional use of binding for BindingContext itself |
| 3 | try-fix (gpt-5.3-codex) | Centralized propagation via PropertyChanged event handler |
✅ PASS | 2 files | Single handler; string-based property name comparisons less robust |
| 4 | try-fix (gpt-5.4, gemini unavailable) | Bind ContentView wrappers' BindingContext to TemplatedParent.BindingContext in BuildDefaultTemplate() |
✅ PASS | 2 files | Template-level fix; fragile if user replaces ControlTemplate |
| 5 | try-fix (claude-sonnet-4.6) | Override IVisualTreeElement.GetVisualChildren() to expose content views as visual tree children |
❌ FAIL | 2 files | MAUI BindingContext uses LogicalChildrenInternal, not visual tree |
| PR | PR #30080 | Override OnBindingContextChanged() + SetInheritedBindingContext in each property-changed handler |
✅ PASSED (Windows Gate) | 1 file | Canonical MAUI pattern; matches ContentView/Frame/Border |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| claude-opus-4.6 | 2 | No | Design space exhausted |
| claude-sonnet-4.6 | 2 | Yes | Visual children approach → ran as Attempt 5 → ❌ FAIL (wrong subsystem) |
| gpt-5.3-codex | 2 | Yes | Owner-driven (Window subscription) → wrong problem scope; round 2 idea: slot host elements (variant of Attempt 1) |
| gpt-5.4 | 2 | Yes | Framework modification (too risky/broad); round 2 idea: bind template root BindingContext (variant of Attempt 4) |
| All models | 3 | No | Round 2 new ideas are variants of existing approaches; exhausted |
Exhausted: Yes
Selected Fix: PR's fix — most canonical/idiomatic approach in MAUI codebase. Matches pattern used by ContentView, Frame, and Border. Directly handles both orderings (BindingContext-before-content and content-before-BindingContext). Minimal, clear, and maintainable. The 4 alternative passing approaches all had trade-offs: Attempt 1 overrides an internal method; Attempt 2 is unconventional binding usage; Attempt 3 uses string comparisons; Attempt 4 is fragile against ControlTemplate replacement.
📋 Report — Final Recommendation
⚠️ Final Recommendation: REQUEST CHANGES
Phase Status
| Phase | Status | Notes |
|---|---|---|
| Pre-Flight | ✅ COMPLETE | Issue #24831, TitleBar BindingContext propagation; Windows/MacCatalyst only |
| Gate | ❌ FAILED | Android — test is `#if MACCATALYST |
| Try-Fix | ✅ COMPLETE | 5 attempts: 4 passing alternatives, 1 fail (visual children); PR's fix selected |
| Report | ✅ COMPLETE |
Summary
PR #30080 correctly fixes BindingContext propagation for TitleBar on Windows/MacCatalyst and was validated on Windows (Gate ✅ PASSED in prior review). However, on Android, the Gate ❌ FAILED because the UITest is correctly scoped to #if MACCATALYST || WINDOWS but the HostApp page uses PlatformAffected.All, which caused the gate test detector to attempt Android testing. The fix itself is sound; the issue is a test scoping mismatch in the HostApp page.
Root Cause (Gate Failure)
Issue24831.cs (HostApp) declares [Issue(..., PlatformAffected.All)] — but TitleBar is a Windows/MacCatalyst-only feature and the corresponding test class (TestCases.Shared.Tests) is wrapped in #if MACCATALYST || WINDOWS. On Android, the test class does not exist in the compiled output, so no test can fail without the fix, no test can pass with the fix — the gate cannot verify correctness and rightly fails.
The fix in TitleBar.cs is correct: It uses SetInheritedBindingContext in OnBindingContextChanged() and in each On*Changed handler — the canonical MAUI pattern matching ContentView, Frame, and Border.
Fix Quality
The PR's core fix is correct and idiomatic:
- Overrides
OnBindingContextChanged()to propagateBindingContextto all three slot views when TitleBar's context changes - Updates
OnLeadingChanged,OnContentChanged,OnTrailingContentChangedto set/clear context when slot views are reassigned - Calls
base.OnBindingContextChanged()at the end — correct MAUI convention - Uses
SetInheritedBindingContext(not direct.BindingContext =assignment) — correct for inherited propagation
Issue (non-functional, test scoping): Issue24831.cs in HostApp declares PlatformAffected.All. Since TitleBar only exists on Windows/MacCatalyst, this should be scoped appropriately (e.g., PlatformAffected.Windows | PlatformAffected.macOS or the platform-specific equivalent). This is the only actionable change needed to fix the Android Gate failure.
Try-Fix results: 4 independent passing approaches were found. All had trade-offs versus the PR's fix:
- Attempt 1 (logical children): Overrides an internal
SetChildInheritedBindingContextmethod - Attempt 2 (explicit binding): Unconventional use of binding infrastructure for
BindingContextitself - Attempt 3 (PropertyChanged): String-based property name comparisons; less robust
- Attempt 4 (template wrappers): Fragile if user replaces
ControlTemplate
The PR's fix is the simplest, most maintainable, and most consistent with MAUI conventions.
Selected Fix: PR
Requested Change
Fix PlatformAffected.All → correct Windows/MacCatalyst-scoped value in src/Controls/tests/TestCases.HostApp/Issues/Issue24831.cs to prevent false gate failures on Android.
🚦 Gate — Test Verification📊 Expand Full Gate —
|
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Issue Details:
BindingContext of the Window TitleBar is not being passed on to its child content (Content, LeadingContent, TrailingContent).
Description of Change
OnBindingContextChangedmethod in theTitleBarclass to propagate theBindingContextto its child elements (Content,LeadingContent, andTrailingContent).OnLeadingChanged,OnContentChanged, andOnTrailingContentChangedmethods to clear and reapply the inheritedBindingContextwhen the respective properties change.Validated the behaviour in the following platforms
Issues Fixed
Fixes #24831
Output