在Xamarin.Forms中,如何将同一视图模型的更改通知回上一页? (可以转到第二页,但不能返回第二页)

时间:2019-05-03 02:13:45

标签: c# mvvm xamarin.forms viewmodel

我有两个页面,“主页”,“设置页面”,包括相同的“ MyView”(那里有一些选择器)。 当我单击主页上的“转到设置”(或显示更多设置)按钮时,这些值将同步到设置页面。但是,当我在设置页面上单击“应用”时,这些值没有恢复。

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

我也在关注此链接:How to set BindingContext of multiple pages to the same ViewModel in Xamarin.Forms? 并在我的代码中执行了相同的全局值。

  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. 主页(包括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. 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>();
        }
    }

也许我应该将我的SettingPage上的更改通知给viewmodel?或在viewmodel的“设置”中做些什么?

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

任何想法,请提前

2 个答案:

答案 0 :(得分:0)

解决方案一:

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

在SecondPage中定义事件:

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

页面消失时调用事件:

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

在FirstPage中,当Naviagtion位置需要在此处添加事件时:

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在应用程序中存储简单和小型数据,在进入页面时将调用它以获取已存储的数据。

在第二页上您要存储数据的地方,如下所示:

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
    }
}

注意:如果要动态更新数据,则需要使用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实现为Dictionary结构,例如:

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

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

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

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
}

答案 1 :(得分:0)

已解决。

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也将被撤销。

但是对于以前的代码为什么会像“单向”绑定那样仍然有些困惑。