Xamarin forms: How to add a contentpage having arguments into a list?

SreeeeSreeee ✭✭✭✭✭INMember ✭✭✭✭✭

I am implementing the Navigation Drawer on my project. I am following this blog for this feature.

Mainpage.xsml.cs:

public partial class NavigationDrawerPage : MasterDetailPage
    {
public List<MasterPageItem> menuList { get; set; }
        public MainPage()
        {
            InitializeComponent();

            menuList = new List<MasterPageItem>();

            // Adding menu items to menuList and you can define title ,page and icon
            menuList.Add(new MasterPageItem() { Title = "Home", Icon = "home.png", TargetType = typeof(HomePage) });
            menuList.Add(new MasterPageItem() { Title = "Setting", Icon = "setting.png", TargetType = typeof(SettingPage) });
            menuList.Add(new MasterPageItem() { Title = "LogOut", Icon = "logout.png", TargetType = typeof(LogoutPage) });

            navigationDrawerList.ItemsSource = menuList;

            Detail = new NavigationPage((Page)Activator.CreateInstance(typeof(HomePage)));
        }
}

Mainpage.xaml

<StackLayout Grid.Row="1" Spacing="15">
                    <ListView x:Name="navigationDrawerList"
                  RowHeight="60"
                  SeparatorVisibility="None"
                  BackgroundColor="#e8e8e8"
                  ItemSelected="OnMenuItemSelected">

                        <ListView.ItemTemplate>
                            <DataTemplate>
                                <ViewCell>
                                    <StackLayout>
                                        <!-- Main design for our menu items -->
                                        <StackLayout VerticalOptions="FillAndExpand"
                             Orientation="Horizontal"
                             Padding="20,10,0,10"
                             Spacing="20">

                                            <Image Source="{Binding Icon}"
                         WidthRequest="30"
                         HeightRequest="30"
                         VerticalOptions="Center" />

                                            <Label Text="{Binding Title}"
                         FontSize="Medium"
                         VerticalOptions="Center"
                         TextColor="Black"/>
                                        </StackLayout>
                                        <BoxView HeightRequest="1" BackgroundColor="Gray"/>
                                    </StackLayout>
                                </ViewCell>
                            </DataTemplate>
                        </ListView.ItemTemplate>
                    </ListView>
                </StackLayout>

MasterPageItem.cs

public class MasterPageItem
    {
        public string Title { get; set; }
        public string Icon { get; set; }
        public Type TargetType { get; set; }
    }

My problem is when adding my content pages to list. The above code working perfect:

menuList.Add(new MasterPageItem() { Title = "Home", Icon = "home.png", TargetType = typeof(SettingPage) });

But I have a page having one argument, I can't add the argument inside of the TargetType. See the below code:

menuList.Add(new MasterPageItem() { Title = "Help", Icon = "help.png", TargetType = typeof(HelpPage(false)) });  //showing syntax errors for this line

How can I add a content page having arguments into the list item? Should I change the TargetType datatype?

Best Answers

  • ColeXColeX Xamurai Xamurai
    edited June 4 Accepted Answer

    You could pass parameters with the method public static object CreateInstance(Type type, params object[] args);

    Item

    public class MasterPageItem
    {
        public string Title { get; set; }
        public string Icon { get; set; }
        public Type TargetType { get; set; }
    
        public object[] args { get; set; }   //
    }
    

    MasterDetailPage

    menuList.Add(new MasterPageItem() { Title = "Home", Icon = "home.png", TargetType = typeof(HomePage), args = new object[] {"string", 1 , false} } ); // pass parameters whatever you want
    

    Add the constructor with corresponding parameters

    public HomePage()   //remain the default constructor 
    {
        InitializeComponent();
    }
    
    public HomePage (string s , int i , bool b)   //add this
    {
        InitializeComponent ();
    }
    

    My test

  • ColeXColeX Xamurai Xamurai
    edited June 4 Accepted Answer

    Have you remained the Default constructor with MyTabbedPage ?

    Check if the following method in MyTabbedPage.cs

      public MyTabbedPage()
        {
            InitializeComponent();
        }
    
  • ColeXColeX Xamurai Xamurai
    Accepted Answer

    No better way , you could hard code here

    Detail = new NavigationPage((Page)Activator.CreateInstance(typeof(HomePage), new object[] { "string", 1, false }));
    
  • ColeXColeX Xamurai Xamurai
    Accepted Answer

    If you only need bool value , just pass one parameter.

     Detail = new NavigationPage((Page)Activator.CreateInstance(typeof(Homepage), new object[] { false }));
    

    And change the constructor with only one parameter

    public HomePage (bool b)   //add this
    {
        InitializeComponent ();
    }
    
  • ColeXColeX Xamurai Xamurai
    edited June 5 Accepted Answer
    1. Detail = new NavigationPage((Page)Activator.CreateInstance(page));
    2. Detail = new NavigationPage((Page)Activator.CreateInstance(page,item.args));

    If you call the first one , it always hits the default constructor (without argument).

    If you call the second one , it hits the constructor with arguments (it is determined by the parameters passed in the MasterPageItem)

    Change the code in item selected event

    private void OnMenuItemSelected(object sender, SelectedItemChangedEventArgs e)
        {
            var item = (MasterPageItem)e.SelectedItem;
            Type page = item.TargetType;
            Detail = new NavigationPage((Page)Activator.CreateInstance(page,item.args));   //this line
            IsPresented = false;
        }
    

    If

    menuList.Add(new MasterPageItem() { Title = "Announcements", Icon = "ic_topics_fill_xx.png", TargetType = typeof(HomePage), args = new object[] { "bool", 1, false } });
    

    it hits

    public HomePage (string s , int i , bool b)  
    {
        InitializeComponent ();
    }
    

    If

    menuList.Add(new MasterPageItem() { Title = "Announcements", Icon = "ic_topics_fill_xx.png", TargetType = typeof(HomePage), args = new object[] { false } });
    

    it hits

     public HomePage ( bool b)  
    {
        InitializeComponent ();
    }
    

    If

    menuList.Add(new MasterPageItem() { Title = "Announcements", Icon = "ic_topics_fill_xx.png", TargetType = typeof(HomePage)});
    

    it hits

    public HomePage ()   //default constructor
    {
        InitializeComponent ();
    }
    

    Is it clear ?

  • ColeXColeX Xamurai Xamurai
    Accepted Answer

    .............

    It should be Detail = new NavigationPage((Page)Activator.CreateInstance(page,item.args));

Answers

  • ClintStLaurentClintStLaurent ✭✭✭✭✭ USUniversity ✭✭✭✭✭

    Personally I'd put those traits on the ViewModel for the pages.
    Then you don't need the extra class of MasterPageItem. Just use the ViewModels as your collection.
    And your other classes aren't responsible for creating items and setting the properties. IE: Why would your NavigationDrawer be responsible for setting the titles and icons of _other_pages? It shouldn't know or control these things.

  • SreeeeSreeee ✭✭✭✭✭ INMember ✭✭✭✭✭

    @ClintStLaurent Can you share sample codes?

  • ColeXColeX Xamurai Member, Xamarin Team Xamurai
    edited June 4 Accepted Answer

    You could pass parameters with the method public static object CreateInstance(Type type, params object[] args);

    Item

    public class MasterPageItem
    {
        public string Title { get; set; }
        public string Icon { get; set; }
        public Type TargetType { get; set; }
    
        public object[] args { get; set; }   //
    }
    

    MasterDetailPage

    menuList.Add(new MasterPageItem() { Title = "Home", Icon = "home.png", TargetType = typeof(HomePage), args = new object[] {"string", 1 , false} } ); // pass parameters whatever you want
    

    Add the constructor with corresponding parameters

    public HomePage()   //remain the default constructor 
    {
        InitializeComponent();
    }
    
    public HomePage (string s , int i , bool b)   //add this
    {
        InitializeComponent ();
    }
    

    My test

  • SreeeeSreeee ✭✭✭✭✭ INMember ✭✭✭✭✭
    edited June 4
    menuList.Add(new MasterPageItem() { Title = "Announcements", Icon = "ic_topics_fill_xx.png", TargetType = typeof(HomePage), args = new object[] { "bool", 1, false } });
    

    Hi @ColeX tried like above, my argument is a bool variable. but getting following Exception.

    Unhandled Exception:
    System.MissingMethodException: Default constructor not found for type appname.MyTabbedPage

  • ColeXColeX Xamurai Member, Xamarin Team Xamurai
    edited June 4 Accepted Answer

    Have you remained the Default constructor with MyTabbedPage ?

    Check if the following method in MyTabbedPage.cs

      public MyTabbedPage()
        {
            InitializeComponent();
        }
    
  • ClintStLaurentClintStLaurent ✭✭✭✭✭ USUniversity ✭✭✭✭✭

    @Sreeee said:
    @ClintStLaurent Can you share sample codes?

    Samples of... ? Ya lost me. Sample of a property called "Title"? I don't understand what it is you want a sample of. If its just a matter of understanding MVVM, base classes for view models etc. you can hit up my tutorials.
    http://redpillxamarin.com/2018/03/12/2018-101-vs2017-new-solution/

  • SreeeeSreeee ✭✭✭✭✭ INMember ✭✭✭✭✭

    @ColeX It is working. One last question.

    // Initial navigation, this can be used for our home page
        Detail = new NavigationPage((Page)Activator.CreateInstance(typeof(HomePage)));
    

    For the initial navigation, I am using the above code. So the same argument needs to pass here also, how can I do that here?

  • ColeXColeX Xamurai Member, Xamarin Team Xamurai
    Accepted Answer

    No better way , you could hard code here

    Detail = new NavigationPage((Page)Activator.CreateInstance(typeof(HomePage), new object[] { "string", 1, false }));
    
  • SreeeeSreeee ✭✭✭✭✭ INMember ✭✭✭✭✭

    @ColeX Why passing "string"? { "string", 1, false })); Actually it is a bool variable, so can I pass like below?

    Detail = new NavigationPage((Page)Activator.CreateInstance(typeof(Homepage), new object[] { "bool", 1, false }));
    
  • ColeXColeX Xamurai Member, Xamarin Team Xamurai
    Accepted Answer

    If you only need bool value , just pass one parameter.

     Detail = new NavigationPage((Page)Activator.CreateInstance(typeof(Homepage), new object[] { false }));
    

    And change the constructor with only one parameter

    public HomePage (bool b)   //add this
    {
        InitializeComponent ();
    }
    
  • NMackayNMackay mod GBInsider, University mod

    Eeeek, I'd be rejecting that pull request :o

    Prism makes these scenarios really simple.
    https://prismlibrary.github.io/docs/xamarin-forms/navigation/passing-parameters.html

  • SreeeeSreeee ✭✭✭✭✭ INMember ✭✭✭✭✭
    edited June 4

    @ColeX The initial navigation part is working fine and I can see the contents on the page. But when I select an item from navigation drawer the page is not loading. I am using the following code when selecting an item from the navigation drawer.

    <ListView x:Name="navigationDrawerList"
                      ItemSelected="OnMenuItemSelected">
    
    private void OnMenuItemSelected(object sender, SelectedItemChangedEventArgs e)
            {
                var item = (MasterPageItem)e.SelectedItem;
                Type page = item.TargetType;
                Detail = new NavigationPage((Page)Activator.CreateInstance(page));
                IsPresented = false;
            }
    

    Code execution is coming on the Homepage and but invoking the constructor without argument.

    public HomePage()  
    {
        InitializeComponent();
    }
    
  • SreeeeSreeee ✭✭✭✭✭ INMember ✭✭✭✭✭

    @ColeX The code execution is coming on the Homepage but invoking the constructor without argument. So is that mean our code for adding arguments to the page is not correct. I try both ways(added below) for adding arguments, but always hitting the constructor without argument.

    menuList.Add(new MasterPageItem() { Title = "Announcements", Icon = "ic_topics_fill_xx.png", TargetType = typeof(HomePage), args = new object[] { "bool", 1, false } });
    
    menuList.Add(new MasterPageItem() { Title = "Announcements", Icon = "ic_topics_fill_xx.png", TargetType = typeof(HomePage), args = new object[] { false } });
    
  • ColeXColeX Xamurai Member, Xamarin Team Xamurai
    edited June 5 Accepted Answer
    1. Detail = new NavigationPage((Page)Activator.CreateInstance(page));
    2. Detail = new NavigationPage((Page)Activator.CreateInstance(page,item.args));

    If you call the first one , it always hits the default constructor (without argument).

    If you call the second one , it hits the constructor with arguments (it is determined by the parameters passed in the MasterPageItem)

    Change the code in item selected event

    private void OnMenuItemSelected(object sender, SelectedItemChangedEventArgs e)
        {
            var item = (MasterPageItem)e.SelectedItem;
            Type page = item.TargetType;
            Detail = new NavigationPage((Page)Activator.CreateInstance(page,item.args));   //this line
            IsPresented = false;
        }
    

    If

    menuList.Add(new MasterPageItem() { Title = "Announcements", Icon = "ic_topics_fill_xx.png", TargetType = typeof(HomePage), args = new object[] { "bool", 1, false } });
    

    it hits

    public HomePage (string s , int i , bool b)  
    {
        InitializeComponent ();
    }
    

    If

    menuList.Add(new MasterPageItem() { Title = "Announcements", Icon = "ic_topics_fill_xx.png", TargetType = typeof(HomePage), args = new object[] { false } });
    

    it hits

     public HomePage ( bool b)  
    {
        InitializeComponent ();
    }
    

    If

    menuList.Add(new MasterPageItem() { Title = "Announcements", Icon = "ic_topics_fill_xx.png", TargetType = typeof(HomePage)});
    

    it hits

    public HomePage ()   //default constructor
    {
        InitializeComponent ();
    }
    

    Is it clear ?

  • SreeeeSreeee ✭✭✭✭✭ INMember ✭✭✭✭✭

    @ColeX I tried like below but showing `NavigationPage' does not contain a constructor that takes 2 arguments.

    Detail = new NavigationPage((Page)Activator.CreateInstance(page), item.args);   
    

    Screenshot:

  • ColeXColeX Xamurai Member, Xamarin Team Xamurai
    Accepted Answer

    .............

    It should be Detail = new NavigationPage((Page)Activator.CreateInstance(page,item.args));

  • SreeeeSreeee ✭✭✭✭✭ INMember ✭✭✭✭✭

    Hi @ColeX

    Today I tested the navigation drawer feature in ios and UWP. In ios a blue box is showing on each pages of drawer items. See the screenshot below:

    In UWP the navigation drawer is not dismissing after selecting an item from it.

    I searched a lot, Can you suggest any solution for these 2 issues?

  • ColeXColeX Xamurai Member, Xamarin Team Xamurai

    @Sreeee said:
    Hi @ColeX

    Today I tested the navigation drawer feature in ios and UWP. In ios a blue box is showing on each pages of drawer items. See the screenshot below:

    In UWP the navigation drawer is not dismissing after selecting an item from it.

    I searched a lot, Can you suggest any solution for these 2 issues?

    This is a another question , could you open a new thread ?

  • SreeeeSreeee ✭✭✭✭✭ INMember ✭✭✭✭✭

    @ColeX Sure. :)
    Started a new thread here.

Sign In or Register to comment.