How to bind selected page menu option to event that loads associated page in the View model?

Cdn_EuroCdn_Euro ✭✭✭Member ✭✭✭
edited May 15 in Xamarin.Forms

I am not sure how to properly bind a selected menu item from a listview to its associated load page event in the view model.

The way I have it right now it works. BUT, the problem is that a menu page stays selected(still highlighted orange when sliding menu from left) and if clicked again the side menu does not disappear and nothing happens, until selecting a different page. ( IPropertyChanged does not trigger if clicking same item...or does it trigger anyways and checks if there is a change and sets it or not?)

How to fix this please in the correct MVVM way?

The MasterDetailPage:

<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
                  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                  xmlns:views="clr-namespace:MyApp.Views"
                  x:Class="MyApp.Views.MyMainPage"
                  Title="My Main Page">


    <MasterDetailPage.Master>
        <ContentPage Title="Menu">
            <ContentPage.Content>

                <Grid BackgroundColor="Transparent">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="130" />
                        <RowDefinition Height="*" />
                    </Grid.RowDefinitions>

                    <Grid>
                        <Image Source="menu_background.png" 
                       Aspect="AspectFill" />

                        <StackLayout Padding="0,20,0,0" 
                             HorizontalOptions="CenterAndExpand" 
                             VerticalOptions="CenterAndExpand">

                            <Image Source="ac_logo.png" 
                           Aspect="AspectFit" 
                           WidthRequest="60" 
                           HeightRequest="60" />

                            <Label Text="myAC" TextColor="White" FontSize="Large" />

                        </StackLayout>
                    </Grid>
                    <StackLayout Grid.Row="1" 
                         Spacing="15">

                        <ListView ItemsSource="{Binding MenuList}"
                                  SelectedItem="{Binding MenuSelectedItem, Mode=TwoWay}"
                          RowHeight="45"
                          SeparatorVisibility="Default"
                          BackgroundColor="#e8e8e8">

                            <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 IconSource}"
                                                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>
                </Grid>

            </ContentPage.Content>

        </ContentPage>
    </MasterDetailPage.Master>


    <MasterDetailPage.Detail>
        <NavigationPage>
            <x:Arguments>
                <views:StudentHomePage />
            </x:Arguments>
        </NavigationPage>
    </MasterDetailPage.Detail>


</MasterDetailPage>

My ViewModel:

public class MyMainPageVM : BaseVM
    {
        public ObservableCollection<MenuPageItem> MenuList { get; set; }

        private MenuPageItem _menuSelectedItem = null;
        public MenuPageItem MenuSelectedItem
        {
            get
            {
                return _menuSelectedItem;
            }

            set
            {
               //if (_menuSelectedItem != value) 
               // {
                    _menuSelectedItem = value;
                    // navigate
                    (App.Current.MainPage as MasterDetailPage).Detail = new NavigationPage((Page)Activator.CreateInstance(_menuSelectedItem.TargetType));
                    (App.Current.MainPage as MasterDetailPage).IsPresented = false;
                //}
            }
        }
        public MyMainPageVM()
        {
            MenuList = new ObservableCollection<MenuPageItem>();
            populateMenuList();


        }

        private void populateMenuList()
        {
            MenuList.Add(new MenuPageItem() { Title = "Home", IconSource = "home.png", TargetType = typeof(MyHomePage) });
            MenuList.Add(new MenuPageItem() { Title = "Setting", IconSource = "setting.png", TargetType = typeof(Page2) });
            MenuList.Add(new MenuPageItem() { Title = "Help", IconSource = "help.png", TargetType = typeof(Page3) });
        }

    }

My Base View Model Class:

public class BaseVM : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
    }

And finally the MasterPageDetail page CS:

public partial class MyMainPage : MasterDetailPage
    {
        private MyMainPageVM myMainPageVM;
        public MyMainPage ()
        {
            InitializeComponent ();
            myMainPageVM = new MyMainPageVM();
            this.BindingContext = myMainPageVM       

        }        
    }

Answers

  • PenutButterJellyHeadPenutButterJellyHead ✭✭✭ USMember ✭✭✭

    It wont be a OnPropertyChanged in this case. You would need to bind an Item Tapped Command to the menu list view, example here: https://forums.xamarin.com/discussion/60937/how-to-bind-a-command-to-listview-itemtapped

    That command will then call a private method inside your MyMainPageVM that would call this
    (App.Current.MainPage as MasterDetailPage).IsPresented = false;
    Which would close the menu

  • PenutButterJellyHeadPenutButterJellyHead ✭✭✭ USMember ✭✭✭
    edited May 16

    On the view it would be somthing like:
    <ListView.GestureRecognizers>
    <TapGestureRecognizer Command="{Binding ItemTappedCommand}"/>
    </ListView.GestureRecognizers>

    In your view model it will be something like:

    public ICommand ItemTappedCommand => new Command((s) => OnItemTapped(s));

    private void OnItemTapped(object s)
    {
        // You can do something with the object s here if you want
        // This will close the menu below
         (App.Current.MainPage as MasterDetailPage).IsPresented = false;
    }
    
  • Cdn_EuroCdn_Euro ✭✭✭ Member ✭✭✭
    edited May 16

    @PeanutButterJellyHead I love the idea. I tested it but could not get it to work.
    The OnItemTapped(object s) does not trigger, I put a stop on it while debugging but it didnt get hit.
    Also the object s should be the list item for sure, so I can cast it as a "MenuPageItem"?

    <ListView ItemsSource="{Binding MenuList}"
                                      SelectedItem="{Binding MenuSelectedItem}"
                              RowHeight="45"
                              SeparatorVisibility="Default"
                              BackgroundColor="#e8e8e8">
    
                                <!--add to test mvvm-->
                                <ListView.GestureRecognizers>
                                    <TapGestureRecognizer Command="{Binding ListMenuItemTappedCommand}"/>
                                </ListView.GestureRecognizers>
    
                                <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 IconSource}"
                                                    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>
    
  • PenutButterJellyHeadPenutButterJellyHead ✭✭✭ USMember ✭✭✭
    edited May 17

    @Cdn_Euro Try to add that gesture to the StackLayout in the child viewcell instead. If you are testing this on Android the child could be swallowing the tap of the parent listview gesture. Having this command in the list item itself might actually be the correct place to have it now that I think of it.

  • jezhjezh Xamurai Member, Xamarin Team Xamurai

    Have you resolved this question?

Sign In or Register to comment.