0

So far I can pass the value to the other view but the problem is I don't know how to do this using MVVM. I tried the documentations and tutorial still no luck. How can I achieve this?

The flow of my project:
- The user will login, when the user provides the correct it will return a JSON array that contains the ContactID of the user.
- This ContactID now be pass to the other view. It will be used to synchronize the server to the local database and vice versa

My Questions are:
1. How can I pass the data to other view with MVVM?
2. How can I check if the data is passed correctly?

The Output of the HTTPWebRequest:

[{"ContactID":"1"}]

My Code:

LoginPageViewModel.cs

using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Net;
using System.Text;
using System.Windows.Input;
using TBSMobileApplication.Data;
using TBSMobileApplication.View;
using Xamarin.Essentials;
using Xamarin.Forms;

namespace TBSMobileApplication.ViewModel
{
    public class LoginPageViewModel : INotifyPropertyChanged
    {
        void OnPropertyChanged(string PropertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
        }

        public string username;
        public string password;

        public string Username
        {
            get { return username; }
            set
            {
                username = value;
                OnPropertyChanged(nameof(Username));
            }
        }

        public string Password
        {
            get { return password; }
            set
            {
                password = value;
                OnPropertyChanged(nameof(Password));
            }
        }

        public class LoggedInUser
        {
            public int ContactID { get; set; }
        }

        public ICommand LoginCommand { get; set; }

        public LoginPageViewModel()
        {
            LoginCommand = new Command(OnLogin);
        }

        public void OnLogin()
        {
            if (string.IsNullOrEmpty(Username) || string.IsNullOrEmpty(Password))
            {
                MessagingCenter.Send(this, "Login Alert", Username);
            }
            else
            {
                var current = Connectivity.NetworkAccess;

                if (current == NetworkAccess.Internet)
                {
                    var link = "http://192.168.1.25:7777/TBS/test.php?User=" + Username + "&Password=" + Password;
                    var request = HttpWebRequest.Create(string.Format(@link));
                    request.ContentType = "application/json";
                    request.Method = "GET";

                    using (HttpWebResponse response = request.GetResponse() as HttpWebResponse)
                    {
                        if (response.StatusCode != HttpStatusCode.OK)
                        {
                            Console.Out.WriteLine("Error fetching data. Server returned status code: {0}", response.StatusCode);
                        }
                        else
                        {
                            using (StreamReader reader = new StreamReader(response.GetResponseStream()))
                            {
                                var content = reader.ReadToEnd();

                                if (content.Equals("[]") || string.IsNullOrWhiteSpace(content) || string.IsNullOrEmpty(content))
                                {
                                    MessagingCenter.Send(this, "Http", Username);
                                }
                                else
                                {
                                    var result = JsonConvert.DeserializeObject<List<LoggedInUser>>(content);
                                    var contactId = result[0].ContactID;
                                    Application.Current.MainPage.Navigation.PushAsync(new DatabaseSyncPage(contactId), true);
                                }

                            }
                        }
                    }
                }
                else
                {
                    MessagingCenter.Send(this, "Not Connected", Username);
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
    }
}

DatabaseSyncPage.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace TBSMobileApplication.View
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class DatabaseSyncPage : ContentPage
    {
        public DatabaseSyncPage (int contanctId)
        {
            InitializeComponent ();
        }
    }
}
maricris
  • 11
  • 3
  • 1
    Possible duplicate of [Xamarin MVVM passing data to other view](https://stackoverflow.com/questions/51533628/xamarin-mvvm-passing-data-to-other-view) – mvermef Jul 27 '18 at 04:35
  • 1
    @mvermef I just want an answer how to get the passed data in mvvm – maricris Jul 27 '18 at 04:50

2 Answers2

3

If you are new to MVVM i would highly recommend using an MVVM helper framework such as Prism, MVVMCross or MVVMLight (there are even more).

I myself use Prism, I believe all of the frameworks are functionally very similar and it comes down more to preference here. I will show you how I pass data between views in my Prism based applications. Before we get started it would be worth to download the prism visual studio extensions and use the template pack to generate a prism project. I use the DryIoc container.

Imagine the scenario where we have ViewA (with ViewAViewModel) and ViewB (with ViewBViewModel). In View A we have an Entry and a Button, when the button is pressed the text from the entry in ViewA is passed to ViewB where it is displayed in a label.

You would first setup your prism project, creating a XAML fronted view for View A & B and then creating 2 class files and creating the relevant View Models (I'll show you how).

Firstly creating the following files:

  • ViewA (Xaml content page)
  • ViewB (Xaml content page)
  • ViewAViewModel (empty class)
  • ViewBViewModel (empty class)

In your app.cs register the views and view models:

//You must register these views with prism otherwise your app will crash!
protected override void RegisterTypes(IContainerRegistry containerRegistry)
{
    containerRegistry.RegisterForNavigation<NavigationPage>();
    containerRegistry.RegisterForNavigation<ViewA, ViewAViewModel>();
    containerRegistry.RegisterForNavigation<ViewB, ViewBViewModel>();
}

Now format your view models by adding the following:

public class ViewAViewModel : ViewModelBase
{
    INavigationService _navigationService;

    public ViewAViewModel(INavigationService navigationService) : base(navigationService)
    {
        Title = "ViewA";

        _navigationService = navigationService;
    }
}

Repeat the above step for ViewBViewModel also (changing the relevant names).

Now in the views xaml lets add some stuff! Add the following to ViewA.xaml (inside <ContentPage.Content></ContentPage.Content>:

<StackLayout>
    <Entry Placeholder="Type Here..." Text="{Binding ViewAText}"/>
    <Button Text="Navigate" Command="{Binding OnNavigateCommand}"/>
</StackLayout>

and in ViewB.xaml:

`<Label Text="{Binding TextFromViewA}"/>`

Now I've already added the binding for you, so lets make the properties!

In View Model A add:

private string _viewAText;
public string ViewAText
{
    get { return _viewAText; }
    set { SetProperty(ref _viewAText, value); }
}

public DelegateCommand OnNavigateCommand { get; set; }

private void OnNavigate()
{
    //Do Something
}

Now we have a bindable property and a command for our button press, add the following to the constructor:

public ViewAViewModel(INavigationService navigationService) : base(navigationService)
{
    Title = "ViewA";

    _navigationService = navigationService;

    _viewAText = string.Empty;

    OnNavigateCommand = new DelegateCommand(OnNavigate);
}

Now View A can bind text from the entry control and has an event handler for the command!

Lets hop into View B and wire that up!

Add the property:

private string _textFromViewA;
public string TextFromViewA
{
    get { return _textFromViewA; }
    set { SetProperty(ref _textFromViewA, value); }
}

and in the constructor:

public ViewBViewModel(INavigationService navigationService) : base(navigationService)
{
    Title = "ViewB";

    TextFromViewA = string.Empty;
}

Now the label we added in ViewB is hooked up to the view model. Lets now pass the text from the entry in A to B!

Back in View A add the following to the OnNavigate method:

private void OnNavigate()
{
    NavigationParameters navParams = new NavigationParameters();
    navParams.Add("PassedValue", _viewAText);
    _navigationService.NavigateAsync("ViewB", navParams);
}

The navigation service is incredibly powerful and allows you to pass a dictionary between views (NavigationParameters). In this code we have created some NavigationParameter, added the value of the text in our entry to them and then asked the navigationService (which handles all navigation from viewmodels in Prism) to navigate to ViewB, passing the parameters to it.

In View B we can listen for these parameters using some built in methods provided by Prism. If you type override in ViewBViewModel you will see the methods:

  • OnNavigatingTo
  • OnNavigatedTo
  • OnNavigatedFrom

In this case we want to use OnNavigatingTo (which is fired during the transition between the views). Pull that method in and the following:

public override void OnNavigatingTo(NavigationParameters parameters)
{
    base.OnNavigatingTo(parameters);

    if (parameters.ContainsKey("PassedValue"))
    {
        _textFromViewA = (string)parameters["PassedValue"];

        RaisePropertyChanged("TextFromViewA");
    }
}

Here we check if the parameters contain the value we added (by searching for the dictionary key) and then retrieve the value (casting it to a string since the dictionary is ). We then set the property the label is bound to = to the passed value and then use a prism method, RaisePropertyChanged() to raise a property changed event so that the label's binded value updates!

Below is a gif of the results!

Gif of the results from this answer!

This might be alot to take in. I would advise you start using an MVVM framework asap, they are really easy to use and I would consider them essential to making testable, decoupled MVVM xamarin apps!

For more on how prism works, I'd suggest to go read the docs and watch Brian Lagunas' appearance on the Xamarin Show!

Good Luck!

Axemasta
  • 763
  • 1
  • 9
  • 24
  • I can get the data however, I want to store that into a variable in order to get the data of the specific user and store it to my local database I just dont know to to do it in MVVM format – maricris Jul 27 '18 at 11:13
  • Create an interface for your database access class, create an interface aswell. In your viewmodel get the interface and initialise it (prism uses DI so thats done automatically through the ctor). Then your database layer is accessable through your view model and has all the MVVM benefits (it is abstracted and can be mocked in tests) – Axemasta Jul 27 '18 at 12:53
  • Can you help me getting started with a code please? It's be 5 days and I am still stuck with this please help – maricris Jul 27 '18 at 15:27
  • please help I am stuck with this problem for 5 days now – maricris Jul 28 '18 at 02:50
  • So instead you want to know how to use a sqlite database in xamarin, and how to access it from a view model? you should first edit the question to reflect this, then I will provide you with an answer! – Axemasta Jul 29 '18 at 16:40
  • I just want to know how to get the passed data using mvvm I am not asking you to use sqlite database. I just want to get the data in MVVM Format that's it that's what I need – maricris Jul 30 '18 at 00:31
  • @maricris The answer i posted details and demonstrates exactly how to pass data around using MVVM paradigms – Axemasta Jul 30 '18 at 08:52
  • Sorry sir, but I need to pattern it with my code otherwise I really dont understand how it works. I am just following tutorials I watched on youtube and documentations I pieced them all together then I stumbled upon this problem – maricris Jul 30 '18 at 10:13
  • I just need sir to pass the data and put it in a variable with mvvm format after that I am set – maricris Jul 30 '18 at 10:15
  • I really need help – maricris Jul 30 '18 at 16:20
  • try https://stackoverflow.com/questions/47873276/passing-data-between-view-in-xamarin-forms-using-mvvm. It would work for now althought it breaks MVVM in my opinion (since you are now tethered to a specific navigation pattern, but it should work in your case. But please start using an MVVM helper such as prism or MVVMCross, it makes life SO MUCH easier I can't stress this enough! – Axemasta Jul 30 '18 at 16:24
  • last question where do I put this? public partial class IdeasSinglePage : ContentPage { public IdeasSinglePage(List ideas) { InitializeComponent(); listViewName.ItemsSource = ideas; } } – maricris Jul 31 '18 at 00:15
  • In other viewmodel or the xaml.cs of another view – maricris Jul 31 '18 at 00:16
  • any code directly interacting with the view should go in xaml.cs. – Axemasta Jul 31 '18 at 13:29
  • @Axemasta How would you go about getting the content from view A if you dont use that string in view A but the data is fetched in view A. Do I need to link the public string in viewmodel to the model? – KalleP Nov 27 '18 at 19:02
  • @KalleP the public string in View A is the binded property, when this value updates in teh viewmodel, the binding will cause the label's text property to update. If you fetch data in view a and then want to use it in view b, you need to pass that data from viewmodel a to viewmodel b and bind a label in view b to that string. – Axemasta Nov 28 '18 at 20:12
  • I tried doing that. See my post: https://stackoverflow.com/questions/53520601/pass-data-parameter-from-model-to-next-page-prism-xamarin-forms/53522720?noredirect=1#comment93916274_53522720 I put the string from my model in the navParam but didnt work. Its null. @Axemasta – KalleP Nov 28 '18 at 21:15
0

i had implemented the same and hope this helps you.

i have create a loginViewModel

  public class LoginVerificationVM : BaseViewModel // INotifyPropertyChanged
{
   private INavigation _navigation;
   private string usermobileno;
   public string UserMobileNo     
 { get { return usermobileno; }set  { usermobileno = value;
           OnPropertyChanged("UserMobileNo");     }

   }

 public LoginVerificationVM(INavigation navigation, string mobileno)
    {
        UserMobileNo = mobileno;
        _navigation = navigation;
    }

 public Command Login
    {
        get
        {
            return new Command(async () =>
            {
                bool status = await WebApi.CheckNetWorkStatus();
                if (status == false)
                {
                    MessageClass.messagesWindow("Check Ur Connectivity");
                    this.Isvisible = false;

                    return;
                }
                Isvisible = true;
                UserAuth ud = new UserAuth();
                ud.username = UserMobileNo;  // UserMobileNo;
                ud.password = Password;     // Password
                ud.grant_type = "password";  //GrantType
                Isvisible = true;
                //  IsBusy = false;
                    await Task.Delay(100);
                var json = Task.Run(() => WebApi.GetUserAuth(ud)).Result;
                //  IsBusy = false;
                if (json.ErrorMessage == "true")
                {
                    Application.Current.MainPage = new MasterPages.MasterPage(json.access_token);  //or use   _navigation.PushAsync(new ForgotPasswordOTP(UserMobileNo));
                }
                else
                {
                    MessageClass.messagesWindow(json.ErrorMessage);
                }
                Isvisible = false;
            });
        }
    }

}

Xaml Code

   <Entry x:Name="PasswordEntry" Grid.Row="2" IsPassword="True" Placeholder="******" HorizontalTextAlignment="Center" FontAttributes="Bold" TextColor="Black"  WidthRequest="150" HeightRequest="35" FontSize="13" Text="{Binding Password, Mode=TwoWay}" >

 <Button x:Name="Login" Grid.Row="3" HorizontalOptions="Center" BorderRadius="8"  Text="Login" WidthRequest="100" BackgroundColor="#f7941d" TextColor="White" Command="{Binding Login}" IsEnabled="{Binding Active,Mode=TwoWay}">

here is implementation to get data on navigated page view model

  public ForgotPasswordOTP(string Ph)
    {
        InitializeComponent();

        BindingContext = new ForgotPasswordOTPViewModel(this.Navigation,Ph);
    }

and the last thing you need to do is bind your view with your viewmodel

** BindingContext = new LoginVerificationVM(this.Navigation);**

And the answer for the last question is you need to deserialize json in c# which can be done in following way

   var userData = JsonConvert.DeserializeObject<YourObject>(result);
Ronak Shetiya
  • 960
  • 6
  • 27