[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [Newsgroup Home]
[news.eclipse.platform] [DataBinding] Cross field validation and WizardPageSupport

Hi all!

I have a wizard page in which -- among other things -- the user can
specify a start date and an end date while, for each date, the month
and the year can be edited separately using combo boxes (see the
attached code snippet).
For implementing that using databinding, I have defined a ComputedValue
for each date which assembles a Date object from the two SWTObservables
defining the month and year contained in the combo boxes.
For validating the wizard page, I have defined another ComputedValue
which depends on the two date values and returns an IStatus which tells
whether the start date is before the end date.
My problem is that I would like to attach this validation status to an
instance of WizardPageSupport which I am already using for validating
other parts of the wizard page. However, the WizardPageSupport class
seems to only support validation status' belonging to bindings. In my
case, I have a stand-alone validation status value which does not belong
to any binding.
So, my question is whether this kind of cross field validation (for
which no real binding exists) can also be integrated into the
WizardPageSupport class? Or am I maybe just going the wrong way with my
approach described above?

I hope my explanation is clear enough. If not, the attached code snippet
may clarify things.

Thanks in advance for any feedback!

Best regards,
Ovidio
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.eclipse.core.databinding.DataBindingContext;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.value.ComputedValue;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.IValueChangeListener;
import org.eclipse.core.databinding.observable.value.ValueChangeEvent;
import org.eclipse.core.databinding.validation.ValidationStatus;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.databinding.swt.SWTObservables;
import org.eclipse.jface.databinding.wizard.WizardPageSupport;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.wizard.IWizard;
import org.eclipse.jface.wizard.Wizard;
import org.eclipse.jface.wizard.WizardDialog;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;

public class CrossFieldValidation {

  static class FirstWizardPage extends WizardPage {

    private static final String[] MONTHS = {
      "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"
    };

    private static final String[] YEARS = { "2006", "2007", "2008", "2009" };

    protected FirstWizardPage() {
      super("First", "First Page",
            ImageDescriptor.createFromImage(new Image(Display.getDefault(), 16, 16)));
    }

    public void createControl(Composite parent) {
      DataBindingContext dbc = new DataBindingContext();
      WizardPageSupport.create(this, dbc);
      Composite composite = new Composite(parent, SWT.NONE);

      final ComputedValue startDateValue = getDateValue(composite, "Start");
      final ComputedValue endDateValue = getDateValue(composite, "End");

      ComputedValue validationStatus = new ComputedValue(IStatus.class) {

        protected Object calculate() {
          Date startDate = (Date) startDateValue.getValue();
          Date endDate = (Date) endDateValue.getValue();
          if (startDate.before(endDate)) {
            return ValidationStatus.ok();
          } else {
            return ValidationStatus.error("Start date is not before end date.");
          }
        }
      };

      // How to integrate this validation status into a WizardPageSupport class?
      validationStatus.addValueChangeListener(new IValueChangeListener() {

        public void handleValueChange(ValueChangeEvent event) {
          IStatus status = (IStatus) event.diff.getNewValue();
          if (status.isOK()) {
            setErrorMessage(null);
            setPageComplete(true);
          } else {
            setErrorMessage(status.getMessage());
            setPageComplete(false);
          }
        }
      });

      GridLayoutFactory.swtDefaults().numColumns(4).generateLayout(composite);
      setControl(composite);
    }

    private static ComputedValue getDateValue(Composite parent, String startOrEnd) {
      Label monthLabel = new Label(parent, SWT.NONE);
      monthLabel.setText(startOrEnd + " month:");
      Combo monthCombo = new Combo(parent, SWT.READ_ONLY);
      monthCombo.setItems(MONTHS);
      monthCombo.select(0);

      Label yearLabel = new Label(parent, SWT.NONE);
      yearLabel.setText(startOrEnd + " year:");
      Combo yearCombo = new Combo(parent, SWT.READ_ONLY);
      yearCombo.setItems(YEARS);
      yearCombo.select(0);

      final IObservableValue monthValue = SWTObservables.observeText(monthCombo);
      final IObservableValue yearValue = SWTObservables.observeText(yearCombo);

      return new ComputedValue(Date.class) {

        protected Object calculate() {
          Date date = null;
          try {
            date = new SimpleDateFormat("mm.yyyy")
                .parse(monthValue.getValue() + "." + yearValue.getValue());
          } catch (ParseException e) {
            e.printStackTrace();
          }
          return date;
        }
      };
    }
  }

  static class SampleWizard extends Wizard {

    public void addPages() {
      addPage(new FirstWizardPage());
    }

    public String getWindowTitle() {
      return "Cross field validation";
    }

    public boolean performFinish() {
      return true;
    }
  }

  public static void main(String[] args) {
    Display display = new Display();
    Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() {
      public void run() {
        IWizard wizard = new SampleWizard();
        WizardDialog dialog = new WizardDialog(null, wizard);
        dialog.open();
        Display display = Display.getCurrent();
        while (dialog.getShell() != null && !dialog.getShell().isDisposed()) {
          if (!display.readAndDispatch()) {
            display.sleep();
          }
        }
      }
    });
  }
}