Theodorus Agum Gumilang
Theodorus Agum Gumilang

Reputation: 1486

MVVM Navigation Force Close Xamarin Forms

I try using mvvm in my xamarin forms but i'm still litle bit confused with the navigation in each page, I Already Create Interface and NavigationService to handle all Navigation in my Xamarin Forms, there is no error code line but when i click the buton and try to navigate to other page it always crash. Here is some of my code My Interface

namespace KGVC.Interfaces
{
    public interface INavigationService
    {
        void NavigateToDashboard();
        void NavigateToLogout();
        void NavigateBack();
        void gotoNews();
        void goEvent();
        void gotoKGCash();
        void gotoCardCommnunity();
        void gotoSetting();
        void gotoNearbyLocation();
    }
}

my navigationservice

namespace KGVC.Services
{
    public class NavigationService : INavigationService
    {
        public  void goEvent()
        {
            var currentPage = GetCurrentPage();

             currentPage.Navigation.PushAsync(new EventPage());
        }

        public  void gotoCardCommnunity()
        {
            var currentPage = GetCurrentPage();

             currentPage.Navigation.PushAsync(new CardCommunityPage());
        }

        public  void gotoKGCash()
        {
            var currentPage = GetCurrentPage();

             currentPage.Navigation.PushAsync(new KGCashPage());
        }

        public  void gotoNearbyLocation()
        {
            var currentPage = GetCurrentPage();

             currentPage.Navigation.PushAsync(new StoreMaps());
        }

        public  void gotoNews()
        {
            var currentPage = GetCurrentPage();

             currentPage.Navigation.PushAsync(new RssFeedView());
        }

        public   void gotoSetting()
        {
            var currentPage = GetCurrentPage();

             currentPage.Navigation.PushAsync(new SettingPages());
        }

        public void NavigateBack()
        {
            throw new NotImplementedException();
        }

        public  void NavigateToDashboard()
        {

            var currentPage = GetCurrentPage();

             Application.Current.MainPage = new MainPage();
        }

        public void NavigateToLogout()
        {
            var currentPage = GetCurrentPage();
            Application.Current.MainPage = new NewPageLogin();
        }

        private Page GetCurrentPage()
        {
            var currentPage = Application.Current.MainPage.Navigation.NavigationStack.LastOrDefault();

            return currentPage;
        }
    }
}

my view model

public class GridMenuViewModel
    {


        public ICommand gotoNews { get; private set; }
        public ICommand goEvent { get; private set; }
        public ICommand gotoKGCash { get; private set; }
        public ICommand gotoSetting { get; private set; }
        public ICommand gotoNearbyLocation { get; private set; }
        public GridMenuViewModel()
        {
            gotoNews = new Command(() =>
            {
                var navigationService = new NavigationService();

                navigationService.gotoNews();
            });
            goEvent = new Command(() =>
            {
                var navigationService = new NavigationService();

                navigationService.goEvent();
            });

            gotoKGCash = new Command(() =>
            {
                var navigationService = new NavigationService();

                navigationService.gotoKGCash();
            });
            gotoSetting = new Command(() =>
            {
                var navigationService = new NavigationService();

                navigationService.gotoSetting();
            });
            gotoNearbyLocation = new Command(() =>
            {
                var navigationService = new NavigationService();

                navigationService.gotoNearbyLocation();
            });
        }
    }

And my view

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             NavigationPage.HasNavigationBar="True"
             NavigationPage.BackButtonTitle="False"
               BindingContext="{Binding GridMenuViewModel, Source={StaticResource Locator}}"
             xmlns:control="clr-namespace:Xamarin.Forms;assembly=Xamarin.Forms.CarouselView"
             xmlns:local="clr-namespace:KGVC.Views.MainPages"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="KGVC.Views.MainPages.GridMenu"
             Title="HOME"
            >


    <ContentPage.Content>
        <StackLayout >
            <StackLayout  BackgroundColor="##CEB053">
                <StackLayout  HeightRequest="35" BackgroundColor="##CEB053" Orientation="Horizontal">
                    <Label  FontAttributes="Italic" TextColor="Black" Margin="9" Text="Hello," FontSize="15"/>
                    <Label  TextColor="Black"  Margin="9" Text="John Doe" FontSize="15" FontAttributes="Bold"/>

                </StackLayout>

                <StackLayout  Padding="0"  HeightRequest="30" BackgroundColor="#E3E6E3" Orientation="Horizontal">
                    <Image Margin="5" Source="ic_logo.png"/>
                    <Label  Text="Points" Margin="5" FontSize="13" TextColor="Black"/>
                </StackLayout>

            </StackLayout>


            <StackLayout  HeightRequest="220" VerticalOptions="StartAndExpand">

                <control:CarouselView HeightRequest="185" ItemsSource="{Binding MyDataSource}" Position="{Binding Position, Mode=TwoWay}">
                    <control:CarouselView.ItemTemplate>
                        <DataTemplate>
                            <Image  Source="{Binding Image}"/>

                        </DataTemplate>
                    </control:CarouselView.ItemTemplate>
                </control:CarouselView>
                <local:CarouselIndicators IndicatorHeight="9" IndicatorWidth="9" UnselectedIndicator="unselected_circle.png" SelectedIndicator="selected_circle.png" Position="{Binding Position}" ItemsSource="{Binding MyDataSource}" />
            </StackLayout>

            <ScrollView IsClippedToBounds="True" VerticalOptions="StartAndExpand" Orientation="Vertical" >

                    <Grid x:Name="controlGrid" VerticalOptions="StartAndExpand" HeightRequest="370" Margin="15">

                        <Grid.RowDefinitions>
                            <RowDefinition Height="0" />
                            <RowDefinition Height="100" />
                            <RowDefinition Height="100" />
                            <RowDefinition Height="100" />

                        </Grid.RowDefinitions>
                        <Grid.ColumnDefinitions>

                            <ColumnDefinition Width="110" />
                            <ColumnDefinition Width="110" />
                            <ColumnDefinition Width="110" />

                        </Grid.ColumnDefinitions>

                        <StackLayout Orientation="Vertical"  Grid.Row="1" Grid.Column="0" >
                            <Button Image="ic_account.png" Margin="5" 
        Style="{StaticResource plainButton}" />
                            <Label FontSize="12" Text="MY ACCOUNT" HorizontalOptions="Center"/>
                        </StackLayout>

                        <StackLayout Orientation="Vertical" Grid.Row="1" Grid.Column="1">
                        <Button Margin="5" Command="{Binding gotoCardCommunity}" Image="ic_card.png" 
        Style="{StaticResource plainButton}" />
                            <Label FontSize="12" Text="CARD" HorizontalOptions="Center"/>
                        </StackLayout>

                        <StackLayout Orientation="Vertical" Grid.Row="1" Grid.Column="2">
                        <Button Margin="5" Command="{Binding goEvent}" Image="ic_event" 
        Style="{StaticResource plainButton}" />
                            <Label FontSize="12" Text="PROMO" HorizontalOptions="Center"/>
                        </StackLayout>

                        <StackLayout Grid.Row="2" Grid.Column="0" Orientation="Vertical">
                        <Button Margin="5" Command="{Binding gotoNearbyLocation}" Image="ic_store" 
        Style="{StaticResource plainButton}" />
                            <Label FontSize="12" Text="STORE LOCATIONS" HorizontalOptions="Center"/>
                        </StackLayout>

                        <StackLayout Orientation="Vertical" Grid.Row="2" Grid.Column="1">
                        <Button Margin="5" Command="{Binding gotoNews}"  Image="ic_news" 
        Style="{StaticResource plainButton}" />
                            <Label FontSize="12" Text="NEWS" HorizontalOptions="Center"/>

                        </StackLayout>


                        <StackLayout Grid.Row="2" Grid.Column="2" Orientation="Vertical">
                        <Button Margin="5"  Command="{Binding gotoKGCash}" Image="ic_kgcash" 
        Style="{StaticResource plainButton}" />
                            <Label FontSize="12" Text="E-WALLET" HorizontalOptions="Center"/>
                        </StackLayout>

                        <StackLayout Orientation="Vertical" Grid.Row="3" Grid.Column="0">
                            <Button Margin="5"  Image="ic_ecommerce" 
        Style="{StaticResource plainButton}" />
                            <Label FontSize="12" Text="E-COMMERCE" HorizontalOptions="Center"/>
                        </StackLayout>

                        <StackLayout Orientation="Vertical" Grid.Row="3" Grid.Column="1">
                        <Button Margin="5"  Command="{Binding gotoSetting}" Image="ic_pointsummary.png" 
        Style="{StaticResource plainButton}" />
                            <Label FontSize="12" Text="POINT SUMMARY" HorizontalOptions="Center"/>
                        </StackLayout>


                        <StackLayout Orientation="Vertical" Grid.Row="3" Grid.Column="2">
                        <Button Margin="5"  Command="{Binding gotoSetting}" Image="ic_setting" 
        Style="{StaticResource plainButton}" />
                            <Label FontSize="12" Text="SETTINGS" HorizontalOptions="Center"/>
                        </StackLayout>



                    </Grid>






            </ScrollView>



        </StackLayout>


    </ContentPage.Content>
</ContentPage>

i already try change the navigation service to use pushmodalasync, make the navigation async but still crash, Anyone can help me ? and i'm not using prism , mvvmlight and other 3rd party mvvm nuget

Upvotes: 1

Views: 1833

Answers (1)

EvZ
EvZ

Reputation: 12179

It depends on your MainPage. Did you check if GetCurrentPage() actually returns something?

Try to change the GetCurrentPage() to:

Page GetCurrentPage() =>
  Application.Current.MainPage.Navigation?.NavigationStack?.LastOrDefault() ??
  Application.Current.MainPage;

Another problems that I spotted:
1. C# naming conventions are different from Java. Method names should start with a capital letter. Currently you have a pure mix.
2. Initialising the BindingContext in XAML the way you did will create the ViewModel twice due to a bug. Change it to:

<ContentPage
  xmlns:vm="your-namespace.vm"
  ...>
  <ContentPage.BindingContext>
    <vm:GridMenuViewModel />
  </ContentPage.BindingContext>
</ContentPage>

3. No need to recreate the NavigationService for each command. Reuse it in your ViewModel.
4. You have some corrupted XAML:

<StackLayout  BackgroundColor="##CEB053">

Fix the hex code. Generally speaking you could enable XAMLC so you would get notified about XAML errors while building your code.
5. In order to use PushAsync your MainPage should be wrapped by NavigationPage.
6. You need to await async code execution:

public async Task GoToCardCommnunity()
{
  var currentPage = GetCurrentPage();
  await currentPage.Navigation.PushAsync(new CardCommunityPage());
}

Check over the list above and pay attention to the application output and exceptions.

Upvotes: 1

Related Questions