验证错误会阻止撤消

时间:2016-10-21 15:13:01

标签: data-binding eclipse-emf emf eclipse-databinding

我的基于EMF的Eclipse应用程序存在以下问题:

撤消工作正常。验证工作正常。但是,当GUI字段中的数据存在验证错误时,会阻止使用撤消操作。例如,无法撤消以返回该字段的有效状态。

在此图片中,无法使用撤消

Not very informative image which hopefully makes the question a little more fun to look at.

应用程序中使用的工具:

  • Eclipse数据绑定
  • UpdateValueStrategy关于验证的绑定
  • 撤消是使用调用UndoAction
  • 的标准CommandStack.undo实现的
  • 将验证框架连接到基于Eclipse Forms的GUI的MessageManagerSupport类。

数据绑定如下所示:

dataBindingContext.bindValue(WidgetProperties.text(...), 
    EMFEditProperties.value(...), validatingUpdateStrategy, null);

问题在于:

  • 撤消系统适用于更改模型的命令。
  • 当存在验证错误时,验证系统会停止更新到达模型。

要在验证错误时使undos正常工作,我想我可以做其中一件事:

  1. 在GUI层上使撤消系统工作。 (这将是一个巨大的变化,根本不可能使用EMF。)
  2. 在GUI触发器命令中生成无效数据,以与有效数据相同的方式更改模型数据。 (只要数据无法保存到磁盘,这样就可以了。但我找不到办法来做到这一点。)
  3. 直接在模型上进行验证,可能由Resource上的内容侦听器触发。 (这是验证策略的一个重大变化。在这个阶段似乎无法跟踪源GUI控件。)
  4. 这些解决方案似乎不可能或有严重的缺点。

    即使存在验证错误,撤消工作的最佳方法是什么?

    注意:我接受Mad Matts的回答,因为他们的建议引导我找到解决方案。但我对此并不满意,我希望有一个更好的。

    如果有人在某个时间找到了更好的解决方案,我很乐意考虑接受它而不是当前的解决方案!

2 个答案:

答案 0 :(得分:1)

Validator可以保护您的Target值不受无效值的影响。 因此,在值无效的情况下,目标命令堆保持不变。 您为什么要强制设置无效值? GUI中的ctrl + z是否足以重置上一个有效状态?

如果您仍想将这些值设置为实际的目标模型,则可以使用UpdateValueStrategy

  

更新阶段是:

     
      
  1. 在get - validateAfterGet(Object)

  2. 之后验证   
  3. 转换 - 转换(对象)

  4.   
  5. 转换后验证 - validateAfterConvert(Object)

  6.   
  7. 在设置之前验证 - validateBeforeSet(Object)

  8.   
  9. 值集 - doSet(IObservableValue,Object)

  10.   

我不确定验证错误(Status.ERROR)的确切位置,但您可以检查位置,然后手动强制SetCommand。 您可以为IValidator的每个步骤设置自定义UpdateValueStrategy来执行此操作。

答案 1 :(得分:1)

注意:这是我最终在我的应用程序中使用的解决方案。我对此并不满意。我认为这有点像黑客。

我接受Mad Matts的回答,因为他们的建议引导我解决这个问题。

如果有人在某个时间找到了更好的解决方案,我会很乐意考虑接受它而不是当前的解决方案!

我最终创建了一个UpdateValueStratety子类,在模型对象上设置了值后,运行验证器。这似乎工作正常。

我创建这个答案来发布我最终使用的代码。这是:

/**
 * An {@link UpdateValueStrategy} that can perform validation AFTER a value is set
 * in the model. This is used because undo dosen't work if no model changed in made.
 */
public class LateValidationUpdateValueStrategy extends UpdateValueStrategy {

    private IValidator afterSetValidator;

    public void setAfterSetValidator(IValidator afterSetValidator) {
        this.afterSetValidator = afterSetValidator;
    }

    @Override
    protected IStatus doSet(IObservableValue observableValue, Object value) {
        IStatus setStatus = super.doSet(observableValue, value);

        if (setStatus.getSeverity() >= IStatus.ERROR || afterSetValidator == null) {
            return setStatus;
        }

        // I used a validator here that calls the EMF generated model validator.
        // In that way I can specify validation of the model.
        IStatus validStatus = afterSetValidator.validate(value); 

        // Merge the two statuses
        if (setStatus.isOK() && validStatus.isOK()) {
            return validStatus;
        } else if (!setStatus.isOK() && validStatus.isOK()) {
            return setStatus;
        } else if (setStatus.isOK() && !validStatus.isOK()) {
            return validStatus;
        } else {
            return new MultiStatus(Activator.PLUGIN_ID, -1, 
                new IStatus[] { setStatus, validStatus },
                setStatus.getMessage() + "; " + validStatus.getMessage(), null);
        }
    }
}