简体   繁体   中英

Two way binding with MVVM Cross

I am stuck with a problem where I am checking network connectivity in an iOS app and trying to binding a boolean hasNetworkConnection in my view controller for it's view model.

View controller UpdateContentView.cs

// This file has been autogenerated from a class added in the UI designer.

using System;
using MvvmCross.iOS.Views;
using MvvmCross.Binding.BindingContext;
using Training.Core;

namespace EdwardsTraining.IOS
{
    public partial class UpdateContentView : MvxViewController
    {
        public UpdateContentView(IntPtr handle) : base(handle)
        {
        }



        public override void ViewDidLoad()
        {
            base.ViewDidLoad();



            var bindingSet = this.CreateBindingSet<UpdateContentView, UpdateContentViewModel>();
            bindingSet.Bind(NoConnectionView).For(x => x.Hidden).To(vm => vm.HasConnection).WithConversion("ReverseBoolean");
            bindingSet.Bind(UpdateInProgressView).For(x => x.Hidden).To(vm => vm.InProgress).WithConversion("ReverseBoolean");
            bindingSet.Bind(UpdateAvailableView).For(x => x.Hidden).To(vm => vm.HasContentUpdate).WithConversion("ReverseBoolean");
            bindingSet.Bind(CancelButton).For(x => x.Hidden).To(vm => vm.CancelVisible).WithConversion("ReverseBoolean");
            bindingSet.Bind(RetryButton).To(vm => vm.DoRetryUpdate);
            bindingSet.Bind(ConfirmButton).To(vm => vm.DoUpdate);
            //bindingSet.Bind(iOSNetworkConnectivitiy).For(x => x.HasNetworkConnection).To(vm => vm.NetworkConnectivitiy).TwoWay()
                      //.For(vm => vm.HasNetworkConnection);

            bindingSet.Bind(iOSNetworkConnectivitiy).To(vm => vm.NetworkConnectivitiy).TwoWay();
            bindingSet.Apply();

            _iOSnetworkConnectivity = new NetworkConnectivity()
            {
                HasNetworkConnection = Reachability.IsNetworkAvailable()
            };
        }
        private NetworkConnectivity _iOSnetworkConnectivity { get; set; }
        public NetworkConnectivity iOSNetworkConnectivitiy {
            get{return _iOSnetworkConnectivity;}
            set { _iOSnetworkConnectivity = value;
            }
        }
    }
}

I would like to check for connectivity using my iOS specific code and bind the boolean returned to aa public view model property.

View Model

using System;
using System.Threading.Tasks;
using EdwardsTraining.BusinessLayer.Interfaces.Services;
using MvvmCross.Core.ViewModels;
using MvvmCross.Platform;


namespace Training.Core
{
    public class UpdateContentViewModel : BaseViewModel
    {

        private IApplicationContentService _applicationContentService;
        private ITrainingContentService _trainingContentService;

        public bool _isNetworkAvailable { get; set; }


        public UpdateContentViewModel(IApplicationContentService applicationContentService, ITrainingContentService trainingContentService)
        {
            _applicationContentService = applicationContentService ?? Mvx.Resolve<IApplicationContentService>();
            _trainingContentService = trainingContentService ?? Mvx.Resolve<ITrainingContentService>();
            IntialSetup();
        }
        protected void IntialSetup()
        {
            _cancelVisible = false;
            _hasContentUpdate = true;
            _inProgress = false;

        }

        public void SetNoConnection()
        {
            _cancelVisible = true;
            _hasContentUpdate = false;
            _inProgress = false;
        }

        public void SetInProgress()
        {
            _cancelVisible = false;
            HasContentUpdate = false;
            InProgress = true;
        }

        public void SetProgessComplete()
        {
            InProgress = false;
            Task.Run(async () => await FinishedUpdating());
        }

        public async Task UpdateContent()
        {
            if (_networkConnectivity.HasNetworkConnection)
            {
                SetInProgress();

                await _trainingContentService.UpdateTrainingContentAsync();
                await _applicationContentService.UpdateContent();

                SetProgessComplete();
                await FinishedUpdating();
            }
            return;
        }

        public async Task FinishedUpdating()
        {
            Close(this);
        }

        public MvxCommand DoUpdate
        {
            get { return new MvxCommand(async () => await UpdateContent()); }
        }

        public MvxCommand DoRetryUpdate
        {
            get { return new MvxCommand(async () => await UpdateContent()); }
        }

        public MvxCommand CancelUpdate
        {
            get { return new MvxCommand(async () => await FinishedUpdating()); }
        }


        private bool _hasContentUpdate;

        public bool HasContentUpdate
        {
            get { return _hasContentUpdate; }
            set
            {
                _hasContentUpdate = value;
                RaisePropertyChanged(() => HasContentUpdate);
            }
        }

        private bool _hasConnection;

        public bool HasConnection
        {
            get { return _hasConnection; }
            set
            {
                _hasConnection = value;
                RaisePropertyChanged(() => HasConnection);
            }
        }

        private bool _inProgress;

        public bool InProgress
        {
            get { return _inProgress; }
            set
            {
                _inProgress = value;
                RaisePropertyChanged(() => InProgress);
            }
        }

        private bool _cancelVisible;

        public bool CancelVisible
        {
            get { return _cancelVisible; }
            set
            {
                _cancelVisible = value;
                RaisePropertyChanged(() => CancelVisible);
            }
        }

        private NetworkConnectivity _networkConnectivity { get; set; }
        public NetworkConnectivity NetworkConnectivitiy
        {
            get { return _networkConnectivity; }
            set { 
                _networkConnectivity = value; 
                RaisePropertyChanged(() => NetworkConnectivitiy);
            }
        }
    }

    public class NetworkConnectivity
    {
        public bool HasNetworkConnection { get; set; }
    }
}

I have a problem with this line of code:

public async Task UpdateContent()
        {
            if (_networkConnectivity.HasNetworkConnection)
            {
                SetInProgress();

                await _trainingContentService.UpdateTrainingContentAsync();
                await _applicationContentService.UpdateContent();

                SetProgessComplete();
                await FinishedUpdating();
            }
            return;
        }

if (_networkConnectivity.HasNetworkConnection) is already null even though I set two way binding. I'm new to MVVM cross for this reason I don't know if my approach is correct.

Is there anyone who could provide some help? Nick

You need to explicitly tell the binding what property you want to bind on your NetworkConnectivity like:

bindingSet.Bind(iOSNetworkConnectivitiy).For(v => v.HasNetworkConnection).To(vm => vm.NetworkConnectivitiy).TwoWay();

However, the binding does not have any way to get notified that your NetworkConnectivity class has gotten any of its values updated. Hence you would have to extend that class to have some kind of event where it can get notified.

Then, you would have to write and register a Target Binding class.

Lets say you simply implement INotifyPropertyChanged in your NetworkConnectivity class:

public class NetworkConnectivity : MvxNotifyPropertyChanged
{
    private bool _hasNetworkConnection;
    public bool HasNetworkConnection { 
        get { return _hasNetworkConnection; }
        set { 
            _hasNetworkConnection = value;
            RaisePropertyChanged();
        }
    }
}

Then you create the following class in your iOS project:

public class NetworkConnectivityTargetBinding
    : MvxPropertyInfoTargetBinding<NetworkConnectivity>
{
    public NetworkConnectivityTargetBinding(object target, PropertyInfo targetPropertyInfo)
        : base(target, targetPropertyInfo)
    {
        var view = View;
        if (view == null)
        {
            MvxBindingTrace.Trace(MvxTraceLevel.Error, 
               "NetworkConnectivity is null in NetworkConnectivityTargetBinding");
        }
        else
        {
            view.PropertyChanged += HandleValueChanged;
        }
    }

    private void HandleValueChanged(object sender, System.EventArgs e)
    {
        var view = View;
        if (view == null)
            return;
        FireValueChanged(view.HasNetworkConnection);
    }

    public override MvxBindingMode DefaultMode => MvxBindingMode.TwoWay;

    protected override void Dispose(bool isDisposing)
    {
        if (isDisposing)
        {
            var view = View;
            if (view != null)
            {
                view.PropertyChanged -= HandleValueChanged;
            }
        }
        base.Dispose(isDisposing);
    }
}

Then in Setup.cs override FillTargetFactories:

protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
    registry.RegisterPropertyInfoBindingFactory(typeof(NetworkConnectivityTargetBinding),
                                                typeof(NetworkConnectivity), "HasNetworkConnection");

    base.FillTargetFactories(registry);
}

Now the TwoWay binding should work. You should also be able to remove the .For(v => v.HasNetworkConnection) from you binding expression.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM