Page 1 of 2 12 LastLast
Results 1 to 10 of 14

Thread: Question about Binding...

  1. #1
    Join Date
    Aug 2005
    Location
    London (the English one!)
    Posts
    378

    Default Question about Binding...

    Hello

    I have a question with regards to the Binding in a form.

    I am binding an object Asset, it has a STRING property called assetType.
    This property will contain a code for a given assetType.
    Code:
    class Asset {
    ....
      private String assetType;
    }
    There are only a given number of assetTypes and they are coming from
    the database. I would like to bind the types to a combo box associated
    with the property assetType (which is a string in Asset).

    My query returns a List of AssetType objects.
    Code:
    class AssetType {
       private String code;
       private String name;
       ....// getters and setters
    }
    For display purpose, I would like the comboBox to contain only the "code" of the AssetTypes?
    Is it possible?

    I have also dabbled with writing a specific binder but the fact that my Asset class has a String rather than AssetType for the property assetType does not allow me to link them...

    1/ First option: How could I force the use of a specific binder whilst using a FormBuilder.add(...) method?

    2/ using the existing formBuilder and NO specific binder, how could I tell it to bind the assetType property of my Asset with the code property of my list of AssetType?
    My Asset Form does the following (I tried...)

    Code:
    formBuilder.add(binding.createBoundComboBox("assetType",new ValueHolder(assetTypeList),"code"));
    If my DB query was simply returning a string, it would work... I was hoping that the "code" parameter would tell SpringRC to use the code property on each assetType of the assetTypeList... obviously, it was wishful thinking...

    The exception I get seems to tell me that the ValueHolder has transformed my list of AssetType into a list of String???

    Code:
    [ERROR,79364,ApplicationLifecycleAdvisor,AWT-EventQueue-0] Invalid property 'code' of bean class [java.lang.String]: Bean property 'code' is not reada
    ble or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
    org.springframework.beans.NotReadablePropertyException: Invalid property 'code' of bean class [java.lang.String]: Bean property 'code' is not readable
     or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
            at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:652)
            at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:644)
            at org.springframework.richclient.list.ComboBoxAutoCompletionInterceptorFactory$ComboBoxAutoCompletionInterceptor$BeanPropertyValueComboBoxEdi
    tor.setItem(ComboBoxAutoCompletionInterceptorFactory.java:127)
            at javax.swing.JComboBox.configureEditor(JComboBox.java:1340)
            at javax.swing.plaf.basic.BasicComboBoxUI.configureEditor(BasicComboBoxUI.java:737)
            at javax.swing.plaf.metal.MetalComboBoxUI.configureEditor(MetalComboBoxUI.java:255)
            at javax.swing.plaf.basic.BasicComboBoxUI.addEditor(BasicComboBoxUI.java:688)
            at javax.swing.plaf.basic.BasicComboBoxUI$Handler.propertyChange(BasicComboBoxUI.java:1478)
            at javax.swing.plaf.basic.BasicComboBoxUI$PropertyChangeHandler.propertyChange(BasicComboBoxUI.java:587)
            at com.jgoodies.looks.plastic.PlasticComboBoxUI$PlasticPropertyChangeListener.propertyChange(PlasticComboBoxUI.java:268)
            at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:333)
            at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:270)
            at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:312)
            at java.awt.Component.firePropertyChange(Component.java:7178)
            at javax.swing.JComponent.firePropertyChange(JComponent.java:4191)
            at javax.swing.JComboBox.setEditable(JComboBox.java:379)
            at org.springframework.richclient.list.ComboBoxAutoCompletion.<init>&#40;ComboBoxAutoCompletion.java&#58;131&#41;
            at org.springframework.richclient.list.ComboBoxAutoCompletionInterceptorFactory$ComboBoxAutoCompletionInterceptor.processComponent&#40;ComboBoxAut
    oCompletionInterceptorFactory.java&#58;180&#41;
            at org.springframework.richclient.form.builder.support.ChainedInterceptorFactory$ChainedInterceptor.processComponent&#40;ChainedInterceptorFactory
    .java&#58;82&#41;
            at org.springframework.richclient.form.binding.support.AbstractBindingFactory.interceptBinding&#40;AbstractBindingFactory.java&#58;90&#41;
            at org.springframework.richclient.form.binding.support.AbstractBindingFactory.createBinding&#40;AbstractBindingFactory.java&#58;76&#41;
            at org.springframework.richclient.form.binding.swing.SwingBindingFactory.createBoundComboBox&#40;SwingBindingFactory.java&#58;119&#41;
            at net.objectlab.safemargin.gui.forms.AssetGeneralForm.createFormControl&#40;AssetGeneralForm.java&#58;71&#41;
            at org.springframework.richclient.form.AbstractForm.createControl&#40;AbstractForm.java&#58;233&#41;
            at org.springframework.richclient.factory.AbstractControlFactory.getControl&#40;AbstractControlFactory.java&#58;48&#41;
            at org.springframework.richclient.dialog.FormBackedDialogPage.createControl&#40;FormBackedDialogPage.java&#58;70&#41;
            at org.springframework.richclient.dialog.AbstractDialogPage$1.createControl&#40;AbstractDialogPage.java&#58;50&#41;
            at org.springframework.richclient.factory.AbstractControlFactory.getControl&#40;AbstractControlFactory.java&#58;48&#41;
            at org.springframework.richclient.dialog.AbstractDialogPage.getControl&#40;AbstractDialogPage.java&#58;201&#41;
            at org.springframework.richclient.dialog.CompositeDialogPage.prepareDialogPage&#40;CompositeDialogPage.java&#58;183&#41;
            at org.springframework.richclient.dialog.CompositeDialogPage.createPageControls&#40;CompositeDialogPage.java&#58;159&#41;
            at org.springframework.richclient.dialog.TabbedDialogPage.createControl&#40;TabbedDialogPage.java&#58;45&#41;
            at org.springframework.richclient.dialog.AbstractDialogPage$1.createControl&#40;AbstractDialogPage.java&#58;50&#41;
            at org.springframework.richclient.factory.AbstractControlFactory.getControl&#40;AbstractControlFactory.java&#58;48&#41;
            at org.springframework.richclient.dialog.AbstractDialogPage.getControl&#40;AbstractDialogPage.java&#58;201&#41;
            at org.springframework.richclient.dialog.TitledPageApplicationDialog.createTitledDialogContentPane&#40;TitledPageApplicationDialog.java&#58;78&#41;
            at org.springframework.richclient.dialog.TitledApplicationDialog.createDialogContentPane&#40;TitledApplicationDialog.java&#58;132&#41;
            at org.springframework.richclient.dialog.TitledApplicationDialog.addDialogComponents&#40;TitledApplicationDialog.java&#58;120&#41;
            at org.springframework.richclient.dialog.ApplicationDialog.createDialog&#40;ApplicationDialog.java&#58;298&#41;
            at org.springframework.richclient.dialog.ApplicationDialog.showDialog&#40;ApplicationDialog.java&#58;265&#41;
            at net.objectlab.safemargin.gui.view.AssetManagerView$AssetPropertiesExecutor.execute&#40;AssetManagerView.java&#58;292&#41;
            at net.objectlab.safemargin.gui.view.AssetManagerView$5.mouseClicked&#40;AssetManagerView.java&#58;175&#41;
            at java.awt.AWTEventMulticaster.mouseClicked&#40;AWTEventMulticaster.java&#58;212&#41;
    Many thanks!

    Benoit

  2. #2
    Join Date
    Aug 2004
    Location
    Melbourne, Australia
    Posts
    335

    Default

    Benoit,

    1/ First option: How could I force the use of a specific binder whilst using a FormBuilder.add(...) method?
    1st) create a custom binder that provides a combobox for editing the assetType property. If you have a look at the petclinic sample app there is a custom binder PetTypeBinder which would be helpful here.

    2nd) register this new binder with the binder selection strategy:

    Code:
    registerBinderForPropertyType&#40;Asset.class, "assetType", new AssetTypeBinder&#40;&#41;&#41;
    this binder will now be used whenever you FormBuilder.add(...) method for the assetType property.

    2/ using the existing formBuilder and NO specific binder, how could I tell it to bind the assetType property of my Asset with the code property of my list of AssetType?
    Extract the codes from each AssetType into a list and then use that (rather than the list of AssetTypes) as the selectable items list.

    HTH

    Ollie

  3. #3
    Join Date
    Aug 2005
    Location
    London (the English one!)
    Posts
    378

    Default Along those lines...

    Hi Ollie

    Thanks for your answer.

    I was also working along those lines but I have found that I had to use the
    Code:
    registerBinderForPropertyName&#40;Class parentObjectType, String propertyName, Binder binder&#41;
    method rather than registerBinderForPropertyType.

    It works fine and I can even add more than 1 property for a given class.

    Code:
    registerBinderForPropertyName&#40;Asset.class,"assetType",new AssetTypeBinder&#40;&#41;&#41;;
    registerBinderForPropertyName&#40;Asset.class,"country",new CountryBinder&#40;&#41;&#41;;
    and the form is created by:

    Code:
    formBuilder.add&#40;"assetType"&#41;;
    formBuilder.add&#40;"country"&#41;;
    I have another question... How would you bind a listener for event on the "assetType"? Say that once an AssetType is selected, the GUI must change another attribute based on the object AssetType (not the String)... The Adapter in AssetTypeBinder knows which AssetType was selected...

    to be more precise:
    Code:
    formBuilder.add&#40;"assetType"&#41;;
    formBuilder.add&#40;bindingFactory.createBoundLabel&#40;"assetClass"&#41;&#41;;
    The assetClass should be automatically changed when the assetType is selected... How can I link the 2 binders? or change the value of the Asset.assetClass in the AssetTypeAdapter?

    Thanks!

    Regards from London,

    Benoit

  4. #4
    Join Date
    Aug 2004
    Location
    Melbourne, Australia
    Posts
    335

    Default

    I was also working along those lines but I have found that I had to use the
    Code:
    registerBinderForPropertyName(Class parentObjectType, String propertyName, Binder binder)
    method rather than registerBinderForPropertyType.
    Ooops - typo on my behaf.

    I have another question... How would you bind a listener for event on the "assetType"? Say that once an AssetType is selected, the GUI must change another attribute based on the object AssetType (not the String)... The Adapter in AssetTypeBinder knows which AssetType was selected...
    You can listen for changes to any form property by adding a property change listener to the value model for that property.

    Code:
    formModel.getValueModel&#40;"assetType"&#41;.addValueChangeListener&#40;new PropertyChangeListener&#40;&#41; &#123;
        public void propertyChange&#40;PropertyChangeEvent evt&#41; &#123;
            updateTheGuiBecauseAsssetTypeHasChanged&#40;&#41;;
        &#125;
    &#125;&#41;

    Ollie

  5. #5
    Join Date
    Aug 2005
    Location
    London (the English one!)
    Posts
    378

    Default cool

    Hi Ollie

    Cool stuff... now my killer question... the value of the assetClass depends on the AssetType OBJECT (not just the string...) so at the moment, my "binder" has the list of AssetTypes but the event triggered is a "assetType" with old/new string. Is there a way to get to the binder from the Model? or actually from the updateTheGuiBecauseAsssetTypeHasChanged() method?

    Or should I put in place a listener mechanism directly between the binder and the updateTheGuiBecauseAsssetTypeHasChanged()... How would you recommend doing this?

    Many thanks!!!

    Benoit

  6. #6
    Join Date
    Aug 2004
    Location
    Melbourne, Australia
    Posts
    335

    Default

    From an architectural perspective your form object would be much cleaner if you just changed the class of the assetType property from String to AssetType - this would also solve all the problems above - however, I assume there's a good reason for not having done this in the first place.

    Or should I put in place a listener mechanism directly between the binder and the updateTheGuiBecauseAsssetTypeHasChanged()... How would you recommend doing this?
    No. This would intorduce a coupling between model and your view (the binder) which is exactly what we're trying to avoid by having a form modle

    In your AssetTypeBinder you must have some code that maps between the an assetCode and an actual AssetType instance - refactor this code into a separate static helper class and then use this new helper in both the AssetTypeBinder and in the updateTheGuiBecauseAsssetTypeHasChanged method

    Ollie

  7. #7
    Join Date
    Aug 2005
    Location
    London (the English one!)
    Posts
    378

    Default thanks

    That is what I've done, but I was not too happy with it... was hoping that there could be something cleaner.

    getting late here...so I shall say thanks a lot for your help!

    regards from London,

    Benoit

    PS: BTW, do you have any problem with
    Code:
    TableUtils.sizeColumnsToFitRowData&#40;assetTable&#41;;
    The call does not seem to do anything... and the table has a model that contains lots of rows... No special setting for auto-resize or anything...

    Looks like the columns are still same size...


    While I would have expected something like this (resized manually)


    Any suggestion?
    Cheers

    Benoit

  8. #8
    Join Date
    Aug 2004
    Location
    Melbourne, Australia
    Posts
    335

    Default

    The call does not seem to do anything... and the table has a model that contains lots of rows... No special setting for auto-resize or anything...
    No idea - I don't use that class. Try tracing into the sizeColumnsToFitRowData methd and see what it's doing.

  9. #9
    Join Date
    Aug 2005
    Location
    London (the English one!)
    Posts
    378

    Default found the problem...

    Hi Ollie & all,

    I think that I have found the issue. The TableUtils.sizeColumnsToFitRowData only checks the first row (probably for speed issue) and, in my example, it just happen that the first row contains smaller strings... (had to be like that, Mr Murphy!)

    I will look into this and post an alternative.

    regards

    Benoit

  10. #10
    Join Date
    Aug 2005
    Location
    London (the English one!)
    Posts
    378

    Default Alternative for column resizing.

    Hi Ollie & all,

    As discussed in previous post, the current implementation just looks at the first row. It is understandable if the table contains thousands of rows...

    Here is an alternative solution; it takes 250ms on my old laptop (1GHz) to resize a table of 7 columsn and 2,000 rows.

    Code:
    /**
    * Resize the columns based on the data contained in all rows and Header.
    * The original width will be based on the value of the column header, and,
    * will be expanded if at least one row requires more space.  The column header
    * size is slightly padded to allow for sorting icons &#40;GlazedTableModel&#41;.
    * @param table the table to resize
    * @author Benoit Xhenseval &#40;modified&#41;
    */
    public static void sizeColumnsToFitAllRowData&#40;JTable table&#41; &#123;
      // go through each column first to take the renderer.
      for &#40;int col = 0; col < table.getColumnCount&#40;&#41;; col++&#41; &#123;
        final TableColumnModel colModel = table.getColumnModel&#40;&#41;;
        final TableModel model = table.getModel&#40;&#41;;
    
        TableColumn column = colModel.getColumn&#40;col&#41;;
        TableCellRenderer r = colModel.getColumn&#40;col&#41;.getCellRenderer&#40;&#41;;
    
        // original width should be based on the size of the HEADER.
        int cWidth = table.getTableHeader&#40;&#41;.getFontMetrics&#40;table.getTableHeader&#40;&#41;.getFont&#40;&#41;&#41;.stringWidth&#40;
    	    model.getColumnName&#40;col&#41; + "XXXX"&#41;; // we have added 'XXXX' to allow space for sorting icons. 
    
        for &#40;int row = 0; row < table.getRowCount&#40;&#41;; row++&#41; &#123;
    	if &#40;r == null&#41; &#123;
    	    Object val = table.getValueAt&#40;row, col&#41;;
    	    if &#40;val != null&#41; &#123;
    		r = table.getDefaultRenderer&#40;val.getClass&#40;&#41;&#41;;
    	    &#125;
    	&#125;
    	if &#40;r != null&#41; &#123;
    	    Component c = r.getTableCellRendererComponent&#40;table, table.getValueAt&#40;row, col&#41;, false, false, 0,
    		    col&#41;;
    	    cWidth = Math.max&#40;c.getPreferredSize&#40;&#41;.width, cWidth&#41;;
    	&#125;
        &#125;
    
        column.setPreferredWidth&#40;cWidth + UIConstants.ONE_SPACE&#41;;
        column.setWidth&#40;cWidth&#41;;
      &#125;
      int width = Math.min&#40;table.getColumnModel&#40;&#41;.getTotalColumnWidth&#40;&#41;, &#40;int&#41; &#40;WindowUtils.getScreenWidth&#40;&#41; * .75&#41;&#41;;
      table.setPreferredScrollableViewportSize&#40;new Dimension&#40;width, 300&#41;&#41;;
    &#125;
    since the name is slightly different, could you add this method to the TableUtils? Thanks!

    We have also experienced some optimizations by quickly going through the values of each row and simply doing a toString() to it, then picking up the row
    where the string is the biggest and only try to render that row. It is not the most accurate method if you have some funny
    rendering (say Date formatting that abbreviate a Date dramatically). But for big tables, it would be faster! For our example
    of 2,000 rows & 7 columns, it is about 30% faster.

    Here is the code, again, we have named the method slightly differently so they can co-exist:

    Code:
    public static void sizeColumnsToFitAllRowDataBasedOnStrings&#40;JTable table&#41; &#123;
      for &#40;int col = 0; col < table.getColumnCount&#40;&#41;; col++&#41; &#123;
        final TableColumnModel colModel = table.getColumnModel&#40;&#41;;
        final TableModel model = table.getModel&#40;&#41;;
    
        TableColumn column = colModel.getColumn&#40;col&#41;;
        TableCellRenderer r = colModel.getColumn&#40;col&#41;.getCellRenderer&#40;&#41;;
    
        // original width should be based on the size of the HEADER.
        int cWidth = table.getTableHeader&#40;&#41;.getFontMetrics&#40;table.getTableHeader&#40;&#41;.getFont&#40;&#41;&#41;.stringWidth&#40;
    	    model.getColumnName&#40;col&#41; + "XXXX"&#41;; // we have added 'XXXX' to allow space for sorting icons.
    
        // now simply spot the row that has the biggest "string"
        int maxStringLength = 0;
        Object maxRowValue = null;
        int maxRowIndex = 0;
        for &#40;int row = 0; row < table.getRowCount&#40;&#41;; row++&#41; &#123;
    	Object value = table.getValueAt&#40;row, col&#41;;
    	if &#40;value != null&#41; &#123;
    	    final int length = value.toString&#40;&#41;.length&#40;&#41;;
    
    	    if &#40;length > maxStringLength&#41; &#123;
    		maxStringLength = length;
    		maxRowValue = value;
    		maxRowIndex = row;
    	    &#125;
    	&#125;
        &#125;
    
        // and uses that row for the rendering.
        if &#40;r == null&#41; &#123;
    	Object val = table.getValueAt&#40;maxRowIndex, col&#41;;
    	if &#40;val != null&#41; &#123;
    	    r = table.getDefaultRenderer&#40;val.getClass&#40;&#41;&#41;;
    	&#125;
        &#125;
        if &#40;r != null&#41; &#123;
    	Component c = r.getTableCellRendererComponent&#40;table, maxRowValue, false, false, 0, col&#41;;
    	cWidth = Math.max&#40;c.getPreferredSize&#40;&#41;.width, cWidth&#41;;
        &#125;
    
        column.setPreferredWidth&#40;cWidth + UIConstants.ONE_SPACE&#41;;
        column.setWidth&#40;cWidth&#41;;
      &#125;
      int width = Math.min&#40;table.getColumnModel&#40;&#41;.getTotalColumnWidth&#40;&#41;, &#40;int&#41; &#40;WindowUtils.getScreenWidth&#40;&#41; * .75&#41;&#41;;
      table.setPreferredScrollableViewportSize&#40;new Dimension&#40;width, 300&#41;&#41;;
    &#125;
    Please let me know if/when you could put this in CVS so I can remove my own class.

    I took the liberty of raising an improvement JIRA:
    http://opensource2.atlassian.com/pro...browse/RCP-189

    Enjoy!

    Thanks

    Benoit

Similar Threads

  1. Question about Spring Binding
    By shaby775 in forum Web
    Replies: 3
    Last Post: Oct 21st, 2005, 12:41 AM
  2. Replies: 7
    Last Post: Sep 13th, 2005, 01:45 AM
  3. Beginner question on form binding
    By attack7 in forum Web Flow
    Replies: 3
    Last Post: Aug 12th, 2005, 06:28 AM
  4. Question on data binding
    By TRiNiTy in forum Swing
    Replies: 10
    Last Post: Jan 25th, 2005, 07:41 PM
  5. Replies: 2
    Last Post: Aug 17th, 2004, 04:16 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •