验证错误阻止撤消

Validation errors block undo

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

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

在这张图片中无法使用撤销:


应用程序中使用的工具:

数据绑定如下所示:

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

问题是这样的:

要在出现验证错误时使撤消起作用,我想我可以做以下不同的事情之一:

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

这些解决方案要么看起来不可能,要么有严重的缺点。

即使出现验证错误也能使撤消工作的最佳方法是什么?


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

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

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

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

The update phases are:

  1. Validate after get - validateAfterGet(Object)

  2. Conversion - convert(Object)

  3. Validate after conversion - validateAfterConvert(Object)

  4. Validate before set - validateBeforeSet(Object)

  5. Value set - doSet(IObservableValue, Object)

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

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

我接受 Mad Matts 的回答,因为他们的建议让我找到了这个解决方案。

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


我最终创建了一个 UpdateValueStratety sub-class,它运行一个验证器 模型对象上设置了一个值。这似乎工作正常。

我为 post 我最终使用的代码创建了这个答案。这是:

/**
 * 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);
        }
    }
}