From 5ba2c8446ac5463ededeef9cdfd0796db0968fdb Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Mon, 8 Jun 2026 09:33:14 +0200 Subject: [PATCH 1/2] Fix Shell title binding for TitleView Synchronize the effective Shell page title to Shell.Title so TitleView bindings can resolve the current page title without duplicating the rendered toolbar title. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Controls/src/Core/ShellToolbar.cs | 29 +++++++++++++---- .../tests/Core.UnitTests/ShellToolbarTests.cs | 32 +++++++++++++++++++ 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/src/Controls/src/Core/ShellToolbar.cs b/src/Controls/src/Core/ShellToolbar.cs index d74cbf82a16d..18de81ea2a0d 100644 --- a/src/Controls/src/Core/ShellToolbar.cs +++ b/src/Controls/src/Core/ShellToolbar.cs @@ -169,27 +169,44 @@ internal void UpdateTitle() Shell.TitleViewProperty, Shell.GetTitleView(_shell)); + var title = GetCurrentTitle(); + if (!IsShellTitleSetByUser()) + _shell.SetValueFromRenderer(Shell.TitleProperty, title); + if (TitleView != null) { Title = String.Empty; return; } + Title = title; + } + + string GetCurrentTitle() + { Page? currentPage = _shell.GetCurrentShellPage(); if (currentPage?.IsSet(Page.TitleProperty) == true) { - Title = currentPage.Title ?? String.Empty; + return currentPage.Title ?? String.Empty; } // We only want to use the ShellContent as a title if no pages have been // Pushed onto the stack else if (_shell.Navigation?.NavigationStack?.Count <= 1) { - Title = _shell.CurrentContent?.Title ?? String.Empty; - } - else - { - Title = String.Empty; + return _shell.CurrentContent?.Title ?? String.Empty; } + + return String.Empty; + } + + bool IsShellTitleSetByUser() + { + var titleContext = _shell.GetContext(Shell.TitleProperty); + if (titleContext == null) + return false; + + var specificity = titleContext.Values.GetSpecificity(); + return specificity != SetterSpecificity.DefaultValue && specificity != SetterSpecificity.FromHandler; } } } diff --git a/src/Controls/tests/Core.UnitTests/ShellToolbarTests.cs b/src/Controls/tests/Core.UnitTests/ShellToolbarTests.cs index d18d47cb440a..274d4bc419ec 100644 --- a/src/Controls/tests/Core.UnitTests/ShellToolbarTests.cs +++ b/src/Controls/tests/Core.UnitTests/ShellToolbarTests.cs @@ -270,6 +270,38 @@ public async Task TitleAndTitleViewAreMutuallyExclusive() Assert.Equal("Test Title", toolbar.Title); } + [Fact] + public void ShellTitleReflectsCurrentPageTitleForTitleViewBindings() + { + var contentPage = new ContentPage() { Title = "Test Title" }; + var label = new Label(); + var titleView = new VerticalStackLayout() + { + Children = + { + label + } + }; + + TestShell testShell = new TestShell(contentPage); + _ = new Window() + { + Page = testShell + }; + + label.SetBinding(Label.TextProperty, new Binding(nameof(Shell.Title), source: testShell)); + Shell.SetTitleView(contentPage, titleView); + + Assert.Empty(testShell.Toolbar.Title); + Assert.Equal("Test Title", testShell.Title); + Assert.Equal("Test Title", label.Text); + + contentPage.Title = "Updated Test Title"; + + Assert.Equal("Updated Test Title", testShell.Title); + Assert.Equal("Updated Test Title", label.Text); + } + [Fact] public void ContentPageColorsPropagateToShellToolbar() { From d3c47ac240eeab63bc47479ea8a2e2029ca507ef Mon Sep 17 00:00:00 2001 From: Gerald Versluis Date: Thu, 11 Jun 2026 09:25:03 +0200 Subject: [PATCH 2/2] Preserve bound Shell titles Treat existing Shell.Title bindings as user-owned state before synchronizing the current page title for TitleView bindings. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- src/Controls/src/Core/ShellToolbar.cs | 3 +++ .../tests/Core.UnitTests/ShellToolbarTests.cs | 26 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/Controls/src/Core/ShellToolbar.cs b/src/Controls/src/Core/ShellToolbar.cs index 18de81ea2a0d..d6c324268168 100644 --- a/src/Controls/src/Core/ShellToolbar.cs +++ b/src/Controls/src/Core/ShellToolbar.cs @@ -205,6 +205,9 @@ bool IsShellTitleSetByUser() if (titleContext == null) return false; + if (titleContext.Bindings.Count > 0) + return true; + var specificity = titleContext.Values.GetSpecificity(); return specificity != SetterSpecificity.DefaultValue && specificity != SetterSpecificity.FromHandler; } diff --git a/src/Controls/tests/Core.UnitTests/ShellToolbarTests.cs b/src/Controls/tests/Core.UnitTests/ShellToolbarTests.cs index 274d4bc419ec..ef4fa4829bcf 100644 --- a/src/Controls/tests/Core.UnitTests/ShellToolbarTests.cs +++ b/src/Controls/tests/Core.UnitTests/ShellToolbarTests.cs @@ -302,6 +302,32 @@ public void ShellTitleReflectsCurrentPageTitleForTitleViewBindings() Assert.Equal("Updated Test Title", label.Text); } + [Fact] + public void ShellTitleBindingIsNotOverwrittenByCurrentPageTitle() + { + var contentPage = new ContentPage() { Title = "Page Title" }; + var titleView = new VerticalStackLayout(); + var viewModel = new TestShellViewModel() { Text = "App Title" }; + + TestShell testShell = new TestShell(contentPage); + _ = new Window() + { + Page = testShell + }; + + testShell.SetBinding(Shell.TitleProperty, new Binding(nameof(TestShellViewModel.Text), BindingMode.TwoWay, source: viewModel)); + Shell.SetTitleView(contentPage, titleView); + + Assert.Empty(testShell.Toolbar.Title); + Assert.Equal("App Title", testShell.Title); + Assert.Equal("App Title", viewModel.Text); + + contentPage.Title = "Updated Page Title"; + + Assert.Equal("App Title", testShell.Title); + Assert.Equal("App Title", viewModel.Text); + } + [Fact] public void ContentPageColorsPropagateToShellToolbar() {