Skip to content
This repository was archived by the owner on May 1, 2024. It is now read-only.

Commit 19a15b5

Browse files
authored
Fix various quirks with flyout item bindings (#11987)
* Fix Shell flyout binding quirks * - fix constructor * Update ShellTests.cs
1 parent 4dc9761 commit 19a15b5

File tree

13 files changed

+434
-294
lines changed

13 files changed

+434
-294
lines changed

Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue10608.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public Issue10608()
2323

2424
void AddPage(string title)
2525
{
26-
var page = AddFlyoutItem(title);
26+
var page = CreateContentPage<FlyoutItem>(title);
2727

2828
page.Content = new Grid()
2929
{

Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue11247.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ public class Issue11247 : TestShell
2323
{
2424
protected override void Init()
2525
{
26-
var page = AddFlyoutItem("FlyoutItem 1");
27-
AddFlyoutItem("FlyoutItem 2");
26+
var page = CreateContentPage<FlyoutItem>("FlyoutItem 1");
27+
CreateContentPage<FlyoutItem>("FlyoutItem 2");
2828

2929
Items.Add(new MenuItem()
3030
{

Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/TestPages/TestPages.cs

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -681,34 +681,22 @@ public ContentPage AddBottomTab(string title, string icon = null)
681681
return page;
682682
}
683683

684-
public ContentPage AddFlyoutItem(string title)
684+
public ContentPage CreateContentPage<TShellItem>(string title)
685+
where TShellItem : ShellItem
685686
{
686687
ContentPage page = new ContentPage() { Title = title };
687-
AddFlyoutItem(page, title);
688+
AddContentPage<TShellItem, Tab>(page, title);
688689
return page;
689690
}
690691

691-
public FlyoutItem AddFlyoutItem(ContentPage page, string title)
692+
public FlyoutItem AddFlyoutItem(string title)
692693
{
693-
var item = new FlyoutItem
694-
{
695-
Title = title,
696-
Items =
697-
{
698-
new Tab
699-
{
700-
Title = title,
701-
Items =
702-
{
703-
page
704-
}
705-
}
706-
}
707-
};
708-
709-
Items.Add(item);
694+
return AddContentPage<FlyoutItem, Tab>(new ContentPage(), title);
695+
}
710696

711-
return item;
697+
public FlyoutItem AddFlyoutItem(ContentPage page, string title)
698+
{
699+
return AddContentPage<FlyoutItem, Tab>(page, title);
712700
}
713701

714702
public ContentPage CreateContentPage(string shellItemTitle = null)
Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
using System;
2+
using System.Linq;
3+
using System.Threading.Tasks;
4+
using NUnit.Framework;
5+
using Xamarin.Forms.Internals;
6+
7+
namespace Xamarin.Forms.Core.UnitTests
8+
{
9+
[TestFixture]
10+
public class ShellFlyoutItemTemplateTests : ShellTestBase
11+
{
12+
[Test]
13+
public void FlyoutItemDefaultStylesApplied()
14+
{
15+
Shell shell = new Shell();
16+
var shellItem = CreateShellItem();
17+
18+
shell.Items.Add(shellItem);
19+
20+
var element = GetFlyoutItemDataTemplateElement<Element>(shell, shellItem);
21+
var label = element.LogicalChildren.OfType<Label>().First();
22+
Assert.AreEqual(TextAlignment.Center, label.VerticalTextAlignment);
23+
}
24+
25+
26+
[Test]
27+
public void FlyoutItemLabelStyleCustom()
28+
{
29+
var classStyle = new Style(typeof(Label))
30+
{
31+
Setters = {
32+
new Setter { Property = Label.VerticalTextAlignmentProperty, Value = TextAlignment.Start }
33+
},
34+
Class = "fooClass",
35+
};
36+
37+
Shell shell = new Shell();
38+
shell.Resources = new ResourceDictionary { classStyle };
39+
var shellItem = CreateShellItem();
40+
shellItem.StyleClass = new[] { "fooClass" };
41+
42+
shell.Items.Add(shellItem);
43+
44+
var element = GetFlyoutItemDataTemplateElement<Element>(shell, shellItem);
45+
var label = element.LogicalChildren.OfType<Label>().First();
46+
Assert.AreEqual(TextAlignment.Start, label.VerticalTextAlignment);
47+
}
48+
49+
[Test]
50+
public void MenuItemLabelStyleCustom()
51+
{
52+
var classStyle = new Style(typeof(Label))
53+
{
54+
Setters = {
55+
new Setter { Property = Label.VerticalTextAlignmentProperty, Value = TextAlignment.Start }
56+
},
57+
Class = "fooClass",
58+
};
59+
60+
Shell shell = new Shell();
61+
shell.Resources = new ResourceDictionary { classStyle };
62+
var shellItem = CreateShellItem();
63+
var menuItem = new MenuItem();
64+
var shellMenuItem = new MenuShellItem(menuItem);
65+
menuItem.StyleClass = new[] { "fooClass" };
66+
shell.Items.Add(shellItem);
67+
shell.Items.Add(shellMenuItem);
68+
69+
var label = GetFlyoutItemDataTemplateElement<Label>(shell, shellMenuItem);
70+
Assert.AreEqual(TextAlignment.Start, label.VerticalTextAlignment);
71+
}
72+
73+
[Test]
74+
public void FlyoutItemLabelStyleDefault()
75+
{
76+
var classStyle = new Style(typeof(Label))
77+
{
78+
Setters = {
79+
new Setter { Property = Label.VerticalTextAlignmentProperty, Value = TextAlignment.Start }
80+
},
81+
Class = FlyoutItem.LabelStyle,
82+
};
83+
84+
Shell shell = new Shell();
85+
shell.Resources = new ResourceDictionary { classStyle };
86+
var shellItem = CreateShellItem();
87+
88+
shell.Items.Add(shellItem);
89+
90+
var label = GetFlyoutItemDataTemplateElement<Label>(shell, shellItem);
91+
Assert.AreEqual(TextAlignment.Start, label.VerticalTextAlignment);
92+
}
93+
94+
[Test]
95+
public void FlyoutItemDefaultTemplates()
96+
{
97+
Shell shell = new Shell();
98+
IShellController sc = (IShellController)shell;
99+
shell.MenuItemTemplate = new DataTemplate(() => new Label() { Text = "MenuItemTemplate" });
100+
shell.ItemTemplate = new DataTemplate(() => new Label() { Text = "ItemTemplate" });
101+
102+
var shellItem = CreateShellItem();
103+
var menuItem = new MenuShellItem(new MenuItem());
104+
shell.Items.Add(shellItem);
105+
shell.Items.Add(menuItem);
106+
107+
108+
DataTemplate triggerDefault = shell.ItemTemplate;
109+
triggerDefault = shell.MenuItemTemplate;
110+
111+
Assert.AreEqual("ItemTemplate", GetFlyoutItemDataTemplateElement<Label>(shell, shellItem).Text);
112+
Assert.AreEqual("MenuItemTemplate", GetFlyoutItemDataTemplateElement<Label>(shell, menuItem).Text);
113+
Assert.AreEqual("MenuItemTemplate", GetFlyoutItemDataTemplateElement<Label>(shell, menuItem.MenuItem).Text);
114+
}
115+
116+
[Test]
117+
public void FlyoutItemLabelVisualStateManager()
118+
{
119+
var groups = new VisualStateGroupList();
120+
var commonGroup = new VisualStateGroup();
121+
commonGroup.Name = "CommonStates";
122+
groups.Add(commonGroup);
123+
var normalState = new VisualState();
124+
normalState.Name = "Normal";
125+
var selectedState = new VisualState();
126+
selectedState.Name = "Selected";
127+
128+
normalState.Setters.Add(new Setter
129+
{
130+
Property = Label.BackgroundColorProperty,
131+
Value = Color.Red,
132+
TargetName = "FlyoutItemLabel"
133+
});
134+
135+
selectedState.Setters.Add(new Setter
136+
{
137+
Property = Label.BackgroundColorProperty,
138+
Value = Color.Green,
139+
TargetName = "FlyoutItemLabel"
140+
});
141+
142+
commonGroup.States.Add(normalState);
143+
commonGroup.States.Add(selectedState);
144+
145+
var classStyle = new Style(typeof(Grid))
146+
{
147+
Setters = {
148+
new Setter
149+
{
150+
Property = VisualStateManager.VisualStateGroupsProperty,
151+
Value = groups
152+
}
153+
},
154+
Class = FlyoutItem.LayoutStyle,
155+
};
156+
157+
Shell shell = new Shell();
158+
shell.Resources = new ResourceDictionary { classStyle };
159+
var shellItem = CreateShellItem();
160+
shell.Items.Add(shellItem);
161+
var grid = GetFlyoutItemDataTemplateElement<Grid>(shell, shellItem);
162+
var label = grid.LogicalChildren.OfType<Label>().First();
163+
164+
Assert.AreEqual(Color.Red, label.BackgroundColor);
165+
Assert.IsTrue(VisualStateManager.GoToState(grid, "Selected"));
166+
Assert.AreEqual(Color.Green, label.BackgroundColor);
167+
}
168+
169+
170+
171+
[Test]
172+
public void BindingContextFlyoutItems()
173+
{
174+
var flyoutItemVM = new TestShellViewModel() { Text = "Dog" };
175+
176+
Shell shell = new Shell();
177+
shell.BindingContext = flyoutItemVM;
178+
179+
var item1 = CreateShellItem<FlyoutItem>();
180+
item1.SetBinding(FlyoutItem.TitleProperty, "Text", mode: BindingMode.TwoWay);
181+
shell.Items.Add(item1);
182+
183+
MenuItem menuItem = new MenuItem();
184+
menuItem.SetBinding(MenuItem.TextProperty, "Text", mode: BindingMode.TwoWay);
185+
shell.Items.Add(menuItem);
186+
187+
var flyoutItemLabel = GetFlyoutItemDataTemplateElement<Label>(shell, shell.Items[0]);
188+
var menuItemLabel = GetFlyoutItemDataTemplateElement<Label>(shell, shell.Items[1]);
189+
190+
Assert.AreEqual("Dog", flyoutItemLabel.Text);
191+
Assert.AreEqual("Dog", menuItemLabel.Text);
192+
193+
flyoutItemVM.Text = "Cat";
194+
195+
Assert.AreEqual("Cat", flyoutItemLabel.Text);
196+
Assert.AreEqual("Cat", menuItemLabel.Text);
197+
198+
}
199+
200+
[Test]
201+
public void BindingContextSetsCorrectlyWhenUsingAsMultipleItemAndImplicitlyGeneratedShellSections()
202+
{
203+
Shell shell = new Shell();
204+
FlyoutItem item = new FlyoutItem() { FlyoutDisplayOptions = FlyoutDisplayOptions.AsMultipleItems };
205+
ShellContent shellContent1 = new ShellContent();
206+
207+
ShellContent shellContent2 = new ShellContent();
208+
209+
item.Items.Add(shellContent1);
210+
item.Items.Add(shellContent2);
211+
shell.Items.Add(item);
212+
213+
var vm = new TestShellViewModel();
214+
vm.SubViewModel = new TestShellViewModel() { Text = "Item1" };
215+
vm.SubViewModel2 = new TestShellViewModel() { Text = "Item2" };
216+
shell.BindingContext = vm;
217+
218+
shellContent1.SetBinding(BindableObject.BindingContextProperty, "SubViewModel");
219+
shellContent2.SetBinding(BindableObject.BindingContextProperty, "SubViewModel2");
220+
221+
shell.ItemTemplate = new DataTemplate(() =>
222+
{
223+
Label label = new Label();
224+
225+
label.SetBinding(Label.TextProperty, "BindingContext.Text");
226+
return label;
227+
});
228+
229+
var flyoutItems = (shell as IShellController).GenerateFlyoutGrouping();
230+
231+
232+
var label1 = GetFlyoutItemDataTemplateElement<Label>(shell, flyoutItems[0][0]);
233+
var label2 = GetFlyoutItemDataTemplateElement<Label>(shell, flyoutItems[0][1]);
234+
235+
Assert.AreEqual(label1.BindingContext, shellContent1);
236+
Assert.AreEqual(label2.BindingContext, shellContent2);
237+
238+
Assert.AreEqual("Item1", label1.Text);
239+
Assert.AreEqual("Item2", label2.Text);
240+
}
241+
242+
243+
T GetFlyoutItemDataTemplateElement<T>(Shell shell, BindableObject bo)
244+
where T : class
245+
{
246+
var content = (shell as IShellController).GetFlyoutItemDataTemplate(bo).CreateContent();
247+
248+
if (content is BindableObject bindableContent)
249+
{
250+
if(bo is MenuItem mi)
251+
bindableContent.BindingContext = mi.Parent;
252+
else
253+
bindableContent.BindingContext = bo;
254+
}
255+
256+
if (content is Element e)
257+
{
258+
e.Parent = shell;
259+
}
260+
else
261+
{
262+
e = null;
263+
}
264+
265+
if (content is T t)
266+
return t;
267+
268+
if (e == null)
269+
return default(T);
270+
271+
return e.LogicalChildren.OfType<T>().First();
272+
}
273+
274+
275+
//[Test]
276+
//public void FlyoutItemLabelStyleCanBeChangedAfterRendered()
277+
//{
278+
// var classStyle = new Style(typeof(Label))
279+
// {
280+
// Setters = {
281+
// new Setter { Property = Label.VerticalTextAlignmentProperty, Value = TextAlignment.Start }
282+
// },
283+
// Class = "fooClass",
284+
// };
285+
286+
// Shell shell = new Shell();
287+
// shell.Resources = new ResourceDictionary { classStyle };
288+
// var shellItem = CreateShellItem();
289+
290+
// shell.Items.Add(shellItem);
291+
292+
// var flyoutItemTemplate = (shell as IShellController).GetFlyoutItemDataTemplate(shellItem);
293+
// var thing = (Element)flyoutItemTemplate.CreateContent();
294+
// thing.Parent = shell;
295+
296+
// var label = thing.LogicalChildren.OfType<Label>().First();
297+
// Assert.AreEqual(TextAlignment.Center, label.VerticalTextAlignment);
298+
// shellItem.StyleClass = new[] { "fooClass" };
299+
// Assert.AreEqual(TextAlignment.Start, label.VerticalTextAlignment);
300+
//}
301+
302+
//[Test]
303+
//public void MenuItemLabelStyleCanBeChangedAfterRendered()
304+
//{
305+
// var classStyle = new Style(typeof(Label))
306+
// {
307+
// Setters = {
308+
// new Setter { Property = Label.VerticalTextAlignmentProperty, Value = TextAlignment.Start }
309+
// },
310+
// Class = "fooClass",
311+
// };
312+
313+
// Shell shell = new Shell();
314+
// shell.Resources = new ResourceDictionary { classStyle };
315+
// var shellItem = CreateShellItem();
316+
// var menuItem = new MenuItem();
317+
// var shellMenuItem = new MenuShellItem(menuItem);
318+
// shell.Items.Add(shellItem);
319+
// shell.Items.Add(shellMenuItem);
320+
321+
// var flyoutItemTemplate = (shell as IShellController).GetFlyoutItemDataTemplate(shellMenuItem);
322+
// var thing = (Element)flyoutItemTemplate.CreateContent();
323+
// thing.Parent = shell;
324+
325+
// var label = thing.LogicalChildren.OfType<Label>().First();
326+
// Assert.AreEqual(TextAlignment.Center, label.VerticalTextAlignment);
327+
// menuItem.StyleClass = new[] { "fooClass" };
328+
// Assert.AreEqual(TextAlignment.Start, label.VerticalTextAlignment);
329+
//}
330+
}
331+
}

0 commit comments

Comments
 (0)