I tried to bind a widget to a viewmodel property but I'm getting an exception
MvxBind:Warning: 14.76 Failed to create target binding for binding Signature for Order.ClientSignature
[0:] MvxBind:Warning: 14.76 Failed to create target binding for binding Signature for Order.ClientSignature
04-26 21:02:15.380 I/mono-stdout(32490): MvxBind:Warning: 14.76 Failed to create target binding for binding Signature for Order.ClientSignature
The widget is courtesy of Al taiar
The axml is
<SignatureWidget
android:layout_width="match_parent"
android:layout_height="100dp"
android:id="@+id/signatureWidget1"
android:layout_marginRight="5dp"
android:layout_marginLeft="5dp"
android:layout_marginBottom="5dp"
local:MvxBind="Signature Order.ClientSignature" />
The code for the view is
using Android.Content;
using Android.Graphics;
using Android.Util;
using Android.Views;
using Core.Models;
using System;
public class SignatureWidget
: View
{
#region Implementation
private Bitmap _bitmap;
private Canvas _canvas;
private readonly Path _path;
private readonly Paint _bitmapPaint;
private readonly Paint _paint;
private float _mX, _mY;
private const float TouchTolerance = 4;
#endregion
public Signature Signature;
public event EventHandler SignatureChanged;
public SignatureWidget(Context context, IAttributeSet attrs)
: base(context, attrs)
{
Signature = new Signature();
_path = new Path();
_bitmapPaint = new Paint(PaintFlags.Dither);
_paint = new Paint
{
AntiAlias = true,
Dither = true,
Color = Color.Argb(250, 00, 0, 0)
};
_paint.SetStyle(Paint.Style.Stroke);
_paint.StrokeJoin = Paint.Join.Round;
_paint.StrokeCap = Paint.Cap.Round;
_paint.StrokeWidth = 5;
}
protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
{
base.OnSizeChanged(w, h, oldw, oldh);
_bitmap = Bitmap.CreateBitmap(w, (h > 0 ? h : ((View)this.Parent).Height), Bitmap.Config.Argb8888);
_canvas = new Canvas(_bitmap);
}
protected override void OnDraw(Canvas canvas)
{
canvas.DrawColor(Color.White);
canvas.DrawBitmap(_bitmap, 0, 0, _bitmapPaint);
canvas.DrawPath(_path, _paint);
}
private void TouchStart(float x, float y)
{
_path.Reset();
_path.MoveTo(x, y);
_mX = x;
_mY = y;
Signature.AddPoint(SignatureState.Start, (int)x, (int)y);
}
private void TouchMove(float x, float y)
{
float dx = Math.Abs(x - _mX);
float dy = Math.Abs(y - _mY);
if (dx >= TouchTolerance || dy >= TouchTolerance)
{
_path.QuadTo(_mX, _mY, (x + _mX) / 2, (y + _mY) / 2);
Signature.AddPoint(SignatureState.Move, (int)x, (int)y);
_mX = x;
_mY = y;
}
}
private void TouchUp()
{
if (!_path.IsEmpty)
{
_path.LineTo(_mX, _mY);
_canvas.DrawPath(_path, _paint);
}
else
{
_canvas.DrawPoint(_mX, _mY, _paint);
}
Signature.AddPoint(SignatureState.End, (int)_mX, (int)_mY);
_path.Reset();
}
public override bool OnTouchEvent(MotionEvent e)
{
var x = e.GetX();
var y = e.GetY();
switch (e.Action)
{
case MotionEventActions.Down:
TouchStart(x, y);
Invalidate();
break;
case MotionEventActions.Move:
TouchMove(x, y);
Invalidate();
break;
case MotionEventActions.Up:
TouchUp();
Invalidate();
break;
}
RaiseSignatureChangedEvent();
return true;
}
public void ClearCanvas()
{
_canvas.DrawColor(Color.White);
Invalidate();
}
public Bitmap CanvasBitmap()
{
return _bitmap;
}
public void Clear()
{
ClearCanvas();
Signature.Clear();
RaiseSignatureChangedEvent();
}
private void RaiseSignatureChangedEvent()
{
var handler = SignatureChanged;
if (handler != null)
handler(this, EventArgs.Empty);
}
}
And the code for the model is
public class Signature
{
private List<Point> _currentPath;
private readonly List<List<Point>> _paths;
public event EventHandler PointAdded;
public Signature()
{
_currentPath = new List<Point>();
_paths = new List<List<Point>>();
}
public IReadOnlyList<IReadOnlyList<Point>> Paths
{
get { return _paths; }
}
public Point LastPoint()
{
if (_currentPath != null && _currentPath.Count > 0)
{
return _currentPath.Last();
}
return new Point(0, 0);
}
public void Clear()
{
_paths.Clear();
_currentPath.Clear();
}
public void AddPoint(SignatureState state, int x, int y)
{
if (state == SignatureState.Start)
{
_currentPath = new List<Point>();
}
if (x != 0 && y != 0)
{
_currentPath.Add(new Point(x, y));
}
if (state == SignatureState.End)
{
if (_currentPath != null)
{
_paths.Add(_currentPath);
}
}
RaisePointAddedEvent();
}
public int Length
{
get { return _paths.Count; }
}
protected void RaisePointAddedEvent()
{
if (PointAdded != null)
PointAdded(this, EventArgs.Empty);
}
}
I will need two-way binding for this widget. Anyone care to help???
I will also need to add a "Clear" text as an overlay on the view. Clicking this text will trigger a command to clear the widget. Any clue how to do this?
P.S: I've followed the informative post and I still cannot get it to work. I've added the following.
public class SignatureWidgetSignatureTargetBinding
: MvxPropertyInfoTargetBinding<SignatureWidget>
{
public SignatureWidgetSignatureTargetBinding(object target, PropertyInfo targetPropertyInfo)
: base(target, targetPropertyInfo)
{
View.SignatureChanged += OnSignatureChanged;
}
public override MvxBindingMode DefaultMode
{
get { return MvxBindingMode.TwoWay; }
}
private void OnSignatureChanged(object sender, EventArgs eventArgs)
{
FireValueChanged(View.Signature);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
if (isDisposing)
{
View.SignatureChanged -= OnSignatureChanged;
}
}
}
and registered using
registry.RegisterFactory(new MvxSimplePropertyInfoTargetBindingFactory(typeof(SignatureWidgetSignatureTargetBinding), typeof(SignatureWidget), "Signature"));
MvvmCross will automatically bind a View property if you model it using the format:
public foo Bar {
get { /* ... your code ... */ }
set { /* ... your code ... */ }
}
public event EventHandler BarChanged;
Based on this I think your problem is that you are trying to use a field - public Signature Signature;
- try using a property instead.
I think the binding mode you are looking for is also the unusual OneWayToSource
instead of TwoWay