Skip to main content

Beans Binding for Me? You Shouldn't Have.

Posted by diverson on April 3, 2007 at 5:17 PM PDT

New Toys!

As it turns out, yesterday was a momentous day. No, not because of my last post, but thank you for thinking so. Actually, Shannon Hickey finally managed to navigate through the licensing maze (scariest thing since Minotaurs lived on Crete) and the new Beans Binding framework has been released!

Greedy bastard that I am, I downloaded it as soon as I found out it was available. I decided to subject it to the Clueless Newbie Test. Which is, "How long would it take a complete neophyte (that'd be me) to download the library, compile it, and make it do something useful?" I'm using a loose definition of the word useful here, but the result was: 20 minutes.

I decided to try to see how nicely beans binding plays with the Swing app framework. You would think that they would integrate nicely, but one never knows. Sometimes the Sun gods like to torture us just to watch us squirm like little ants under a magnifying glass. Ok, I've never put an ant under a magnifying glass but I'm sure it's uncomfortable for the little guys and there must be squirming involved. Which is just how I've felt at times using certain Java technologies.

So what brilliant demonstration of the awesome power of beans binding did I come up with? Well, I was thinking that if counting aloud can be rude, then we need some way to determine when counting is allowed. The really scary part is, that's all the logic I need to pursue my latest plot to take over the world. This time my instrument of world domination will be...the JCheckBox! Mwah, hah, hah, hah.

Um...What?

So I started with the code from Action Game 1, and transformed it into the new and improved Action Game 2 by adding my weapon of mass construction, the check box.

  protected void startup(String[] args)
    {
      ...
     
      JCheckBox check = new JCheckBox();
      check.setName( "allowCountingCheck" );

      JPanel panel = new JPanel();
      panel.setBorder( BorderFactory.createTitledBorder( rMap.getString( "countingDemo" ) ) );
      panel.setLayout( new BorderLayout() );
      panel.add( label, BorderLayout.NORTH );
      panel.add( btn, BorderLayout.CENTER );
      panel.add( check, BorderLayout.SOUTH );
      show( panel );     
    }

And I made a small addition to the .properties file as well (now called ActionGame2.properties, naturally).

  allowCountingCheck.text=Allow Counting
  allowCountingCheck.font=Arial-BOLD-14

The idea is that the check box enables or disables the "Start Counting" button. Simple right? One thing I've learned from watching James Bond is that all world domination plans should be kept as simple as possible. Oh, and don't explain them to the hero until after you shoot him put him to sleep with a gentle tranquilizer. The whole thing now looks like this:

Dear UN, that will be 1 MILLION dollars...
countingAllowed1.png

Look Mom, No ActionListeners!

All we need now is to somehow tie the state of the checkbox to the button's enabled property. An ActionListener you say? Hmm. Why don't you just step out onto that bridge over the shark tank while I push this big red button here.

With beans binding, there are no action listeners required. Just a minor modification to our startup method:

protected void startup(String[] args)
{
  ...
 
  JButton btn = new JButton();
  btn.setName( "startCountingBtn" );
  btn.setAction( aMap.get( "startCounting" ) );

  JCheckBox check = new JCheckBox();
  check.setName( "allowCountingCheck" );

  /*** NO ActionListeners ALLOWED!!! ***/
  BindingContext context = new BindingContext();
  context.addBinding( btn, "${enabled}", check, "selected" );
  context.bind();

  JPanel panel = new JPanel();
  panel.setBorder( BorderFactory.createTitledBorder( rMap.getString( "countingDemo" ) ) );
  panel.setLayout( new BorderLayout() );
  panel.add( label, BorderLayout.NORTH );
  panel.add( btn, BorderLayout.CENTER );
  panel.add( check, BorderLayout.SOUTH );
  show( panel );
}

Those three little lines with the BindingContext in them are all you need to tie the check box to the button. How's that for slick?

But Wait, There's More!

My blogs have never been short and to the point. So why start now? There is another cool way to do this. Say you don't want to tie the checkbox directly to the button. Say you need the check box's state to be tracked by a property in some Java bean. Say whatever you want, but I'm going to show this to you anyway.

The first thing we need is a new property in our ActionGame2 class:

  private boolean countingAllowed = true;
 
  public boolean isCountingAllowed()
  {
    return countingAllowed;
  }
 
  public void setCountingAllowed(boolean allowed)
  {
    boolean old = isCountingAllowed();
    countingAllowed = allowed;
    firePropertyChange( "countingAllowed", old, countingAllowed );
  }

Since the app framework's Application class is derived from AbstractBean we inherit all of the property change support we need. All we have to do to implement proper bean behavior is to call firePropertyChange as I did above. Next we just alter our binding a little.

  context.addBinding( this, "${countingAllowed}", check, "selected" );

And viola! Now we are tracking the check box's state through our bean property! Oh, we still need to enable the button don't we. Picky, picky. Actually this gives us a chance to show off another cool feature of the Swing app framework's @Action annotation.

I Can Disable That Button In 35 Characters

All we need to do is add an enabledProperty to our startCounting annotation:

  @Action( block = Block.COMPONENT, enabledProperty = "countingAllowed" )
  public CountingTask startCounting( ActionEvent ev )
  {
    return new CountingTask();
  }

And that's all there is to it! Now the enabled state of the action is tied to the state of our countingAllowed property. OMG this is, like, so easy! Are you sure this is Swing?

The complete source code is listed below.

  package playground.swing.framework.actions;

  // import lots.of.classes

  public class ActionGame2 extends SingleFrameApplication
  {
    private boolean countingAllowed = true;

    public boolean isCountingAllowed()
    {
      return countingAllowed;
    }

    public void setCountingAllowed(boolean allowed)
    {
      boolean old = isCountingAllowed();
      countingAllowed = allowed;
      firePropertyChange( "countingAllowed", old, countingAllowed );
    }

    @Action( block = Block.COMPONENT, enabledProperty = "countingAllowed" )
    public CountingTask startCounting( ActionEvent ev )
    {
      return new CountingTask();
    }

    protected void startup(String[] args)
    {
      ApplicationActionMap aMap = ApplicationContext.getInstance().getActionMap( getClass(), this );
      ResourceMap rMap = ApplicationContext.getInstance().getResourceMap( getClass() );

      JLabel label = new JLabel();
      label.setName( "titleLabel" );

      JButton btn = new JButton();
      btn.setName( "startCountingBtn" );
      btn.setAction( aMap.get( "startCounting" ) );

      JCheckBox check = new JCheckBox();
      check.setName( "allowCountingCheck" );

      BindingContext context = new BindingContext();
      context.addBinding( this, "${countingAllowed}", check, "selected" );
      context.bind();

      JPanel panel = new JPanel();
      panel.setBorder( BorderFactory.createTitledBorder( rMap.getString( "countingDemo" ) ) );
      panel.setLayout( new BorderLayout() );
      panel.add( label, BorderLayout.NORTH );
      panel.add( btn, BorderLayout.CENTER );
      panel.add( check, BorderLayout.SOUTH );
      show( panel );
    }

    public static void main( String[] args )
    {
      Application.launch( ActionGame2.class, args );
    }
  } 

And here are a couple of gratuitous screen shots of our masterpiece.

countingIsAllowed.png countingNotAllowed.png
Counting is allowed. Counting aloud is rude!

I'm definitely going to be playing with this new framework a lot over the next few weeks. I wonder what I'll do first when I'm Grand Overlord of the Earth...

Related Topics >>