Custom page with custom toolbar

Hi all,
I'm trying to create custom content page with toolbar element. Actually I'm not fully understand what I'm doing :smile: but I'm following some samples.
I want to create custom renderer for this page:

[assembly: ExportRenderer(typeof(WizardContentPage), typeof (CustomContentPageRenderer.Droid.Renderers.WizardContentPageRenderer))]
namespace CustomContentPageRenderer.Droid.Renderers
{
    public class WizardContentPageRenderer : PageRenderer
    {
        private Android.Widget.Button goNextBtn;
        private Android.Widget.Button goBackBtn;
        private Android.Views.View view;

        private Activity activity;

        protected override void OnElementChanged(ElementChangedEventArgs<Page> e)
        {
            base.OnElementChanged(e);
            if (e.OldElement != null || Element == null)
            {
                return;
            }

            try
            {
                SetUpUserInterface();
                SetUpEventHandlers();
                AddView(view);
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(@"           ERROR: ", ex.Message);
            }
        }

        private void SetUpUserInterface()
        {
            activity = this.Context as Activity;
            view = activity.LayoutInflater.Inflate(Resource.Layout.WizardPage, this, false);
        }

        private void SetUpEventHandlers()
        {
            var wizardPage = Element as WizardContentPage;
            if (wizardPage == null)
            {
                return;
            }

            goNextBtn = view.FindViewById<Android.Widget.Button>(Resource.Id.nextButton);
            goBackBtn = view.FindViewById<Android.Widget.Button>(Resource.Id.backButton);

            goNextBtn.Click += (sender, e) => wizardPage.GoNextCommand.Execute(null);
            goBackBtn.Click += (sender, e) => wizardPage.GoBackCommand.Execute(null);
        }
    }
}

This is my custom control:

namespace CustomContentPageRenderer.Controls
{
    public class WizardContentPage : ContentPage
    {
        public static readonly BindableProperty GoNextCommandProperty = BindableProperty.Create(nameof(GoNextCommand), typeof(ICommand), typeof(WizardContentPage));
        public static readonly BindableProperty GoBackCommandProperty = BindableProperty.Create(nameof(GoBackCommand), typeof(ICommand), typeof(WizardContentPage));

        public WizardContentPage() : base()
        {
            //NavigationPage.SetHasNavigationBar(this, false);
        }

        public ICommand GoNextCommand
        {
            get
            {
                return (ICommand)GetValue(GoNextCommandProperty);
            }
            set
            {
                SetValue(GoNextCommandProperty, value);
            }
        }

        public ICommand GoBackCommand
        {
            get
            {
                return (ICommand)GetValue(GoBackCommandProperty);
            }
            set
            {
                SetValue(GoBackCommandProperty, value);
            }
        }
    }
}

This is the view model for this page:

namespace CustomContentPageRenderer.ViewModels
{
    public class WizardPageViewModel
    {
        private void OnPropertyChanged([CallerMemberName] string propertyName = null) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        public event PropertyChangedEventHandler PropertyChanged;
        public ICommand GoNextCommand { get; }
        public ICommand GoBackCommand { get; }

        public WizardPageViewModel(INavigation navigation, Page page)
        {
            this.GoNextCommand = new Command(async () =>
            {
                if (page != null)
                {
                    await navigation.PushAsync(page);
                }
            });

            this.GoBackCommand = new Command(async () =>
            {
                await navigation.PopAsync();
            });
        }
    }
}

Here is the control usage:

namespace CustomContentPageRenderer.Views
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Page1 : WizardContentPage
    {
        public Page1() : base()
        {
            this.BindingContext = new WizardPageViewModel(this.Navigation, new Page2());
            InitializeComponent();
        }       
    }
}

xaml:

<?xml version="1.0" encoding="utf-8" ?>
<controls:WizardContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="CustomContentPageRenderer.Views.Page1"
             xmlns:controls="clr-namespace:CustomContentPageRenderer.Controls;assembly=CustomContentPageRenderer"
             GoNextCommand="{Binding GoNextCommand}"
             GoBackCommand="{Binding GoBackCommand}">
</controls:WizardContentPage>

This is the layout for the custom page (I really don't want to use it but don't know how to avoid such approach):

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="50dp"
    android:background="?android:attr/colorPrimary"
    android:theme="@android:style/ThemeOverlay.Material.Dark.ActionBar">
    <Button
        android:id="@+id/backButton"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_gravity="top|left"
        android:layout_marginLeft="25dp"
        android:layout_marginTop="10dp"
        android:background="@drawable/backIcon" />
    <Button
        android:id="@+id/nextButton"
        android:layout_width="30dp"
        android:layout_height="30dp"
        android:layout_gravity="top|right"
        android:layout_marginRight="25dp"
        android:layout_marginTop="10dp"
        android:background="@drawable/nextIcon" />
</FrameLayout>

So...when I'm opening this custom page I don't see anything. No errors, but no content.
My question is what I'm doing wrong and how can I avoid using of the android layout?
The biggest problem for me is that I not fully understand the samples code of the CustomRenderers
(http://www.xamboy.com/2017/12/06/navigation-bar-customization-in-xamarin-forms/
https://github.com/rohitvipin/Xamarin-Forms-Custom-Renderer-Samples
https://github.com/xamarin/xamarin-forms-samples/tree/master/CustomRenderers/ContentPage)

Thanks in advance for your help and time!

Answers

  • okay that's my fault. I added this to the render and it show's up:

        protected override void OnLayout(bool changed, int l, int t, int r, int b)
        {
            base.OnLayout(changed, l, t, r, b);
    
            var msw = MeasureSpec.MakeMeasureSpec(r - l, MeasureSpecMode.Exactly);
            var msh = MeasureSpec.MakeMeasureSpec(b - t, MeasureSpecMode.Exactly);
    
            view.Measure(msw, msh);
            view.Layout(0, 0, r - l, b - t);
        }
    

    but how can I design the page? This layout fills the whole screen and when I added a label to my custom page:

    <controls:WizardContentPage.Content>
        <Label Text="Test overlay"/>
    </controls:WizardContentPage.Content>
    

    I've got this:

Sign In or Register to comment.