[英]MvvmCross not binding
我正在使用MvvmCross開發Pong。 按住向上和向下按鈕(Android視圖/活動中的按鈕-而非鍵盤按鈕)時,Paddles Y值會更改。 但是,該視圖未在視圖中顯示(槳板保持在一個位置,即使我在控制台日志中看到槳板已上升或下降)。
為什么槳Y值未正確綁定到視圖?
這是代碼:
ViewModel:
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屬性的新實例時,該設置程序才會被調用:
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事件。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.