2016-03-22 23:02:25 +03:00
|
|
|
using System;
|
2016-08-30 20:46:14 +03:00
|
|
|
using System.Collections.Generic;
|
2017-04-11 21:02:06 +03:00
|
|
|
using System.ComponentModel;
|
2017-03-07 22:56:24 +03:00
|
|
|
using Xamarin.Forms.Internals;
|
2016-03-22 23:02:25 +03:00
|
|
|
using Xamarin.Forms.Platform;
|
|
|
|
|
|
|
|
namespace Xamarin.Forms
|
|
|
|
{
|
|
|
|
[RenderWith(typeof(_MasterDetailPageRenderer))]
|
2016-08-30 20:46:14 +03:00
|
|
|
public class MasterDetailPage : Page, IMasterDetailPageController, IElementConfiguration<MasterDetailPage>
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
public static readonly BindableProperty IsGestureEnabledProperty = BindableProperty.Create("IsGestureEnabled", typeof(bool), typeof(MasterDetailPage), true);
|
|
|
|
|
|
|
|
public static readonly BindableProperty IsPresentedProperty = BindableProperty.Create("IsPresented", typeof(bool), typeof(MasterDetailPage), default(bool),
|
|
|
|
propertyChanged: OnIsPresentedPropertyChanged, propertyChanging: OnIsPresentedPropertyChanging);
|
|
|
|
|
|
|
|
public static readonly BindableProperty MasterBehaviorProperty = BindableProperty.Create("MasterBehavior", typeof(MasterBehavior), typeof(MasterDetailPage), default(MasterBehavior),
|
|
|
|
propertyChanged: OnMasterBehaviorPropertyChanged);
|
|
|
|
|
|
|
|
Page _detail;
|
|
|
|
|
|
|
|
Rectangle _detailBounds;
|
|
|
|
|
|
|
|
Page _master;
|
|
|
|
|
|
|
|
Rectangle _masterBounds;
|
|
|
|
|
|
|
|
public Page Detail
|
|
|
|
{
|
|
|
|
get { return _detail; }
|
|
|
|
set
|
|
|
|
{
|
|
|
|
if (_detail != null && value == null)
|
|
|
|
throw new ArgumentNullException("value", "Detail cannot be set to null once a value is set.");
|
|
|
|
|
|
|
|
if (_detail == value)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (value.RealParent != null)
|
|
|
|
throw new InvalidOperationException("Detail must not already have a parent.");
|
|
|
|
|
|
|
|
OnPropertyChanging();
|
|
|
|
if (_detail != null)
|
2017-04-11 21:02:06 +03:00
|
|
|
InternalChildren.Remove(_detail);
|
2016-03-22 23:02:25 +03:00
|
|
|
_detail = value;
|
2017-04-11 21:02:06 +03:00
|
|
|
InternalChildren.Add(_detail);
|
2016-03-22 23:02:25 +03:00
|
|
|
OnPropertyChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool IsGestureEnabled
|
|
|
|
{
|
|
|
|
get { return (bool)GetValue(IsGestureEnabledProperty); }
|
|
|
|
set { SetValue(IsGestureEnabledProperty, value); }
|
|
|
|
}
|
|
|
|
|
|
|
|
public bool IsPresented
|
|
|
|
{
|
|
|
|
get { return (bool)GetValue(IsPresentedProperty); }
|
|
|
|
set { SetValue(IsPresentedProperty, value); }
|
|
|
|
}
|
|
|
|
|
|
|
|
public Page Master
|
|
|
|
{
|
|
|
|
get { return _master; }
|
|
|
|
set
|
|
|
|
{
|
|
|
|
if (_master != null && value == null)
|
|
|
|
throw new ArgumentNullException("value", "Master cannot be set to null once a value is set");
|
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(value.Title))
|
|
|
|
throw new InvalidOperationException("Title property must be set on Master page");
|
|
|
|
|
|
|
|
if (_master == value)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (value.RealParent != null)
|
|
|
|
throw new InvalidOperationException("Master must not already have a parent.");
|
|
|
|
|
|
|
|
OnPropertyChanging();
|
|
|
|
if (_master != null)
|
2017-04-11 21:02:06 +03:00
|
|
|
InternalChildren.Remove(_master);
|
2016-03-22 23:02:25 +03:00
|
|
|
_master = value;
|
2017-04-11 21:02:06 +03:00
|
|
|
InternalChildren.Add(_master);
|
2016-03-22 23:02:25 +03:00
|
|
|
OnPropertyChanged();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public MasterBehavior MasterBehavior
|
|
|
|
{
|
|
|
|
get { return (MasterBehavior)GetValue(MasterBehaviorProperty); }
|
|
|
|
set { SetValue(MasterBehaviorProperty, value); }
|
|
|
|
}
|
|
|
|
|
2017-04-11 21:02:06 +03:00
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
|
|
public bool CanChangeIsPresented { get; set; } = true;
|
2016-03-22 23:02:25 +03:00
|
|
|
|
2017-04-11 21:02:06 +03:00
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
|
|
public Rectangle DetailBounds
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
get { return _detailBounds; }
|
|
|
|
set
|
|
|
|
{
|
|
|
|
_detailBounds = value;
|
|
|
|
if (_detail == null)
|
|
|
|
throw new InvalidOperationException("Detail must be set before using a MasterDetailPage");
|
|
|
|
_detail.Layout(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-11 21:02:06 +03:00
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
|
|
public Rectangle MasterBounds
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
get { return _masterBounds; }
|
|
|
|
set
|
|
|
|
{
|
|
|
|
_masterBounds = value;
|
|
|
|
if (_master == null)
|
|
|
|
throw new InvalidOperationException("Master must be set before using a MasterDetailPage");
|
|
|
|
_master.Layout(value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-04-11 21:02:06 +03:00
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
|
|
public bool ShouldShowSplitMode
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
if (Device.Idiom == TargetIdiom.Phone)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
MasterBehavior behavior = MasterBehavior;
|
|
|
|
DeviceOrientation orientation = Device.Info.CurrentOrientation;
|
|
|
|
|
|
|
|
bool isSplitOnLandscape = (behavior == MasterBehavior.SplitOnLandscape || behavior == MasterBehavior.Default) && orientation.IsLandscape();
|
|
|
|
bool isSplitOnPortrait = behavior == MasterBehavior.SplitOnPortrait && orientation.IsPortrait();
|
|
|
|
return behavior == MasterBehavior.Split || isSplitOnLandscape || isSplitOnPortrait;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public event EventHandler IsPresentedChanged;
|
|
|
|
|
|
|
|
public virtual bool ShouldShowToolbarButton()
|
|
|
|
{
|
|
|
|
if (Device.Idiom == TargetIdiom.Phone)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
MasterBehavior behavior = MasterBehavior;
|
|
|
|
DeviceOrientation orientation = Device.Info.CurrentOrientation;
|
|
|
|
|
|
|
|
bool isSplitOnLandscape = (behavior == MasterBehavior.SplitOnLandscape || behavior == MasterBehavior.Default) && orientation.IsLandscape();
|
|
|
|
bool isSplitOnPortrait = behavior == MasterBehavior.SplitOnPortrait && orientation.IsPortrait();
|
|
|
|
return behavior != MasterBehavior.Split && !isSplitOnLandscape && !isSplitOnPortrait;
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void LayoutChildren(double x, double y, double width, double height)
|
|
|
|
{
|
|
|
|
if (Master == null || Detail == null)
|
|
|
|
throw new InvalidOperationException("Master and Detail must be set before using a MasterDetailPage");
|
|
|
|
_master.Layout(_masterBounds);
|
|
|
|
_detail.Layout(_detailBounds);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void OnAppearing()
|
|
|
|
{
|
2017-04-11 21:02:06 +03:00
|
|
|
CanChangeIsPresented = true;
|
2016-03-22 23:02:25 +03:00
|
|
|
UpdateMasterBehavior(this);
|
|
|
|
base.OnAppearing();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override bool OnBackButtonPressed()
|
|
|
|
{
|
|
|
|
if (IsPresented)
|
|
|
|
{
|
|
|
|
if (Master.SendBackButtonPressed())
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-04-11 21:02:06 +03:00
|
|
|
EventHandler<BackButtonPressedEventArgs> handler = BackButtonPressed;
|
2016-03-22 23:02:25 +03:00
|
|
|
if (handler != null)
|
|
|
|
{
|
|
|
|
var args = new BackButtonPressedEventArgs();
|
|
|
|
handler(this, args);
|
|
|
|
if (args.Handled)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (Detail.SendBackButtonPressed())
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return base.OnBackButtonPressed();
|
|
|
|
}
|
|
|
|
|
|
|
|
protected override void OnParentSet()
|
|
|
|
{
|
|
|
|
if (RealParent != null && (Master == null || Detail == null))
|
|
|
|
throw new InvalidOperationException("Master and Detail must be set before adding MasterDetailPage to a container");
|
|
|
|
base.OnParentSet();
|
|
|
|
}
|
|
|
|
|
2017-04-11 21:02:06 +03:00
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
|
|
public event EventHandler<BackButtonPressedEventArgs> BackButtonPressed;
|
2016-05-04 17:22:34 +03:00
|
|
|
|
2017-04-11 21:02:06 +03:00
|
|
|
[EditorBrowsable(EditorBrowsableState.Never)]
|
|
|
|
public void UpdateMasterBehavior()
|
2016-05-04 17:22:34 +03:00
|
|
|
{
|
|
|
|
UpdateMasterBehavior(this);
|
|
|
|
}
|
2016-03-22 23:02:25 +03:00
|
|
|
|
|
|
|
internal static void UpdateMasterBehavior(MasterDetailPage page)
|
|
|
|
{
|
2017-04-11 21:02:06 +03:00
|
|
|
if (page.ShouldShowSplitMode)
|
2016-03-22 23:02:25 +03:00
|
|
|
{
|
|
|
|
page.SetValueCore(IsPresentedProperty, true);
|
|
|
|
if (page.MasterBehavior != MasterBehavior.Default)
|
2017-04-11 21:02:06 +03:00
|
|
|
page.CanChangeIsPresented = false;
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OnIsPresentedPropertyChanged(BindableObject sender, object oldValue, object newValue)
|
|
|
|
{
|
|
|
|
var page = (MasterDetailPage)sender;
|
|
|
|
EventHandler handler = page.IsPresentedChanged;
|
|
|
|
if (handler != null)
|
|
|
|
handler(page, EventArgs.Empty);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OnIsPresentedPropertyChanging(BindableObject sender, object oldValue, object newValue)
|
|
|
|
{
|
|
|
|
var page = (MasterDetailPage)sender;
|
2017-04-11 21:02:06 +03:00
|
|
|
if (!page.CanChangeIsPresented)
|
2016-03-22 23:02:25 +03:00
|
|
|
throw new InvalidOperationException(string.Format("Can't change IsPresented when setting {0}", page.MasterBehavior));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void OnMasterBehaviorPropertyChanged(BindableObject sender, object oldValue, object newValue)
|
|
|
|
{
|
|
|
|
var page = (MasterDetailPage)sender;
|
|
|
|
UpdateMasterBehavior(page);
|
|
|
|
}
|
2016-08-30 20:46:14 +03:00
|
|
|
|
|
|
|
public MasterDetailPage()
|
|
|
|
{
|
|
|
|
_platformConfigurationRegistry = new Lazy<PlatformConfigurationRegistry<MasterDetailPage>>(() => new PlatformConfigurationRegistry<MasterDetailPage>(this));
|
|
|
|
}
|
|
|
|
|
|
|
|
readonly Lazy<PlatformConfigurationRegistry<MasterDetailPage>> _platformConfigurationRegistry;
|
|
|
|
|
|
|
|
public new IPlatformElementConfiguration<T, MasterDetailPage> On<T>() where T : IConfigPlatform
|
|
|
|
{
|
|
|
|
return _platformConfigurationRegistry.Value.On<T>();
|
|
|
|
}
|
2016-03-22 23:02:25 +03:00
|
|
|
}
|
|
|
|
}
|