Re-calling a custom renderer of a previous page in a navigation stack

ChrisH_at_PLChrisH_at_PL USMember
edited June 2015 in Xamarin.Forms

Hi all,

Say I have the following ContentPages in a navigation stack:

Page1 --> Page2 --> Page3

Navigation down the hierarchy is done via PushAsync(page)

Now let's say Page2 has a custom renderer attached to it called Page2Renderer. i.e.

    [assembly: ExportRenderer(typeof(Page2), typeof(Page2Renderer))]
    namespace FooProject.iOS
    {
            public class Page2Renderer : PageRenderer
                {
                        // Custom render stuff specific to Page2
                }
        }

Similarly, Page3 has a completely separate custom renderer attached to it called Page3Renderer

When navigating from Page2 --> Page3, the Page3Renderer gets applied fine! but when I press the back button on the navigation bar to go to back to Page2, the Page3 renderer stays applied, when I want Page2Renderer to re-apply.

I can see that it's doing this because when you press the back button on the nav bar, it's basically just calling INavigation's PopAsync() method which doesn't re-instantiate Page2, and thus doesn't recall the Page2Renderer

Any ideas how I can work around this? I was thinking of perhaps some way to override that PopAsync() method that happens on press of the back button, and forcing it to re-instantiate Page2 for me.

Thanks,
Chris

EDIT: clarity

Best Answer

  • AndrewMobileAndrewMobile US ✭✭✭✭
    Accepted Answer

    in the Page3Renderer, you're changing UIButton.Appearance which basically changes the appearance of the UIButton in all the application, including Page2

    but note that you're calling the exact same code in both pages:
    UIButton.Appearance.SetBackgroundImage(ProlificTheme.SharedTheme.RegularButtomImage, UIControlState.Normal);

    even if the comments say you apply a different theme

Answers

  • AndrewMobileAndrewMobile USMember ✭✭✭✭
    edited June 2015

    it can't be possible for Page2 to get Page3Renderer
    also, it's not reproducing for me

    just to make sure, can you confirm you have this too:

    [assembly: ExportRenderer(typeof(Page2), typeof(Page2Renderer))]
    [assembly: ExportRenderer(typeof(Page3), typeof(Page3Renderer))]
    
    namespace FooProject.iOS
    {
                    public class Page2Renderer : PageRenderer
                    {
                            protected override void OnElementChanged(VisualElementChangedEventArgs e)
                            {
                                    base.OnElementChanged(e);
                                    Element.BackgroundColor = Color.Green;
                             }
                    }
    
                    public class Page3Renderer : PageRenderer
                    {
                            protected override void OnElementChanged(VisualElementChangedEventArgs e)
                            {
                                    base.OnElementChanged(e);
                                    Element.BackgroundColor = Color.Blue;
                             }
                    }
    }
    

    Also, can you say what are you changing in those renderers which make you think Page2 gets the Page3Renderer? Maybe that matters

  • ChrisH_at_PLChrisH_at_PL USMember
    edited June 2015

    Hi, thank you for the reply.

    Yes, that is what I have in a nutshell.

    Also, can you say what are you changing in those renderers which make you think Page2 gets the Page3Renderer?

    I'm actually playing around with different implementation of an iOS Component theme (ProlificTheme to be specific https://components.xamarin.com/view/prolifictheme), so I actually have something more like the following

    [assembly: ExportRenderer(typeof(ThemedPageWithRegularButtons), typeof( ThemedPageWithRegularButtonsRenderer))]
    [assembly: ExportRenderer(typeof(ThemedPageWithConfirmedButtons), typeof(ThemedPageWithConfirmedButtonsRenderer))]
    
    namespace FooProject.iOS
    {
       public class ThemedPageWithRegularButtonsRenderer : PageRenderer // Page2 implements ThemedPageWithRegularButtons
       {
          protected override void OnElementChanged(VisualElementChangedEventArgs e)
          {
             base.OnElementChanged(e);
             if (e.OldElement == null)
             {
                var page = e.NewElement as ThemedPageWithRegularButtons
                if (page != null)
                {
                   // Apply a "REGULAR BUTTON" themed image to all Page2 buttons
                   UIButton.Appearance.SetBackgroundImage(ProlificTheme.SharedTheme.RegularButtomImage, UIControlState.Normal);
                }
             }
          }
        }
    
        public class ThemedPageWithConfirmedButtonsRenderer : PageRenderer // Page3 implements ThemedPageWithConfirmedButtons
       {
          protected override void OnElementChanged(VisualElementChangedEventArgs e)
          {
             base.OnElementChanged(e);
             if (e.OldElement == null)
             {
                var page = e.NewElement as ThemedPageWithConfirmedButtons
                if (page != null)
                {
                   // Apply a "CONFIRMED BUTTON" themed image to all Page3 buttons
                   UIButton.Appearance.SetBackgroundImage(ProlificTheme.SharedTheme.ConfirmButtomImage, UIControlState.Normal);
                }
             }
          }
        }                 
     }
    

    So when I navigate back from Page3 to Page2, the confirmed button themed image remains applied.

    An alternative approach I could probably do is just have 1 custom renderer for iOS and apply specific implementations of the theme to specific button instances, but this is easier said than done as I have a lot of pages, wrapped under different shared themed pages.

    I.e. Page2 XAML code-behind:

    public partial class Page2 : ThemedPageWithRegularButtons
    

    and Page3 XAML code-behind:

    public partial class Page3 : ThemedPageWithConfirmedButtons
    

    Thanks,
    Chris

    EDIT: added in my actual code for clarity

  • AndrewMobileAndrewMobile USMember ✭✭✭✭
    Accepted Answer

    in the Page3Renderer, you're changing UIButton.Appearance which basically changes the appearance of the UIButton in all the application, including Page2

    but note that you're calling the exact same code in both pages:
    UIButton.Appearance.SetBackgroundImage(ProlificTheme.SharedTheme.RegularButtomImage, UIControlState.Normal);

    even if the comments say you apply a different theme

  • ChrisH_at_PLChrisH_at_PL USMember

    Ah I knew it had to be something simple like that, also sorry that was a typo - you're too quick for me I didn't have time to edit :D, one renderer is calling

    UIButton.Appearance.SetBackgroundImage(ProlificTheme.SharedTheme.RegularButtomImage, UIControlState.Normal);

    the other renderer is calling

    UIButton.Appearance.SetBackgroundImage(ProlificTheme.SharedTheme.ConfirmButtomImage, UIControlState.Normal);

    Thank you very much!

  • ChrisH_at_PLChrisH_at_PL USMember

    Update for anyone interested...

    I decided to split my custom UIButton code away from the PageRenderer and instead implemented two ButtonRenderer classes for each of my two button types. There's probably a better way, but everything is working as it should now.

Sign In or Register to comment.