> Instead of requiring anyone who wants to construct a control to make a messy
> call on the bxml serializer, I've taken to the following methodology:
>
> * For any new user control, create a pair of files ClassName.java and
> classname.bxml. (I'm following the naming convention I see in the samples,
> but now that I think of it, is there any reason for them not to be named
> identically up to the extension?)
We talked about adopting this convention a while back. However, I actually
prefer the current naming convention. Naming the files Foo.java and Foo.bxml
implies that they are partial classes, which isn't the case. Developers may
expect "new Foo()" to produce the same results as deserializing Foo.bxml -
since it won't, this could be confusing. Using the "Foo.java/foo.bxml"
convention avoids this ambiguity.
> * The bxml top-level element is my:ClassName, with a suitable "my" namespace
> defined; ClassName is declared to implement Bindable and extend whatever
> Pivot control I'm interested in acting like. Eclipse helpfully supplies me
> semi-automatically with an initialize method, which I can sort of treat like
> a parameterless constructor.
It's sort of like a constructor, though you of course can also define an actual
constructor.
> * In lieu of a proper constructor, I add a static factory method
>
> public static ClassName create() throws IOException,
> SerializationException {
> BXMLSerializer bxmlSerializer = new BXMLSerializer();
> return (ClassName)bxmlSerializer.readObject(ClassName.class,
> "classname.bxml");
> }
>
> so that code which in other systems might call new ClassName() can almost as
> painlessly call ClassName.create().
This is an interesting idea. I wonder if it might be worth adding a
parameterized static method to BXMLSerializer to do this, so you don't have to
add this to every class:
public static <T> T create(URL, Resources) throws IOException,
SerializationException { ... }
(or something like that)
> So the big remaining hole (well, besides the lack of Intellisense in the bxml
> editor, which I'm not really expecting to see filled any time soon), is
> hooking up event listeners. It's kind of painful right now. I typically end
> up writing things like this in the initialize method:
>
> buttonLogin.getButtonPressListeners().add(new ButtonPressListener() {
> @Override
> public void buttonPressed(Button button) {
> loginButtonPressed();
> }
> });
Yup - typical inner class listener implementation.
> writing the program logic in a separate method (unless it's completely
> trivial), which in many cases has to be a separate method anyway, because
> "this" refers to the wrong thing in the event listener, thanks to Java not
> really having closures.
The "this" reference in the inner class points to itself, but you can always
use <OuterClass>.this to get access to the containing object if you need to.
However, in most cases this isn't necessary since inner classes don't need to
qualify access to outer class methods.
> Of course, what I want to write is something like the following in the bxml
> file:
>
> <PushButton bxml:id="buttonLogin" buttonData="Login"
> buttonPressed="loginButtonPressed"/>
Seems like a reasonable request, and I like the concept. However, I see a
couple potential issues with it:
- Without the "ButtonPressListener" prefix on "buttonPressed", we don't know
which interface defines that method. We'd have to look for it based on naming
convention. First, we'd have to look for all interfaces whose names start with
"PushButton". Since none of those define "buttonPressed", we'd have to move up
the class hierarchy to "Button", and so on (this isn't necessary in XAML
because .NET uses delegates instead of interfaces for listeners).
- In general, I'd expect handler methods such as "loginButtonPressed" to be
private (you probably don't want to expose this logic to arbitrary callers). We
can use reflection to call a private method, but that requires that the code be
trusted (i.e. you couldn't do this in an applet unless it was signed). This
isn't necessarily a blocker - the same applies to the @BXML annotation, which
is often used to populate private members. It's just something to consider.
- As a developer, I might expect the "loginButtonPressed" method to be defined
in script within the page, rather than as a method on the root object. In other
words, there's nothing in the syntax that lets me know what object defines the
method. With the existing syntax, it is clear:
<my:MyWindow bxml:id="myWindow">
<!-- Calls script function -->
<PushButton buttonData="Login"
ButtonPressListeners.buttonPressed="loginButtonPressed()"/>
<!-- Calls login() method on root object -->
<PushButton buttonData="Login"
ButtonPressListeners.buttonPressed="myWindow.login()"/>
</my:MyWindow>
The downside is that it is not possible to call private methods this way -
however, it could be argued that exposing higher-level operations like "login"
via public methods promotes better design anyways.
- Finally, it seems like the loginButtonPressed() method should implement the
syntax defined by the buttonPressed() method. Because .NET uses delegates for
event handlers (and XAML is compiled), this can be enforced by the compiler.
However, since BXML is not compiled (and Java does not support delegates), we
can't enforce it. This again suggests that the handler should be script code
rather than the name of a method on the root object.
FWIW, I generally don't use attribute-based event handlers - I prefer the
element-based syntax:
<PushButton buttonData="Login">
<buttonPressListeners>
function buttonPressed(button) {
myWindow.login();
}
</buttonPressListeners>
</PushButton>
Yes, it's a bit more verbose, but I find it easier to read and write. It is
also much easier to implement listeners with multiple handler methods this way.
G