Xamarin.Droid Binding Visibility Converter using MvxVisibility wont work - xamarin.android

I have tree parts of layout in my file.axml
I wanna show/hide some of them based on a property in my viewmodel
My property is an enumerator, and Iam using an converter to return the visibility based on the enum.
Iam using the type "MvxVisibility" in the core, but for some reason, the android is not understand the return type (visibility)
Here is my android binding:
<LinearLayout
local:MvxBind="Visibility RedeemCodeState, Converter=RedeemStateToVisibility, ConverterParameter=0"
Here is my converter (core)
public class RedeemStateToVisibilityConverter :
MvxValueConverter<RedeemCodeState, MvxVisibility>
{
protected override MvxVisibility Convert(RedeemCodeState mark, Type targetType, object parameter, CultureInfo culture)
{
switch (mark)
{
case RedeemCodeState.RedeemCodeSubmit:
if (parameter.ToString() == "0")
return MvxVisibility.Visible;
return MvxVisibility.Collapsed;
case RedeemCodeState.AudiosSelection:
if (parameter.ToString() == "1")
return MvxVisibility.Visible;
return MvxVisibility.Collapsed;
case RedeemCodeState.Confirmation:
if (parameter.ToString() == "2")
return MvxVisibility.Visible;
return MvxVisibility.Collapsed;
default:
return MvxVisibility.Collapsed;
}
}
}
Here is my ViewModel (core):
public partial class RedeemCodeViewModel
{
private RedeemCodeState _redeemCodeState = 0;
public RedeemCodeState RedeemCodeState
{
get { return _redeemCodeState; }
set
{
_redeemCodeState = value;
RaisePropertyChanged(() => RedeemCodeState);
}
}
public RedeemCodeViewModel(string code)
{
RedeemCode = code;
}
}
public enum RedeemCodeState
{
RedeemCodeSubmit = 0,
AudiosSelection = 1,
Confirmation = 2
}
What Im doing wrong?

Value converters are meant to convert portable values to platform specific values. MvxVisibility is a portable type.
You need to add the Visibility plugins to both your Core and Droid projects. You can call multiple value converters in your binding.
local:MvxBind="Visibility Visibility(RedeemStateToVisibility(RedeemCodeState, 0))"
Note: I'm using Tibet binding syntax
This calls your value converter to convert your code state to a MvxVisibility value. We finally call Visibility value converter to convert that to the Android visibility value.

Related

Dart: lists of supertype takes subtype only at runtime

I ran into an issue similar to this:
void main() {
_buildMixedList([1,2.3,4,5.6,7.6,8]);
_buildHomogeneousList([1,2,4,5,7,8]);
}
abstract class NumberWrapper {}
class DoubleWrapper extends NumberWrapper{
final double myDouble;
DoubleWrapper(this.myDouble);
}
class IntWrapper extends NumberWrapper{
final int myInt;
IntWrapper(this.myInt);
}
List<NumberWrapper?> _buildMixedList(List<dynamic> numbers) {
List<NumberWrapper?> wrappers = numbers.map((number) {
if(number is int){
return IntWrapper(number);
}
if(number is double){
return DoubleWrapper(number);
}
return null;
}).toList();
wrappers.add(DoubleWrapper(0.2));
return wrappers;
}
List<NumberWrapper?> _buildHomogeneousList(List<dynamic> numbers) {
List<NumberWrapper?> wrappers = numbers.map((number) {
if(number is int){
return IntWrapper(number);
}
return null;
}).toList();
wrappers.add(DoubleWrapper(0.2));
return wrappers;
}
As you can see, the two methods are doing something similar (adding object of different types to a list). The first one adds different objects inside a map() function and the other adds only one type in map() and then adds another after.
The second one throws this error:
: TypeError: Instance of 'DoubleWrapper': type 'DoubleWrapper' is not a subtype of type 'IntWrapper?'Error: TypeError: Instance of 'DoubleWrapper': type 'DoubleWrapper' is not a subtype of type 'IntWrapper?'
As if the list is being changed to List<IntWrapper?> just because we only added IntWrappers in the map().
I wrote this test code after encountering this in one of my projects, so it's not representative of a real case. I tried it on dartPad.
Coming from a java background I was expecting the second method to work. Is it a bug or is it intended? If intended, why is that so?
Your problem is that there are a difference between the type of the variable and the type of the object which you are pointing to.
So in this case:
List<NumberWrapper?> wrappers = numbers.map((number) {
if(number is int){
return IntWrapper(number);
}
return null;
}).toList();
What you are actually are doing is creating a List<IntWrapper?> which you are using a variable of the type List<NumberWrapper?> to point at. Why? Because the type of the variable in this case does not change the type of the returned List from toList() (which type is determined by what type map() returns).
The reason the type is List<IntWrapper?> is because Dart are trying to be smart about automatically assigning the type. In this case, the analyzer can see you List will only contain IntWrapper or null.
I think the best solution here is to rewrite this part to something like this:
List<NumberWrapper?> _buildHomogeneousList(List<num> numbers) {
final wrappers = <NumberWrapper?>[
for (final number in numbers)
if (number is int) IntWrapper(number) else null
];
wrappers.add(DoubleWrapper(0.2));
return wrappers;
}
By using the [] syntax to create the List, it is easier to specify the type you want the List to be.
Alternative, you can do this where we add the expected type to the map method:
List<NumberWrapper?> _buildHomogeneousList(List<num> numbers) {
List<NumberWrapper?> wrappers = numbers.map<NumberWrapper?>((number) {
if (number is int) {
return IntWrapper(number);
}
return null;
}).toList();
wrappers.add(DoubleWrapper(0.2));
return wrappers;
}

JAXB implementation in Java 1.8 is incompatible with java 1.7 Collection instances being persisted now must be the same instances [duplicate]

The code below used to work under the JAXB implementation used by JDK 1.7, but now under JDK 1.8 it's broken. In the code below you will find the key change that seems to make it work in 1.8. The "fix" under 1.8 is not really a fix because it's bad practice to expose internal collections for direct modification by the outside world. I want to control access to the internal list through my class and I don't want to complicate things by making observable collections and listening to them. This is not acceptable.
Is there any way to get my original code to work under the JAXB of JD 1.8?
#XmlElementWrapper(name = "Wrap")
#XmlElement(name = "Item", required = true)
public synchronized void setList(List<CustomObject> values) {
list.clear();
list.addAll(values);
}
public synchronized List<CustomObject> getList() {
// return new ArrayList(list); // this was the original code that worked under 1.7
return list; //this is the only thing that works under 1.8
}
After more analysis, the problem seems to be coming from JAXB not calling the setter method for collections anymore (it used to under JDK 1.7). Now under JDK 1.8, it calls the getter and modifies the collection directly. This poses several problems:
1-forces the user to expose an internal collection to the outside world for free modification (bad practice)
2-doesn't allow the user to do any custom code when the list changes (such as what you could do if the setter was called). It might be possible to make an observable collection and listen to it, but this is a much more complicated workaround than just calling the setter method.
Background
When a collection property is mapped in JAXB it first checks the getter to see if the collection property has been pre-initialized. In the example below I want to have my property exposed as List<String>, but have the backing implementation be a LinkedList ready to hold 1000 items.
private List<String> foos = new LinkedList<String>(1000);
#XmlElement(name="foo")
public List<String> getFoos() {
return foos;
}
Why Your Code Used to Work
If you previously had JAXB call the setter on a property mapped to a collection that returned a non-null response from the getter, then there was a bug in that JAXB implementation. Your code should not have worked in the previous version either.
How to Get the Setter Called
To have the setter called you just need to have your getter return null, on a new instance of the object. Your code could look something like:
import java.util.*;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "Foo")
public class Foo {
private List<CustomObject> list = null;
#XmlElementWrapper(name = "Wrap")
#XmlElement(name = "Item", required = true)
public synchronized void setList(List<CustomObject> values) {
if (null == list) {
list = new ArrayList<CustomObject>();
} else {
list.clear();
}
list.addAll(values);
}
public synchronized List<CustomObject> getList() {
if (null == list) {
return null;
}
return new ArrayList(list);
}
}
UPDATE
If you don't need to perform any logic on the List returned from JAXB's unmarshalling then using field access may be an acceptable solution.
#XmlRootElement(name = "Foo")
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
#XmlElementWrapper(name = "Wrap")
#XmlElement(name = "Item", required = true)
private List<CustomObject> list = null;
public synchronized void setList(List<CustomObject> values) {
if(null == list) {
list = new ArrayList<CustomObject>();
} else {
list.clear();
}
list.addAll(values);
}
public synchronized List<CustomObject> getList() {
return new ArrayList(list);
}
}

How can I use Automapper to map all zero int values to null values for nullable int targets?

I use int? for all my required 'FK' properties in ViewModels. This gives me an easy way of specifying on a Create view model that a value is nullable and must be assigned a value to satisfy the Required attribute.
My problem comes in because I create the domain model entity first, using a domain factory, then map it to the view model. Now, many of the nullable ints in the view model get assigned 0 from non-nullable ints in the domain model. I would prefer not to build the new entity in the view model and only map it back to the domain model to avoid his. What else can I do? i'm sure there is som Automapper voodoo that can help me.
EDIT: you dont need to do any of this, but i thought i'd leave it here for people looking for a similar solution. really all you have to do is just provide a mapping from int to int? like this: Mapper.Map<int, int?>()
in that case, I believe you could use a custom type converter, which inherits from automappers ITypeConverter. This code works, I've run it through .NET Fiddle:
using System;
using AutoMapper;
public class Program
{
public void Main()
{
CreateMappings();
var vm = Mapper.Map<MyThingWithInt, MyThingWithNullInt>(new MyThingWithInt());
if (vm.intProp.HasValue)
{
Console.WriteLine("Value is not NULL!");
}
else
{
Console.WriteLine("Value is NULL!");
}
}
public void CreateMappings()
{
Mapper.CreateMap<int, int?>().ConvertUsing(new ZeroToNullIntTypeConverter ());
Mapper.CreateMap<MyThingWithInt, MyThingWithNullInt>();
}
public class ZeroToNullIntTypeConverter : ITypeConverter<int, int?>
{
public int? Convert(ResolutionContext ctx)
{
if((int)ctx.SourceValue == 0)
{
return null;
}
else
{
return (int)ctx.SourceValue;
}
}
}
public class MyThingWithInt
{
public int intProp = 0;
}
public class MyThingWithNullInt
{
public int? intProp {get;set;}
}
}
You can always use the .ForMember() method on your mapping. Something like this:
Mapper
.CreateMap<Entity, EntityDto>()
.ForMember(
dest => dest.MyNullableIntProperty,
opt => opt.MapFrom(src => 0)
);

Enum in Grails Domain

I'm trying to use an enum in a Grails 2.1 domain class. I'm generating the controller and views via the grails generate-all <domain class> command, and when I access the view I get the error shown below. What am I missing here?
Error
Failed to convert property value of type java.lang.String to required type
com.domain.ActionEnum for property action; nested exception is
java.lang.IllegalStateException: Cannot convert value of type
[java.lang.String] to required type [com.domain.ActionEnum] for property action:
no matching editors or conversion strategy found
Enum (in /src/groovy)
package com.domain
enum ActionEnum {
PRE_REGISTER(0), PURCHASE(2)
private final int val
public ActionEnum(int val) {
this.val = val
}
int value() { return value }
}
Domain
package com.domain
class Stat {
ActionEnum action
static mapping = {
version false
}
}
View
<g:select name="action"
from="${com.domain.ActionEnum?.values()}"
keys="${com.domain.ActionEnum.values()*.name()}" required=""
value="${xyzInstance?.action?.name()}"/>
EDIT
Now getting error Property action must be a valid number after changing the following.
View
<g:select optionKey='id' name="action"
from="${com.domain.ActionEnum?.values()}"
required=""
value="${xyzInstance?.action}"/> // I tried simply putting a number here
Enum
package com.domain
enum ActionEnum {
PRE_REGISTER(0), PURCHASE(2)
final int id
public ActionEnum(int id) {
this.id = id
}
int value() { return value }
static ActionEnum byId(int id) {
values().find { it.id == id }
}
}
Domain
package com.domain.site
class Stat {
static belongsTo = Game;
Game game
Integer action
static mapping = {
version false
}
static constraints = {
action inList: ActionEnum.values()*.id
}
String toString() {
return "${action}"
}
}
Take a look here ...
Grails Enum Mapping
Grails GORM & Enums
Also you may be hitting this as well. From the docs:
1) Enum types are now mapped using their String value rather than the ordinal value. You can revert to the old behavior by changing your mapping as follows:
static mapping = {
someEnum enumType:"ordinal"
}

JSR303 validator message recursive resolution?

I have written a JSR303 validator that compares property value to constraint:
#Documented
#Constraint(validatedBy = Cmp.LongCmpValidator.class)
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
public #interface Cmp {
String message() default "{home.lang.validator.Cmp.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
long value();
public enum REL { LT,LT_EQ,EQ,GT,GT_EQ;
#Override
public String toString() {
return toString_property();
}
public String toString_property() {
switch(this) {
case LT : return "{home.lang.validator.Cmp.REL.LT}";
case LT_EQ: return "{home.lang.validator.Cmp.REL.LT_EQ}";
case EQ: return "{home.lang.validator.Cmp.REL.EQ}";
case GT : return "{home.lang.validator.Cmp.REL.GT}";
case GT_EQ: return "{home.lang.validator.Cmp.REL.GT_EQ}";
}
throw new UnsupportedOperationException();
}
public String toString_common() { return super.toString(); }
public String toString_math() { switch(this) {
case LT : return "<";
case LT_EQ: return "\u2264";
case EQ: return "=";
case GT : return ">";
case GT_EQ: return "\u2265";
}
throw new UnsupportedOperationException();
}
}
REL prop_rel_cnstr();
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
#Documented
#interface List {
Cmp[] value();
}
class LongCmpValidator implements ConstraintValidator<Cmp, Number> {
long cnstr_val;
REL prop_rel_cnstr;
public void initialize(Cmp constraintAnnotation) {
cnstr_val = constraintAnnotation.value();
prop_rel_cnstr = constraintAnnotation.prop_rel_cnstr();
}
public boolean isValid(Number _value, ConstraintValidatorContext context) {
if(_value == null) return true;
if(_value instanceof Integer) {
int value = _value.intValue();
switch(prop_rel_cnstr) {
case LT : return value < cnstr_val;
case LT_EQ: return value <= cnstr_val;
case EQ: return value == cnstr_val;
case GT : return value > cnstr_val;
case GT_EQ: return value >= cnstr_val;
}
}
// ... handle other types
return true;
}
}
}
ValidationMessages.properties :
home.lang.validator.Cmp.REL.LT=less than
home.lang.validator.Cmp.REL.LT_EQ=less than or equal
home.lang.validator.Cmp.REL.EQ=equal
home.lang.validator.Cmp.REL.GT=greater
home.lang.validator.Cmp.REL.GT_EQ=greater than or equal
home.lang.validator.Cmp.message=Failure: validated value is to be in relation "{prop_rel_cnstr}" to {value}.
Works fine. Almost. The validation message I get looks like this:
Failure: validated value is to be in relation "{home.lang.validator.Cmp.REL.GT}" to 0.
Would anybody please suggest easy and convenient way, how to make Validator recognize and resolve nested {home.lang.validator.Cmp.REL.GT} key? I need it to be nicely usable in JSF2, which handles validation.
I'm not using Spring, but use hibernate-validator 4.
By the way, looks like hibernate-validator 4 doesn't fully implement JSR303, since later states in the 4.3.1.1.:
Message parameters are extracted from
the message string and used as keys to
search the ResourceBundle named
ValidationMessages (often materialized
as the property file
/ValidationMessages.properties and its
locale variations) using the defined
locale (see below). If a property is
found, the message parameter is
replaced with the property value in
the message string. Step 1 is applied
recursively until no replacement is
performed (i.e. a message parameter
value can itself contain a message
parameter).
Ok, I did dig into this. The algorithm specified by JSR303 has an unintuitive mess with what (props) are recursively resolvable and what's not. I think, that's mainly due to bad distinction in grammar of annotation''s properties and RB's properties.
So I've made my own MessageInterpolator, which you can find in my repo: http://github.com/Andrey-Sisoyev/adv-msg-interpolator. It solves almost all the problems, and also allows to address the resource bundle, where to look for the property.

Resources