Mvvmcross Binding - mvvmcross

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

Related

Libgdx clickable object?

Can someone explain me how to detect a click on an object? I have already seen an answer to this question but it does not work.
public class TiledMapActor extends Actor {
private TiledMap tiledMap;
private TiledMapTileLayer tiledLayer;
private TiledMapTileLayer.Cell cell;
public TiledMapActor(TiledMap tiledMap, TiledMapTileLayer tiledLayer, TiledMapTileLayer.Cell cell) {
this.tiledMap = tiledMap;
this.tiledLayer = tiledLayer;
this.cell = cell;
}
}
public class TiledMapClickListener extends ClickListener {
private TiledMapActor actor;
public TiledMapClickListener(TiledMapActor actor) {
this.actor = actor;
}
#Override
public void clicked(InputEvent event, float x, float y) {
System.out.println(actor.cell + " has been clicked.");
}
}
public class TiledMapStage extends Stage {
private TiledMap tiledMap;
public TiledMapStage(TiledMap tiledMap) {
this.tiledMap = tiledMap;
for (MapLayer layer : tiledMap.getLayers()) {
TiledMapTileLayer tiledLayer = (TiledMapTileLayer)layer; //THE ERROR IS IN THIS LINE
createActorsForLayer(tiledLayer);
}
}
private void createActorsForLayer(TiledMapTileLayer tiledLayer) {
for (int x = 0; x < tiledLayer.getWidth(); x++) {
for (int y = 0; y < tiledLayer.getHeight(); y++) {
TiledMapTileLayer.Cell cell = tiledLayer.getCell(x, y);
TiledMapActor actor = new TiledMapActor(tiledMap, tiledLayer, cell);
actor.setBounds(x * tiledLayer.getTileWidth(), y * tiledLayer.getTileHeight(), tiledLayer.getTileWidth(),
tiledLayer.getTileHeight());
addActor(actor);
EventListener eventListener = new TiledMapClickListener(actor);
actor.addListener(eventListener);
}
}
}
}
Stage stage = new TiledMapStage(tiledMap);
Gdx.input.setInputProcessor(stage);
I have tried it with this code but I get this error message:
com.badlogic.gdx.maps.MapLayer cannot be cast to com.badlogic.gdx.maps.tiled.TiledMapTileLayer
I do not understand how select which object is clickable
As you already found out, the problem is your casting here:
for (MapLayer layer : tiledMap.getLayers()) {
TiledMapTileLayer tiledLayer = (TiledMapTileLayer)layer;
Looks like the layers are not of type TiledMapTileLayer - or at least not all of them. (Afaik there are also ObjectLayers in a TiledMap) The simplest thing to get your code running again would ab an instanceof check:
for (MapLayer layer : tiledMap.getLayers()) {
if (layer instanceof TiledMapTileLayer) {
TiledMapTileLayer tiledLayer = (TiledMapTileLayer)layer;
createActorsForLayer(tiledLayer);
}

LWJGL glDrawArrays doesn't draw

I'm kinda new to the LWJGL library and im trying to draw a simple triangle. Only my code seems okay with it won't draw my triangle. It also gives no errors or anything else. I searched everywhere but found no solution at all. Anyone with a solution?
Code:
public class Cube {
private int width, height;
private int ID, bufferID;
private int count;
private float[] vertices;
public Cube() {
vertices = new float[] {
0, 0, 0,
1, 0, 0,
0, 1, 0,
};
count = vertices.length / 3;
FloatBuffer buffer = BufferUtils.createFloatBuffer(vertices.length);
buffer.put(vertices);
buffer.flip();
ID = GL30.glGenVertexArrays();
GL30.glBindVertexArray(ID);
bufferID = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferID);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
GL20.glEnableVertexAttribArray(0);
GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0);
GL30.glBindVertexArray(0);
GL20.glDisableVertexAttribArray(0);
}
public void render() {
GL30.glBindVertexArray(ID);
GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, count);
GL30.glBindVertexArray(0);
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
}
public class Launcher {
public static void main(String[] args) {
Window window = new Window("Cube Wave", 800, 600);
window.setBackground(1, 0, 1);
window.setup();
}
}
public class Window {
private String title;
private int width, height;
private Vector3f color;
private long window;
private ArrayList<Cube> cubes = new ArrayList<Cube>();
public Window(String title, int width, int height) {
this.title = title;
this.width = width;
this.height = height;
setBackground(0, 0, 0);
}
public void setup() {
if (!glfwInit()) {
return;
}
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
window = glfwCreateWindow(width, height, title, NULL, NULL);
if (window == NULL) {
return;
}
GLFWVidMode monitor = glfwGetVideoMode(glfwGetPrimaryMonitor());
glfwSetWindowPos(window, (monitor.width() - width) / 2, (monitor.height() - height) / 2);
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
GL.createCapabilities();
glfwShowWindow(window);
loop();
}
public long getWindow() {
return window;
}
public void loop() {
init();
glClearColor(color.x, color.y, color.z, 1.0f);
while(!closed()) {
tick();
render();
}
}
public void render() {
for(Cube cube : cubes) {
cube.render();
}
glfwSwapBuffers(window);
}
public void tick() {
glfwPollEvents();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
public void init() {
cubes.add(new Cube());
}
public boolean closed() {
return glfwWindowShouldClose(window);
}
public void setBackground(float r, float g, float b) {
color = new Vector3f(r, g, b);
}
}
It seems that you have a mistake in your render function.
Below is an example for how to use glDrawArrays in a render function.
public void render() {
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
// bind the VBO and enable the attribute
GL30.glBindVertexArray(ID);
GL20.glEnableVertexAttribArray(0);
GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, count);
// disable the VBO and disable the attribute
GL20.glDisableVertexAttribArray(0);
GL30.glBindVertexArray(0);
}
Also it is considered best practice to unbind buffers when you are done with them. So your initialization function should look like this.
public Cube() {
// ... vertex stuff
ID = GL30.glGenVertexArrays();
GL30.glBindVertexArray(ID);
bufferID = GL15.glGenBuffers();
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, bufferID);
GL15.glBufferData(GL15.GL_ARRAY_BUFFER, buffer, GL15.GL_STATIC_DRAW);
GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 0, 0);
// unbind both the Array Buffer and the Vertex Array
GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0);
GL30.glBindVertexArray(0);
}
You can find a more in depth discussion and example of this from the lwjgl wiki

Unable to put swing's JPopupMenu to SystemTray [duplicate]

Currently, the PopupMenu will show up when I right-click on the TrayIcon in the SystemTray. However, I want it to do the same when I left-click on the TrayIcon.
I thought I might accomplish this by using a mouseListener on the TrayIcon, but I don't know what method to invoke in the mouseClicked event to achieve the desired results.
icon = new TrayIcon(img, tooltip, popup);
icon.addMouseListener(
new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
popup.setEnabled(true);
}
});
Using the setEnabled() method does not make the popup menu appear when I left-click the TrayIcon. It actually has no noticeable effect. I'm wondering what method I should use in the mouseClicked() body in order to make the popup show up when it is left-clicked.
Basically, in your mouse listener, you need to determine which button was pressed (and optional, how many times).
The critical piece of code is this...
if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 1) { ... }
I've also included some additional code that makes sure the popup does not cover the task bar and is displayed within the viewable area of the screen (it's a nit pick of mine ;))
public class TestTrayIcon02 {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
try {
final TrayIcon ti = new TrayIcon(ImageIO.read(getClass().getResource("/Smiley.png")), "Have a nice day");
final JPopupMenu popup = new JPopupMenu();
JMenuItem mi = new JMenuItem("Get me some");
mi.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
SystemTray.getSystemTray().remove(ti);
System.exit(0);
}
});
popup.add(mi);
ti.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 1) {
Rectangle bounds = getSafeScreenBounds(e.getPoint());
Point point = e.getPoint();
int x = point.x;
int y = point.y;
if (y < bounds.y) {
y = bounds.y;
} else if (y > bounds.y + bounds.height) {
y = bounds.y + bounds.height;
}
if (x < bounds.x) {
x = bounds.x;
} else if (x > bounds.x + bounds.width) {
x = bounds.x + bounds.width;
}
if (x + popup.getPreferredSize().width > bounds.x + bounds.width) {
x = (bounds.x + bounds.width) - popup.getPreferredSize().width;
}
if (y + popup.getPreferredSize().height > bounds.y + bounds.height) {
y = (bounds.y + bounds.height) - popup.getPreferredSize().height;
}
popup.setLocation(x, y);
popup.setVisible(true);
}
}
});
SystemTray.getSystemTray().add(ti);
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
}
public static Rectangle getSafeScreenBounds(Point pos) {
Rectangle bounds = getScreenBoundsAt(pos);
Insets insets = getScreenInsetsAt(pos);
bounds.x += insets.left;
bounds.y += insets.top;
bounds.width -= (insets.left + insets.right);
bounds.height -= (insets.top + insets.bottom);
return bounds;
}
public static Insets getScreenInsetsAt(Point pos) {
GraphicsDevice gd = getGraphicsDeviceAt(pos);
Insets insets = null;
if (gd != null) {
insets = Toolkit.getDefaultToolkit().getScreenInsets(gd.getDefaultConfiguration());
}
return insets;
}
public static Rectangle getScreenBoundsAt(Point pos) {
GraphicsDevice gd = getGraphicsDeviceAt(pos);
Rectangle bounds = null;
if (gd != null) {
bounds = gd.getDefaultConfiguration().getBounds();
}
return bounds;
}
public static GraphicsDevice getGraphicsDeviceAt(Point pos) {
GraphicsDevice device = null;
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice lstGDs[] = ge.getScreenDevices();
ArrayList<GraphicsDevice> lstDevices = new ArrayList<GraphicsDevice>(lstGDs.length);
for (GraphicsDevice gd : lstGDs) {
GraphicsConfiguration gc = gd.getDefaultConfiguration();
Rectangle screenBounds = gc.getBounds();
if (screenBounds.contains(pos)) {
lstDevices.add(gd);
}
}
if (lstDevices.size() > 0) {
device = lstDevices.get(0);
} else {
device = ge.getDefaultScreenDevice();
}
return device;
}
}
What you're trying to do is apparently not possible:
You cannot show the PopupMenu with its show method since you need to specify a JComponent but your TrayIcon isn't one (weird enough though that TrayIcon still manages to do it, so apparently there is a way, don't ask me though..). So, as MadProgrammer suggested, you should try using JPopupMenu instead. Don't add it to your TrayIcon for that won't be possible, but display your JPopupMenu by adding a MouseListener to your TrayIcon. That should do the trick:
final TrayIcon tray = new TrayIcon( img, tooltip, null);
final JPopupMenu menu = new JPopupMenu();
... // your menu initialization.
tray.addMouseListener( new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent evt) {
menu.setLocation( evt.getPoint() );
menu.setVisible( true );
}
}

Trouble with moving entity

I am currently trying to get acclimated to FlashDevelop, but am having trouble moving an entity, despite following tutorials very closely.
public class Main extends Engine
{
private var gameWorld:MyWorld;
public function Main()
{
super(800, 600, 60, false);
gameWorld = new MyWorld;
}
override public function init():void
{
FP.world = gameWorld;
trace("FlashPunk has started successfully!");
}
}
public class MyWorld extends World
{
private var gameEntity:MyEntity;
public function MyWorld()
{
gameEntity = new MyEntity();
add(gameEntity);
}
override public function update():void
{
//trace("MyEntity updates");
}
}
public class MyEntity extends Entity
{
[Embed(source = "dragon.png")] private const PLAYER:Class;
public function MyEntity()
{
name = "player";
graphic = new Image(PLAYER);
}
override public function update():void
{
x += 10;
y += 5;
//if (Input.check(Key.LEFT)) { x -= 5; }
//if (Input.check(Key.RIGHT)) { x += 5; }
//if (Input.check(Key.UP)) { y -= 5; }
//if (Input.check(Key.DOWN)) { y += 5; }
}
override public function added():void
{
trace("Entity added");
}
}
It is supposed to move across the screen, but does not for some reason. Any thoughts?

Binding views to ICommand.CanExecute

Is it somehow possible to bind view properties to ICommand.CanExecute?
I'd for example like to be able to do something like this in a touch view:
this
.CreateBinding(SignInWithFacebookButton)
.For(b => b.Enabled)
.To((SignInViewModel vm) => vm.SignInWithFacebookCommand.CanExecute)
.Apply();
I've already read How to use CanExecute with Mvvmcross, but unfortunately it skips the questions and instead just proposes another implementation.
One way of doing this is to use your own custom button inheriting from UIButton.
For Android, I've got an implementation of this to hand - it is:
public class FullButton : Button
{
protected FullButton(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
Click += OnClick;
}
public FullButton(Context context) : base(context)
{
Click += OnClick;
}
public FullButton(Context context, IAttributeSet attrs) : base(context, attrs)
{
Click += OnClick;
}
public FullButton(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle)
{
Click += OnClick;
}
private IDisposable _subscription;
private object _commandParameter;
public object CommandParameter
{
get { return _commandParameter; }
set
{
_commandParameter = value;
UpdateEnabled();
}
}
private ICommand _command;
public ICommand Command
{
get { return _command; }
set
{
if (_subscription != null)
{
_subscription.Dispose();
_subscription = null;
}
_command = value;
if (_command != null)
{
var cec = typeof (ICommand).GetEvent("CanExecuteChanged");
_subscription = cec.WeakSubscribe(_command, (s, e) =>
{
UpdateEnabled();
});
}
UpdateEnabled();
}
}
private void OnClick(object sender, EventArgs eventArgs)
{
if (Command == null)
return;
if (Command.CanExecute(CommandParameter))
Command.Execute(CommandParameter);
}
private void UpdateEnabled()
{
Enabled = ShouldBeEnabled();
}
private bool ShouldBeEnabled()
{
if (_command == null)
return false;
return _command.CanExecute(CommandParameter);
}
}
and this can be bound as:
<FullButton
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Show Detail"
local:MvxBind="Command ShowDetailCommand; CommandParameter CurrentItem" />
For iOS, I'd expect the same type of technique to work... inheriting from a UIButton and using TouchUpInside instead of Click - but I'm afraid I don't have this code with me at the moment.