JavaFX access controller's variables from Scene Builder - javafx-2

If I declare something like
#FXML
private final static double PREF_SPACING = 10d;
or
#FXML
private Insets insets = new Insets(10d);
in the controller class,
is there a way to use their values in Scene Builder?
When I want to change the value, I want to change
it only once, in the controller class.

PRELIMINARY ANSWER
I haven't yet tried all of the techniques below, but it seems to be the way you would do it from reading the documentation. If I get some time, I'll try it out later and update this answer with results (or somebody else can do this and post a new answer or edit this one to create a definitive answer). I just wanted to publish something now to point you in what I believe to be the right direction.
If the below is not what you are looking for, add a few more specifics to your questions to fully describe what you want.
Don't using the #FXML annotation here. #FXML is for injecting values from the markup into the controller, not the other way around.
For your first example which is a constant, let's say your controller class is:
class MyControllerType {
public final static double PREF_SPACING = 10d;
}
Then in your fxml, reference the constant:
<?import MyControllerType?>
...
<VBox>
<spacing><MyControllerType fx:constant="PREF_SPACING"/></spacing>
</VBox>
For your second sample which is not a constant or a part of the SceneGraph, you can use an fx:define element to instantiate an instance of the class. Note that you can't directly instantiate an Insets class from FXML as it has no builder class nor zero length constructor. So what you might be able to do is create another placeholder class for the information and instantiate a reference to that in your FXML (or you can create a Builder that FXML can use to instantiate the Insets).
class InsetsHolder {
private Insets insets = new Insets(10d);
public Insets getInsets();
}
<?import InsetsHolder?>
<fx:define>
<InsetsHolder fx:id="i"/>
</fx:define>
<VBox>
<Button text="Click Me!" VBox.margin="$i.insets"/>
</VBox>
SceneBuilder should be able to read fxml files which use the fx:define and fx:constant notation, as well as (possibly) make use of the reference expression $i.insets. SceneBuilder might not have any UI to allow you to edit the values from within the SceneBuilder application, so you will probably need to hand edit the fxml file portions related to the fx:define and fx:constant elements if you wish to make use of these structures.
There is an executable example of using an fx:define element in this mailing list post on designing resolution independent units in FXML.
In general, I think I'd be a bit cautious of maintaining these kind of dependencies between fxml and java code. It may be more prudent to do more of this kind of stuff in plain Java code within the context of the controller initialize method as scottb suggests.

The #FXML annotation enables the JavaFX objects whose names you defined (fx:id) to have their references reflectively injected into nonpublic fields in the controller object as the scene graph is loaded from the fxml markup.
To the best of my knowledge, this is a one way operation. There is no provision for having named static class variables in the controller object injected into the scene graph during loading.
You can accomplish something very similar to what you are requesting by defining the values that you want set as class variables in your controller object's class, and then setting the appropriate object properties programmatically (rather than in markup) in the initialize() method of your controller object.
The initialize() method is called (if it is present) after the loading of the scene graph is complete (so all the GUI objects will have been instantiated) but before control has returned to your application's invoking code.

Related

Is there a way to prevent creation of a data class item in C# WindowsForms UserControl

If I create a UserControl, to create and edit an instance of a data class e.g. Person in C# WindowsForms (call it PersonControl), the framework automatically adds an instance of Person in PersonControl.Designer with some default values for the properties and fills the item controls with those values. This behavior has a number of side effects which I would like to avoid.
Question: is there a defined way to prevent creation of a data class instance in UserControl.Designer?
I think you missing the DesignerSerializationVisibility attribute. If you have a custom control every public property that you add will automatically be serialized. You can use this attribute to disable the serialization for a property. I also recommend to add the Browsable attribute which will hide the property from the designer. If you want more control over serialization, like you want to serialize only when another property is set to true you can create a special named method which will then called by the designer Defining Default Values with the ShouldSerialize and Reset Methods. There was a MSDN Magazine where a lots of winform learning resource was relased there are some gems about winform internal working. If you interested in you can quickly look trhrough it. My favorite is. Create And Host Custom Designers With The .NET Framework 2.0
Sorry but i didn't mention another attribute DefaultValue You can use the attribute the following way.
public partial class PersonEditControl : UserControl
{
[DefaultValue(null)] // This attribute tells the designer if the property value matches what we specified in the attribute(null) it should not store the property value.
public PersonData? Person { get; set; }
public PersonEditControl()
{
InitializeComponent();
}
}

How to display data from a model from a Yii 1.1 layout file?

I'd like to have the layout reflect some data from a model. However, the render method from the CController class passes the structured data only to the view file, while the layout file only gets the rendered view passed.
So, how to best have the layout display data from a model?
Two possibilities come to mind:
Make Yii's layout file a no-op, mimicking layout logic manually from the view.
Override CController's render method in its subclass.
I'm not so happy with either variant, so maybe someone has a cleaner idea on how to do it?
Another way is to define a public variable in your controller class, something like:
class MyController extends Controller {
public $test = 'foo';
....
That value can then be accessed within a layout:
echo $this->test;
And manipulated in an action:
public function actionMyaction(){
$this->test = "bar";
...
Obviously it's not ideal if you have many variables that you need to use in a layout. One solution is to use an array of parameters. Alternatively you could look at making your layout more minimal and using CWidget to create reusable components for use inside your views.
For example, you obviously wouldn't want to have the code for your main navigation duplicated inside every view, so the obvious solution is to have in the layout, but if it becomes inconvenient to handle the data you could have an instance of a widget that renders out the navigation inside each view (and you can pass data to the CWidget class) something like:
$this->widget("MainNavigation",array("params"=>$params));

extend class in Java FX?

In many occasions JavaFX needs to be customized with classes that extend existing ones. I tried this approach, for example to add a method to the NumberAxis class that would enable the label of the axis to be rotated.
But I got a "NumberAxis is declared final, can't be extended" compiler error. I wonder how people who extend classes do? Do they have access to the source code of javafx, modify it to make some classes not final, and recompile it? (sounds tricky! )
Making lots of classes final in the JavaFX framework was an intentional decision by the framework developers. To get a flavor of why it's done, see the Making Color Final proposal. That's just an example, there are other reasons. I think experience with subclassing in the Swing framework was that it caused errors and maintenance issues that the JavaFX designers wanted to avoid, so many things are made final.
There are other way to extend functionality than to directly subclass. Some alternatives for your rotation example:
aggregation: include the NumberAxis as a member of new class (e.g. NumberAxisWithRotatableText) which adds an accessor to get the underlying NumberAxis node and a method to perform the rotation (e.g. via a lookup as explained below).
composition: for example extend Pane, add a NumberAxis, disable the standard text drawing on the axis and add rotated labels yourself as needed.
css stylesheet: for example use a selector to lookup the text in the NumberAxis and the -fx-rotate attribute to rotate it.
node lookup: Use a node.lookup to get at the underlying text node, and apply the rotation via an API.
skin: All controls have a skin class attached them, replace the default skin class with a custom one.
subclass an alternate class: Subclass the abstract ValueAxis class rather than the final NumberAxis class.
Source code for JavaFX is available with build instructions. However, I don't recommend hacking a personal copy of the source code to remove final constructs unless you also submit it as an accepted patch to the JavaFX system so that you can be sure that your app won't break on a standard JavaFX install.
If you really think it is a good idea for a given class to be subclassable, then log a change request. Sometimes the JavaFX developers are overzealous and make stuff final which would be better not being final. NumberAxis perhaps falls into that category.

JavaFx: how to reference main Controller class instance from CustomComponentController class?

WHAT I HAVE is a standard JavaFX application: Main.java, MainController.java & main.fxml. To add custom component, I created CustomComponentController.java and custom_component_controller.fxml.
PROBLEM is that in CustomComponentController methods I need to reference other methods and standard components from MenuController. I add public static MainController mc; to MainController class body, so that it can be seen from CustomComponentController (MainController.mc.neededMethod()). Then I try to pass everything to it in MainController.initialize() method (mc = this;) - when debugging this breakpoint, I see this full of components instances, but mc remains with null components afterwards.
QUESTION is how to reference the running instance of MainController to use its components and methods in other classes and to crossreference different custom components from each other? How to clean MainController code from event handlers and assistance methods of components by moving it all to component's own class?
I tried the following approaches, but found no way to make them work without errors:
Accessing FXML controller class
How can I access a Controller class in JavaFx 2.0?
JavaFX 2.0 + FXML. Updating scene values from a different Task
JavaFX 2.2 -fx:include - how to access parent controller from child controller
The problem can be solved if you comply the following conditions:
Not only public, but obligatory static MainController mc should be.
Do not forget id in fxml for CustomComponentController: <CustomComponentController fx:id="cc"/>, where cc is the name of the "#FXML imported" CustomComponentController in your MainController class.
Omit parameter fx:controller="main.CustomComponentController" in custom_component_controller.fxml as it results in "Controller value already specified" error (a conflict between main.fxml and custom_component_controller.fxml markup declared controllers).
Put mc = this; in the beginning of MainController's initialize() method. Before using mc in CustomComponentController class, check if it's not null. It can be null when all components, including CustomComponentController, are instantiated at application startup, but there is no mc instance yet. MainController method initialize() where MainController is instantiated is called after components are loaded. Therefore better practice is to use approach in the next paragraph.
In main.fxml create primary component of the same type that CustomComponentController and with the only fx:id parameter. Replace primary component with your CustomComponentController by creating reloadCustomComponents() method and calling it from CustomComponentController's initialize() method. Do it by adding the following to reloadCustomComponents() method:
customComponentAnchorPane.getChildren().remove(customComponent);
customComponent = new customComponent();
customComponentAnchorPane.getChildren().add(customComponent);
Thus all components can be placed outside CustomComponentController with all their methods and reloaded at the startup of the apllication. All component declarations stay in MainController class and can be reached through MainController mc reference. No duplicate creating of components in detail with parameters is needed.
Your problem looks like the classic catalog-crud forms updating, I implemented an interface that I called Updatable with an update method so I could reference any catalog form with any crud form easy after passing Controller Main Class as the UserData Property of the Child Root Component's Form
Hope it Can Solve your problem

Loading Data-Backed ComboBox with Simple Types

I have a RPC method that returns a List of Strings. I want to create a ComboBox with a store that will load the values through a RpcProxy, but I can't find an example that doesn't use some sort of ModelData class.
I would prefer not to have to create a simple Bean with only one property (the string) and then have to convert the List one item at a time.
My ideal would be to create something like this:
RpcProxy<List<String>> proxy = new RpcProxy<List<String>>()...
Any suggestions?
Unfortunately, with GXT 2.2.5, you can't get around not using ModelData.
The class definition for ComboBox says it all:
public class ComboBox<D extends ModelData> extends TriggerField<D> implements SelectionProvider<D> {
...
protected ListStore<D> store;
...
So, at this point your biggest concern is keeping your code clean. If you have to make a specialized ModelData derived class, you could subclass ComboBox and keep a nested class definition for your wrapper object.
If you're not tied to using GXT 2.2.5, I would update to GXT 3.0.x and GWT 2.5.0. GXT 3 moved away from using ModelData. Now, everything accepts bean-like objects.

Resources