MvvmCross 没有绑定

MvvmCross not binding

我正在使用 MvvmCross 开发 Pong。按住向上和向下按钮(android view/activity 中的按钮 - 不是键盘按钮)时,桨 Y 值会发生变化。然而,这并没有显示在视图中(桨停留在一个位置,即使我可以在控制台日志中看到桨已经上升或下降)。

为什么桨 Y 值没有正确绑定到视图?

代码如下:

视图模型:

using System.Linq;
using System.Text;
using System.Threading;

using Cirrious.MvvmCross.ViewModels;

using Pong.Core.ViewModels;
using Pong.Core.Models;

namespace Pong.Core.ViewModels
{
    public class GamePlayViewModel 
        : MvxViewModel
    {

        private string _hello = "Hello MvvmCross";
        public string Hello
        { 
            get { return _hello; }
            set { _hello = value; RaisePropertyChanged(() => Hello); }
        }

        private int _totalFramesBeenHad;
        public int TotalFramesBeenHad
        { 
            get { return _totalFramesBeenHad; }
            set { _totalFramesBeenHad = value; RaisePropertyChanged(() => TotalFramesBeenHad); }
        }



        private PlayerPaddle _paddle1;
        public int Paddle1
        { 
            get { return _paddle1.Y; }
            set { _paddle1.Y = value; RaisePropertyChanged(() => Paddle1); }
        }

        private ComputerPaddle _paddle2;
        public int Paddle2
        { 
            get { return _paddle2.Y; }
            set { _paddle2.Y = value; RaisePropertyChanged(() => Paddle2); }
        }



        protected StandardBall StandardBall;

        public GamePlayViewModel()
        {
            _paddle1 = new PlayerPaddle();
            _paddle2 = new ComputerPaddle();
            StandardBall = new StandardBall();

        }

        public void UpdatePaddle1()
        {
            switch (_paddle1.DetectWallCollision())
            {
            case "upper":
                _paddle1.UpperWallHit();
                break;
            case "lower":
                _paddle1.LowerWallHit();
                break;
            case "none":
                _paddle1.MoveOneFrame();
                break;
            }
        }

        public void UpdateBall()
        {
            if (StandardBall.DetectWallCollision()) StandardBall.HandleWallCollision();
            StandardBall.MoveOneFrame();
        }

        public void SetPaddleDirection(string direction)
        {
            _paddle1.SetDirection(direction);
        }

        public void StopPaddle()
        {
            _paddle1.StopMoving();
        }
    }
}

子视图模型(实际使用的一个):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

using Pong.Core.ViewModels;
using Pong.Core.Models;
using Pong.Droid.Views;

namespace Pong.Droid.ViewModels
{
    public class GamePlayViewModelAndroid : GamePlayViewModel
    {
        public readonly Timer _dispatcherTimer;

        public GamePlayView gpv;
        public GamePlayViewModelAndroid (GamePlayView gpv)
        {
            this.gpv = gpv;
            TimerCallback timerDelegate = new TimerCallback (Tick);
            _dispatcherTimer = new Timer (timerDelegate, null, 0, 1000/Court.FPS);
        }

        public void Tick(object state)
        {
            UpdatePaddle1();
            gpv.move ();
        }
    }
}

查看:

using Android.App;
using Android.OS;
using Cirrious.MvvmCross.Droid.Views;
using Cirrious;
using Cirrious.CrossCore;
using Cirrious.MvvmCross.Binding;
using Cirrious.MvvmCross.ViewModels;
using Pong.Droid.ViewModels;
using Android.Content.PM;
using Pong.Droid;
using Android.Views;
using Android.Widget;
using Android.Graphics;
using Android.Content;
using Android.Content.Res;
using Cirrious.MvvmCross.Binding.BindingContext;

using Pong.Core.Models;


namespace Pong.Droid.Views
{
    [Activity(Label = "!PONG!", ScreenOrientation = ScreenOrientation.Landscape)]
    public class GamePlayView : MvxActivity
    {
        private GamePlayViewModelAndroid _viewModel;
        private Button _buttonUp;
        private Button _buttonDown;
        public GameView GameView;
        public LinearLayout ParentLayout;
        public LinearLayout ButtonsLayout;
        public int _paddle1y;
        public int _paddle2y;

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

            GameView = new GameView (this);
            _viewModel = new GamePlayViewModelAndroid(this);
            SetUpControls ();
            SetUpButtonEvents ();
            SetContentView(ParentLayout);
            DataContext = _viewModel;


            this.ClearAllBindings();
            var set = this.CreateBindingSet<GamePlayView, GamePlayViewModelAndroid>();
            set.Bind(this).For(v => v._paddle1y).To(vm => vm.Paddle1);
            set.Bind(this).For(v => v._paddle2y).To(vm => vm.Paddle2);
            set.Apply();
        }

        void SetUpButtonEvents ()
        {
            _buttonUp.Touch += (s, e) =>  {
                var handled = false;
                if (e.Event.Action == MotionEventActions.Down) {
                    _viewModel.SetPaddleDirection ("up");
                    handled = true;
                }
                else
                    if (e.Event.Action == MotionEventActions.Up) {
                        _viewModel.StopPaddle ();
                        handled = true;
                    }
                e.Handled = handled;
            };
            _buttonDown.Touch += (s, e) =>  {
                var handled = false;
                if (e.Event.Action == MotionEventActions.Down) {
                    _viewModel.SetPaddleDirection ("down");
                    handled = true;
                }
                else
                    if (e.Event.Action == MotionEventActions.Up) {
                        _viewModel.StopPaddle ();
                        handled = true;
                    }
                e.Handled = handled;
            };
        }

        void SetUpControls ()
        {
            _buttonUp = new Button (this);
            _buttonDown = new Button (this);
            ParentLayout = new LinearLayout (this);
            ButtonsLayout = new LinearLayout (this);
            ParentLayout.Orientation = Android.Widget.Orientation.Horizontal;
            ButtonsLayout.Orientation = Android.Widget.Orientation.Vertical;
            ButtonsLayout.AddView (_buttonUp);
            ButtonsLayout.AddView (_buttonDown);
            ParentLayout.AddView (ButtonsLayout);
            ParentLayout.AddView (GameView);
        }

        public void move() {
            //GameView.paddle1y = _viewModel.Paddle1.Y;
            //GameView.paddle2y = _viewModel.Paddle2.Y;
            RunOnUiThread (() => GameView.Invalidate ());
        }
    }

    public class GameView : View {
        private Bitmap _paddleBmp;
        private int _paddle1x; 
        public int _paddle1y;
        private int _paddle2x; 
        public int _paddle2y;
        public GamePlayViewModelAndroid vm;
        public GamePlayView View;

        public GameView(Context context) : base (context) {
            SetPaddleBmp ();
//          this.ClearAllBindings();
//          var set = this.CreateBindingSet<GameView, GamePlayViewModelAndroid>();
//          set.Bind(_paddle1y).To(vm => vm.Paddle1.Y);
//          set.Bind(_paddle2y).To(vm => vm.Paddle2.Y);
//          set.Apply();
            //var set = this.CreateBindingSet<PolicySummaryCell, PolicyComponent<BasePolicy>>();
            //set.Bind(_periodOfInsurance).To(vm => vm.PeriodOfInsurance);
            //set.Bind(_title).To(vm => vm.Title);
            View = (GamePlayView)context;

        }

        void SetPaddleBmp ()
        {
            var paddlebmpTemp = BitmapFactory.DecodeResource (Resources, Resource.Drawable.Icon);
            _paddleBmp = Bitmap.CreateScaledBitmap (paddlebmpTemp, Paddle.Width, Paddle.Height, false);
        }

        protected override void OnDraw(Canvas canvas) {
            canvas.DrawColor(Color.Aqua);
            canvas.DrawBitmap (_paddleBmp, _paddle1x, View._paddle1y, null);
            canvas.DrawBitmap (_paddleBmp, _paddle2x, View._paddle2y, null);
        }

        protected override void OnSizeChanged(int w, int h, int oldw, int oldh) {
            SetUpCourt (w, h);
        }

        void SetUpCourt (int w, int h)
        {
            Court.Width = w;
            Court.Height = h;
            Court.UpperBound = 0;
            Court.LowerBound = h;
            Court.LeftBound = 0;
            Court.RightBound = w;
            ComputerPaddle.X = Court.RightBound - Paddle.Width - 20;
            _paddle2x = ComputerPaddle.X;
            _paddle1x = PlayerPaddle.X;
        }
    }
}

型号:

using System.Diagnostics;

namespace Pong.Core.Models
{
    public class Paddle
    {
        public int Y { get; set; }
        public int VY { get; set; }
        public static readonly int Speed = 600;
        public static readonly int Height = 300;
        public static readonly int Width = 100;

        public void StopMoving()
        {
            VY = 0;
        }

        public void SetDirection(string direction)
        {
            if (direction == "up")
            {
                VY = -Speed;
            }
            else if (direction == "down")
            {
                VY = Speed;
            }
        }

        public string DetectWallCollision()
        {
            if (Y < Court.UpperBound)
            {
                return "upper";
            }
            if (Y > (Court.LowerBound - Paddle.Height))
            {
                return "lower";
            }
            return "none";
        }

        public void UpperWallHit()
        {
            StopMoving();
            Y = Court.UpperBound;
            Debug.WriteLine("You hit the top wall");
        }

        public void LowerWallHit()
        {
            StopMoving();
            Y = Court.LowerBound - Paddle.Height;
            Debug.WriteLine("You hit the bottom wall");
        }

        public void MoveOneFrame()
        {
            Y += VY/Court.FPS;//this should trigger the RaisePropertyChanged(() => Paddle1)
        }
    }

    public class PlayerPaddle : Paddle {
        public static readonly int X = 20;
    }

    public class ComputerPaddle : Paddle {
        public static int X;
    }
}

我认为您的问题在于,您正在更新 Paddle.Y 字段,但 RaisePropertyChanged() 在更新 Paddle 时被调用(在片场)。 看出区别了吗?

只有当您在 GamePlayViewModel 中设置 Paddle1 和 Paddle2 的新实例 属性 时,才会调用 setter:

Paddle1 = new Paddle(); //will call the setter of Paddle1 property
Paddle1.Y = 90; //would not call the setter property

您需要做的是在更新 Paddle1 和 Paddle2 属性的 X 和 Y 值时调用 RaisePropertyChanged。

这个问题的答案是,在视图上,要以编程方式将字段绑定到 viewModel,该字段必须是 属性 以及 getter 和 setter。像这样。在视图中:

public int _paddle1y { get; set; }
public int _paddle2y { get; set; }

不知道为什么会这样。我认为这是 MvvmCross 中的一个错误。但也许这是有正当理由的。

不要直接调用 _paddle1,而是调用 Paddle1(和 2)以便调用 RaisePropertyChanged 事件。