Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set width for MasterDetailPage in xamarin forms

I am using the MasterDetailPage for app on Xamarin forms for ios and android.

however the width in MasterDetailPage menu is too large and i would like to adjust it.

How i can Set custom width for MasterDetailPage on android and iOS?

My MasterDetailPage init code:

            MyChatsMasterView _myChatsMasterView;
            MyChatsView _myChatsView;

            public MyChatsMasterDetailView(MyChatsMasterView myChatsMasterView, MyChatsView myChatsView)
            {
                NavigationPage.SetHasNavigationBar(this, false);

                InitializeComponent();
                this.MasterBehavior = MasterBehavior.Popover;

                _myChatsMasterView = myChatsMasterView;
                _myChatsView = myChatsView;

                Master = _myChatsMasterView;
                Detail = _myChatsView;

                _myChatsMasterView.SetDetailView(this);
            }
like image 550
Artem Polishchuk Avatar asked Nov 03 '16 11:11

Artem Polishchuk


2 Answers

I solved the problem by copying the renderers from the Xamarin.Forms sources. Basically I needed a master with a width of 400 dp in order to display a calendar properly inside of it.

Here is what I did:

Shared Library


I created a SplitViewPage, deriving from the MasterDetail Page:

<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CompApp.Views.SplitViewPage">
    <MasterDetailPage.Master>
        <ContentPage Title=" "/>
    </MasterDetailPage.Master>
    <MasterDetailPage.Detail>
        <ContentPage Title=" "/>
    </MasterDetailPage.Detail>
</MasterDetailPage>

Android


Since the master width is a constant within the internal class 'MasterDetailContainer', I copied the code from the base class (see here) to a new class named 'DroidMasterDetailContainer.cs' while changing the class visibility from internal to public.

I made two changes to the code:

  1. I changed const int DefaultMasterSize = 320 to const int DefaultMasterSize = 400
  2. I commented out the line _childView.ClearValue(Platform.RendererProperty); (line 116, which lies in void DisposeChildRenderers() since the Platform.RendererProperty is inaccessible

The next thing to do is to copy the Code of the MasterDetailRenderer (see here) to a new class (in my case 'SplitViewPageRenderer.cs')

using System;
using System.ComponentModel;
using System.Linq;
using System.Threading.Tasks;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Support.V4.Widget;
using Android.Views;
using MyApp.Droid.Renderer;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Xamarin.Forms.Platform.Android.AppCompat;
using AColor = Android.Graphics.Drawables.ColorDrawable;

using AView = Android.Views.View;

[assembly: ExportRenderer(typeof(SplitViewPage), typeof(SplitViewPageRenderer))]
namespace MyApp.Droid.Renderer
{
public class SplitViewPageRenderer : DrawerLayout, IVisualElementRenderer, DrawerLayout.IDrawerListener
{
    public SplitViewPageRenderer(Context context) : base(context)
    {

    }

    //from Android source code
    const uint DefaultScrimColor = 0x99000000;
    int _currentLockMode = -1;
    DroidMasterDetailContainer _detailLayout;
    bool _isPresentingFromCore;
    DroidMasterDetailContainer _masterLayout;
    MasterDetailPage _page;
    bool _presented;

    [Obsolete("This constructor is obsolete as of version 2.5. Please use MasterDetailRenderer(Context) instead.")]
    public SplitViewPageRenderer() : base(Forms.Context)
    {
    }

    IMasterDetailPageController MasterDetailPageController => _page as IMasterDetailPageController;

    public bool Presented
    {
        get { return _presented; }
        set
        {
            if (value == _presented)
                return;
            UpdateSplitViewLayout();
            _presented = value;
            if (_page.MasterBehavior == MasterBehavior.Default && MasterDetailPageController.ShouldShowSplitMode)
                return;
            if (_presented)
                OpenDrawer(_masterLayout);
            else
                CloseDrawer(_masterLayout);
        }
    }

    IPageController MasterPageController => _page.Master as IPageController;
    IPageController DetailPageController => _page.Detail as IPageController;
    IPageController PageController => Element as IPageController;

    public void OnDrawerClosed(AView drawerView)
    {
    }

    public void OnDrawerOpened(AView drawerView)
    {
    }

    public void OnDrawerSlide(AView drawerView, float slideOffset)
    {
    }

    public void OnDrawerStateChanged(int newState)
    {
        _presented = IsDrawerVisible(_masterLayout);
        UpdateIsPresented();
    }

    public VisualElement Element
    {
        get { return _page; }
    }

    public event EventHandler<VisualElementChangedEventArgs> ElementChanged;

    event EventHandler<PropertyChangedEventArgs> ElementPropertyChanged;
    event EventHandler<PropertyChangedEventArgs> IVisualElementRenderer.ElementPropertyChanged
    {
        add { ElementPropertyChanged += value; }
        remove { ElementPropertyChanged -= value; }
    }

    public SizeRequest GetDesiredSize(int widthConstraint, int heightConstraint)
    {
        Measure(widthConstraint, heightConstraint);
        return new SizeRequest(new Size(MeasuredWidth, MeasuredHeight));
    }

    public void SetElement(VisualElement element)
    {
        MasterDetailPage oldElement = _page;
        _page = element as MasterDetailPage;

        _detailLayout = new DroidMasterDetailContainer(_page, false, Context) { LayoutParameters = new LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent) };

        _masterLayout = new DroidMasterDetailContainer(_page, true, Context)
        {
            LayoutParameters = new LayoutParams(ViewGroup.LayoutParams.WrapContent, ViewGroup.LayoutParams.WrapContent) { Gravity = (int)GravityFlags.Start }
        };

        AddView(_detailLayout);

        AddView(_masterLayout);

        var activity = Context as Activity;
        activity.ActionBar.SetDisplayShowHomeEnabled(true);
        activity.ActionBar.SetHomeButtonEnabled(true);

        UpdateBackgroundColor(_page);
        UpdateBackgroundImage(_page);

        OnElementChanged(oldElement, element);

        if (oldElement != null)
            ((IMasterDetailPageController)oldElement).BackButtonPressed -= OnBackButtonPressed;

        if (_page != null)
            MasterDetailPageController.BackButtonPressed += OnBackButtonPressed;

        if (Tracker == null)
            Tracker = new VisualElementTracker(this);

        _page.PropertyChanged += HandlePropertyChanged;
        _page.Appearing += MasterDetailPageAppearing;
        _page.Disappearing += MasterDetailPageDisappearing;

        UpdateMaster();
        UpdateDetail();

        Device.Info.PropertyChanged += DeviceInfoPropertyChanged;
        SetGestureState();

        Presented = _page.IsPresented;

        AddDrawerListener(this);

        //if (element != null)
        //  element.SendViewInitialized(this);

        if (element != null && !string.IsNullOrEmpty(element.AutomationId))
            ContentDescription = element.AutomationId;
    }

    public VisualElementTracker Tracker { get; private set; }

    public void UpdateLayout()
    {
        if (Tracker != null)
            Tracker.UpdateLayout();
    }

    public ViewGroup ViewGroup => this;
    AView IVisualElementRenderer.View => this;

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (Tracker != null)
            {
                Tracker.Dispose();
                Tracker = null;
            }

            if (_detailLayout != null)
            {
                _detailLayout.Dispose();
                _detailLayout = null;
            }

            if (_masterLayout != null)
            {
                _masterLayout.Dispose();
                _masterLayout = null;
            }

            Device.Info.PropertyChanged -= DeviceInfoPropertyChanged;

            if (_page != null)
            {
                MasterDetailPageController.BackButtonPressed -= OnBackButtonPressed;
                _page.PropertyChanged -= HandlePropertyChanged;
                _page.Appearing -= MasterDetailPageAppearing;
                _page.Disappearing -= MasterDetailPageDisappearing;
                //_page.ClearValue(Platform.RendererProperty);
                _page = null;
            }
        }

        base.Dispose(disposing);
    }

    protected override void OnAttachedToWindow()
    {
        base.OnAttachedToWindow();
        PageController.SendAppearing();
    }

    protected override void OnDetachedFromWindow()
    {
        base.OnDetachedFromWindow();
        PageController.SendDisappearing();
    }

    protected virtual void OnElementChanged(VisualElement oldElement, VisualElement newElement)
    {
        EventHandler<VisualElementChangedEventArgs> changed = ElementChanged;
        if (changed != null)
            changed(this, new VisualElementChangedEventArgs(oldElement, newElement));
    }

    protected override void OnLayout(bool changed, int l, int t, int r, int b)
    {
        base.OnLayout(changed, l, t, r, b);
        //hack to make the split layout handle touches the full width
        if (MasterDetailPageController.ShouldShowSplitMode && _masterLayout != null)
            _masterLayout.Right = r;
    }

    async void DeviceInfoPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "CurrentOrientation")
        {
            if (!MasterDetailPageController.ShouldShowSplitMode && Presented)
            {
                MasterDetailPageController.CanChangeIsPresented = true;
                //hack : when the orientation changes and we try to close the Master on Android     
                //sometimes Android picks the width of the screen previous to the rotation      
                //this leaves a little of the master visible, the hack is to delay for 50ms closing the drawer
                await Task.Delay(50);
                CloseDrawer(_masterLayout);
            }
            UpdateSplitViewLayout();
        }
    }

    void HandleMasterPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        //if (e.PropertyName == Page.TitleProperty.PropertyName || e.PropertyName == Page.IconProperty.PropertyName)
            //((Platform)_page.Platform).UpdateMasterDetailToggle(true);
    }

    void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        ElementPropertyChanged?.Invoke(this, e);
        if (e.PropertyName == "Master")
            UpdateMaster();
        else if (e.PropertyName == "Detail")
        {
            UpdateDetail();
            //((Platform)_page.Platform).UpdateActionBar();
        }
        else if (e.PropertyName == MasterDetailPage.IsPresentedProperty.PropertyName)
        {
            _isPresentingFromCore = true;
            Presented = _page.IsPresented;
            _isPresentingFromCore = false;
        }
        else if (e.PropertyName == "IsGestureEnabled")
            SetGestureState();
        else if (e.PropertyName == Page.BackgroundImageProperty.PropertyName)
            UpdateBackgroundImage(_page);
        if (e.PropertyName == VisualElement.BackgroundColorProperty.PropertyName)
            UpdateBackgroundColor(_page);
    }

    void MasterDetailPageAppearing(object sender, EventArgs e)
    {
        MasterPageController?.SendAppearing();
        DetailPageController?.SendAppearing();
    }

    void MasterDetailPageDisappearing(object sender, EventArgs e)
    {
        MasterPageController?.SendDisappearing();
        DetailPageController?.SendDisappearing();
    }

    void OnBackButtonPressed(object sender, BackButtonPressedEventArgs backButtonPressedEventArgs)
    {
        if (IsDrawerOpen((int)GravityFlags.Start))
        {
            if (_currentLockMode != LockModeLockedOpen)
            {
                CloseDrawer((int)GravityFlags.Start);
                backButtonPressedEventArgs.Handled = true;
            }
        }
    }

    void SetGestureState()
    {
        SetDrawerLockMode(_page.IsGestureEnabled ? LockModeUnlocked : LockModeLockedClosed);
    }

    void IVisualElementRenderer.SetLabelFor(int? id)
    {
    }

    void SetLockMode(int lockMode)
    {
        if (_currentLockMode != lockMode)
        {
            SetDrawerLockMode(lockMode);
            _currentLockMode = lockMode;
        }
    }

    void UpdateBackgroundColor(Page view)
    {
        if (view.BackgroundColor != Color.Default)
            SetBackgroundColor(view.BackgroundColor.ToAndroid());
    }

    void UpdateBackgroundImage(Page view)
    {
        if (!string.IsNullOrEmpty(view.BackgroundImage))
            this.SetBackground(Context.GetDrawable(view.BackgroundImage));
    }

    void UpdateDetail()
    {
        Context.HideKeyboard(this);
        _detailLayout.ChildView = _page.Detail;
    }

    void UpdateIsPresented()
    {
        if (_isPresentingFromCore)
            return;
        if (Presented != _page.IsPresented)
            ((IElementController)_page).SetValueFromRenderer(MasterDetailPage.IsPresentedProperty, Presented);
    }

    void UpdateMaster()
    {
        if (_masterLayout != null && _masterLayout.ChildView != null)
            _masterLayout.ChildView.PropertyChanged -= HandleMasterPropertyChanged;
        _masterLayout.ChildView = _page.Master;
        if (_page.Master != null)
            _page.Master.PropertyChanged += HandleMasterPropertyChanged;
    }

    void UpdateSplitViewLayout()
    {
        if (Device.Idiom == TargetIdiom.Tablet)
        {
            bool isShowingSplit = MasterDetailPageController.ShouldShowSplitMode
                || (MasterDetailPageController.ShouldShowSplitMode && _page.MasterBehavior != MasterBehavior.Default && _page.IsPresented);
            SetLockMode(isShowingSplit ? LockModeLockedOpen : LockModeUnlocked);
            unchecked
            {
                SetScrimColor(isShowingSplit ? Color.Transparent.ToAndroid() : (int)DefaultScrimColor);
            }
            //((Platform)_page.Platform).UpdateMasterDetailToggle();
        }
    }
}
}

Notice, that instead of using the internal class 'MasterDetailContainer' for the variables _masterLayout and _detailLayout, we are using the 'DroidMasterDetailContainer' created earlier.

iOS


Basically the same story: Take the MasterDetailRenderer Code (see here), create your own class, paste the code and some adaptions later we have:

using CompApp.Customs;
using MyApp.iOS.Renderer;
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms;
using Xamarin.Forms.Platform.iOS;
using System.ComponentModel;
using CoreGraphics;
using MyApp.Views;
using UIKit;
using Xamarin.Forms.Internals;
using System.Reflection;

[assembly: ExportRenderer(typeof(SplitViewPage), typeof(SplitViewPageRenderer))]
namespace MyApp.iOS.Renderer
{
public class SplitViewPageRenderer : UISplitViewController, IVisualElementRenderer, IEffectControlProvider
{
    UIViewController _detailController;

    bool _disposed;
    EventTracker _events;
    InnerDelegate _innerDelegate;
    public static nfloat MasterWidth = 400;
    EventedViewController _masterController;

    SplitViewPage _masterDetailPage;

    bool _masterVisible;

    VisualElementTracker _tracker;

    Page PageController => Element as Page;
    Element ElementController => Element as Element;

    protected SplitViewPage MasterDetailPage => _masterDetailPage ?? (_masterDetailPage = (SplitViewPage)Element);

    public VisualElement Element { get; private set; }

    public event EventHandler<VisualElementChangedEventArgs> ElementChanged;

    UIBarButtonItem PresentButton
    {
        get { return _innerDelegate == null ? null : _innerDelegate.PresentButton; }
    }

    public UIView NativeView
    {
        get { return View; }
    }

    protected virtual void OnElementChanged(VisualElementChangedEventArgs e)
    {
        if (e.OldElement != null)
            e.OldElement.PropertyChanged -= HandlePropertyChanged;

        if (e.NewElement != null)
        {
            e.NewElement.PropertyChanged += HandlePropertyChanged;
        }

        if (UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.LandscapeLeft || UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.LandscapeRight)
        {
            PreferredDisplayMode = UISplitViewControllerDisplayMode.AllVisible;
        }
        else if (UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.Portrait || UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.PortraitUpsideDown)
        {
            PreferredDisplayMode = UISplitViewControllerDisplayMode.PrimaryOverlay;
        }

        MasterWidth = 400;
        MasterDetailPage.Master.WidthRequest = 400;

        MasterDetailPage.UpdateMasterBehavior();

        var changed = ElementChanged;
        if (changed != null)
            changed(this, e);

        UpdateControllers();
    }

    public SizeRequest GetDesiredSize(double widthConstraint, double heightConstraint)
    {
        return NativeView.GetSizeRequest(widthConstraint, heightConstraint);
    }

    public void SetElement(VisualElement element)
    {
        var oldElement = Element;
        Element = element;

        ViewControllers = new[] { _masterController = new EventedViewController(), _detailController = new ChildViewController() };

        Delegate = _innerDelegate = new InnerDelegate(MasterDetailPage.MasterBehavior);
        Element.BackgroundColor = Color.Transparent;
        UpdateControllers();

        _masterController.WillAppear += MasterControllerWillAppear;
        _masterController.WillDisappear += MasterControllerWillDisappear;

        PresentsWithGesture = MasterDetailPage.IsGestureEnabled;

        OnElementChanged(new VisualElementChangedEventArgs(oldElement, element));

        EffectUtilities.RegisterEffectControlProvider(this, oldElement, element);

        if (element != null)
        {
            MethodInfo sendViewInitialized = typeof(Xamarin.Forms.Forms).GetMethod("SendViewInitialized", BindingFlags.Static | BindingFlags.NonPublic);
            sendViewInitialized?.Invoke(element, new object[] { element, NativeView });
        }
    }

    public void SetElementSize(Size size)
    {
        Element.Layout(new Rectangle(Element.X, Element.Width, size.Width, size.Height));
    }

    public UIViewController ViewController
    {
        get { return this; }
    }

    public override void ViewDidAppear(bool animated)
    {
        PageController.SendAppearing();
        base.ViewDidAppear(animated);
        ToggleMaster();
    }

    public override void ViewDidDisappear(bool animated)
    {
        base.ViewDidDisappear(animated);
        PageController?.SendDisappearing();
    }

    public override void ViewDidLayoutSubviews()
    {
        if (View.Subviews.Length < 2)
            return;

        var frameBounds = View.Bounds;
        var masterBounds = _masterController.View.Frame;
        var detailsBounds = _detailController.View.Frame;

        nfloat statusBarHeight = UIApplication.SharedApplication.StatusBarFrame.Height;
        masterBounds.Width = 400;
        MasterWidth = (nfloat)Math.Max(MasterWidth, masterBounds.Width);
        if (Xamarin.Forms.Device.Idiom == TargetIdiom.Tablet)
        {
            bool interfaceInLandscape = UIApplication.SharedApplication.StatusBarOrientation == UIInterfaceOrientation.LandscapeLeft || UIApplication.SharedApplication.StatusBarOrientation == UIInterfaceOrientation.LandscapeRight;
            if (UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.LandscapeLeft || UIDevice.CurrentDevice.Orientation == UIDeviceOrientation.LandscapeRight || interfaceInLandscape)
            {
                detailsBounds.X = 400;
                detailsBounds.Width = frameBounds.Width - 400;
            }
            else
            {
                detailsBounds.X = 0;
                detailsBounds.Width = frameBounds.Width;
            }
            _detailController.View.Frame = detailsBounds;
            _masterController.View.Frame = new CGRect(masterBounds.X, masterBounds.Y, masterBounds.Width, masterBounds.Height);
            if (!masterBounds.IsEmpty)
            {
                MasterDetailPage.MasterBounds = new Rectangle(masterBounds.X, masterBounds.Y, masterBounds.Width, masterBounds.Height);
            }

            if (!detailsBounds.IsEmpty)
            {
                MasterDetailPage.DetailBounds = new Rectangle(detailsBounds.X, detailsBounds.Y, detailsBounds.Width, detailsBounds.Height);
            }
            _masterController.View.SetNeedsLayout();
            _detailController.View.SetNeedsLayout();

        }
        else
        {
            if (!masterBounds.IsEmpty)
            {
                MasterDetailPage.MasterBounds = new Rectangle(MasterWidth, 0, MasterWidth, masterBounds.Height);
            }

            if (!detailsBounds.IsEmpty)
            {
                MasterDetailPage.DetailBounds = new Rectangle(0, 0, detailsBounds.Width, detailsBounds.Height);
            }
        }


    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();
        UpdateBackground();
        UpdateFlowDirection();
        _tracker = new VisualElementTracker(this);
        _events = new EventTracker(this);
        _events.LoadEvents(NativeView);
    }

    public override void ViewWillDisappear(bool animated)
    {
        if (_masterVisible)
            PerformButtonSelector();

        base.ViewWillDisappear(animated);
    }

    public override void ViewWillLayoutSubviews()
    {
        base.ViewWillLayoutSubviews();
        _masterController.View.BackgroundColor = UIColor.White;
        CGRect bounds = _masterController.View.Bounds;
        CGRect frame = _masterController.View.Frame;
        (this.ViewController as UISplitViewController).MinimumPrimaryColumnWidth = 400;
        (this.ViewController as UISplitViewController).MaximumPrimaryColumnWidth = 400;
    }

    public override void WillRotate(UIInterfaceOrientation toInterfaceOrientation, double duration)
    {
        // On IOS8 the MasterViewController ViewAppear/Disappear weren't being called correctly after rotation 
        // We now close the Master by using the new SplitView API, basicly we set it to hidden and right back to the Normal/AutomaticMode
        if (!MasterDetailPage.ShouldShowSplitMode && _masterVisible)
        {
            MasterDetailPage.CanChangeIsPresented = true;
            PreferredDisplayMode = UISplitViewControllerDisplayMode.PrimaryHidden;
            PreferredDisplayMode = UISplitViewControllerDisplayMode.Automatic;
        }

        var masterBounds = _masterController.View.Frame;
        MessagingCenter.Send<IVisualElementRenderer>(this,  "Xamarin.UpdateToolbarButtons");

        MasterDetailPage.UpdateMasterBehavior();
        base.WillRotate(toInterfaceOrientation, duration);
    }

    public override void DidRotate(UIInterfaceOrientation fromInterfaceOrientation)
    {
        base.DidRotate(fromInterfaceOrientation);
        var masterBounds = _masterController.View.Frame;

        MasterWidth = (nfloat)Math.Max(MasterWidth, masterBounds.Width);

        if (!masterBounds.IsEmpty)
        {
            MasterDetailPage.MasterBounds = new Rectangle(MasterWidth, 0, MasterWidth, masterBounds.Height);
        }
    }

    public override UIViewController ChildViewControllerForStatusBarHidden()
    {
        if (((MasterDetailPage)Element).Detail != null)
            return (UIViewController)Platform.GetRenderer(((MasterDetailPage)Element).Detail);
        else
            return base.ChildViewControllerForStatusBarHidden();
    }

    void ClearControllers()
    {
        foreach (var controller in _masterController.ChildViewControllers)
        {
            controller.View.RemoveFromSuperview();
            controller.RemoveFromParentViewController();
        }

        foreach (var controller in _detailController.ChildViewControllers)
        {
            controller.View.RemoveFromSuperview();
            controller.RemoveFromParentViewController();
        }
    }

    void HandleMasterPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == Page.IconProperty.PropertyName || e.PropertyName == Page.TitleProperty.PropertyName)
            MessagingCenter.Send<IVisualElementRenderer>(this, "Xamarin.UpdateToolbarButtons");
    }

    void HandlePropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (_tracker == null)
            return;

        if (e.PropertyName == "Master" || e.PropertyName == "Detail")
            UpdateControllers();
        else if (e.PropertyName == Xamarin.Forms.MasterDetailPage.IsPresentedProperty.PropertyName)
            ToggleMaster();
        else if (e.PropertyName == Xamarin.Forms.MasterDetailPage.IsGestureEnabledProperty.PropertyName)
            base.PresentsWithGesture = this.MasterDetailPage.IsGestureEnabled;
        else if (e.PropertyName == "FlowDirection")
            UpdateFlowDirection();
        MessagingCenter.Send<IVisualElementRenderer>(this, "Xamarin.UpdateToolbarButtons");
    }

    void MasterControllerWillAppear(object sender, EventArgs e)
    {
        _masterVisible = true;
        if (MasterDetailPage.CanChangeIsPresented)
            ElementController.SetValueFromRenderer(Xamarin.Forms.MasterDetailPage.IsPresentedProperty, true);
    }

    void MasterControllerWillDisappear(object sender, EventArgs e)
    {
        _masterVisible = false;
        if (MasterDetailPage.CanChangeIsPresented)
            ElementController.SetValueFromRenderer(Xamarin.Forms.MasterDetailPage.IsPresentedProperty, false);
    }

    void PerformButtonSelector()
    {
        DisplayModeButtonItem.Target.PerformSelector(DisplayModeButtonItem.Action, DisplayModeButtonItem, 0);
    }

    void ToggleMaster()
    {
        if (_masterVisible == MasterDetailPage.IsPresented || MasterDetailPage.ShouldShowSplitMode)
            return;

        PerformButtonSelector();
    }

    void UpdateBackground()
    {
        if (!string.IsNullOrEmpty(((Page)Element).BackgroundImage))
            View.BackgroundColor = UIColor.FromPatternImage(UIImage.FromBundle(((Page)Element).BackgroundImage));
        else if (Element.BackgroundColor == Color.Default)
            View.BackgroundColor = UIColor.White;
        else
            View.BackgroundColor = Element.BackgroundColor.ToUIColor();
    }

    void UpdateControllers()
    {
        MasterDetailPage.Master.PropertyChanged -= HandleMasterPropertyChanged;

        if (Platform.GetRenderer(MasterDetailPage.Master) == null)
            Platform.SetRenderer(MasterDetailPage.Master, Platform.CreateRenderer(MasterDetailPage.Master));
        if (Platform.GetRenderer(MasterDetailPage.Detail) == null)
            Platform.SetRenderer(MasterDetailPage.Detail, Platform.CreateRenderer(MasterDetailPage.Detail));

        ClearControllers();

        MasterDetailPage.Master.PropertyChanged += HandleMasterPropertyChanged;

        var master = Platform.GetRenderer(MasterDetailPage.Master).ViewController;
        var detail = Platform.GetRenderer(MasterDetailPage.Detail).ViewController;

        _masterController.View.AddSubview(master.View);
        _masterController.AddChildViewController(master);

        _detailController.View.AddSubview(detail.View);
        _detailController.AddChildViewController(detail);
    }

    void UpdateFlowDirection()
    {
        bool ios9orLater = UIDevice.CurrentDevice.CheckSystemVersion(9, 0);

        if (NativeView == null || View == null || !ios9orLater)
            return;
        View.SemanticContentAttribute = UISemanticContentAttribute.ForceLeftToRight;
    }

    class InnerDelegate : UISplitViewControllerDelegate
    {
        readonly MasterBehavior _masterPresentedDefaultState;

        public InnerDelegate(MasterBehavior masterPresentedDefaultState)
        {
            _masterPresentedDefaultState = masterPresentedDefaultState;
        }

        public UIBarButtonItem PresentButton { get; set; }

        public override bool ShouldHideViewController(UISplitViewController svc, UIViewController viewController, UIInterfaceOrientation inOrientation)
        {
            bool willHideViewController;
            switch (_masterPresentedDefaultState)
            {
                case MasterBehavior.Split:
                    willHideViewController = false;
                    break;
                case MasterBehavior.Popover:
                    willHideViewController = true;
                    break;
                case MasterBehavior.SplitOnPortrait:
                    willHideViewController = !(inOrientation == UIInterfaceOrientation.Portrait || inOrientation == UIInterfaceOrientation.PortraitUpsideDown);
                    break;
                default:
                    willHideViewController = inOrientation == UIInterfaceOrientation.Portrait || inOrientation == UIInterfaceOrientation.PortraitUpsideDown;
                    break;
            }
            return willHideViewController;
        }

        public override void WillHideViewController(UISplitViewController svc, UIViewController aViewController, UIBarButtonItem barButtonItem, UIPopoverController pc)
        {
            PresentButton = barButtonItem;
        }
    }

    void IEffectControlProvider.RegisterEffect(Effect effect)
    {
        VisualElementRenderer<VisualElement>.RegisterEffect(effect, View);
    }

    protected override void Dispose(bool disposing)
    {
        if (_disposed)
        {
            return;
        }

        _disposed = true;

        if (disposing)
        {
            if (Element != null)
            {
                PageController.SendDisappearing();
                Element.PropertyChanged -= HandlePropertyChanged;

                if (MasterDetailPage?.Master != null)
                {
                    MasterDetailPage.Master.PropertyChanged -= HandleMasterPropertyChanged;
                }

                Element = null;
            }

            if (_tracker != null)
            {
                _tracker.Dispose();
                _tracker = null;
            }

            if (_events != null)
            {
                _events.Dispose();
                _events = null;
            }

            if (_masterController != null)
            {
                _masterController.WillAppear -= MasterControllerWillAppear;
                _masterController.WillDisappear -= MasterControllerWillDisappear;
            }

            ClearControllers();
        }

        base.Dispose(disposing);
    }
}

internal class ChildViewController : UIViewController
{
    public override void ViewDidLayoutSubviews()
    {
        foreach (var vc in ChildViewControllers)
        {
            CGRect rect = View.Bounds;
            vc.View.Frame = rect;
        }
    }
}

internal class EventedViewController : ChildViewController
{
    public override void ViewWillAppear(bool animated)
    {
        base.ViewWillAppear(animated);

        var eh = WillAppear;
        if (eh != null)
            eh(this, EventArgs.Empty);
    }

    public override void ViewWillDisappear(bool animated)
    {
        base.ViewWillDisappear(animated);

        var eh = WillDisappear;
        if (eh != null)
            eh(this, EventArgs.Empty);
    }

    public override void ViewDidLayoutSubviews()
    {
        CGRect rect = View.Bounds;
        View.Bounds = rect;

        foreach (var vc in ChildViewControllers)
        {
            rect = vc.View.Frame;
            vc.View.Frame = rect;

            rect = vc.View.Bounds;
            vc.View.Bounds = rect;

        }
    }

    public event EventHandler WillAppear;

    public event EventHandler WillDisappear;
}
}

I only used the TabletMasterDetailRenderer, since I used the MasterDetailView to create a particular View for tablet devices containing a calendar view inside the master and displaying the details of the selected entry inside the detail view.

If you need it for phones, the same changes made using the PhoneMasterDetailRenderer should work as well.

like image 195
Markus Michel Avatar answered Oct 13 '22 11:10

Markus Michel


we cant currently adjust the width of the MasterDetailPage details

like image 38
Artem Polishchuk Avatar answered Oct 13 '22 10:10

Artem Polishchuk