MvvmCross - 视图模型 mvxInteraction 始终为 null

MvvmCross - View Model mvxInteraction is always null

我是 MvvmCross 和 Xamarin 的新手,也许我在这里遗漏了一些东西,但是当我尝试将一些数据从 ViewModel 传递到 View 时,就像这里描述的那样(几乎)

https://www.mvvmcross.com/documentation/fundamentals/mvxinteraction

但问题是 - 我在 View 中的 MvxInteraction 变量始终为 null,即使我向那里传递了字符串!

   public IMvxInteraction<MenuViewModel.YesNoQuestion> Interaction
        {
            get
            {
                return _interaction;
            }
            set
            {
                if (_interaction != null) // <-- Always NULL!
                    _interaction.Requested -= OnInteractionRequested;

                _interaction = value;
                _interaction.Requested += OnInteractionRequested;
            }
        }

视图模型代码:

  public class MenuViewModel : MvxViewModel
    {
        private IDataScanner _dataScanner { get; set; }

        //demo
        public class YesNoQuestion
        {
            public string Question { get; set; }
        }

        private MvxInteraction<YesNoQuestion> _interaction = new MvxInteraction<YesNoQuestion>();
        // need to expose it as a public property for binding (only IMvxInteraction is needed in the view)
        public IMvxInteraction<YesNoQuestion> Interaction
        {
            get
            {
                return _interaction;
            }
        }


        private void DoFinishProfileCommand()
        {
            // 1. do cool stuff with profile data
            // ...

            // 2. request interaction from view
            // 3. execution continues in callbacks
            var request = new YesNoQuestion
            {
                Question = "Do you want to save your profile?"
            };

            _interaction.Raise(request);
        }
        // demo

        public class DataEventArgs : EventArgs
        {
            public List<string> DataList;
        }

        public delegate void ScanDoneEvent(object sender, DataEventArgs args);
        public event ScanDoneEvent ScanDone;

        protected virtual void OnScanDone(List<string> dataList)
        {
            ScanDone?.Invoke(this, new DataEventArgs { DataList = dataList });
        }

        public MenuViewModel(IDataScanner dataScanner)
        {
            _dataScanner = dataScanner;

            RunScan();
        }

        private ObservableCollection<string> _filesCollection;
        public ObservableCollection<string> FilesCollection
        {
            get { return _filesCollection; }
            set
            {
                _filesCollection = value;
                RaisePropertyChanged(() => FilesCollection);
            }
        }

        private async void RunScan()
        {
            var files = await _dataScanner.GetDataListAsync().ConfigureAwait(false);
            FilesCollection = new ObservableCollection<string>(files);
            DoFinishProfileCommand();
        }

    }

查看代码:

  [Activity]
    public class MenuView : MvxActivity
    {
        public MenuView()
        {
            var set = this.CreateBindingSet<MenuView, MenuViewModel>();
            set.Bind(this).For(view => view.Interaction).To(viewModel => viewModel.Interaction).OneWay();
            set.Apply();
        }
        protected override void OnViewModelSet()
        {
            SetContentView(Resource.Layout.menu_view);
        }

        //demo
        private IMvxInteraction<MenuViewModel.YesNoQuestion> _interaction;
        public IMvxInteraction<MenuViewModel.YesNoQuestion> Interaction
        {
            get
            {
                return _interaction;
            }
            set
            {
                if (_interaction != null)// <-- Always NULL!
                    _interaction.Requested -= OnInteractionRequested;

                _interaction = value;
                _interaction.Requested += OnInteractionRequested;
            }
        }

        private async void OnInteractionRequested(object sender, MvxValueEventArgs<MenuViewModel.YesNoQuestion> eventArgs)
        {
            var yesNoQuestion = eventArgs.Value.Question;
            // show dialog
            Toast.MakeText(this, yesNoQuestion, ToastLength.Long).Show();
        }


    }

UPD 09.11.2017

终于让它按照我想要的方式工作了。

我只是简单地使用服务而不是 MvxInteraction,正如 nmilcoff(顺便说一句,非常感谢您的回答)所建议的那样,当我从它调用 toast 时,将其包装在 runOnUiThread 中。

所以,最终代码在这里。

吐司服务:

public class ToastService : IToastService
    {
        private readonly IMvxAndroidCurrentTopActivity _topActivity;

        public ToastService(IMvxAndroidCurrentTopActivity topActivity)
        {
            _topActivity = topActivity;
        }

        public void ShowToast(string message)
        {
            _topActivity.Activity.RunOnUiThread(
                () => Toast.MakeText(_topActivity.Activity.ApplicationContext, message, ToastLength.Long).Show()
            );
        }
    }

菜单视图模型:

 public class MenuViewModel : MvxViewModel
    {
        private IDataScanner _dataScanner { get; set; }
        private IToastService _toastService { get; set; }

        public MenuViewModel(IDataScanner dataScanner, IToastService toastService)
        {
            _dataScanner = dataScanner;
            _toastService = toastService;
        }

        public override void ViewAppeared()
        {
            base.ViewAppeared();
            RunScan();
        }

        private ObservableCollection<string> _filesCollection;
        public ObservableCollection<string> FilesCollection
        {
            get { return _filesCollection; }
            set
            {
                _filesCollection = value;
               // RaisePropertyChanged(() => FilesCollection);

            }
        }

        private async void RunScan()
        {
            var files = await _dataScanner.GetDataListAsync().ConfigureAwait(false);
            FilesCollection = new ObservableCollection<string>(files);
            //  Someval = files[0];
           _toastService.ShowToast(files[0]);
        }

    }

您可能会找到 StarWarsSample interesting, as it uses MvxInteraction

如您在该应用中所见,您需要稍后在视图代码中声明绑定。您可以将 Fluent Binding 块移动到 Activity 的 OnCreate(Android.OS.Bundle bundle) 方法吗?像这样:

protected override void OnCreate(Android.OS.Bundle bundle)
{
    base.OnCreate(bundle);

    var set = this.CreateBindingSet<MenuView, MenuViewModel>();
    set.Bind(this).For(view => view.Interaction).To(viewModel => viewModel.Interaction).OneWay();
    set.Apply();
}

但这可能行不通,因为您在代码中过早地请求了 MvxInteraction。因此,我建议您改为使用依赖服务:

1) 在 Core 级别创建一个界面,使用显示 toast 的方法:

public interface IToastService
{
    void ShowToast(string message);
}

2) 在平台级别创建服务的实现:

public class ToastService : IToastService
{
    private readonly IMvxAndroidCurrentTopActivity _topActivity;

    public ToastService(IMvxAndroidCurrentTopActivity topActivity)
    {
        _topActivity = topActivity;
    }

    public void ShowToast(string message)
    {
        Toast.MakeText(activity.ApplicationContext, message, ToastLength.Short).Show();
    }
}

3) 向 IoC 容器注册您的服务。像这样的东西(在你的平台项目的设置 class 中):

protected override void InitializeLastChance()
{
        base.InitializeLastChance();

        Mvx.RegisterType<IToastService, ToastService>();
}

就是这样。您已准备好 inject/resolve 您的 IToastService 随处可用!