Container Properties

Container properties are a mechanism that allows you to pass objects around your commands and groups in a loosely coupled manner.

Once a command or group is bound to a container it has access to all the properties defined within the container hierarchy. Containers will request the property from their parent if it's not locally defined.

Standard Properties

There are a number of standard properties are used to provide common infrastructure required by the library. These are:

   CommandContainer container = new CommandContainer();
   // set the delegate mediator
   container.setDelegateMediator(new FocusTrackingDelegateMediator(myFrame));

   // set the glass pane strategy
   container.setGlassPaneStrategy(new DefaultGlassPaneStrategy());

   // set the undo context
   container.setUndoContext(new UndoContext());

User Defined Properties

You can also define your own properties using putProperty(String, Object) and access them from commands and groups as required.

   container.putProperty("my-custom-component", myCustomComponent);

Accessing Container Properties

Container properties can be accessed from commands using the convenience getContainerProperty(...) method. This method will check that the container isn't null as would be the case if the command wasn't bound. You can also call getCommandContainer() and invoke getProperty() if you need to ensure the container has been bound.

The following example shows accessing a container property during command execution.

   public void handleExecute()
   {
      // get the property
      Object propertyValue = getContainerProperty("myProperty");

      // and use it...
      ...
   }

Monitoring Container Properties

The situation is a little more complex if you need to add listeners to a container property to monitor its state. Container properties are only available once commands are bound and as such listeners must be updated in any of the following cases:

  1. After binding or unbinding
  2. After changes to the container hierarchy.
  3. After changes to the containers properties.

To simplify monitoring of container properties both commands and groups allow you to add ContainerPropertyListeners. These are conceptually very similar to Java bean PropertyChangeListeners, but they are notified in all the cases listed above.

Unlike PropertyChangeListeners, ContainerPropertyListeners:

  1. Only provide the new property value.
  2. May be invoked more than once with the same value.

The following shows a typical example of monitoring a container property:

   public class MyCommand extends ActionCommand
   {
      // Our model which is stored as a container property.
      private Model model;

      // Our model listener.
      private ModelListener modelListener = ...;

      /**
       * Create our command and add our property listener
       */
      public MyCommand()
      {
         super("my-command");

         // Add a container property listener to monitor the 
         // "model" property of our container.
         addContainerPropertyListener("model", 
                                      new ContainerPropertyListener()
         {
            public void propertyChanged(ContainerPropertyEvent e)
            {
               // update our model reference
               setModel((Model) e.getNewValue())
            }
         }

         updateState();
      }

      /**
       * Configure our model
       */
      public void setModel(Model model)
      {
         if (this.model != null)
         {
            // remove the listener from the old model
            this.model.removeModelListener(modelListener);
         }

         this.model = model;

         if (this.model != null)
         {
            // and listen to the new model
            this.model.addModelListener(modelListener);
         }

         // update our state to reflect the new model
         updateState();
      }

      private void updateState()
      {
         // we're only enabled if the model is configured and ok
         setEnabled(model != null && model.isInSuitableState());
      }

   }