Side Menu - Xamarin Forms

Asked

Viewed 447 times

0

I am creating a project in Xamarin.Forms who implements a side menu (Master/Detail). So far I managed to display the menu correctly and list some items in it, the idea is to click on one of these menu items open another page and here is where this my problem.

How I’m using model MVVM, mine View is making Binding with a ViewModel, when I select one of the menu items generates a exception I can’t seem to solve.

I’ll show you the code to make it clearer:

View:

<StackLayout>
    <ListView x:Name="listaEmpresas" 
              ItemsSource="{Binding ListaMenu}" 
              SelectedItem="{Binding ItemSelecionado}">
        <ListView.Header>
            <StackLayout BackgroundColor="Gray" 
                         WidthRequest="100"  
                         HeightRequest="40">
                <Label Text="Menu de Navegação" 
                       TextColor="White" FontSize="18"
                       VerticalOptions="CenterAndExpand" 
                       HorizontalOptions="Center"/>
            </StackLayout>
        </ListView.Header>
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <ViewCell.View>
                        <StackLayout>
                            <Label Text="{Binding Nome}" 
                                   FontSize="15"
                                   VerticalOptions="CenterAndExpand"
                                   HorizontalOptions="CenterAndExpand"/>
                        </StackLayout>
                    </ViewCell.View>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</StackLayout>

Code Behind:

public partial class MasterView : ContentPage
{
    public MasterViewModel ViewModel { get; set; }

    public MasterView (ItensMenu menu)
    {
        InitializeComponent ();
        this.ViewModel = new MasterViewModel();
        this.BindingContext = this.ViewModel;
     }

    protected async override void OnAppearing()
    {
        base.OnAppearing();

        MessagingCenter.Subscribe<ItensMenu>(this, "ItemSelecionadoMenu",
            (msg) =>
            {
                Navigation.PushAsync(new VeiculoView());
                App.MasterDetail.Detail.Navigation.PushAsync(new VeiculoView());
            }); 
    }

Viewmodel:

public class MasterViewModel : BaseViewModel
{
    public string teste { set; get; }
    public List<ItensMenu> ListaMenu { get; set; }

    public MasterViewModel()
    {
        this.teste = "Teste";
        this.ListaMenu = new List<ItensMenu>
        {
            new ItensMenu {Nome = "Creditos", Id = "1" },
            new ItensMenu {Nome = "Editar Perfil", Id = "2"},
            new ItensMenu {Nome = "Veiculos", Id = "3"},
            new ItensMenu {Nome = "Historico", Id = "4"},
            new ItensMenu {Nome = "Alertas", Id = "5"}
        };
    }

    private ItensMenu itemSelecionado;

    //Pegar o valor do item Selcionado do Menu
    public ItensMenu ItemSelecionado
    {
        get
        {
            return itemSelecionado;
        }
        set
        {
            itemSelecionado = value;

            if (value != null)
            {
                MessagingCenter.Send<ItensMenu>(itemSelecionado, "ItemSelecionadoMenu");
            }
        }
    }
}

When I click on one of the items the following Exception occurs:

Unhandled Exception:

System.Reflection.Targetinvocationexception: Exception has been thrown by the target of an Invocation. occurred

One detail is that when I change the code Navigation.Async by a DisplayAlert in Codebehind it works normal.

  • I’m not sure, but I believe that if you put one try/catch in the code block that is inside the anonymous delegate being passed on MessagingCenter can help. I believe I should be avenging a thread access vialation. Have you tried writing the PushAsync within a block Device.BeginInvokeOnMainThread? I also noticed that you are pushing twice... is that right? It was intentional?

  • Hello Diego, I forgot to edit, the Pushasync is only once, I left there another method I was trying. I’ll try the way you indicated and come back with the answer. Thank you.

  • @Diegorafaelsouza, I put the Try/catch block inside the delegate and the problem is there, but I can’t verify what it is. Could you give me an example of how to use the Device block.Begininvokeonmainthread? I tried to find out how it does, but I did not succeed.

  • Clear-cut. See this example on dotnetfiddle. Usually you need to put this guy when Oce is doing an intervention on the UI thread from a background thread. Since your method is asymptomatic, and you have this anonymous delegate, that may be the case. If it is not, please edit your question including more details of the error you are experiencing from what you find on catch you added to the test method.

1 answer

1

I’ll leave some code snippets for you to try replicating in your application, in case you have any questions I’m available.

--------- Masterdetailview ------------ View

Note: Change the code snippets I tell you.

Swap x:Class for your class name.

Swap xmlns:pages for the path name of your Views.

Switch pages:Menulateralview to your View name for the side menu.

Exchange pages:Checkinmenuview for the name of your View that will be the home page of your application.

<?xml version="1.0" encoding="utf-8" ?>
<MasterDetailPage xmlns="http://xamarin.com/schemas/2014/forms"
                  xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
                  x:Class="YesShowroom.Views.MasterDetailView"
                  xmlns:pages="clr-namespace:YesShowroom.Views">
<MasterDetailPage.Master>
    <pages:MenuLateralView x:Name="MasterPage" />
</MasterDetailPage.Master>
<MasterDetailPage.Detail>
    <NavigationPage>
        <x:Arguments>
            <pages:CheckInMenuView />
        </x:Arguments>
    </NavigationPage>
</MasterDetailPage.Detail>

--------- Masterdetailview ------------ Code Behind

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MasterDetailView : MasterDetailPage
{
    public MasterDetailView()
    {
        InitializeComponent();

        MasterPage.ListView.ItemSelected += ListView_ItemSelected;
        NavigationPage.SetHasNavigationBar(this, false);
    }

    private void ListView_ItemSelected(object sender, SelectedItemChangedEventArgs e)
    {
        var item = e.SelectedItem as MenuItemModel;

        if (item == null)
            return;

        var page = (Page)Activator.CreateInstance(item.TargetType);
        page.Title = item.Title;

        Detail = new NavigationPage(page);
        IsPresented = false;

        MasterPage.ListView.SelectedItem = null;
    }
}

--------- Menuitemmodel class ------------

public class MenuItemModel
{
    public MenuItemModel()
    {
        TargetType = typeof(MasterDetailViewDetail);
    }

    public int Id { get; set; }
    public string Title { get; set; }

    public Type TargetType { get; set; }
}

--------- Masterdetailviewdetail ----------- View

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="YesShowroom.Views.MasterDetailViewDetail"
             Title="Detail">
  <StackLayout Padding="10">
    <Label Text="Detail"/>
  </StackLayout>
</ContentPage>

--------- Masterdetailviewdetail ----------- Code Behind

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MasterDetailViewDetail : ContentPage
{
    public MasterDetailViewDetail()
    {
        InitializeComponent();
    }
}

--------- Menulateralview ------------- View

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="YesShowroom.Views.MenuLateralView"
             Title="Master">
  <StackLayout>
    <ListView x:Name="MenuItemsListView"
              SeparatorVisibility="None"
              HasUnevenRows="true"
              ItemsSource="{Binding MenuItems}">
      <ListView.Header>
        <Grid BackgroundColor="#16335a">
          <Grid.ColumnDefinitions>
            <ColumnDefinition Width="10"/>
            <ColumnDefinition Width="*"/>
            <ColumnDefinition Width="10"/>
          </Grid.ColumnDefinitions>

         <Grid.RowDefinitions>
            <RowDefinition Height="30"/>
            <RowDefinition Height="80"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="10"/>
         </Grid.RowDefinitions>

          <Label Grid.Column="1"
                 Grid.Row="2"
                 Text="Yes Showroom"
                 TextColor="White"
                 FontSize="28"/>
         </Grid>
      </ListView.Header>
      <ListView.ItemTemplate>
        <DataTemplate>
          <ViewCell>
            <StackLayout Padding="15,10" HorizontalOptions="FillAndExpand">
              <Label VerticalOptions="FillAndExpand" 
                     VerticalTextAlignment="Center" 
                     Text="{Binding Title}" 
                     FontSize="24"/>
            </StackLayout>
          </ViewCell>
        </DataTemplate>
      </ListView.ItemTemplate>
    </ListView>
  </StackLayout>
</ContentPage>

--------- Menulateralview ------------ Code Behind

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class MenuLateralView : ContentPage
{
    public ListView ListView;

    public MenuLateralView()
    {
        InitializeComponent();

        BindingContext = new MenuLateralViewModel();
        ListView = MenuItemsListView;
    }

    class MenuLateralViewModel : INotifyPropertyChanged
    {
        public ObservableCollection<MenuItemModel> MenuItems { get; set; }

        public MenuLateralViewModel()
        {
            MenuItems = new ObservableCollection<MenuItemModel>(new[]
            {
                new MenuItemModel { Id = 0, Title = "Check In", TargetType = typeof(CheckInMenuView) },
                new MenuItemModel { Id = 1, Title = "Status", TargetType = typeof(StatusView) },
                new MenuItemModel { Id = 3, Title = "Relatório", TargetType = typeof(RelatorioView) },
                new MenuItemModel { Id = 2, Title = "Sair", TargetType = typeof(LoginView) }
            });
        }

        #region INotifyPropertyChanged Implementation
        public event PropertyChangedEventHandler PropertyChanged;
        void OnPropertyChanged([CallerMemberName] string propertyName = "")
        {
            if (PropertyChanged == null)
                return;

            PropertyChanged.Invoke(this, new PropertyChangedEventArgs(propertyName));
        }
        #endregion
    }
}

Browser other questions tagged

You are not signed in. Login or sign up in order to post.