0

Working in a personal project learning xamarin, got stucked in opening a detailed page about a selected item which I got from an API. The API, it returns an array like this :

{
  "Restaurant": [
 {
   "Address": "Route Bounes Aires",
   "RestaurantID": "1",
   "RestaurantName": "John Doe",
   "City": "Lorem Ipsum"
 }
 {
   "Address": "Route Bounes Aires",
   "RestaurantID": "2",
   "RestaurantName": "John Doe",
   "City": "Lorem Ipsum"
 }]

I managed to bind these informations in a list view using the MVVM pattern. Now I can't seem to open a detailed page for the selcted item. This is what I have so far: restaurantviewmodel.cs

public class RestaurantViewModel : INotifyPropertyChanged
{
    Service _services = new Service();
    List<Restaurant> _restaurant;
    public List<Restaurant> Restaurant
    {
        get { return _restaurant; }
        set
        {
            if (value == _restaurant) return;
            _branches = value;
            OnPropertyChanged();
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;

    public ICommand ReastaurantCommand
    {
        get
        {
            return new Command(async () =>
            {
                Reastaurant = await _apiServices.GetReastaurant();
                await Application.Current.MainPage.Navigation.PushAsync(new ReastaurantPage(_restaurant));
            });
        }
    }        
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

The service.cs

public async Task<List<Reastaurant>> GetReastaurant()
    {

        ListReastaurant restaurant = null;
        try { 
            var client = new HttpClient();
            client.DefaultRequestHeaders.Add("xxx", "xxx");
            client.DefaultRequestHeaders.Add("xxx", xxx);

            HttpContent content = new StringContent("");
            content.Headers.ContentType = new MediaTypeHeaderValue("application/json");

            var response = await client.PostAsync("https:www.here/goes/the/call/to/the/api", content);
            response.EnsureSuccessStatusCode();
            string json = await response.Content.ReadAsStringAsync();
            restaurant = JsonConvert.DeserializeObject<ListReataurantDetails>(json);
        }
        catch (Exception ex)
        {
            Debug.WriteLine(ex.Message.ToString());
        }
        return restaurant.Restaurant;
    }

The model restaurant.cs

public class Restaurant
{
    public string Address { get; set; }
    public string restaurantID { get; set; }
    public string RestaurantName  { get; set; }
    public string City  { get; set; }
}

The page restaurant.xaml :

<ListView x:Name="restaurantlistview"
              HasUnevenRows="True" ItemSelected="restaurantlistview_ItemSelected">
        <ListView.ItemTemplate>
            <DataTemplate>
                <ViewCell>
                    <StackLayout Padding="20, 10">
                        <Label  Text="{Binding RestaurantName}"
                                 FontSize="20"
                    </StackLayout>
                </ViewCell>
            </DataTemplate>
        </ListView.ItemTemplate>
    </ListView>
</StackLayout>

The code behind restaurant.xaml.cs

[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Restaurant : ContentPage
{
    public Restaurant(List<Models.Restaurant> restaurant)
    {
        InitializeComponent();
        NavigationPage.SetTitleIcon(this, "icon.png");
        restaurantlistview.ItemsSource = restaurant;
    }
     private async void restaurantlistview_ItemSelected(object sender, SelectedItemChangedEventArgs e)
    {
        await Navigation.PushAsync(new RestaurantSinglePageDetails());
    }
}

How can I approach to this problem? I want to use the details of one restaurant to another page so I can show the address and the city and use these information to do different things. I think it's pretty easy I just didn't grasp well the concept of the mvvm pattern. To clarify I'm not trying to pass all the data to another page, but just trying to access the information about a single item(restaurant). I would really need some help. Thanks guys!

===edit===

public partial class RestaurantSinglePageDetails: ContentPage
{
  public RestaurantSinglePageDetails(Models.Restaurant res)
    {
        InitializeComponent();
        NavigationPage.SetTitleIcon(this, "logosmall.png");
        BindingContext = new RestaurantDetailsViewModel(res);
       //and here I'm supposed to have access to my selected restaurant.
    }
}

restaurantdetailsdviewmodel.cs

public class RestaurantDetailsViewModel : INotifyPropertyChanged
{
    // ==edit==
   Restaurant restaurant;
   public RestaurantDetailsViewModel(Restaurant restaurant)
   {
    this.restaurant = restaurant; // now we can use it in ViewModel
   }
   Service _services = new Service();
   List<Info> _info;
   public List<Info> Info
    {
        get { return _info; }
        set
        {
            if (value == _info) return;
            _info = value; 
            OnPropertyChanged();
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    public ICommand GetInfoCommand
    {
        get
        {
            return new Command(async () =>
            {
                InfoData = await _apiServices.GetInfoData();
                await Application.Current.MainPage.Navigation.PushAsync(new SingleDetailsAboutPrice(InfoData, restaurant));
            });
        }
    }
}

I would like to use the RestaurantID here : SingleDetailsAboutPrice.xaml.cs:

 Restaurant restaurant;
 public SingleDetailsAboutPrice(List<Models.InfoData> data, Restaurant restaurant)
    {
        InitializeComponent();
        this.restaurant = restaurant;
        //can't use the restaurantid here; 
        //some code goes here;
    }

The error

The given key was not present in the dictionary

John
  • 78
  • 2
  • 19
  • Possible duplicate of [Passing data between view in xamarin forms using mvvm](https://stackoverflow.com/questions/47873276/passing-data-between-view-in-xamarin-forms-using-mvvm) – EvZ Jan 03 '18 at 09:48
  • Where do you have Constructor in your Restaurant class ? – qubuss Jan 03 '18 at 09:55
  • Trying to pass data for a single item, not about all item in another page. Do I need another json? Or do I need another service? @evz How can I pass the parameters? – John Jan 03 '18 at 09:57
  • @qubuss I don't have one. Should I create it? – John Jan 03 '18 at 10:14
  • @John yes. Look at answer – qubuss Jan 03 '18 at 10:22

1 Answers1

1

In your contentPage Restaurant class you should

InitializeComponent();

in the constructor class

XamlCompilation(XamlCompilationOptions.Compile)]
public partial class Restaurant : ContentPage
{
    public Restaurant(List<Models.Restaurant> restaurant)
    {
        InitializeComponent();
        NavigationPage.SetTitleIcon(this, "icon.png");
        restaurantlistview.ItemsSource = restaurant;
    }
    private async void restaurantlistview_ItemSelected(object sender,   SelectedItemChangedEventArgs e)
    {
    //edit
    var restaurant = (Restaurant)sender;
    await Navigation.PushAsync(new RestaurantSinglePageDetails(restaurant));
    }
}

to get item in what you selected:

var restaurant = (Restaurant)sender;

and next you have to create new Page

public partial class RestaurantSinglePageDetails: ContentPage
{
    Restaurant res;
    public RestaurantSinglePageDetails(Restaurant res)
    {
        InitializeComponent();
        this.res = res; 

        //and here you have access to your selected restaurant. 
    }
}

To the res you have access from all class. So you can put this res when you move to another page.

===EDIT===

If I mean correctly, you want to pass RestaurantID to SingleDetailsAboutPrice so you have to pass it to RestaurantDetailsViewModeland then if you click on button put it to SingleDetailsAboutPrice(RestaurantId).

public partial class RestaurantSinglePageDetails: ContentPage
{
    Restaurant res;
    public RestaurantSinglePageDetails(Restaurant res)
    {
        InitializeComponent();
        BindingContext = new RestaurantDetailsViewModel(item); //now you have access to restaurant in your viewModel. In this way you don't need use BindingContext in XAML
        this.res = res; 


        //and here you have access to your selected restaurant. 
    }
}

And now in the RestaurantDetailsViewModel we need to create the constructor with Restaurant

public class RestaurantDetailsViewModel : INotifyPropertyChanged
{
    Service _services = new Service();
    Restaurant restaurant;
    public RestaurantDetailsViewModel(Restaurant restaurant)
    {
        this.restaurant = restaurant; // now we can use it in ViewModel
    }
    List<Info> _info;
    public List<Info> Info
    {
        get { return _info; }
        set
        {
            if (value == _info) return;
            _info = value; 
             OnPropertyChanged();
        }
    }
    public event PropertyChangedEventHandler PropertyChanged;
    public ICommand GetInfoCommand
    {
        get
        {
            return new Command(async () =>
            {
                InfoData = await _apiServices.GetInfoData();
                await  Application.Current.MainPage.Navigation.PushAsync(new  SingleDetailsAboutPrice(restaurant)); // or if you want u can pass only restaurant.restaurantID.
            });
        }
    }
}

And in SingleDetailsAboutPrice we create constructor with Restaurant or only RestaurantId

public partial class Restaurant : ContentPage
{
    Restaurant restaurant;
    public SingleDetailsAboutPrice(Restaurant restaurant) 
    {
        InitializeComponent();
        this.restaurant = restaurant;
    }

    String restaurantID;
    // if you want only restaurantID
    public SingleDetailsAboutPrice(String restaurantID) 
    {
        InitializeComponent();
        this.restaurantID = restaurantID;
    }
}
qubuss
  • 268
  • 3
  • 12
  • yeah thanks, I already figured out. Anyway, is this a good approach to the problem?! Is there any other way to solve it? Thanks @qubuss – John Jan 03 '18 at 11:24
  • I think it is good, maybe there is other way but i don't know. :P – qubuss Jan 03 '18 at 11:30
  • @GeraldVersluis why ? If we make RestaurantSinglePageDetails with MVVM and we will pass restaurant to view model it should be. And ok you have right because we should move item selected to viewModel class and make binding context. And list's item source have to be in modelView. – qubuss Jan 03 '18 at 12:06
  • You’ve answered your own comment ;) no code-behind code should be involved – Gerald Versluis Jan 03 '18 at 12:09
  • well thank you anyway for the help. One more thing if you don't mind asking. What if I want to use this information yet in another page? For example from `RestaurantSinglePageDetails` to another page. @qubuss – John Jan 03 '18 at 14:01
  • You can make global var with restaurant and pass it when you want to go to another page. In the same way like here – qubuss Jan 03 '18 at 14:06
  • I don't want to make it global. I just want to pass the data I have in the `RestaurantSinglePageDetails` use it in another page on which I navigate from `RestaurantSinglePageDetails`. Is there any other way? – John Jan 03 '18 at 14:10
  • The thing is that I navigate from `RestaurantSinglePageDetails ` to `SingleDetailsAboutPrice` using a button in which I bind a command in the view of `RestaurantSinglePageDetails`. See the `ICommand ReastaurantCommand` in `ResturantViewModel`. Maybe I don't fully understand :/ – John Jan 03 '18 at 14:48
  • @John ok i see your code but I don't understand what do you want to do or what is a problem ? :D – qubuss Jan 03 '18 at 16:31
  • I want to use the `RestaurantID` in the `SingleDetailsAboutPrice`. How can I do this? Maybe I'm trying a totally wrong approach. I have access to the `RestaurantID` in the `RestaurantSinglePageDetails`. I also want to use it in `SingleDetailsAboutPrice`. Do you understand?! @qubuss – John Jan 03 '18 at 18:16
  • @qubuss so can you help me? – John Jan 03 '18 at 23:00
  • When you go to the SingleDetailsAboutPrice you should pass item not list if i mean right. And you will have access to one element and to the RestaurantID. To pass only one item you have pass element from RestaurantSinglePageDetails to viewModel and then use it in command. I will try today create answer. If not i will make it tomorrow – qubuss Jan 03 '18 at 23:05
  • Yes you are right. That's basically what I'm trying to do. Ok, can't wait for your answer :D. Meanwhile I will try solve it myself. @qubuss – John Jan 03 '18 at 23:24
  • @qubuss I tried the following but I had an error `The given key was not present in the dictionary` which comes from this line `BindingContext = new RestaurantDetailsViewModel(item);` I think you can't pass parameters in `RestaurantDetailsViewModel`. ??? – John Jan 04 '18 at 09:19
  • no, I can pass. I make in the same way in my work. Please paste all error and your code – qubuss Jan 04 '18 at 09:22
  • Check the edit @qubuss . I did exactly what you did. I referred to this question : https://stackoverflow.com/questions/44308294/given-key-not-present-in-dictionary-xamarin-forms that's why I said maybe can't pass parameters in `RestaurantDetailsViewModel` . :/ – John Jan 04 '18 at 09:45
  • I know I'm boring you I'm just in a little bit of hurry. :/ @qubuss – John Jan 04 '18 at 09:48
  • And show me your xaml code RestaurantSinglePageDetails – qubuss Jan 04 '18 at 09:52
  • Never mind I fixed it. I forgot to remove `binding.context` on `SingleDetailsAboutPrice` in xaml code. I only removed it in the `RestaurantDetailsViewModel` xaml. So thanks again and again and again @qubuss. Your help was enormous. Happy coding. :))) – John Jan 04 '18 at 10:03