Entry value converter hangs converting and converting back again and again - binding

I have an Entry which holds the Price and I wanted to format it as currency.
Here is the Entry tag
<Entry x:Name="Price" StyleId="Price"
Text="{Binding Model.Price, Converter={StaticResource CurrencyEntryFormatConverter}, Mode=TwoWay}"
Placeholder="{x:Static resx:Resources.PricePlaceholder}"
Style="{StaticResource DefaultEntry}" Keyboard="Numeric"/>
and here is the property in the Model
public decimal Price
{
get
{
return this.price;
}
set
{
if (this.price== value)
{
return;
}
this.price= value;
this.OnPropertyChanged();
}
}
Finally here is the converter:
public class CurrencyEntryFormatConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
{
return value;
}
string result = string.Format(Resources.CurrencyFormatString, (decimal)value);
return result;
}
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if (value == null || string.IsNullOrWhiteSpace(value.ToString()))
{
return 0;
}
string result = value.ToString().Replace(" ", "").Replace("$", "").Replace(",", "");
return result;
}
}
Question: My problem is that when I run the project and try to enter values in the Price field, the code repeats executing between Convert and ConvertBack functions of the converter and application hangs!
Any advice?

In my case the problem was the property implementation
If we define a property of a class which is implementing INotifyPropertyChanged, in order to update the view when the property value changes, we need to call OnPropertyChanged method in the set block:
public decimal Amount
{
get
{
return this.amount;
}
set
{
this.amount = value;
this.OnPropertyChanged(); // like this line
}
}
But just this code like so makes a loop with your bindings. So we need to be checking if the value is different than the current property's value and then if its new, update it. Look at this code:
public decimal Amount
{
get
{
return this.amount;
}
set
{
if (this.amount == value)
{
return;
}
this.amount = value;
this.OnPropertyChanged();
}
}
That if block helps you to stop looping between get and set.
I hope it helps someone.

Related

launchDate2 is also a DateTime? type. Why the heck it could be passed to a function that requires a DateTime argument?

We couldn't pass a DateTime? value to a function that requires a DateTime argument.
The code below results in an error.
class Spacecraft {
String name;
DateTime? launchDate;
// Read-only non-final property
int? get launchYear => launchDate?.year;
// Constructor, with syntactic sugar for assignment to members.
Spacecraft(this.name, this.launchDate) {
// Initialization code go here.
}
// Named constructor that forwards to the default one.
Spacecraft.unlaunched(String name) : this(name, null);
// Method
void describe() {
print('Spacecraft: $name');
if (launchDate != null) {
int years = DateTime.now().difference(launchDate).inDays ~/ 365;
print('Launched: $launchYear ($years years ago)');
} else {
print('Unlaunched');
}
}
}
The argument type 'DateTime?' can't be assigned to the parameter type
'DateTime'.
However, this worked:
class Spacecraft {
String name;
DateTime? launchDate;
// Read-only non-final property
int? get launchYear => launchDate?.year;
// Constructor, with syntactic sugar for assignment to members.
Spacecraft(this.name, this.launchDate) {
// Initialization code go here.
}
// Named constructor that forwards to the default one.
Spacecraft.unlaunched(String name) : this(name, null);
// Method
void describe() {
print('Spacecraft: $name');
// Type promotion doesn't work on getters
var launchDate2 = launchDate;
if (launchDate2 != null) {
int years = DateTime.now().difference(launchDate2).inDays ~/ 365;
print('Launched: $launchYear ($years years ago)');
} else {
print('Unlaunched');
}
}
}
And heck, launchDate2 is also a DateTime? type. Why the heck it could be passed to a function that requires a DateTime argument?
launchDate refers to a property so it couldn't be promoted or cast. See http://dart.dev/go/non-promo-property and also https://dart.dev/null-safety/understanding-null-safety

How do I bind Character to a TextField?

I've found an example of how to bind Integer to a TextField:
Binder<Person> b = new Binder<>();
b.forField(ageField)
.withNullRepresentation("")
.withConverter(new StringToIntegerConverter("Must be valid integer !"))
.withValidator(integer -> integer > 0, "Age must be positive")
.bind(p -> p.getAge(), (p, i) -> p.setAge(i));
The problem is - there is no StringToCharacterConverter and if have an error if I bind fields as is. The error is:
Property type 'java.lang.Character' doesn't match the field type 'java.lang.String'. Binding should be configured manually using converter.
You need to implement custom converter, here is very simplified version of what could be StringToCharacterConverter for getting the pattern what the they look like:
public class StringToCharacterConverter implements Converter<String,Character> {
#Override
public Result<Character> convertToModel(String value, ValueContext context) {
if (value == null) {
return Result.ok(null);
}
value = value.trim();
if (value.isEmpty()) {
return Result.ok(null);
} else if (value.length() == 1) {
Character character = value.charAt(0);
return Result.ok(character);
} else {
return Result.error("Error message here");
}
}
#Override
public String convertToPresentation(Character value, ValueContext context) {
String string = value.toString();
return string;
}
}

C# Indexers with Ref Return Gets that also Support Sets

Am I doing something wrong here, or as of C# 7.2 Indexers that return by ref and allow set are not supported?
Works:
public ref byte this[int index] {
get {
return ref bytes[index];
}
}
Works too:
public byte this[int index] {
get {
return bytes[index];
}
set {
bytes[index] = value;
}
}
Fails:
public ref byte this[int index] {
get {
return ref bytes[index];
}
set { //<-- CS8147 Properties which return by reference cannot have set accessors
bytes[index] = value;
}
}
Fails too:
public ref byte this[int index] {
get {
return ref bytes[index];
}
}
public byte this[int index] { //<-- CS0111 Type already defines a member called 'this' with the same parameter types
set {
bytes[index] = value;
}
}
So, is there no way to have a ref return yet allow the indexer also support Set?
As #IvanStoev correctly pointed out, there is no need for set, since the value is returned by reference. Therefore the caller of the indexer has complete control over the returned value and can therefore can assign it a new value, with the changes being reflected in the underlying data structure (whose indexer was being called) since the value was returned by reference and not by value.

Custom ValidationAttribute for multiple data types

I have a ValidationAttribute like below which validates that a certain amount of values have been entered on a form. Currently it is only being used on a property with type short?[]
public class RequiredArrayLength : ValidationAttribute
{
public int TotalRequired { get; set; }
public override bool IsValid(object value)
{
if(value != null)
{
var array = value as short?[];
return array.Where(v => v.HasValue).Count() >= TotalRequired;
}
return false;
}
}
Is there a way I can modify this ValidationAttribute so it will work with other numeric arrays such as int?[]
One option would be to cast to IEnumerable (System.Collections namespace) and enumerate the collection to determine the number of items in the collection.
IEnumerable collection = value as IEnumerable;
if (collection!= null)
{
IEnumerator enumerator = collection.GetEnumerator();
int count = 0;
while(enumerator.MoveNext())
{
count++;
}
return count >= TotalRequired;
}
return false;
If you only want to count non-null values, then modify the code to
while(enumerator.MoveNext())
{
if (enumerator.Current != null)
{
count++;
}
}
If you specifically wanted to limit this to only numeric data types, you can use the .GetType() method of IEnumerable to test the type (for example, C# - how to determine whether a Type is a number).

ConversionErrorInterceptor throws conversion error during quit/cancel (Struts 2)

The scenario of the problem is this
1) We map the struts field values to the dtos. The dtos contain integer fields which again are displayed on the screen.
2) Now I enter an incorrect value which gives conversion error for that integer field.
3) At that point in time I decide to quit the page(i.e press cancel), I get a conversion error. This is because the StrutsConversionErrorInterceptor gets called everytime.
Is there any way that I can skip the strutsConversionErrorInterceptor when I am calling a particular method the way we can skip validation using excludeMethods
Use this code to override Struts's StrutsConversionErrorInterceptor...
public class MyConversionErrorInterceptor extends AbstractInterceptor {
private static final long serialVersionUID = 1L;
public static final String ORIGINAL_PROPERTY_OVERRIDE = "original.property.override";
protected Object getOverrideExpr(ActionInvocation invocation, Object value) {
ValueStack stack = invocation.getStack();
try {
stack.push(value);
return "'" + stack.findValue("top", String.class) + "'";
} finally {
stack.pop();
}
}
#Override
public String intercept(ActionInvocation invocation) throws Exception {
ActionContext invocationContext = invocation.getInvocationContext();
Map<String, Object> conversionErrors = invocationContext.getConversionErrors();
ValueStack stack = invocationContext.getValueStack();
HashMap<Object, Object> fakie = null;
BaseAction baseAction = (BaseAction) invocation.getAction();
String buttonName = baseAction.getButtonName();
for (Map.Entry<String, Object> entry : conversionErrors.entrySet()) {
String propertyName = entry.getKey();
Object value = entry.getValue();
if (shouldAddError(propertyName, value)) {
String message = XWorkConverter.getConversionErrorMessage(propertyName, stack);
Object action = invocation.getAction();
if (action instanceof ValidationAware) {
ValidationAware va = (ValidationAware) action;
if(buttonName.equalsIgnoreCas("Next")){
va.addFieldError(propertyName, message);
}
}
if (fakie == null) {
fakie = new HashMap<Object, Object>();
}
if(buttonName.equalsIgnoreCas("Next")){
fakie.put(propertyName, getOverrideExpr(invocation, value));
}
}
}
if (fakie != null) {
// if there were some errors, put the original (fake) values in
// place right before the result
stack.getContext().put(ORIGINAL_PROPERTY_OVERRIDE, fakie);
invocation.addPreResultListener(new PreResultListener() {
public void beforeResult(ActionInvocation invocation, String resultCode) {
Map<Object, Object> fakie = (Map<Object, Object>) invocation.getInvocationContext().get(ORIGINAL_PROPERTY_OVERRIDE);
if (fakie != null) {
invocation.getStack().setExprOverrides(fakie);
}
}
});
}
return invocation.invoke();
}
protected boolean shouldAddError(String propertyName, Object value) {
if (value == null) {
return false;
}
if ("".equals(value)) {
return false;
}
if (value instanceof String[]) {
String[] array = (String[]) value;
if (array.length == 0) {
return false;
}
if (array.length > 1) {
return true;
}
String str = array[0];
if ("".equals(str)) {
return false;
}
}
return true;
}
}
You can specify you button names on which you want validation to fire. In above code I have used "Next" in code you can see
if(buttonName.equalsIgnoreCas("Next"))
Yes, you can skip calling the interceptor.
Just remove the interceptor definition from your action definition in struts.xml file.
i.e., remove <interceptor-ref name="conversionError"/>
Mainly this interceptor adds any error found in the ActionContext's conversionErrors map as a field error (provided that the action implements ValidationAware). In addition, any field that contains a validation error has its original value saved such that any subsequent requests for that value return the original value rather than the value in the action. This is important because if the value "abc" is submitted and can't be converted to an int, we want to display the original string ("abc") again rather than the int value (likely 0, which would make very little sense to the user).
After you removed this interceptor, if the struts failed to map the field with parameter of the object(i.e., from string to int), it throws result input action error.
This seems to be a better method to handle this scenario - using Conversion Validator. Repopulating Field upon conversion Error section is something very useful:
http://struts.apache.org/2.0.14/docs/conversion-validator.html

Resources