Skip to content

Commit

Permalink
Fixing lifecycle so Region created once fully parented to the Page
Browse files Browse the repository at this point in the history
  • Loading branch information
dansiegel committed Aug 10, 2020
1 parent ed97ba9 commit 3c3460c
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 6 deletions.
26 changes: 26 additions & 0 deletions src/Forms/Prism.Forms.Regions/Behaviors/RegionCleanupBehavior.cs
@@ -0,0 +1,26 @@
using System;
using Prism.Regions;
using Xamarin.Forms;

namespace Prism.Behaviors
{
internal class RegionCleanupBehavior : BehaviorBase<Page>
{
private WeakReference<IRegion> _regionReference;

public RegionCleanupBehavior(IRegion region)
{
_regionReference = new WeakReference<IRegion>(region);
}

public IRegion Region => _regionReference.TryGetTarget(out var target) ? target : null;

protected override void OnDetachingFrom(Page bindable)
{
if (Region != null && Region.RegionManager.Regions.ContainsRegionWithName(Region.Name))
{
Region.RegionManager.Regions.Remove(Region.Name);
}
}
}
}
36 changes: 36 additions & 0 deletions src/Forms/Prism.Forms.Regions/Extensions/ElementExtensions.cs
@@ -0,0 +1,36 @@
using Xamarin.Forms;

namespace Prism.Extensions
{
internal static class ElementExtensions
{
public static bool CheckForParentPage(this VisualElement visualElement)
{
return GetParentPage(visualElement) != null;
}

public static Element GetRoot(this Element element)
{
switch(element.Parent)
{
case null:
return element;
default:
return GetRoot(element.Parent);
}
}

public static Page GetParentPage(this Element visualElement)
{
switch(visualElement.Parent)
{
case Page page:
return page;
case null:
return null;
default:
return GetParentPage(visualElement.Parent);
}
}
}
}
Expand Up @@ -3,6 +3,8 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using Prism.Behaviors;
using Prism.Extensions;
using Prism.Properties;
using Prism.Regions.Adapters;
using Xamarin.Forms;
Expand All @@ -26,7 +28,9 @@ public class DelayedRegionCreationBehavior
private readonly RegionAdapterMappings _regionAdapterMappings;
private readonly object _trackerLock = new object();

private WeakReference _trackingElement;
private WeakReference _elementWeakReference;
private WeakReference _pageWeakReference;
private bool _regionCreated = false;

/// <summary>
Expand Down Expand Up @@ -59,13 +63,26 @@ public VisualElement TargetElement
set => _elementWeakReference = new WeakReference(value);
}

private Page ParentPage
{
get => _pageWeakReference != null ? _pageWeakReference.Target as Page : null;
set => _pageWeakReference = new WeakReference(value);
}

private Element TrackingElement
{
get => _trackingElement != null ? _trackingElement.Target as Element : null;
set => _trackingElement = new WeakReference(value);
}

/// <summary>
/// Start monitoring the <see cref="RegionManager"/> and the <see cref="TargetElement"/> to detect when the <see cref="TargetElement"/> becomes
/// part of the Visual Tree. When that happens, the Region will be created and the behavior will <see cref="Detach"/>.
/// </summary>
public void Attach()
{
RegionManagerAccessor.UpdatingRegions += OnUpdatingRegions;
TrackingElement = TargetElement;
WireUpTargetElement();
}

Expand Down Expand Up @@ -96,9 +113,7 @@ private void TryCreateRegion()
return;
}

// DependencyObject inherits from DispatcherObject which provides CheckAccess...
// TODO: Determine proper Forms replacement for CheckAccess...
//if (TargetElement.CheckAccess())
if (TargetElement.CheckForParentPage())
{
Detach();

Expand All @@ -109,6 +124,12 @@ private void TryCreateRegion()
_regionCreated = true;
}
}
else
{
TrackingElement.PropertyChanged -= TargetElement_ParentChanged;
TrackingElement = TargetElement.GetRoot();
TrackingElement.PropertyChanged += TargetElement_ParentChanged;
}
}

/// <summary>
Expand All @@ -127,7 +148,8 @@ protected virtual IRegion CreateRegion(VisualElement targetElement, string regio
// Build the region
var regionAdapter = _regionAdapterMappings.GetMapping(targetElement.GetType());
var region = regionAdapter.Initialize(targetElement, regionName);

var cleanupBehavior = new RegionCleanupBehavior(region);
TargetElement.GetParentPage().Behaviors.Add(cleanupBehavior);
return region;
}
catch (Exception ex)
Expand All @@ -138,7 +160,7 @@ protected virtual IRegion CreateRegion(VisualElement targetElement, string regio

private void WireUpTargetElement()
{
TargetElement.PropertyChanged += TargetElement_ParentChanged;
TrackingElement.PropertyChanged += TargetElement_ParentChanged;
Track();

//if the element is a dependency object, and not a FrameworkElement, nothing is holding onto the reference after the DelayedRegionCreationBehavior
Expand All @@ -148,7 +170,7 @@ private void WireUpTargetElement()

private void UnWireTargetElement()
{
TargetElement.PropertyChanged -= TargetElement_ParentChanged;
TrackingElement.PropertyChanged -= TargetElement_ParentChanged;
Untrack();
}

Expand Down

0 comments on commit 3c3460c

Please sign in to comment.