Announcement Announcement Module
Collapse
No announcement yet.
ComboBoxBinder: changes in one combobox changes another Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • ComboBoxBinder: changes in one combobox changes another

    Hi.

    I'm using the formbuilder to create a form. If the user selectes a value in one combobox, I want another combobox to alter it's content.

    What I want is that if the user chooses "item1" in the first combobox, the second combobox will allow the user to choose between
    "item1 - 1"
    "item1 - 2"

    If the user chooses "item2" in the first combobox, the second combobox will allow the user to choose between
    "item2 - 1"
    "item2 - 2"

    It seems that the changes are not reflected in the gui.

    This is my bean that is backing the form:

    Code:
    public class TestObject {
    	private String selectedInList1;
    	private String selectedInList2;
    	private List<String> list1;
    	private List<String> list2;
    	
    	private List<String> list2whenItem1Selected;
    	private List<String> list2whenItem2Selected;
    	 private ExtendedPropertyChangeSupport changeSupport = new ExtendedPropertyChangeSupport(
    			this);
    	
    	public TestObject() 
    	{
    		list1 = new ArrayList<String>();
    		list1.add("item1");
    		list1.add("item2");
    		
    		list2whenItem1Selected = new ArrayList<String>();
    		list2whenItem1Selected.add("item1 - 1");
    		list2whenItem1Selected.add("item1 - 2");
    		
    		list2whenItem2Selected = new ArrayList<String>();
    		list2whenItem2Selected.add("item2 - 1");
    		list2whenItem2Selected.add("item2 - 2");
    		
    		list2 = list2whenItem1Selected;
    		
    		selectedInList1 = list1.get(0);
    		selectedInList2 = list2.get(0);
    	}
    
    	public String getSelectedInList1() {
    		return selectedInList1;
    	}
    
    	public void setSelectedInList1(String selectedInList1) {
    		String old = this.selectedInList1;
    		this.selectedInList1 = selectedInList1;
    		if( "item1".equals(selectedInList1) ) {
    			setList2(list2whenItem1Selected);
    		} else {
    			setList2(list2whenItem2Selected);
    		}
    		setSelectedInList2(list2.get(0));		
    		changeSupport.firePropertyChange("selectedInList1", old, selectedInList1);
    		
    	}
    
    	public String getSelectedInList2() {
    		return selectedInList2;
    	}
    
    	public void setSelectedInList2(String selectedInList2) {
    		String old = this.selectedInList2;
    		this.selectedInList2 = selectedInList2;
    		changeSupport.firePropertyChange("selectedInList2", old, selectedInList2);
    	}
    
    	public List<String> getList1() {
    		return list1;
    	}
    
    	public void setList1(List<String> list1) {
    		List<String> old = this.list1;
    		this.list1 = list1;
    		changeSupport.firePropertyChange("list1", old, list1);
    	}
    
    	public List<String> getList2() {
    		return list2;
    	}
    
    	public void setList2(List<String> list2) {
    		List<String> old = this.list2;
    		this.list2 = list2;
    		changeSupport.firePropertyChange("list2", old, list2);
    	}
    	
    	public void removePropertyChangeListener(PropertyChangeListener x) {
    		changeSupport.removePropertyChangeListener(x);
    	}
    	
    	public void addPropertyChangeListener(PropertyChangeListener x) {
    		changeSupport.addPropertyChangeListener(x);
    	}
    	
    	
    }
    Then I create the form like this:

    Code:
    public class TestForm extends AbstractForm {
    	public TestForm(final TestObject testObject) {
    		super(FormModelHelper.createFormModel(testObject, false, "testForm"));
    		testObject.addPropertyChangeListener(new PropertyChangeListener() {
    			
    			public void propertyChange(PropertyChangeEvent evt) {
    				getControl().repaint();
    				System.out.println("testObject list2: " + testObject.getList2() + " form:" + getFormModel().getValueModel("list2").getValue());
    				
    			}
    		});
    	}
    	
    	@Override
    	public JComponent createFormControl() {
    		TableFormBuilder builder = new TableFormBuilder(getBindingFactory() );
    		{
    			Map context = new HashMap();
    			context.put(ComboBoxBinder.SELECTABLE_ITEMS_KEY,getValueModel("list1"));
    			ComboBoxBinder comboBoxBinder = new ComboBoxBinder();
    			Binding binding = comboBoxBinder.bind(getFormModel(), "selectedInList1", context);
    			builder.add(binding);
    		}
    		
    		{
    			Map context = new HashMap();
    			context.put(ComboBoxBinder.SELECTABLE_ITEMS_KEY,getValueModel("list2"));
    			ComboBoxBinder comboBoxBinder = new ComboBoxBinder();
    			Binding binding = comboBoxBinder.bind(getFormModel(), "selectedInList2", context);
    			builder.add(binding);
    		}
    		return builder.getForm();
    	}
    
    }
    The output from the listner is:

    testObject list2: [item1 - 1, item1 - 2] form:[item1 - 1, item1 - 2]
    testObject list2: [item2 - 1, item2 - 2] form:[item2 - 1, item2 - 2]

    the second combox (that reflects list2) show
    "item1 - 1".

    When I resize the window, the text changes to
    "item2 - 1"

    But when I click on the second combobox my options are
    "item1 - 1" and "item1 - 2"

    I would expect it to be
    "item2 - 1" and "item2 - 2"

    Why doesn't it behave as I expect? Does anyone have any idea how I can achive my wanted behavior?

    Best Regards,
    .t

  • #2
    Hmmm...

    If I remember correctly, changing the list behind a combobox will indeed not show the changes. You need to change the internal model instead of the backing list. Also, you need to make sure you make those changes on the EDT.

    Comment


    • #3
      Originally posted by LievenDoclo View Post
      Hmmm...

      If I remember correctly, changing the list behind a combobox will indeed not show the changes. You need to change the internal model instead of the backing list. Also, you need to make sure you make those changes on the EDT.
      Thanks for the fast response. :-)

      What do you mean by internal model? The Valuemodel? (I'm sorry if I ask stupid questions, but I'm new to Spring RCP).

      It seems the valuemodel is changed when I change the backing bean. The listener set up in forms constructor:

      Code:
      System.out.println("testObject list2: " + testObject.getList2() + " form:" + getFormModel().getValueModel("list2").getValue());
      will print

      testObject list2: [item2 - 1, item2 - 2] form:[item2 - 1, item2 - 2]

      which indicates that the valuemodel in the form contains the correct values.

      Comment


      • #4
        No, the internal model of the combobox, the combobox model. In your backing bean, you're changing the backing list of the combobox. But Swing doesn't show changes in a backing list, only changes to the comboboxmodel are propagated iirc. I'll see if I can create a test case to mimic your case.
        What you could do is add a changelistener to the valuemodel of one of the comboboxes and change the selectable items list in there (I'm freewheeling here), perhaps that'll work, but I can't say for sure.

        Comment


        • #5
          Originally posted by LievenDoclo View Post
          No, the internal model of the combobox, the combobox model. In your backing bean, you're changing the backing list of the combobox. But Swing doesn't show changes in a backing list, only changes to the comboboxmodel are propagated iirc. I'll see if I can create a test case to mimic your case.
          What you could do is add a changelistener to the valuemodel of one of the comboboxes and change the selectable items list in there (I'm freewheeling here), perhaps that'll work, but I can't say for sure.
          Oki. I thought the point of using the form was that the backing bean was bound to the gui components so that changes to the gui are propagated to the backing bean and vice versa.

          I have figured out one way of doing it that actually works, but I find it's a lot of code for something that should be really easy. I replace the combobox model, listen to changes in the backing bean's property and update the combobox model accordingly.

          The changes to the selected value seems to work as I would expect - changes to the backing bean are propagated to the gui, without the need to explicitly change them. It is only the list of selectable items that are not propagated back to the gui. Seems a bit strange to me.

          Code:
          {
          			Map context = new HashMap();
          			context.put(ComboBoxBinder.SELECTABLE_ITEMS_KEY,getValueModel("list2"));
          			
          			Binding binding = comboBoxBinder.bind(getFormModel(), "selectedInList2", context);
          			
          			
          			final JComboBox comboBox = (JComboBox) builder.add(binding)[1];
          			TestObject testobject = (TestObject) getFormObject();	
          			comboBox.setModel(new DynamicComboBoxListModel(getValueModel("selectedInList2"), getValueModel("list2")));
          			testobject.addPropertyChangeListener(new PropertyChangeListener() {
          				
          				public void propertyChange(PropertyChangeEvent evt) {
          					if( "selectedInList1".equals(evt.getPropertyName())) {
          						DynamicComboBoxListModel comboBoxModel = (DynamicComboBoxListModel) comboBox.getModel();
          						comboBoxModel.replaceWith((Collection) getValueModel("list2").getValue());
          					}
          					
          				}
          			});
          			
          		}

          Comment


          • #6
            If you look closely to your example, you've explained it yourself:

            Code:
            comboBoxBinder.bind(getFormModel(), "selectedInList2", context);
            You're binding the selected value, not the selectable values. At the moment, it's not yet possible to bind the selectable list, so your example does the trick quite nicely.

            Comment


            • #7
              I'm not sure, but this should work too and looks a bit more polished:
              Code:
              Map context = new HashMap();		
              context.put(ComboBoxBinder.SELECTABLE_ITEMS_KEY,getValueModel("list2"));
              final ComboBoxBinding binding = (ComboBoxBinding) comboBoxBinder.bind(getFormModel(), "selectedInList2", context);
              getValueModel("selectedInList1").addValueChangeListener(new PropertyChangeListener() {
                  @Override
                  public void propertyChange(PropertyChangeEvent evt) {
                      binding.setSelectableItems(getValueModel("list2").getValue());
                  }
              });

              Comment


              • #8
                Thanks a lot. That makes it much smoother! I have selveral dropdownboxes that needs to be refreshed when one other changes. I have now created a method that enables me to add them using one method:

                Code:
                private void addRefreshableComboBox(TableFormBuilder formBuilder, String selectedFieldName, final String selectableFieldName, String refreshWhenChangedFieldName) {
                		Map context = new HashMap();
                		ComboBoxBinder comboBoxBinder = new ComboBoxBinder();
                		final ComboBoxBinding binding = (ComboBoxBinding) comboBoxBinder.bind(getFormModel(), selectedFieldName, context);
                		binding.setSelectableItems(getValueModel(selectableFieldName).getValue());
                		getValueModel(refreshWhenChangedFieldName).addValueChangeListener(new PropertyChangeListener() {
                			public void propertyChange(PropertyChangeEvent evt) {
                				binding.setSelectableItems(getValueModel(selectableFieldName).getValue());
                				
                			}
                		});
                		formBuilder.add(binding);
                	}
                I still thinks it would be even smoother if it were possible to add the binding to the list in the context, though...

                Anyway, thanks a lot!


                Originally posted by LievenDoclo View Post
                I'm not sure, but this should work too and looks a bit more polished:
                Code:
                Map context = new HashMap();		
                context.put(ComboBoxBinder.SELECTABLE_ITEMS_KEY,getValueModel("list2"));
                final ComboBoxBinding binding = (ComboBoxBinding) comboBoxBinder.bind(getFormModel(), "selectedInList2", context);
                getValueModel("selectedInList1").addValueChangeListener(new PropertyChangeListener() {
                    @Override
                    public void propertyChange(PropertyChangeEvent evt) {
                        binding.setSelectableItems(getValueModel("list2").getValue());
                    }
                });

                Comment


                • #9
                  I used a refreshableValueHolder (taken from one of the examples) and used that to update the model.. not sure if this is the best way, but it working for me:

                  Code:
                  private RefreshableValueHolder refreshableValueHolder;
                  private List someList;
                  
                  ...
                  refreshableValueHolder = new RefreshableValueHolder(new Closure() {
                      public Object call(Object object) {
                          return someList;
                      }
                  }, true, false);
                  
                  builder.addBinding(bf.createBoundComboBox("myProperty", refreshableValueHolder), 3);
                  addFormValueChangeListener("myProperty", new myChangedValueListener());
                  ...
                  
                  ...
                  private class myChangedValueListener implements PropertyChangeListener {
                      public myChangedValueListener() {
                      }
                  
                      public void propertyChange(final PropertyChangeEvent evt) {
                          if (getFormModel().getValueModel("myProperty").getValue() instanceof someClass) {
                              someList.clear();
                              someList.addAll(otherList);
                              refreshableValueHolder.setValue(Collections.EMPTY_LIST);
                              refreshableValueHolder.refresh();
                      } else {				
                              refreshableValueHolder.refresh();
                      }
                  }

                  Comment

                  Working...
                  X