Search code examples
c#mvvmxamarin.formsviewmodel

In Xamarin.Forms, how to notify the changes of the same viewmodel back to the previous page? (can pass to the second page, but not back)


I got two pages, "HomePage", "SettingPage", including the same "MyView" (some Pickers there). When I click "Go Setting"(or show more settings) Button from Homepage, the values syncs to the setting page. But When I click "Apply" on the setting page, the values did not come back.

I am new in c# and Xamarin and tried to search online and Microsoft docs. But I couldn't find a way to fix this issue.

Also I was following this link: How to set BindingContext of multiple pages to the same ViewModel in Xamarin.Forms? and did the same global value in my code.

  1. MyView (ContentView)
public MyView()
{
    InitializeComponent();
    BindingContext = GlobalVar.MyViewModel;

    Setting1.SetBinding(Picker.ItemsSourceProperty, "ObList1");
    Setting1.ItemDisplayBinding = new Binding("obj_text");
    Setting1.SetBinding(Picker.SelectedItemProperty, "SelectedItem1");
    //also other pickers
}
  1. HomePage (including the MyView)
public SearchPage ()
{
    InitializeComponent ();
    BindingContext = GlobalVar.MyViewModel;
}

private async void Click_GoSetting(object sender, EventArgs e)
{
    await Navigation.PushAsync(new SettingPage());
}
  1. SettingPage (including the same MyView)
public partial class SettingPage : ContentPage
{
  MyViewModel viewModel { get; set; } = GlobalVar.MyViewModel;

  public SettingPage ()
  {
    BindingContext = viewModel;
  }

  private async void Click_ApplySetting(object sender, EventArgs e)
  {
    await Navigation.PopAsync(true);
  }

  //some other method deal with viewModel
}
  1. GLobalVar.cs
        private static  MyViewModel _myViewModel = new MyrViewModel();
        public static MyViewModel MyViewModel
        {
            get
            {
                return _myViewModel;
            }
        }
  1. ViewModel
    public class MyViewModel : BaseViewModel
    {
        public ObservableCollection<obj> ObList1 { get; set; }
        public ObservableCollection<obj> ObList2 { get; set; }
        public ObservableCollection<obj> ObList3 { get; set; }
        public obj SelectedItem1 { get; set; }
        public obj SelectedItem2 { get; set; }
        public obj SelectedItem3 { get; set; }

        public MyViewModel()
        {
            ObList1 = new ObservableCollection<obj>();
            ObList2 = new ObservableCollection<obj>();
            ObList3 = new ObservableCollection<obj>();
        }
    }

Maybe I should notify the changes on my SettingPage to viewmodel? or do something in the "set" in viewmodel?

The confusing point is that two pages embed the same view using the same viewmodel, but notify the change from Page1 to Page2 only, not Page2 to Page1.

Any ideas, thx in advance.


Solution

  • Solution One:

    Using Event can pass value back to Previous Page.

    Define Event in SecondPage :

    public delegate void EventHandler(string status);
    public event EventHandler EventPass;
    

    Invoke Event when Page disappear:

    protected override void OnDisappearing()
    {
        base.OnDisappearing();
        EventPass("Back Code");
    }
    

    In FirstPage, when Naviagtion place need to add the Event here:

    string title = "PageSecondParamater";
    PageSecond pageSecond = new PageSecond(title);
    pageSecond.EventPass += PageSecond_EventPass; ;
    Navigation.PushAsync(pageSecond);
    

    Now value will be passed here:

    private void PageSecond_EventPass(string status)
    {
        Title = status;
        Console.WriteLine("---" + status);
    }
    

    Solution Two:

    Using Properties Dictionary to store easy and small size data in Application, when enter in page will invoke it to get data from which has been stored.

    In Second Page Where you want to store data, writing as bellow:

    Application.Current.Properties ["value"] = valuedata;
    

    When back to First Page, override OnAppearing method to update UI:

    protected override void OnAppearing()
    {
        base.OnAppearing();
        if (Application.Current.Properties.ContainsKey("value"))
        {
            var ValueGet = Application.Current.Properties ["value"] as DataType;
            // do something with other things
        }
    }
    

    Note: ViewModel if want to dynamic update data , need to use INotifyPropertyChanged .

    Sample Implementation:

    public class ObservableProperty : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
    
        protected void OnPropertyChanged(string propertyName)
        {
            var handler = PropertyChanged;
            if (handler != null)
                handler(this, new PropertyChangedEventArgs(propertyName));
        }
    }
    

    ViewModelBase suggest implementing ICommand as a Dictionary structure like:

    public abstract class ViewModelBase : ObservableProperty
    {
        public Dictionary<string,ICommand> Commands { get; protected set; }
    
        public ViewModelBase()
        {
            Commands = new Dictionary<string,ICommand>();
        }
    }
    

    So all todo in your ViewModel is just inherit the ViewModelBase class and use it:

    class LoginViewModel : ViewModelBase
    {
        string userName;
        string password;
    
        public string UserName 
        {
             get {return userName;}
            set 
            {
                userName = value;
                OnPropertyChanged("UserName");
            }
         }
    
        public string Password 
        {
            get{return password;}
            set
            {
                password = value;
                OnPropertyChanged("Password");
            }
        }
        #endregion
    
        #region ctor
        public LoginViewModel()
        {
            //Add Commands
            Commands.Add("Login", new Command(CmdLogin));
        }
        #endregion
    
    
        #region UI methods
    
        private void CmdLogin()
        {
            // do your login jobs here
        }
        #endregion
    }