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

[UWP] Fixes to Cell for ListView and CollectionView #13277

Merged
merged 7 commits into from
Jan 15, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 75 additions & 8 deletions Xamarin.Forms.Platform.UAP/CellControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,34 @@ public CellControl()

DataContextChanged += OnDataContextChanged;

Unloaded += (sender, args) =>
{
Cell?.SendDisappearing();
};
Loaded += OnLoaded;
Unloaded += OnUnloaded;

_propertyChangedHandler = OnCellPropertyChanged;
}

void OnLoaded(object sender, RoutedEventArgs e)
{
if (Cell == null)
return;

/// 🚀 subscribe topropertychanged
// make sure we do not subscribe twice (because this could happen in SetSource(Cell oldCell, Cell newCell))
Cell.PropertyChanged -= _propertyChangedHandler;
Cell.PropertyChanged += _propertyChangedHandler;
}

void OnUnloaded(object sender, RoutedEventArgs e)
{
if (Cell == null)
return;

Cell.SendDisappearing();
/// 🚀 unsubscribe from propertychanged
Cell.PropertyChanged -= _propertyChangedHandler;
}


public Cell Cell
{
get { return (Cell)GetValue(CellProperty); }
Expand Down Expand Up @@ -312,16 +332,49 @@ void SetCell(object newContext)
ListView lv = _listView.Value;
if (lv != null)
{
// 🚀 If there is an old cell, check if it was a group header
// we need this later to know whether we can recycle this cell
bool? wasGroupHeader = null;
var oldCell = Cell;
if (oldCell != null)
{
wasGroupHeader = oldCell.GetIsGroupHeader<ItemsView<Cell>, Cell>();
}

bool isGroupHeader = IsGroupHeader;
DataTemplate template = isGroupHeader ? lv.GroupHeaderTemplate : lv.ItemTemplate;
object bindingContext = newContext;

if (template is DataTemplateSelector)
bool sameTemplate = false;
if (template is DataTemplateSelector dataTemplateSelector)
{
template = ((DataTemplateSelector)template).SelectTemplate(bindingContext, lv);
template = dataTemplateSelector.SelectTemplate(bindingContext, lv);

// 🚀 If there exists an old cell, get its data template and check
// whether the new- and old template matches. In that case, we can recycle it
if (oldCell?.BindingContext != null)
{
DataTemplate oldTemplate = dataTemplateSelector.SelectTemplate(oldCell?.BindingContext, lv);
sameTemplate = oldTemplate == template;
}
}
// 🚀 if there is no datatemplateselector, we now verify if the old cell
// was a groupheader and whether the new one is as well.
// Again, this is only to verify we can reuse this cell
else if (wasGroupHeader.HasValue)
{
sameTemplate = wasGroupHeader == isGroupHeader;
}

// reuse cell
var canReuseCell = Cell != null && sameTemplate;

if (template != null)
// 🚀 If we can reuse the cell, just reuse it...
if (canReuseCell)
{
cell = Cell;
}
else if (template != null)
{
cell = template.CreateContent() as Cell;
}
Expand All @@ -345,7 +398,18 @@ void SetCell(object newContext)
cell.SetIsGroupHeader<ItemsView<Cell>, Cell>(isGroupHeader);
}

Cell = cell;
// 🚀 Only set the cell if it DID change
// Note: The cleanup (SendDisappearing(), etc.) is done by the Cell propertychanged callback so we do not need to do any cleanup ourselves.

if (Cell != cell)
Cell = cell;
// 🚀 even if the cell did not change, we **must** call SendDisappearing() and SendAppearing()
// because frameworks such as Reactive UI rely on this! (this.WhenActivated())
else if (Cell != null)
{
Cell.SendDisappearing();
Cell.SendAppearing();
}
}

void SetSource(Cell oldCell, Cell newCell)
Expand All @@ -364,10 +428,13 @@ void SetSource(Cell oldCell, Cell newCell)
UpdateFlowDirection(newCell);
SetupContextMenu();

// 🚀 make sure we do not subscribe twice (OnLoaded!)
newCell.PropertyChanged -= _propertyChangedHandler;
newCell.PropertyChanged += _propertyChangedHandler;
}
}


void SetupContextMenu()
{
if (CellContent == null || Cell == null)
Expand Down