在Xamarin.Forms中,如何将同一个viewmodel的变化通知回上一页? (可以传递到第二页,但不能返回)

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)

我有两个页面,"HomePage","SettingPage",包括相同的 "MyView"(那里有一些选择器)。 当我从主页单击 "Go Setting"(或显示更多设置)按钮时,值会同步到设置页面。但是当我在设置页面上点击"Apply"时,数值没有返回。

我是 c# 和 Xamarin 的新手,尝试在线搜索和 Microsoft 文档。但是我找不到解决这个问题的方法。

我也在关注这个link:How to set BindingContext of multiple pages to the same ViewModel in Xamarin.Forms? 并在我的代码中做了相同的全局值。

  1. 我的视图(内容视图)
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. 首页(包括MyView)
public SearchPage ()
{
    InitializeComponent ();
    BindingContext = GlobalVar.MyViewModel;
}

private async void Click_GoSetting(object sender, EventArgs e)
{
    await Navigation.PushAsync(new SettingPage());
}
  1. SettingPage(包括同一个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. 视图模型
    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>();
        }
    }

也许我应该将 SettingPage 上的更改通知给 viewmodel?或者在 viewmodel 的 "set" 中做一些事情?

令人困惑的一点是,两个页面使用相同的视图模型嵌入相同的视图,但仅通知从Page1到Page2的更改,而不是Page2到Page1的更改。

任何想法,提前谢谢。

方案一:

使用 Event 可以将值传回上一页。

在第二页定义事件:

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

页面消失时调用事件:

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

在FirstPage中,当Naviagtion地方需要在此处添加Event时:

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

现在值将传递到这里:

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

方案二:

使用Properties Dictionary在应用程序中存储简单且体积小的数据,当进入页面时将调用它来获取已存储的数据。

在Second Page你要存放数据的地方,写成如下:

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

返回第一页时,覆盖 OnAppearing 方法以更新 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
    }
}

注意:ViewModel如果要动态更新数据,需要用到INotifyPropertyChanged.

示例实现:

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 建议将 ICommand 实现为字典结构,例如:

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

    public ViewModelBase()
    {
        Commands = new Dictionary<string,ICommand>();
    }
}

因此,您的 ViewModel 中的所有待办事项都只是继承 ViewModelBase class 并使用它:

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
}

已解决。

MyViewModel(更新)

    public class MyViewModel : BaseViewModel
    {
        public ObservableCollection<obj> ObList1 { get; set; }
        public ObservableCollection<obj> ObList2 { get; set; }
        public ObservableCollection<obj> ObList3 { get; set; }

        private obj _selectedItem1 = new obj();
        public obj SelectedItem1 
        {
            get { return _selectedItem1; }

            //this is the line solved the problem
            //but still not understood thoroughly
            set { SetProperty(ref _selectedItem1, value); }
        }

        //same for _selectedItem2 _selectedItem3

    }

ps: BaseViewModel 代码在这里(未更改,来自模板代码)

 public class BaseViewModel : INotifyPropertyChanged
    {
        //some other attributes
        //...

        protected bool SetProperty<T>(ref T backingStore, T value,
            [CallerMemberName]string propertyName = "",
            Action onChanged = null)
        {
            if (EqualityComparer<T>.Default.Equals(backingStore, value))
                return false;

            backingStore = value;
            onChanged?.Invoke();
            OnPropertyChanged(propertyName);
            return true;
        }

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

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

好像通过调用SetProperty,OnPropertyChanged也会被撤销

但对于为什么以前的代码像某种 "one-way" 绑定一样,仍然有点困惑。