简体   繁体   中英

How to set bindings for Start/StopTrackingTouch for Seekbar using MvvmCross

I got sort of a typical music player window, music plays and seekbar point is moving while it plays.

I've done it using default mvvmcross binding to the property (which is changed through the EventHandler binding) like here:

 <SeekBar
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:id="@+id/playprogress"
      local:MvxBind="Progress ValueOfTimer"
        />

So now I want the user to be able to move it forward and back.

I've been trying to bind it like this:

 public class PlayWindowView : MvxActivity
    {

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

            var set = this.CreateBindingSet<PlayWindowView, PlayWindowViewModel>();

            SeekBar seek = FindViewById<SeekBar>(Resource.Id.playprogress);

            set.Bind(seek).For("Max").To(viewModel => viewModel.MaxTimerValue);
            set.Bind(seek).For("StopTrackingTouch").To(viewModel => viewModel.PlayProgressChanged);
            set.Apply();
        }

        protected override void OnViewModelSet()
        {
            SetContentView(Resource.Layout.playwindow_view);
        } 
     }

Viewmodel part looks like this:

public class PlayWindowViewModel : MvxViewModel<ListMenuItemDto>
{

    private long _valueOfTimer;

    public long ValueOfTimer
    {
        get { return _valueOfTimer; }

        set
        {
            _valueOfTimer = value;
            RaisePropertyChanged(() => ValueOfTimer);
        }
    }

    //...

    public MvxAsyncCommand<long> PlayProgressChanged
    {
        get { return new MvxAsyncCommand<long>(OnPlayProgressChange);}
    }

    private async Task OnPlayProgressChange(long progr)
    {
        await _playingService.SetTime((int) progr).ConfigureAwait(false);
    }
}

But looks like it's not working. I mean, it's not even getting into OnPlayProgressChange . But on view is appearing it goes into command PlayProgressChanged one time.

How can I bind this event (and such kind of events like StartTrackingTouch , StopTrackingTouch ) to the function correctly?

PS just FYI I using MvvmCross 5


UPD 28.11.2017

Tried custom binding and even Progress binding stoped working now. So, xaml looks like this now:

  <SeekBar
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:id="@+id/playprogress"
      local:MvxBind="Progress ValueOfTimer, OnStopTrackingTouch PlayProgressChanged"
        />

And binder code is here

public class SeekbarStopTrackingTouchEventBinding: MvxAndroidTargetBinding
{
    private readonly SeekBar _seekbar;
    private IMvxAsyncCommand _command;

    public SeekbarStopTrackingTouchEventBinding(SeekBar seekbar) : base(seekbar)
    {
        _seekbar = seekbar;
        _seekbar.StopTrackingTouch += ViewOnStopTrackingTouch;
    }

    private void ViewOnStopTrackingTouch(object sender, SeekBar.StopTrackingTouchEventArgs e)
    {
        if (_command != null)
        {
            _command.Execute(e);
        }
    }

    public override Type TargetType
    {
        get { return typeof (IMvxAsyncCommand); } 
    }

    protected override void SetValueImpl(object target, object value)
    {
        _command = (IMvxAsyncCommand)value;
    }


    protected override void Dispose(bool isDisposing)
    {
        if (isDisposing)
        {
            _seekbar.StopTrackingTouch -= ViewOnStopTrackingTouch;
        }
        base.Dispose(isDisposing);
    }

    public override MvxBindingMode DefaultMode
    {
        get { return MvxBindingMode.OneWay; }
    }
}

}

In Setup:

 protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
    {
        base.FillTargetFactories(registry);

        registry.RegisterFactory(new MvxCustomBindingFactory<SeekBar>("OnStopTrackingTouch", (seekbar) => new SeekbarStopTrackingTouchEventBinding(seekbar)));
    }

UPD2

Changed binding like this: local:MvxBind="Progress ValueOfTimer; OnStopTrackingTouch PlayProgressChanged" (notice ; here) and event fires now, yes!

But now the thing is - in binder _command is stays null even after SetValueImpl fired and _command = (IMvxAsyncCommand)value; is fine, value points to setted ViewModel property.

在此处输入图片说明

How come?

UPD3

Found out it can't cast object to IMvxAsyncCommand .

I fixed that by changing IMvxAsyncCommand _command to IMvxAsyncCommand<SeekBar.StopTrackingTouchEventArgs> _command ;

Will sum everything up in the answer.

But now I got the question - whats best practice in this case?

So, I don't know if it's a good way, but for now its working and I'm kinda happy with it.

Hope this would help somebody like me.

Custom Bindings approach is the key here. Really useful stuff is here:

In MvvmCross how do I do custom bind properties

MvvmCross Custom Event Binding Event Args

MVVMCross Bindings in Android

So, in my case, to make app listen to SeekBar OnStopTrackingTouch event I done this:

Created binding class:

public class SeekbarStopTrackingTouchEventBinding: MvxAndroidTargetBinding
    {
        private readonly SeekBar _seekbar;
        private IMvxAsyncCommand<SeekBar.StopTrackingTouchEventArgs> _command;
        private string  testString;

        public SeekbarStopTrackingTouchEventBinding(SeekBar seekbar) : base(seekbar)
        {
            _seekbar = seekbar;
            _seekbar.StopTrackingTouch += ViewOnStopTrackingTouch;
        }

        private void ViewOnStopTrackingTouch(object sender, SeekBar.StopTrackingTouchEventArgs e)
        {
            if (_command != null)
            {
                _command.Execute(e);
            }
        }

        public override Type TargetType
        {
            get { return typeof (IMvxAsyncCommand); } 
        }

        protected override void SetValueImpl(object target, object value)
        {
            try
            {
                _command = (IMvxAsyncCommand<SeekBar.StopTrackingTouchEventArgs>)value;
            }
            catch (Exception e)
            {
                Log.Error("SOME BINDER FAIL\n\t" + e.Message + "\n", "SOME BINDER FAIL\n\t" + e.Message + "\n");
                throw;
            }
        }


        protected override void Dispose(bool isDisposing)
        {
            if (isDisposing)
            {
                _seekbar.StopTrackingTouch -= ViewOnStopTrackingTouch;
            }
            base.Dispose(isDisposing);
        }

        public override MvxBindingMode DefaultMode
        {
            get { return MvxBindingMode.OneWay; }
        }
    }

In Setup.cs placed this code:

protected override void FillTargetFactories(IMvxTargetBindingFactoryRegistry registry)
{
    base.FillTargetFactories(registry);

    registry.RegisterFactory(new MvxCustomBindingFactory<SeekBar>("OnStopTrackingTouch", (seekbar) => new SeekbarStopTrackingTouchEventBinding(seekbar)));
}

Prepared property in my ViewModel and command executing function:

    public IMvxAsyncCommand<SeekBar.StopTrackingTouchEventArgs> PlayProgressChanged
    {
        get
        {
            return new MvxAsyncCommand<SeekBar.StopTrackingTouchEventArgs>(OnPlayProgressChange);
        }
    }

    private async Task OnPlayProgressChange(SeekBar.StopTrackingTouchEventArgs e)
    {
        var progr = e.SeekBar.Progress;
        await _playingService.SetTime((int) progr).ConfigureAwait(false);
    }

In view layout, inside local:MvxBind linked my ViewModel command with evend name, provided in registry.RegisterFactory in Setup.cs

  <SeekBar
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:id="@+id/playprogress"
      local:MvxBind="Progress ValueOfTimer; OnStopTrackingTouch PlayProgressChanged"
        />

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