[Shell] Propagate Page bindings to TitleView and Shell Binding to Flyout (#5934)...
authorShane Neuville <shane94@hotmail.com>
Thu, 18 Apr 2019 15:25:31 +0000 (09:25 -0600)
committerRui Marinho <me@ruimarinho.net>
Thu, 18 Apr 2019 15:25:31 +0000 (16:25 +0100)
* propagate bindingcontext

* - add exception message and fix poorly named xaml file

* add ui test automation

* - fix unit test to represent new code

* - changed from ui test to unit test

* - propagate visual, parent, bc to titleview

* - style fixes

15 files changed:
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/TestPages/TestPages.cs
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
Xamarin.Forms.Controls/ShellContentTest.xaml [moved from Xamarin.Forms.Controls/ShellContent.xaml with 94% similarity]
Xamarin.Forms.Controls/ShellContentTest.xaml.cs [moved from Xamarin.Forms.Controls/ShellContent.xaml.cs with 93% similarity]
Xamarin.Forms.Controls/Xamarin.Forms.Controls.csproj
Xamarin.Forms.Core.UnitTests/ShellTests.cs
Xamarin.Forms.Core/BindableObject.cs
Xamarin.Forms.Core/Cells/Cell.cs
Xamarin.Forms.Core/Element.cs
Xamarin.Forms.Core/Shell/BaseShellItem.cs
Xamarin.Forms.Core/Shell/IShellController.cs
Xamarin.Forms.Core/Shell/Shell.cs
Xamarin.Forms.Core/Shell/ShellContent.cs
Xamarin.Forms.Platform.Android/Renderers/ShellItemRenderer.cs
Xamarin.Forms.Platform.Android/Renderers/ShellToolbarTracker.cs

index 607e60f..b1bc3e7 100644 (file)
@@ -581,6 +581,30 @@ namespace Xamarin.Forms.Controls
 #endif
                }
 
+               public ContentPage CreateContentPage()
+               {
+                       ContentPage page = new ContentPage();
+                       ShellItem item = new ShellItem()
+                       {
+                               Items =
+                               {
+                                       new ShellSection()
+                                       {
+                                               Items =
+                                               {
+                                                       new ShellContent()
+                                                       {
+                                                               Content = page
+                                                       }
+                                               }
+                                       }
+                               }
+                       };
+
+                       Items.Add(item);
+                       return page;
+
+               }
 #if UITEST
                [SetUp]
                public void Setup()
index 6173e4e..83084d5 100644 (file)
@@ -34,7 +34,7 @@
     <Compile Include="$(MSBuildThisFileDirectory)Issue4484.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Issue3509.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Issue4597.cs" />
-       <Compile Include="$(MSBuildThisFileDirectory)A11yTabIndex.xaml.cs">
+    <Compile Include="$(MSBuildThisFileDirectory)A11yTabIndex.xaml.cs">
       <DependentUpon>A11yTabIndex.xaml</DependentUpon>
       <SubType>Code</SubType>
     </Compile>
       <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
     </EmbeddedResource>
   </ItemGroup>
-    <ItemGroup>
+  <ItemGroup>
     <EmbeddedResource Include="$(MSBuildThisFileDirectory)A11yTabIndex.xaml">
       <SubType>Designer</SubType>
       <Generator>MSBuild:Compile</Generator>
similarity index 94%
rename from Xamarin.Forms.Controls/ShellContent.xaml
rename to Xamarin.Forms.Controls/ShellContentTest.xaml
index e5fb110..8fd4ecc 100644 (file)
@@ -5,7 +5,7 @@
                         Routing.Route="shellcontent"
                         Shell.SetPaddingInsets="true"
                         Shell.TabBarIsVisible="false"
-             x:Class="Xamarin.Forms.Controls.ShellContent">
+             x:Class="Xamarin.Forms.Controls.ShellContentTest">
        <Page.ToolbarItems>
                <ToolbarItem Text="Search" Icon="bank.png" />
        </Page.ToolbarItems>
@@ -13,7 +13,7 @@ namespace Xamarin.Forms.Controls
        [Preserve]
        [QueryProperty("Text", "welcome")]
        [XamlCompilation(XamlCompilationOptions.Compile)]
-       public partial class ShellContent : ContentPage
+       public partial class ShellContentTest : ContentPage
        {
                private class MySearchHandler : SearchHandler
                {
@@ -51,7 +51,7 @@ namespace Xamarin.Forms.Controls
 
                private string _text;
 
-               public ShellContent()
+               public ShellContentTest()
                {
                        InitializeComponent();
 
@@ -89,7 +89,7 @@ namespace Xamarin.Forms.Controls
 
                private void InsertClicked(object sender, EventArgs e)
                {
-                       Navigation.InsertPageBefore(new ShellContent(), this);
+                       Navigation.InsertPageBefore(new ShellContentTest(), this);
                }
 
                private void ToggleClicked(object sender, EventArgs e)
@@ -122,7 +122,7 @@ namespace Xamarin.Forms.Controls
 
                private async void PushClicked(object sender, EventArgs e)
                {
-                       await Navigation.PushAsync(new ShellContent()
+                       await Navigation.PushAsync(new ShellContentTest()
                        {
                                Text = Text + "1"
                        });
index 93ad41c..df98d49 100644 (file)
@@ -50,6 +50,9 @@
     <Compile Update="GalleryPages\VisualStateManagerGalleries\OnPlatformExample.xaml.cs">
       <DependentUpon>OnPlatformExample.xaml</DependentUpon>
     </Compile>
+    <Compile Update="ShellContentTest.xaml.cs">
+      <DependentUpon>ShellContentTest.xaml</DependentUpon>
+    </Compile>
     <EmbeddedResource Update="GalleryPages\BindableLayoutGalleryPage.xaml">
       <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
     </EmbeddedResource>
index 84cfcb1..495647f 100644 (file)
@@ -303,6 +303,9 @@ namespace Xamarin.Forms.Core.UnitTests
 
                        var label = new Label();
 
+                       var viewModel = new Object();
+                       shell.BindingContext = viewModel;
+
                        shell.FlyoutHeader = label;
 
                        Assert.AreEqual(((IShellController)shell).FlyoutHeader, label);
@@ -315,7 +318,7 @@ namespace Xamarin.Forms.Core.UnitTests
                        });
 
                        Assert.AreEqual(((IShellController)shell).FlyoutHeader, label2);
-                       Assert.AreEqual(((IShellController)shell).FlyoutHeader.BindingContext, label);
+                       Assert.AreEqual(((IShellController)shell).FlyoutHeader.BindingContext, viewModel);
 
                        shell.FlyoutHeaderTemplate = null;
 
@@ -370,5 +373,110 @@ namespace Xamarin.Forms.Core.UnitTests
                        shell.GoToAsync("//rootlevelcontent1");
                        Assert.AreEqual(shell.CurrentItem, item1);
                }
+
+               [Test]
+               public async Task TitleViewBindingContext()
+               {
+                       Shell shell = new Shell();
+                       ContentPage page = new ContentPage();
+                       shell.Items.Add(CreateShellItem(page));
+                       page.BindingContext = new { Text = "Binding" };
+
+                       // setup title view
+                       StackLayout layout = new StackLayout() { BackgroundColor = Color.White };
+                       Label label = new Label();
+                       label.SetBinding(Label.TextProperty, "Text");
+                       layout.Children.Add(label);
+                       Shell.SetTitleView(page, layout);
+
+                       Assert.AreEqual("Binding", label.Text);
+                       page.BindingContext = new { Text = "Binding Changed" };
+                       Assert.AreEqual("Binding Changed", label.Text);
+               }
+
+               [Test]
+               public async Task VisualPropagationPageLevel()
+               {
+                       Shell shell = new Shell();
+                       ContentPage page = new ContentPage();
+                       shell.Items.Add(CreateShellItem(page));
+
+                       // setup title view
+                       StackLayout titleView = new StackLayout() { BackgroundColor = Color.White };
+                       Button button = new Button();
+                       titleView.Children.Add(button);
+                       Shell.SetTitleView(page, titleView);
+                       IVisualController visualController = button as IVisualController;
+
+
+                       Assert.AreEqual(page, titleView.Parent);
+
+                       Assert.AreEqual(VisualMarker.Default, ((IVisualController)button).EffectiveVisual);
+                       page.Visual = VisualMarker.Material;
+                       Assert.AreEqual(VisualMarker.Material, ((IVisualController)button).EffectiveVisual);
+               }
+
+               [Test]
+               public async Task VisualPropagationShellLevel()
+               {
+                       Shell shell = new Shell();
+                       ContentPage page = new ContentPage();
+                       shell.Items.Add(CreateShellItem(page));
+
+                       // setup title view
+                       StackLayout titleView = new StackLayout() { BackgroundColor = Color.White };
+                       Button button = new Button();
+                       titleView.Children.Add(button);
+                       Shell.SetTitleView(page, titleView);
+                       IVisualController visualController = button as IVisualController;
+
+
+                       Assert.AreEqual(page, titleView.Parent);
+                       Assert.AreEqual(VisualMarker.Default, ((IVisualController)button).EffectiveVisual);
+                       shell.Visual = VisualMarker.Material;
+                       Assert.AreEqual(VisualMarker.Material, ((IVisualController)button).EffectiveVisual);
+               }
+
+               [Test]
+               public async Task FlyoutViewVisualPropagation()
+               {
+                       Shell shell = new Shell();
+                       ContentPage page = new ContentPage();
+                       shell.Items.Add(CreateShellItem(page));
+
+                       
+                       // setup title view
+                       StackLayout flyoutView = new StackLayout() { BackgroundColor = Color.White };
+                       Button button = new Button();
+                       flyoutView.Children.Add(button);
+                       shell.SetValue(Shell.FlyoutHeaderProperty, flyoutView);
+
+                       IVisualController visualController = button as IVisualController;
+                       Assert.AreEqual(VisualMarker.Default, visualController.EffectiveVisual);
+                       shell.Visual = VisualMarker.Material;
+                       Assert.AreEqual(VisualMarker.Material, visualController.EffectiveVisual);
+               }
+
+               [Test]
+               public async Task FlyoutViewBindingContext()
+               {
+                       Shell shell = new Shell();
+                       ContentPage page = new ContentPage();
+                       shell.Items.Add(CreateShellItem(page));
+                       shell.BindingContext = new { Text = "Binding" };
+
+                       // setup title view
+                       StackLayout flyoutView = new StackLayout() { BackgroundColor = Color.White };
+                       Label label = new Label();
+                       label.SetBinding(Label.TextProperty, "Text");
+                       flyoutView.Children.Add(label);
+                       shell.SetValue(Shell.FlyoutHeaderProperty, flyoutView);
+
+                       Assert.AreEqual("Binding", label.Text);
+                       shell.BindingContext = new { Text = "Binding Changed" };
+                       Assert.AreEqual("Binding Changed", label.Text);
+                       shell.SetValue(Shell.FlyoutHeaderProperty, new ContentView());
+                       Assert.AreEqual(null, flyoutView.BindingContext);
+               }
        }
 }
index fdf3863..3416464 100644 (file)
@@ -202,6 +202,9 @@ namespace Xamarin.Forms
 
                        if (Shell.GetSearchHandler(this) is SearchHandler searchHandler)
                                SetInheritedBindingContext(searchHandler, BindingContext);
+
+                       if (Shell.GetTitleView(this) is View titleView)
+                               SetInheritedBindingContext(titleView, BindingContext);
                }
 
                protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
index e5e4f20..c8898d6 100644 (file)
@@ -49,6 +49,9 @@ namespace Xamarin.Forms
                        get { return _effectiveVisual; }
                        set
                        {
+                               if (value == _effectiveVisual)
+                                       return;
+
                                _effectiveVisual = value;
                                OnPropertyChanged(VisualElement.VisualProperty.PropertyName);
                        }
index c7274f2..19ad039 100644 (file)
@@ -338,6 +338,10 @@ namespace Xamarin.Forms
                {
                        base.OnPropertyChanged(propertyName);
 
+                       IPropertyPropagationController titleView = Shell.GetTitleView(this) ?? NavigationPage.GetTitleView(this);
+                       if(titleView != null)
+                               PropertyPropagationExtensions.PropagatePropertyChanged(propertyName, this, new[] { titleView });
+
                        if (_effects == null || _effects.Count == 0)
                                return;
 
index 8311d83..da705fd 100644 (file)
@@ -67,6 +67,9 @@ namespace Xamarin.Forms
                        get { return _effectiveVisual; }
                        set
                        {
+                               if (value == _effectiveVisual)
+                                       return;
+
                                _effectiveVisual = value;
                                OnPropertyChanged(VisualElement.VisualProperty.PropertyName);
                        }
index 9d62c05..fcf8e0a 100644 (file)
@@ -16,8 +16,6 @@ namespace Xamarin.Forms
 
        public interface IShellController : IPageController
        {
-               event EventHandler HeaderChanged;
-
                event EventHandler StructureChanged;
 
                View FlyoutHeader { get; }
index 5d5f99a..bae08e6 100644 (file)
@@ -187,19 +187,12 @@ namespace Xamarin.Forms
                List<(IAppearanceObserver Observer, Element Pivot)> _appearanceObservers = new List<(IAppearanceObserver Observer, Element Pivot)>();
                List<IFlyoutBehaviorObserver> _flyoutBehaviorObservers = new List<IFlyoutBehaviorObserver>();
 
-               event EventHandler IShellController.HeaderChanged
-               {
-                       add { _headerChanged += value; }
-                       remove { _headerChanged -= value; }
-               }
-
                event EventHandler IShellController.StructureChanged
                {
                        add { _structureChanged += value; }
                        remove { _structureChanged -= value; }
                }
 
-               event EventHandler _headerChanged;
                event EventHandler _structureChanged;
 
                View IShellController.FlyoutHeader => FlyoutHeaderView;
@@ -708,10 +701,16 @@ namespace Xamarin.Forms
                                _flyoutHeaderView = value;
                                if (_flyoutHeaderView != null)
                                        OnChildAdded(_flyoutHeaderView);
-                               _headerChanged?.Invoke(this, EventArgs.Empty);
                        }
                }
 
+               protected override void OnBindingContextChanged()
+               {
+                       base.OnBindingContextChanged();
+                       if (FlyoutHeaderView != null)
+                               SetInheritedBindingContext(FlyoutHeaderView, BindingContext);
+               }
+
                List<List<Element>> IShellController.GenerateFlyoutGrouping()
                {
                        // The idea here is to create grouping such that the Flyout would
@@ -1033,10 +1032,6 @@ namespace Xamarin.Forms
                                else
                                        FlyoutHeaderView = null;
                        }
-                       else
-                       {
-                               FlyoutHeaderView.BindingContext = newVal;
-                       }
                }
 
                void OnFlyoutHeaderTemplateChanged(DataTemplate oldValue, DataTemplate newValue)
@@ -1051,7 +1046,6 @@ namespace Xamarin.Forms
                        else
                        {
                                var newHeaderView = (View)newValue.CreateContent(FlyoutHeader, this);
-                               newHeaderView.BindingContext = FlyoutHeader;
                                FlyoutHeaderView = newHeaderView;
                        }
                }
index 799f208..8403efe 100644 (file)
@@ -89,7 +89,8 @@ namespace Xamarin.Forms
 
                internal override ReadOnlyCollection<Element> LogicalChildrenInternal => _logicalChildrenReadOnly ?? (_logicalChildrenReadOnly = new ReadOnlyCollection<Element>(_logicalChildren));
 
-               Page ContentCache {
+               Page ContentCache
+               {
                        get { return _contentCache; }
                        set
                        {
index 09452a5..e6840f7 100644 (file)
@@ -64,6 +64,9 @@ namespace Xamarin.Forms.Platform.Android
                        _bottomView.SetBackgroundColor(Color.White.ToAndroid());
                        _bottomView.SetOnNavigationItemSelectedListener(this);
 
+                       if(ShellItem == null)
+                               throw new ArgumentException("Active Shell Item not set. Have you added any Shell Items to your Shell?", nameof(ShellItem));
+
                        HookEvents(ShellItem);
                        SetupMenu();
 
index c7a5611..3d80d5d 100644 (file)
@@ -386,8 +386,6 @@ namespace Xamarin.Forms.Platform.Android
                        }
                        else
                        {
-                               // FIXME
-                               titleView.Parent = _shellContext.Shell;
                                _titleViewContainer = new ContainerView(context, titleView);
                                _titleViewContainer.MatchHeight = _titleViewContainer.MatchWidth = true;
                                _titleViewContainer.LayoutParameters = new Toolbar.LayoutParams(LP.MatchParent, LP.MatchParent)