Binding a textfield to a number with Griffon and JavaFX - validation

I'm using Griffon 1.2.0 and JavaFX, and I'd like to bind a textfield to a number. I thought I'd be able to do it simply by defining the model property as a Float, but it doesn't seem to like that, even if I define a converter. I looked into the Validator plugin but that only seems to work for Swing (not JavaFX) - which is a shame because this presentation makes it look really good: http://www.slideshare.net/ecspike/introduction-to-griffon (page 67 shows exactly the kind of functionality I'd like).
In the meantime, I've just added a property change listener in the noparent block of my view as suggested here What is the recommended way to make a numeric TextField in JavaFX?:
amount2.textProperty().addListener({ ObservableValue<? extends String> observable, String oldValue, String newValue ->
try {
Integer.parseInt(newValue);
} catch (Exception e) {
observable.setValue(oldValue);
}
} as ChangeListener<String>)
This ensures the user can only enter numbers, but are there better options? I haven't found anything in the JavaFX space that rivals the Validation plugin functionality - should I give up on JavaFX and go back to Swing?

I thought this functionality would be already provided by GroovyFX's #FXBindable and bind() node, that is
class SampleModel {
#FxBindable float number
}
// View
textField(text:bind(target: model, 'number', converter: { v -> /* insert converter code here */))
considering that GroovyFX's bind() is a copy of SwingBuilder's bind().

Related

How can I code a class to receive a property for use with custom renderers?

I have seen this coding style:
public CustomTextCell()
{
}
public static readonly BindableProperty IsCheckedProperty =
BindableProperty.Create(
"IsChecked", typeof(bool), typeof(CustomTextCell),
defaultValue: false);
public bool IsChecked
{
get { return (bool)GetValue(IsCheckedProperty); }
set { SetValue(IsCheckedProperty, value); }
}
}
and this:
public class ExtViewCell : ViewCell
{
public bool NoTap { get; set; }
}
Can someone help explain the difference. Is one serving a different function from the other? In my case all I need is to pass to a custom renderer the value of NoTap. Should I code it like in the first or second example?
The second one is a POCO - a plain old C# object - that is relatively self-explanatory, but serves not much more purpose that holding data - and not that much in this case.
The first one is a bit more interesting, especially in the context of MVVM. SetValue does more than just setting the value, but will (in most cases) raise PropertyChanged event (see INotifyPropertyChanged), to notify subscribers that, well, a property has changed.
Now how does this relate to your custom renderer? You could implement the property in your view as a plain property - i.e. without notifications - and it might work (cannot tell, though, since I do not know your custom renderer) when setting IsChecked initially (and without binding). Anyway, imagine you'll update the value of IsChecked. You do so from your code and wonder, why this change is not reflected in your custom renderer. But how is your renderer supposed to know? Polling each and every property might be possible for smaller forms, but is a terrible waste of resources. (And Xamarin.Forms just does not work this way.) You'll page/view has to tell your custom renderer, that something has changed. INotifyPropertyChanged to the rescue. In your custom renderer you can subscribe to PropertyChanged event and react to IsChecked being changed, updating your native view.

Is it possible to provide UI selectable options to custom msbuild tasks?

I have built a custom msbuild task that I use to convert 3D models in the format I use in my engine. However there are some optional behaviours that I would like to provide. For example allowing the user to choose whether to compute the tangent array or not, whether to reverse the winding order of the indices, etc.
In the actual UI where you select the Build action for each file, is it possible to define custom fields that would then be fed to the input parameters of the task? Such as a "Compute Tangents" dropbox where you can choose True or False?
If that is possible, how? Are there any alternatives besides defining multiple tasks? I.e. ConvertModelTask, ConvertModelComputeTangentTask, ConvertModelReverseIndicesTask, etc.
Everything in a MsBuild Custom Task, has to have "settable properties" to drive behavior.
Option 1.
Define an ENUM-esque to drive you behavior.
From memory, the MSBuild.ExtensionPack.tasks and MSBuild.ExtensionPack.Xml.XmlFile TaskAction="ReadElementText" does this type of thing.
The "TaskAction" is the enum-esque thing. I say "esque", because all you can do on the outside is set a string. and then in the code, convert the string to an internal enum.
See code here:
http://searchcode.com/codesearch/view/14325280
Option 2: You can still use OO on the tasks. Create a BaseTask (abstract) for shared logic), and then subclass it, and make the other class a subclass, and the msbuild task that you call.
SvnExport does this. SvnClient is the base class. And it has several subclasses.
See code here:
https://github.com/loresoft/msbuildtasks/blob/master/Source/MSBuild.Community.Tasks/Subversion/SvnExport.cs
You can probably dive deep with EnvDTE or UITypeEditor but since you already have a custom task why not keep it simple with a basic WinForm?
namespace ClassLibrary1
{
public class Class1 : Task
{
public bool ComputeTangents { set { _computeTangents = value; } }
private bool? _computeTangents;
public override bool Execute()
{
if (!_computeTangents.HasValue)
using (var form1 = new Form1())
{
form1.ShowDialog();
_computeTangents = form1.checkBox1.Checked;
}
Log.LogMessage("Compute Tangents: {0}", _computeTangents.Value);
return !Log.HasLoggedErrors;
}
}
}

How to prevent TableView from doing TableColumn re-order in javaFX 8?

I read this https://bugs.openjdk.java.net/browse/JDK-8102128 but I haven't found something in the Api of JavaFX 8.There isn't any way of prevent reordering in TableView?
Spent half of a day trying to solve the problem. Maybe my investigation can be useful for someone else.
It's possible to disable column reordering using some hacks. And here're the steps:
Get table header. This can be done as described here: https://stackoverflow.com/a/12465155
Add change event listener to reorderingProperty which sets reordering back to false.
Full code is here:
tableView.widthProperty().addListener(new ChangeListener<Number>()
{
#Override
public void changed(ObservableValue<? extends Number> source, Number oldWidth, Number newWidth)
{
TableHeaderRow header = (TableHeaderRow) tableView.lookup("TableHeaderRow");
header.reorderingProperty().addListener(new ChangeListener<Boolean>() {
#Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
header.setReordering(false);
}
});
}
});
I'm not sure about side effects of this solution, but quick tests show that solution works well.
I know the question tagged java-8 but for those who wander along, In Java 9 all above codes would break because of modularity which make .sun package inaccessible and removal of impl_. Despite these changes, it introduces convenient public methods that you can use which are:
setReorderable(boolean value)
getReorderable()
for TableColumnBase such as TableColumn to be used for set Reorderable,
I like the idea from Alexander Chingarev but I think the code will produce memory leaks! Every time the width property changes a new listener is registered and older listeners are never garbage collected!
So you can either store a reference to the listener and make sure to remove it from the observable value (in this case the reordering property) before adding a new listener or you make sure the listener is only added once to the reordering property.
I used the skin property which only changes once (if I'm not mistaken) to add my listener:
tableView.skinProperty().addListener((obs, oldSkin, newSkin) -> {
final TableHeaderRow header = (TableHeaderRow) lookup("TableHeaderRow");
header.reorderingProperty().addListener((o, oldVal, newVal) -> header.setReordering(false));
});
An alternative solution which does not require use of private API in Java 8.
public static <T> void preventColumnReordering(TableView<T> tableView) {
Platform.runLater(() -> {
for (Node header : tableView.lookupAll(".column-header")) {
header.addEventFilter(MouseEvent.MOUSE_DRAGGED, Event::consume);
}
});
}
The API added in Java 8 RT-24669, is column.impl_setReorderable(false);.
You can see the impl_setReorderable definition in the TableColumnBase.java source.
However, this API is only intended for internal use, marked as deprecated and does not form part of the public JavaFX API.
In general, impl_ methods in JavaFX will be removed at some time in the future, potentially breaking your code at that time if you tried to use them.
In reviewing the code for the implementation of the reorderable property on table columns, it works by ignoring certain mouse events directed to the table column header. Search the TableColumnHeader.java code for isReorderable for more info.
I'm not sure how you would accomplish the exact same behaviour as the impl_setReorderable API using only the public JavaFX API without performing quite a lot of work.
Expanding on a previous contribution I found this
tableView.getColumns().forEach(e -> e.setReorderable(false));
to be very useful in Java 13.
Thanks! It works with lambda as well if you have multiple tables you want to disable.
private void yourTableListeners(){
yourTableView.widthProperty()
.addListener((observable, oldValue, newValue) -> yourReusableDisablingMethod(yourTableView));
anotherTableView.widthProperty()
.addListener((observable, oldValue, newValue) -> yourReusableDisablingMethod(anotherTableView));
}
#SuppressWarnings("restriction")
private void yourReusableDisablingMethod(TableView tableView) {
TableHeaderRow header = (TableHeaderRow) tableView.lookup("TableHeaderRow");
header.reorderingProperty().addListener(new ChangeListener<Boolean>() {
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
header.setReordering(false);
}
});
}
For FXML user it can easily done by adding reorderable="false"
<TableColumn fx:id="test" text="ColumnTitle" reorderable="false"/>
If you down want sorting on the tableview columns and tab reordering there is a simple solution
private void disableHeaderReorderingAndSorting() {
tableView.skinProperty().addListener((a, b, newSkin) ->
{
Pane header = (Pane) tableView.lookup("TableHeaderRow");
header.setMouseTransparent(true);
});
}

MongoDB - override default Serializer for a C# primitive type

I'd like to change the representation of C# Doubles to rounded Int64 with a four decimal place shift in the serialization C# Driver's stack for MongoDB. In other words, store (Double)29.99 as (Int64)299900
I'd like this to be transparent to my app. I've had a look at custom serializers but I don't want to override everything and then switch on the Type with fallback to the default, as that's a bit messy.
I can see that RegisterSerializer() won't let me add one for an existing type, and that BsonDefaultSerializationProvider has a static list of primitive serializers and it's marked as internal with private members so I can't easily subclass.
I can also see that it's possible to RepresentAs Int64 for Doubles, but this is a cast not a conversion. I need essentially a cast AND a conversion in both serialization directions.
I wish I could just give the default serializer a custom serializer to override one of it's own, but that would mean a dirty hack.
Am I missing a really easy way?
You can definitely do this, you just have to get the timing right. When the driver starts up there are no serializers registered. When it needs a serializer, it looks it up in the dictionary where it keeps track of the serializers it knows about (i.e. the ones that have been registered). Only it it can't find one in the dictionary does it start figuring out where to get one (including calling the serialization providers) and if it finds one it registers it.
The limitation in RegisterSerializer is there so that you can't replace an existing serializer that has already been used. But that doesn't mean you can't register your own if you do it early enough.
However, keep in mind that registering a serializer is a global operation, so if you register a custom serializer for double it will be used for all doubles, which could lead to unexpected results!
Anyway, you could write the custom serializer something like this:
public class CustomDoubleSerializer : BsonBaseSerializer
{
public override object Deserialize(BsonReader bsonReader, Type nominalType, Type actualType, IBsonSerializationOptions options)
{
var rep = bsonReader.ReadInt64();
return rep / 100.0;
}
public override void Serialize(BsonWriter bsonWriter, Type nominalType, object value, IBsonSerializationOptions options)
{
var rep = (long)((double)value * 100);
bsonWriter.WriteInt64(rep);
}
}
And register it like this:
BsonSerializer.RegisterSerializer(typeof(double), new CustomDoubleSerializer());
You could test it using the following class:
public class C
{
public int Id;
public double X;
}
and this code:
BsonSerializer.RegisterSerializer(typeof(double), new CustomDoubleSerializer());
var c = new C { Id = 1, X = 29.99 };
var json = c.ToJson();
Console.WriteLine(json);
var r = BsonSerializer.Deserialize<C>(json);
Console.WriteLine(r.X);
You can also use your own serialization provider to tell Mongo which serializer to use for certain types, which I ended up doing to mitigate some of the timing issues mentioned when trying to override existing serializers. Here's an example of a serialisation provider that overrides how to serialize decimals:
public class CustomSerializationProvider : IBsonSerializationProvider
{
public IBsonSerializer GetSerializer(Type type)
{
if (type == typeof(decimal)) return new DecimalSerializer(BsonType.Decimal128);
return null; // falls back to Mongo defaults
}
}
If you return null from your custom serialization provider, it will fall back to using Mongo's default serialization provider.
Once you've written your provider, you just need to register it:
BsonSerializer.RegisterSerializationProvider(new CustomSerializationProvider());
I looked through the latest iteration of the driver's code and checked if there's some sort of backdoor to set custom serializers. I am afraid there's none; you should open an issue in the project's bug tracker if you think this needs to be looked at for future iterations of the driver (https://jira.mongodb.org/).
Personally, I'd open a ticket -- and if a quick workaround is necessary or required, I'd subclass DoubleSerializer, implement the new behavior, and then use Reflection to inject it into either MongoDB.Bson.Serialization.Serializers.DoubleSerializer.__instance or MongoDB.Bson.Serialization.BsonDefaultSerializationProvider.__serializers.

Enterprise Library Validation Block - Should validation be placed on class or interface?

I am not sure where the best place to put validation (using the Enterprise Library Validation Block) is? Should it be on the class or on the interface?
Things that may effect it
Validation rules would not be changed in classes which inherit from the interface.
Validation rules would not be changed in classes which inherit from the class.
Inheritance will occur from the class in most cases - I suspect some fringe cases to inherit from the interface (but I would try and avoid it).
The interface main use is for DI which will be done with the Unity block.
The way you are trying to use the Validation Block with DI, I dont think its a problem if you set the attributes at interface level. Also, I dont think it should create problems in the inheritance chain. However, I have mostly seen this block used at class level, with an intent to keep interfaces not over specify things. IMO i dont see a big threat in doing this.
Be very careful here, your test is too simple.
This will not work as you expect for SelfValidation Validators or Class Validators, only for the simple property validators like you have there.
Also, if you are using the PropertyProxyValidator in an ASP.NET page, iI don;t believe it will work either, because it only looks a field validators, not inherited/implemented validators...
Yes big holes in the VAB if you ask me..
For the sake of completeness I decided to write a small test to make sure it would work as expected and it does, I'm just posting it here in case anyone else wants it in future.
using System;
using Microsoft.Practices.EnterpriseLibrary.Validation;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
ISpike spike = new Spike();
spike.Name = "A really long name that will fail.";
ValidationResults r = Validation.Validate<ISpike>(spike);
if (!r.IsValid)
{
throw new InvalidOperationException("Validation error found.");
}
}
}
public class Spike : ConsoleApplication1.ISpike
{
public string Name { get; set; }
}
interface ISpike
{
[StringLengthValidator(2, 5)]
string Name { get; set; }
}
}
What version of Enterprise Library are you using for your code example? I tried it using Enterprise Library 5.0, but it didn't work.
I tracked it down to the following section of code w/in the EL5.0 source code:
[namespace Microsoft.Practices.EnterpriseLibrary.Validation]
[public static class Validation]
public static ValidationResults Validate<T>(T target, ValidationSpecificationSource source)
{
Type targetType = target != null ? target.GetType() : typeof(T);
Validator validator = ValidationFactory.CreateValidator(targetType, source);
return validator.Validate(target);
}
If the target object is defined, then target.GetType() will return the most specific class definition, NOT the interface definition.
My workaround is to replace your line:
ValidationResults r = Validation.Validate<ISpike>(spike);
With:
ValidationResults r ValidationFactory.CreateValidator<ISpike>().Validate(spike);
This got it working for me.

Resources