locked
TabbedPage not tracking selected tab. RRS feed

  • Question

  • User212383 posted

    I need to track which tab users are pressing in a tabbed page. I feel I am very close to the solution, but just can't get round the last little bit and getting a little bit of a sore forehead from banging my head on the desk. I'm receiving a System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation when I try to set the SelectedItem to the tab pages CurrentPage (see near the bottom - commented the offending line). - I believe its an recursion error, but not quite sure how to resolve it. Can any of you help?

    Here's all the code I got so far:

    the ViewModel:

    public class TabTestViewModel : BaseViewModel
    {
        private List<Page> tabs;
        private Page selectedTab;
    
        public TabTestViewModel()
        {
            tabs = new List<Page>
            {
                new HomeView { Title = "Tab 1" },
                new HomeView { Title = "Tab 2" },
                new HomeView { Title = "Tab 3" },
            };
        }
    
        public List<Page> Tabs
        {
            get { return tabs; }
            set
            {
                tabs = value;
                OnPropertyChanged();
            }
        }
    
        public Page SelectedTab
        {
            get { return selectedTab; }
            set
            {
                selectedTab = value;
                Debug.WriteLine(string.Format("THIS IS WORKING TAB SELECT IS {0}", SelectedTab.Title));
                OnPropertyChanged();
            }
        }
    

    The BaseViewModel (for completeness):

    public class BaseViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        public void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
    

    The View:

    <controls:BindableTabbedPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:controls="clr-namespace:Mobile.Controls;assembly=Mobile" x:Class="Mobile.Views.TabTestView" Title="TabbedPage" Children="{Binding Tabs}" SelectedItem="{Binding SelectedTab}"> </controls:BindableTabbedPage>

    And my custom BindableTabbedPage control:

    public class BindableTabbedPage : TabbedPage
    {
        public static readonly BindableProperty ChildrenProperty =
            BindableProperty.Create<BindableTabbedPage, IEnumerable>(x => x.Children, null, BindingMode.TwoWay,
                propertyChanged: OnItemsSourcePropertyChanged);
    
        public BindableTabbedPage()
        {
            this.PropertyChanged += OnPropertyChanged;
        }
    
        public new IEnumerable<Page> Children
        {
            get { return base.Children; }
            set { SetValue(ChildrenProperty, value); }
        }
    
        public new Object SelectedItem
        { 
            get { return base.SelectedItem; }
            set { SetValue(SelectedItemProperty, value); }
        }
    
        private static void OnItemsSourcePropertyChanged(BindableObject bindable, IEnumerable value,
            IEnumerable newValue)
        {
            var tabbedPage = (TabbedPage) bindable;
            var notifyCollection = newValue as INotifyCollectionChanged;
            if (notifyCollection != null)
            {
                notifyCollection.CollectionChanged += (sender, args) =>
                {
                    if (args.NewItems != null)
                    {
                        foreach (var newItem in args.NewItems)
                        {
                            tabbedPage.Children.Add((Page) newItem);
                        }
                    }
    
                    if (args.OldItems != null)
                    {
                        foreach (var oldItem in args.OldItems)
                        {
                            tabbedPage.Children.Remove((Page) oldItem);
                        }
                    }
                };
            }
    
            if (newValue == null)
                return;
    
            tabbedPage.Children.Clear();
            foreach (var item in newValue)
            {
                tabbedPage.Children.Add((Page)item);
            }
        }
    
        private void OnPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "CurrentPage")
            {
                var tabbedPage = sender as TabbedPage;
                if (tabbedPage != null)
                {
                    if (tabbedPage.CurrentPage != null)
                    {
                        //SelectedItem = tabbedPage.CurrentPage; // Uncommenting this line causes the System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
                    }
                }
            }
        }
    }
    
    Wednesday, September 28, 2016 10:39 AM

Answers

  • User212383 posted

    Ok, I got to the bottom of the Invocation error and I have it working now. It was trying to assign a tab to 'null' as I didn't initialise the SelectedItem in the viewModel. Updating:

        public TabTestViewModel()
        {
            tabs = new List<Page>
            {
                new HomeView { Title = "Tab 1" },
                new HomeView { Title = "Tab 2" },
                new HomeView { Title = "Tab 3" },
            };
        }
    

    TO

        public TabTestViewModel()
        {
            tabs = new List<Page>
            {
                new HomeView { Title = "Tab 1" },
                new HomeView { Title = "Tab 2" },
                new HomeView { Title = "Tab 3" },
            };
    
            SelectedTab = tabs[0];
        }
    

    Fixes the problem.

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Wednesday, September 28, 2016 1:15 PM

All replies

  • User1786 posted

    Can you fix the code block of the view? Something is missing, reproducing the project is hard.

    Even better, can you put together a small repro project on github? So we can test it? Thanks

    Wednesday, September 28, 2016 11:46 AM
  • User212383 posted

    Sure - attached zipped up project, should have the pcl and android (not bothered about any other platforms).

    Any problems with the attached let me know.

    Wednesday, September 28, 2016 11:52 AM
  • User212383 posted

    Oh, the only line missing from the view code block is the <?xml version="1.0" encoding="utf-8" ?>

    Wednesday, September 28, 2016 11:57 AM
  • User212383 posted

    Ok, I got to the bottom of the Invocation error and I have it working now. It was trying to assign a tab to 'null' as I didn't initialise the SelectedItem in the viewModel. Updating:

        public TabTestViewModel()
        {
            tabs = new List<Page>
            {
                new HomeView { Title = "Tab 1" },
                new HomeView { Title = "Tab 2" },
                new HomeView { Title = "Tab 3" },
            };
        }
    

    TO

        public TabTestViewModel()
        {
            tabs = new List<Page>
            {
                new HomeView { Title = "Tab 1" },
                new HomeView { Title = "Tab 2" },
                new HomeView { Title = "Tab 3" },
            };
    
            SelectedTab = tabs[0];
        }
    

    Fixes the problem.

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Wednesday, September 28, 2016 1:15 PM