[Shell] Propagate Page bindings to TitleView and Shell Binding to Flyout (#5934) fixes #5650 fixes #5501

* 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
This commit is contained in:
Shane Neuville 2019-04-18 09:25:31 -06:00 коммит произвёл Rui Marinho
Родитель d63002c25f
Коммит dbf4037a31
15 изменённых файлов: 168 добавлений и 26 удалений

Просмотреть файл

@ -581,6 +581,30 @@ namespace Xamarin.Forms.Controls
#endif #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 #if UITEST
[SetUp] [SetUp]
public void Setup() public void Setup()

Просмотреть файл

@ -34,7 +34,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Issue4484.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Issue4484.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue3509.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Issue3509.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue4597.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Issue4597.cs" />
<Compile Include="$(MSBuildThisFileDirectory)A11yTabIndex.xaml.cs"> <Compile Include="$(MSBuildThisFileDirectory)A11yTabIndex.xaml.cs">
<DependentUpon>A11yTabIndex.xaml</DependentUpon> <DependentUpon>A11yTabIndex.xaml</DependentUpon>
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
@ -1158,7 +1158,7 @@
<Generator>MSBuild:UpdateDesignTimeXaml</Generator> <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource> </EmbeddedResource>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)A11yTabIndex.xaml"> <EmbeddedResource Include="$(MSBuildThisFileDirectory)A11yTabIndex.xaml">
<SubType>Designer</SubType> <SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator> <Generator>MSBuild:Compile</Generator>

Просмотреть файл

@ -5,7 +5,7 @@
Routing.Route="shellcontent" Routing.Route="shellcontent"
Shell.SetPaddingInsets="true" Shell.SetPaddingInsets="true"
Shell.TabBarIsVisible="false" Shell.TabBarIsVisible="false"
x:Class="Xamarin.Forms.Controls.ShellContent"> x:Class="Xamarin.Forms.Controls.ShellContentTest">
<Page.ToolbarItems> <Page.ToolbarItems>
<ToolbarItem Text="Search" Icon="bank.png" /> <ToolbarItem Text="Search" Icon="bank.png" />
</Page.ToolbarItems> </Page.ToolbarItems>

Просмотреть файл

@ -13,7 +13,7 @@ namespace Xamarin.Forms.Controls
[Preserve] [Preserve]
[QueryProperty("Text", "welcome")] [QueryProperty("Text", "welcome")]
[XamlCompilation(XamlCompilationOptions.Compile)] [XamlCompilation(XamlCompilationOptions.Compile)]
public partial class ShellContent : ContentPage public partial class ShellContentTest : ContentPage
{ {
private class MySearchHandler : SearchHandler private class MySearchHandler : SearchHandler
{ {
@ -51,7 +51,7 @@ namespace Xamarin.Forms.Controls
private string _text; private string _text;
public ShellContent() public ShellContentTest()
{ {
InitializeComponent(); InitializeComponent();
@ -89,7 +89,7 @@ namespace Xamarin.Forms.Controls
private void InsertClicked(object sender, EventArgs e) private void InsertClicked(object sender, EventArgs e)
{ {
Navigation.InsertPageBefore(new ShellContent(), this); Navigation.InsertPageBefore(new ShellContentTest(), this);
} }
private void ToggleClicked(object sender, EventArgs e) private void ToggleClicked(object sender, EventArgs e)
@ -122,7 +122,7 @@ namespace Xamarin.Forms.Controls
private async void PushClicked(object sender, EventArgs e) private async void PushClicked(object sender, EventArgs e)
{ {
await Navigation.PushAsync(new ShellContent() await Navigation.PushAsync(new ShellContentTest()
{ {
Text = Text + "1" Text = Text + "1"
}); });

Просмотреть файл

@ -50,6 +50,9 @@
<Compile Update="GalleryPages\VisualStateManagerGalleries\OnPlatformExample.xaml.cs"> <Compile Update="GalleryPages\VisualStateManagerGalleries\OnPlatformExample.xaml.cs">
<DependentUpon>OnPlatformExample.xaml</DependentUpon> <DependentUpon>OnPlatformExample.xaml</DependentUpon>
</Compile> </Compile>
<Compile Update="ShellContentTest.xaml.cs">
<DependentUpon>ShellContentTest.xaml</DependentUpon>
</Compile>
<EmbeddedResource Update="GalleryPages\BindableLayoutGalleryPage.xaml"> <EmbeddedResource Update="GalleryPages\BindableLayoutGalleryPage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator> <Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource> </EmbeddedResource>

Просмотреть файл

@ -303,6 +303,9 @@ namespace Xamarin.Forms.Core.UnitTests
var label = new Label(); var label = new Label();
var viewModel = new Object();
shell.BindingContext = viewModel;
shell.FlyoutHeader = label; shell.FlyoutHeader = label;
Assert.AreEqual(((IShellController)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, label2);
Assert.AreEqual(((IShellController)shell).FlyoutHeader.BindingContext, label); Assert.AreEqual(((IShellController)shell).FlyoutHeader.BindingContext, viewModel);
shell.FlyoutHeaderTemplate = null; shell.FlyoutHeaderTemplate = null;
@ -370,5 +373,110 @@ namespace Xamarin.Forms.Core.UnitTests
shell.GoToAsync("//rootlevelcontent1"); shell.GoToAsync("//rootlevelcontent1");
Assert.AreEqual(shell.CurrentItem, item1); 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);
}
} }
} }

Просмотреть файл

@ -202,6 +202,9 @@ namespace Xamarin.Forms
if (Shell.GetSearchHandler(this) is SearchHandler searchHandler) if (Shell.GetSearchHandler(this) is SearchHandler searchHandler)
SetInheritedBindingContext(searchHandler, BindingContext); SetInheritedBindingContext(searchHandler, BindingContext);
if (Shell.GetTitleView(this) is View titleView)
SetInheritedBindingContext(titleView, BindingContext);
} }
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)

Просмотреть файл

@ -49,6 +49,9 @@ namespace Xamarin.Forms
get { return _effectiveVisual; } get { return _effectiveVisual; }
set set
{ {
if (value == _effectiveVisual)
return;
_effectiveVisual = value; _effectiveVisual = value;
OnPropertyChanged(VisualElement.VisualProperty.PropertyName); OnPropertyChanged(VisualElement.VisualProperty.PropertyName);
} }

Просмотреть файл

@ -338,6 +338,10 @@ namespace Xamarin.Forms
{ {
base.OnPropertyChanged(propertyName); 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) if (_effects == null || _effects.Count == 0)
return; return;

Просмотреть файл

@ -67,6 +67,9 @@ namespace Xamarin.Forms
get { return _effectiveVisual; } get { return _effectiveVisual; }
set set
{ {
if (value == _effectiveVisual)
return;
_effectiveVisual = value; _effectiveVisual = value;
OnPropertyChanged(VisualElement.VisualProperty.PropertyName); OnPropertyChanged(VisualElement.VisualProperty.PropertyName);
} }

Просмотреть файл

@ -16,8 +16,6 @@ namespace Xamarin.Forms
public interface IShellController : IPageController public interface IShellController : IPageController
{ {
event EventHandler HeaderChanged;
event EventHandler StructureChanged; event EventHandler StructureChanged;
View FlyoutHeader { get; } View FlyoutHeader { get; }

Просмотреть файл

@ -187,19 +187,12 @@ namespace Xamarin.Forms
List<(IAppearanceObserver Observer, Element Pivot)> _appearanceObservers = new List<(IAppearanceObserver Observer, Element Pivot)>(); List<(IAppearanceObserver Observer, Element Pivot)> _appearanceObservers = new List<(IAppearanceObserver Observer, Element Pivot)>();
List<IFlyoutBehaviorObserver> _flyoutBehaviorObservers = new List<IFlyoutBehaviorObserver>(); List<IFlyoutBehaviorObserver> _flyoutBehaviorObservers = new List<IFlyoutBehaviorObserver>();
event EventHandler IShellController.HeaderChanged
{
add { _headerChanged += value; }
remove { _headerChanged -= value; }
}
event EventHandler IShellController.StructureChanged event EventHandler IShellController.StructureChanged
{ {
add { _structureChanged += value; } add { _structureChanged += value; }
remove { _structureChanged -= value; } remove { _structureChanged -= value; }
} }
event EventHandler _headerChanged;
event EventHandler _structureChanged; event EventHandler _structureChanged;
View IShellController.FlyoutHeader => FlyoutHeaderView; View IShellController.FlyoutHeader => FlyoutHeaderView;
@ -708,10 +701,16 @@ namespace Xamarin.Forms
_flyoutHeaderView = value; _flyoutHeaderView = value;
if (_flyoutHeaderView != null) if (_flyoutHeaderView != null)
OnChildAdded(_flyoutHeaderView); OnChildAdded(_flyoutHeaderView);
_headerChanged?.Invoke(this, EventArgs.Empty);
} }
} }
protected override void OnBindingContextChanged()
{
base.OnBindingContextChanged();
if (FlyoutHeaderView != null)
SetInheritedBindingContext(FlyoutHeaderView, BindingContext);
}
List<List<Element>> IShellController.GenerateFlyoutGrouping() List<List<Element>> IShellController.GenerateFlyoutGrouping()
{ {
// The idea here is to create grouping such that the Flyout would // The idea here is to create grouping such that the Flyout would
@ -1033,10 +1032,6 @@ namespace Xamarin.Forms
else else
FlyoutHeaderView = null; FlyoutHeaderView = null;
} }
else
{
FlyoutHeaderView.BindingContext = newVal;
}
} }
void OnFlyoutHeaderTemplateChanged(DataTemplate oldValue, DataTemplate newValue) void OnFlyoutHeaderTemplateChanged(DataTemplate oldValue, DataTemplate newValue)
@ -1051,7 +1046,6 @@ namespace Xamarin.Forms
else else
{ {
var newHeaderView = (View)newValue.CreateContent(FlyoutHeader, this); var newHeaderView = (View)newValue.CreateContent(FlyoutHeader, this);
newHeaderView.BindingContext = FlyoutHeader;
FlyoutHeaderView = newHeaderView; FlyoutHeaderView = newHeaderView;
} }
} }

Просмотреть файл

@ -89,7 +89,8 @@ namespace Xamarin.Forms
internal override ReadOnlyCollection<Element> LogicalChildrenInternal => _logicalChildrenReadOnly ?? (_logicalChildrenReadOnly = new ReadOnlyCollection<Element>(_logicalChildren)); internal override ReadOnlyCollection<Element> LogicalChildrenInternal => _logicalChildrenReadOnly ?? (_logicalChildrenReadOnly = new ReadOnlyCollection<Element>(_logicalChildren));
Page ContentCache { Page ContentCache
{
get { return _contentCache; } get { return _contentCache; }
set set
{ {

Просмотреть файл

@ -64,6 +64,9 @@ namespace Xamarin.Forms.Platform.Android
_bottomView.SetBackgroundColor(Color.White.ToAndroid()); _bottomView.SetBackgroundColor(Color.White.ToAndroid());
_bottomView.SetOnNavigationItemSelectedListener(this); _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); HookEvents(ShellItem);
SetupMenu(); SetupMenu();

Просмотреть файл

@ -386,8 +386,6 @@ namespace Xamarin.Forms.Platform.Android
} }
else else
{ {
// FIXME
titleView.Parent = _shellContext.Shell;
_titleViewContainer = new ContainerView(context, titleView); _titleViewContainer = new ContainerView(context, titleView);
_titleViewContainer.MatchHeight = _titleViewContainer.MatchWidth = true; _titleViewContainer.MatchHeight = _titleViewContainer.MatchWidth = true;
_titleViewContainer.LayoutParameters = new Toolbar.LayoutParams(LP.MatchParent, LP.MatchParent) _titleViewContainer.LayoutParameters = new Toolbar.LayoutParams(LP.MatchParent, LP.MatchParent)