Skip to content

Commit

Permalink
[iOS] Add templated cells as children of the CollectionView2 (#26357)
Browse files Browse the repository at this point in the history
* [testing] Add repo for issue #26066

* [iOS] Make sure to call disconnect handler

* [iOS] TemplatedCell2 should be added as logical child

* [testing] Add test for issue #26066

* [iOS] We just need container when is not template

[iOS] Remove dispose

[ios] Revert to view.SetValueFromRenderer

Revert dispose changes

Fix copy

Add CV1 and CV2 hardcoded

Revert "Revert dispose changes"

This reverts commit 94affdd.

Cleanup
  • Loading branch information
rmarinho authored Dec 6, 2024
1 parent 0685c4d commit b479513
Show file tree
Hide file tree
Showing 5 changed files with 129 additions and 108 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -211,83 +211,6 @@ internal void DisposeItemsSource()
CollectionView.ReloadData();
}

// void InvalidateMeasureIfContentSizeChanged()
// {
// var contentSize = CollectionView?.CollectionViewLayout?.CollectionViewContentSize;
//
// if (!contentSize.HasValue)
// {
// return;
// }
//
// bool widthChanged = _previousContentSize.Width != contentSize.Value.Width;
// bool heightChanged = _previousContentSize.Height != contentSize.Value.Height;
//
// if (_initialized && (widthChanged || heightChanged))
// {
// var screenFrame = CollectionView?.Window?.Frame;
//
// if (!screenFrame.HasValue)
// {
// return;
// }
//
// var screenWidth = screenFrame.Value.Width;
// var screenHeight = screenFrame.Value.Height;
// bool invalidate = false;
//
// // If both the previous content size and the current content size are larger
// // than the screen size, then we know that we're already maxed out and the
// // CollectionView items are scrollable. There's no reason to force an invalidation
// // of the CollectionView to expand/contract it.
//
// // If either size is smaller than that, we need to invalidate to ensure that the
// // CollectionView is re-measured and set to the correct size.
//
// if (widthChanged && (contentSize.Value.Width < screenWidth || _previousContentSize.Width < screenWidth))
// {
// invalidate = true;
// }
//
// if (heightChanged && (contentSize.Value.Height < screenHeight || _previousContentSize.Height < screenHeight))
// {
// invalidate = true;
// }
//
// if (invalidate)
// {
// (ItemsView as IView)?.InvalidateMeasure();
// }
// }
// _previousContentSize = contentSize.Value;
// }

const int HeaderTag = 111;

// internal Size? GetSize()
// {
// if (_emptyViewDisplayed)
// {
// return _emptyUIView.Frame.Size.ToSize();
// }

// nfloat headerHeight = 0;
// var headerView = CollectionView.ViewWithTag(HeaderTag);

// if (headerView != null)
// headerHeight = headerView.Frame.Height;

// var sizeColl = CollectionView.CollectionViewLayout.CollectionViewContentSize;
// return sizeColl.ToSize();
// }

// void ConstrainItemsToBounds()
// {
// var contentBounds = CollectionView.AdjustedContentInset.InsetRect(CollectionView.Bounds);
// var constrainedSize = contentBounds.Size;
// ItemsViewLayout.UpdateConstraints(constrainedSize);
// }

void EnsureLayoutInitialized()
{
if (_initialized)
Expand Down Expand Up @@ -370,7 +293,6 @@ protected virtual string DetermineCellReuseId(NSIndexPath indexPath)

if (!_cellReuseIds.Contains(reuseId))
{
Console.WriteLine($"REGISTER CELL ID: {reuseId}");
CollectionView.RegisterClassForCell(cellType, new NSString(reuseId));
_cellReuseIds.Add(reuseId);
}
Expand Down Expand Up @@ -408,9 +330,8 @@ internal void UpdateView(object view, DataTemplate viewTemplate, ref UIView uiVi
//Platform.GetRenderer(formsElement)?.DisposeRendererAndChildren();
}

uiView?.Dispose();
uiView = null;

formsElement?.Handler?.DisconnectHandler();
formsElement = null;
}
else
Expand Down
49 changes: 21 additions & 28 deletions src/Controls/src/Core/Handlers/Items2/iOS/TemplatedCell2.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ internal void Unbind()
{
//view.MeasureInvalidated -= MeasureInvalidated;
view.BindingContext = null;
(view.Parent as ItemsView)?.RemoveLogicalChild(view);
}
}

Expand Down Expand Up @@ -107,49 +108,41 @@ public override void PrepareForReuse()

public void Bind(DataTemplate template, object bindingContext, ItemsView itemsView)
{
if (PlatformHandler is null)
{
var virtualView = template.CreateContent(bindingContext, itemsView) as View;

var mauiContext = itemsView.FindMauiContext()!;
var nativeView = virtualView!.ToPlatform(mauiContext);

PlatformView = nativeView;

PlatformHandler = virtualView.Handler as IPlatformViewHandler;

InitializeContentConstraints(nativeView);

virtualView.BindingContext = bindingContext;
}

if (PlatformHandler?.VirtualView is View view)
{
view.SetValueFromRenderer(BindableObject.BindingContextProperty, bindingContext);
}
var virtualView = template.CreateContent(bindingContext, itemsView) as View;
BindVirtualView(virtualView, bindingContext, itemsView, false);
}

public void Bind(View virtualView, ItemsView itemsView)
{
BindVirtualView(virtualView, itemsView.BindingContext, itemsView, true);
}

void BindVirtualView(View virtualView, object bindingContext, ItemsView itemsView, bool needsContainer)
{
if (PlatformHandler is null && virtualView is not null)
{
var mauiContext = itemsView.FindMauiContext()!;
var nativeView = virtualView!.ToPlatform(mauiContext);
var nativeView = virtualView.ToPlatform(mauiContext);

var mauiWrapperView = new UIContainerView2(virtualView, mauiContext);

PlatformView = mauiWrapperView;
if (needsContainer)
{
PlatformView = new UIContainerView2(virtualView, mauiContext);
}
else
{
PlatformView = nativeView;
}

PlatformHandler = virtualView.Handler as IPlatformViewHandler;
InitializeContentConstraints(PlatformView);

InitializeContentConstraints(mauiWrapperView);

virtualView.BindingContext = itemsView.BindingContext;
virtualView.BindingContext = bindingContext;
itemsView.AddLogicalChild(virtualView);
}

if (PlatformHandler?.VirtualView is View view)
{
view.SetValueFromRenderer(BindableObject.BindingContextProperty, itemsView.BindingContext);
view.SetValueFromRenderer(BindableObject.BindingContextProperty, bindingContext);
}
}

Expand Down
42 changes: 42 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue26066.xaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Issues.Issue26066"
xmlns:sample="clr-namespace:Maui.Controls.Sample"
xmlns:local="clr-namespace:Maui.Controls.Sample.Issues"
x:DataType="local:Issue26066ViewModel">
<ContentPage.BindingContext>
<local:Issue26066ViewModel/>
</ContentPage.BindingContext>
<Grid RowDefinitions="Auto,*,Auto,*">
<Grid.Resources>
<DataTemplate x:DataType="local:Issue22035Model"
x:Key="template">
<StackLayout Padding="10" BackgroundColor="Lightgray">
<Button
AutomationId="{Binding AutomationId}"
Command="{Binding ShowDialogCommand, Source={x:RelativeSource AncestorType={x:Type local:Issue26066ViewModel}}, x:DataType=local:Issue26066ViewModel}"
CommandParameter="{Binding .}"
Text="{Binding Text}"/>
</StackLayout>
</DataTemplate>
</Grid.Resources>
<Button BackgroundColor="LightBlue"
Grid.Row="0"
AutomationId="TestLoadButtonCV2"
Text="Load Items CV2"
Command="{Binding LoadCommandCV2}"/>
<sample:CollectionView2 Grid.Row="1" BackgroundColor="LightBlue"
ItemsSource="{Binding Images2}"
ItemTemplate="{StaticResource template}"/>
<Button BackgroundColor="LightGreen "
Grid.Row="2"
AutomationId="TestLoadButtonCV"
Text="Load Items CV1"
Command="{Binding LoadCommand}"/>
<sample:CollectionView1 Grid.Row="3" BackgroundColor="LightGreen"
ItemsSource="{Binding Images}"
ItemTemplate="{StaticResource template}"/>
</Grid>
</ContentPage>
42 changes: 42 additions & 0 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue26066.xaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Collections.ObjectModel;
using System.Windows.Input;

namespace Maui.Controls.Sample.Issues
{
[XamlCompilation(XamlCompilationOptions.Compile)]
[Issue(IssueTracker.Github, 26066, "CollectionViewHandler2 RelativeSource binding to AncestorType not working", PlatformAffected.Android | PlatformAffected.iOS)]
public partial class Issue26066 : ContentPage
{
public Issue26066()
{
InitializeComponent();
}
}

class Issue26066ViewModel
{
public Issue26066ViewModel()
{
LoadCommand.Execute(null);
LoadCommandCV2.Execute(null);
}

public ObservableCollection<Issue22035Model> Images { get; set; } = new();
public ObservableCollection<Issue22035Model> Images2 { get; set; } = new();

public ICommand ShowDialogCommand => new Command(async () => await Application.Current.MainPage.DisplayAlert("New Dialog", "Hello from Espinho", "OK"));

public ICommand LoadCommand => new Command(() => LoadItems(Images, "CV1"));

public ICommand LoadCommandCV2 => new Command(() => LoadItems(Images2, "CV2"));

static void LoadItems(ObservableCollection<Issue22035Model> items, string text)
{
items.Clear();
for (int i = 0; i < 3 ; i++)
{
items.Add(new Issue22035Model { Text = $"{text} - Item {i}", ImagePath = i% 2 == 0 ? "photo21314.jpg" : "oasis.jpg" });
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;

namespace Microsoft.Maui.TestCases.Tests.Issues;

public class Issue26066(TestDevice testDevice) : _IssuesUITest(testDevice)
{
public override string Issue => "CollectionViewHandler2 RelativeSource binding to AncestorType not working";

[Test]
[Category(UITestCategories.CollectionView)]
public void CollectionView2ShouldFindAncestorType()
{
var text = "CV2";
var i = 1;
var automationId = $"{text} - Item {i}".Replace(" ", "", StringComparison.Ordinal);
App.WaitForElement(automationId);
App.Click(automationId);
App.WaitForElement("OK");
App.Click("OK");
}
}

0 comments on commit b479513

Please sign in to comment.