Skip to content

Commit

Permalink
getting Region Navigation working
Browse files Browse the repository at this point in the history
  • Loading branch information
dansiegel committed Aug 10, 2020
1 parent 2d793b9 commit 17c3225
Show file tree
Hide file tree
Showing 39 changed files with 342 additions and 250 deletions.
2 changes: 1 addition & 1 deletion e2e/Forms/src/HelloRegions/Views/RegionDemoPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<ContentView prism:RegionManager.RegionName="ContentRegion" />
<Button Text="Region A"
Command="{Binding NavigateCommand}"
CommandParameter="RegionA" />
CommandParameter="RegionA?message=This%20is%20awesome!" />
<Button Text="Region B"
Command="{Binding NavigateCommand}"
CommandParameter="RegionB" />
Expand Down
5 changes: 3 additions & 2 deletions e2e/Forms/src/HelloWorld/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,11 @@ protected override void OnInitialized()
//behaviors.AddIfMissing(MyCustomBehavior.BehaviorName, typeof(MyCustomBehavior));
});

this.ConfigureRegionAdapterMappings((mappings, container) =>
this.ConfigureRegionAdapterMappings(mappings =>
{
// Demo purposes only... you could add aditional Adapter mappings here...
// mappings.RegisterMapping(typeof(RefreshView), container.Resolve<MyCustomAdapter>());
// mappings.RegisterMapping<RefreshView, MyCustomAdapter>();
});

NavigationService.NavigateAsync($"MyMasterDetail/MyTabbedPage").OnNavigationError(OnNavigationError);
Expand Down
35 changes: 29 additions & 6 deletions src/Forms/Prism.Forms.Regions/Common/MvvmHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,51 @@ namespace Prism.Common
{
internal static class MvvmHelpers
{
public static void ViewAndViewModelAction<T>(object view, Action<T> action) where T : class =>
PageUtilities.InvokeViewAndViewModelAction(view, action);
public static void ViewAndViewModelAction<T>(object view, Action<T> action)
where T : class
{
var stack = new System.Diagnostics.StackTrace();
if (view is T viewAsT)
action(viewAsT);

public static bool GetImplementerFromViewOrViewModel<T>(object view, out T implementer) where T : class =>
if (view is BindableObject bindable && bindable.BindingContext is T vmAsT)
action(vmAsT);
}

public static bool GetImplementerFromViewOrViewModel<T>(object view, out T implementer)
where T : class =>
(implementer = GetImplementerFromViewOrViewModel<T>(view)) != null;

public static T GetImplementerFromViewOrViewModel<T>(object view) where T : class
public static T GetImplementerFromViewOrViewModel<T>(object view)
where T : class
{
if (view is T viewAsT)
{
return viewAsT;
}

if (view is VisualElement element)
if (view is VisualElement element && element.BindingContext is T vmAsT)
{
var vmAsT = element.BindingContext as T;
return vmAsT;
}

return null;
}

public static bool IsNavigationTarget(object view, INavigationContext navigationContext)
{
bool isViewTarget = false;
bool isVMTarget = false;

if (view is IRegionAware viewAsRegionAware)
isViewTarget = viewAsRegionAware.IsNavigationTarget(navigationContext);

if (view is BindableObject bindable && bindable.BindingContext is IRegionAware vmAsRegionAware)
isVMTarget = vmAsRegionAware.IsNavigationTarget(navigationContext);

return isViewTarget || isVMTarget;
}

public static void OnNavigatedFrom(object view, INavigationContext navigationContext)
{
ViewAndViewModelAction<IRegionAware>(view, x => x.OnNavigatedFrom(navigationContext));
Expand Down
8 changes: 3 additions & 5 deletions src/Forms/Prism.Forms.Regions/Common/ObservableObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,11 @@

namespace Prism.Common
{
// TODO: Refactor this... we probably do not need this for Xamarin
/// <summary>
/// Class that wraps an object, so that other classes can notify for Change events. Typically, this class is set as
/// a Dependency Property on DependencyObjects, and allows other classes to observe any changes in the Value.
/// Class that wraps an object, so that other classes can notify for Change events. Typically, this class is set as
/// a Bindable Property on BindableObjects, and allows other classes to observe any changes in the Value.
/// </summary>
/// <remarks>
/// This class is required, because in Silverlight, it's not possible to receive Change notifications for Dependency properties that you do not own.
/// </remarks>
/// <typeparam name="T">The type of the property that's wrapped in the Observable object</typeparam>
public class ObservableObject<T> : BindableObject, INotifyPropertyChanged
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ namespace Prism.Ioc
public static class RegionNavigationRegistrationExtensions
{
/// <summary>
/// Registers a Page for navigation.
/// Registers a <see cref="View"/> for region navigation.
/// </summary>
/// <typeparam name="TView">The Type of Page to register</typeparam>
/// <typeparam name="TView">The Type of <see cref="View"/> to register</typeparam>
/// <param name="containerRegistry"><see cref="IContainerRegistry"/> used to register type for Navigation.</param>
/// <param name="name">The unique name to register with the Page</param>
public static void RegisterForNavigation<TView>(this IContainerRegistry containerRegistry, string name = null)
Expand All @@ -27,9 +27,9 @@ public static void RegisterForNavigation<TView>(this IContainerRegistry containe
}

/// <summary>
/// Registers a Page for navigation.
/// Registers a <see cref="View"/> for region navigation.
/// </summary>
/// <typeparam name="TView">The Type of Page to register</typeparam>
/// <typeparam name="TView">The Type of <see cref="View" to register</typeparam>
/// <typeparam name="TViewModel">The ViewModel to use as the BindingContext for the Page</typeparam>
/// <param name="name">The unique name to register with the Page</param>
/// <param name="containerRegistry"></param>
Expand Down
34 changes: 18 additions & 16 deletions src/Forms/Prism.Forms.Regions/Ioc/RegionRegistrationExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.ComponentModel;
using Prism.Behaviors;
using Prism.Regions;
using Prism.Regions.Adapters;
using Prism.Regions.Behaviors;
Expand Down Expand Up @@ -46,14 +48,14 @@ public static PrismApplicationBase InitializeRegionConfigurations(this PrismAppl
public static PrismApplicationBase ConfigureDefaultRegionBehaviors(this PrismApplicationBase app, Action<IRegionBehaviorFactory> configure = null)
{
var regionBehaviors = app.Container.Resolve<IRegionBehaviorFactory>();
regionBehaviors.AddIfMissing(BindRegionContextToVisualElementBehavior.BehaviorKey, typeof(BindRegionContextToVisualElementBehavior));
regionBehaviors.AddIfMissing(RegionActiveAwareBehavior.BehaviorKey, typeof(RegionActiveAwareBehavior));
regionBehaviors.AddIfMissing(SyncRegionContextWithHostBehavior.BehaviorKey, typeof(SyncRegionContextWithHostBehavior));
regionBehaviors.AddIfMissing(RegionManagerRegistrationBehavior.BehaviorKey, typeof(RegionManagerRegistrationBehavior));
regionBehaviors.AddIfMissing(RegionMemberLifetimeBehavior.BehaviorKey, typeof(RegionMemberLifetimeBehavior));
regionBehaviors.AddIfMissing(ClearChildViewsRegionBehavior.BehaviorKey, typeof(ClearChildViewsRegionBehavior));
regionBehaviors.AddIfMissing(AutoPopulateRegionBehavior.BehaviorKey, typeof(AutoPopulateRegionBehavior));
regionBehaviors.AddIfMissing(DestructibleRegionBehavior.BehaviorKey, typeof(DestructibleRegionBehavior));
regionBehaviors.AddIfMissing<BindRegionContextToVisualElementBehavior>(BindRegionContextToVisualElementBehavior.BehaviorKey);
regionBehaviors.AddIfMissing<RegionActiveAwareBehavior>(RegionActiveAwareBehavior.BehaviorKey);
regionBehaviors.AddIfMissing<SyncRegionContextWithHostBehavior>(SyncRegionContextWithHostBehavior.BehaviorKey);
regionBehaviors.AddIfMissing<RegionManagerRegistrationBehavior>(RegionManagerRegistrationBehavior.BehaviorKey);
regionBehaviors.AddIfMissing<RegionMemberLifetimeBehavior>(RegionMemberLifetimeBehavior.BehaviorKey);
regionBehaviors.AddIfMissing<ClearChildViewsRegionBehavior>(ClearChildViewsRegionBehavior.BehaviorKey);
regionBehaviors.AddIfMissing<AutoPopulateRegionBehavior>(AutoPopulateRegionBehavior.BehaviorKey);
regionBehaviors.AddIfMissing<DestructibleRegionBehavior>(DestructibleRegionBehavior.BehaviorKey);

configure?.Invoke(regionBehaviors);
return app;
Expand All @@ -69,14 +71,14 @@ public static PrismApplicationBase ConfigureRegionAdapterMappings(this PrismAppl
{
var container = app.Container;
var regionAdapterMappings = container.Resolve<RegionAdapterMappings>();
regionAdapterMappings.RegisterMapping(typeof(CarouselView), container.Resolve<CarouselViewRegionAdapter>());
regionAdapterMappings.RegisterMapping(typeof(CollectionView), container.Resolve<CollectionViewRegionAdapter>());
regionAdapterMappings.RegisterMapping(typeof(FlexLayout), container.Resolve<LayoutViewRegionAdapter>());
regionAdapterMappings.RegisterMapping(typeof(StackLayout), container.Resolve<LayoutViewRegionAdapter>());
regionAdapterMappings.RegisterMapping(typeof(ScrollView), container.Resolve<ScrollViewRegionAdapter>());
regionAdapterMappings.RegisterMapping(typeof(ContentView), container.Resolve<ContentViewRegionAdapter>());
regionAdapterMappings.RegisterMapping(typeof(Frame), container.Resolve<ContentViewRegionAdapter>());
regionAdapterMappings.RegisterMapping(typeof(RefreshView), container.Resolve<ContentViewRegionAdapter>());
regionAdapterMappings.RegisterMapping<CarouselView, CarouselViewRegionAdapter>();
regionAdapterMappings.RegisterMapping<CollectionView, CollectionViewRegionAdapter>();
regionAdapterMappings.RegisterMapping<FlexLayout, LayoutViewRegionAdapter>();
regionAdapterMappings.RegisterMapping<StackLayout, LayoutViewRegionAdapter>();
regionAdapterMappings.RegisterMapping<ScrollView, ScrollViewRegionAdapter>();
regionAdapterMappings.RegisterMapping<ContentView, ContentViewRegionAdapter>();
regionAdapterMappings.RegisterMapping<Frame, ContentViewRegionAdapter>();
regionAdapterMappings.RegisterMapping<RefreshView, ContentViewRegionAdapter>();

configure?.Invoke(regionAdapterMappings);
return app;
Expand Down
3 changes: 3 additions & 0 deletions src/Forms/Prism.Forms.Regions/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using Xamarin.Forms;

[assembly: XmlnsDefinition("http://prismlibrary.com", "Prism.Regions.Xaml")]
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,11 @@ protected virtual void AttachDefaultBehaviors(IRegion region, T regionTarget)
{
IRegionBehavior behavior = behaviorFactory.CreateFromKey(behaviorKey);

if (regionTarget is VisualElement dependencyObjectRegionTarget)
if (regionTarget is VisualElement visualElementRegionTarget)
{
if (behavior is IHostAwareRegionBehavior hostAwareRegionBehavior)
{
hostAwareRegionBehavior.HostControl = dependencyObjectRegionTarget;
hostAwareRegionBehavior.HostControl = visualElementRegionTarget;
}
}

Expand Down Expand Up @@ -137,7 +137,7 @@ private static void SetObservableRegionOnHostingControl(IRegion region, T region
// Set the region as a dependency property on the control hosting the region
// Because we are using an observable region, the hosting control can detect that the
// region has actually been created. This is an ideal moment to hook up custom behaviors
RegionManager.GetObservableRegion(targetElement).Value = region;
Xaml.RegionManager.GetObservableRegion(targetElement).Value = region;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,19 @@ public class RegionAdapterMappings
/// <summary>
/// Registers the mapping between a type and an adapter.
/// </summary>
/// <param name="controlType">The type of the control.</param>
/// <param name="adapter">The adapter to use with the <paramref name="controlType"/> type.</param>
/// <exception cref="ArgumentNullException">When any of <paramref name="controlType"/> or <paramref name="adapter"/> are <see langword="null" />.</exception>
/// <exception cref="InvalidOperationException">If a mapping for <paramref name="controlType"/> already exists.</exception>
public void RegisterMapping(Type controlType, IRegionAdapter adapter)
/// <typeparam name="TControl">The type of the control</typeparam>
/// <typeparam name="TAdapter">The type of the IRegionAdapter to use with the TControl</typeparam>
public void RegisterMapping<TControl, TAdapter>() where TAdapter : IRegionAdapter
{
if (controlType == null)
throw new ArgumentNullException(nameof(controlType));

if (adapter == null)
throw new ArgumentNullException(nameof(adapter));
var controlType = typeof(TControl);

if (mappings.ContainsKey(controlType))
throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture,
Resources.MappingExistsException, controlType.Name));

mappings.Add(controlType, adapter);
}
var adapter = ContainerLocator.Container.Resolve<TAdapter>();

/// <summary>
/// Registers the mapping between a type and an adapter.
/// </summary>
/// <typeparam name="TControl">The type of the control</typeparam>
public void RegisterMapping<TControl>(IRegionAdapter adapter)
{
RegisterMapping(typeof(TControl), adapter);
}

/// <summary>
/// Registers the mapping between a type and an adapter.
/// </summary>
/// <typeparam name="TControl">The type of the control</typeparam>
/// <typeparam name="TAdapter">The type of the IRegionAdapter to use with the TControl</typeparam>
public void RegisterMapping<TControl, TAdapter>() where TAdapter : IRegionAdapter
{
RegisterMapping(typeof(TControl), ContainerLocator.Container.Resolve<TAdapter>());
mappings.Add(controlType, adapter);
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class ClearChildViewsRegionBehavior : RegionBehavior
BindableProperty.CreateAttached("ClearChildViews", typeof(bool), typeof(ClearChildViewsRegionBehavior), false);

/// <summary>
/// Gets the ClearChildViews attached property from a DependencyObject.
/// Gets the ClearChildViews attached property from a BindableObject.
/// </summary>
/// <param name="target">The object from which to get the value.</param>
/// <returns>The value of the ClearChildViews attached property in the target specified.</returns>
Expand All @@ -38,7 +38,7 @@ public static bool GetClearChildViews(BindableObject target)
}

/// <summary>
/// Sets the ClearChildViews attached property in a DependencyObject.
/// Sets the ClearChildViews attached property in a BindableObject.
/// </summary>
/// <param name="target">The object in which to set the value.</param>
/// <param name="value">The value of to set in the target object's ClearChildViews attached property.</param>
Expand All @@ -64,7 +64,7 @@ private static void ClearChildViews(IRegion region)
{
if (GetClearChildViews(view))
{
view.ClearValue(RegionManager.RegionManagerProperty);
view.ClearValue(Xaml.RegionManager.RegionManagerProperty);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@ namespace Prism.Regions.Behaviors
/// </remarks>
public class DelayedRegionCreationBehavior
{
private static readonly ICollection<DelayedRegionCreationBehavior> _instanceTracker =
new Collection<DelayedRegionCreationBehavior>();

private readonly RegionAdapterMappings _regionAdapterMappings;
private readonly object _trackerLock = new object();

private WeakReference _elementWeakReference;
private bool _regionCreated = false;

private static ICollection<DelayedRegionCreationBehavior> _instanceTracker = new Collection<DelayedRegionCreationBehavior>();
private object _trackerLock = new object();

/// <summary>
/// Initializes a new instance of the <see cref="DelayedRegionCreationBehavior"/> class.
/// </summary>
Expand All @@ -45,7 +47,7 @@ public DelayedRegionCreationBehavior(RegionAdapterMappings regionAdapterMappings
/// so this behavior can be tested in isolation.
/// </summary>
/// <value>The region manager accessor.</value>
public IRegionManagerAccessor RegionManagerAccessor { get; set; }
public IRegionManagerAccessor RegionManagerAccessor { get; }

/// <summary>
/// The element that will host the Region.
Expand Down Expand Up @@ -76,15 +78,6 @@ public void Detach()
UnWireTargetElement();
}

#if false
#region NeedToRemove

private void WireUpTargetElement() { }
private void UnWireTargetElement() { }

#endregion
#endif

/// <summary>
/// Called when the <see cref="IRegionManager"/> is updating it's <see cref="IRegionManager.Regions"/> collection.
/// </summary>
Expand Down Expand Up @@ -132,8 +125,8 @@ protected virtual IRegion CreateRegion(VisualElement targetElement, string regio
try
{
// Build the region
IRegionAdapter regionAdapter = this._regionAdapterMappings.GetMapping(targetElement.GetType());
IRegion region = regionAdapter.Initialize(targetElement, regionName);
var regionAdapter = _regionAdapterMappings.GetMapping(targetElement.GetType());
var region = regionAdapter.Initialize(targetElement, regionName);

return region;
}
Expand All @@ -147,19 +140,6 @@ private void WireUpTargetElement()
{
TargetElement.PropertyChanged += TargetElement_ParentChanged;
Track();
//var element = TargetElement;
//if (element != null)
//{
// element.Loaded += this.ElementLoaded;
// return;
//}

//var fcElement = this.TargetElement as FrameworkContentElement;
//if (fcElement != null)
//{
// fcElement.Loaded += this.ElementLoaded;
// return;
//}

//if the element is a dependency object, and not a FrameworkElement, nothing is holding onto the reference after the DelayedRegionCreationBehavior
//is instantiated inside RegionManager.CreateRegion(VisualElement element). If the GC runs before RegionManager.UpdateRegions is called, the region will
Expand All @@ -170,24 +150,11 @@ private void UnWireTargetElement()
{
TargetElement.PropertyChanged -= TargetElement_ParentChanged;
Untrack();
//var element = TargetElement;
//if (element != null)
//{
// element.Loaded -= this.ElementLoaded;
// return;
//}

//var fcElement = this.TargetElement as FrameworkContentElement;
//if (fcElement != null)
//{
// fcElement.Loaded -= this.ElementLoaded;
// return;
//}
}

private void TargetElement_ParentChanged(object sender, PropertyChangedEventArgs e)
{
if(e.PropertyName == nameof(VisualElement.Parent))
if(e.PropertyName == nameof(VisualElement.Parent) && TargetElement?.Parent != null)
{
UnWireTargetElement();
TryCreateRegion();
Expand Down
Loading

0 comments on commit 17c3225

Please sign in to comment.