简体   繁体   English

验证错误阻止撤消

[英]Validation errors block undo

I have the following problem with my EMF based Eclipse application: 我的基于EMF的Eclipse应用程序存在以下问题:

Undo works fine. 撤消工作正常。 Validation works fine. 验证工作正常。 But when there is a validation error for the data in a GUI field, this blocks the use of the undo action . 但是,如果GUI字段中的数据存在验证错误,则会阻止undo操作的使用 For example, it is not possible to undo to get back to a valid state for that field. 例如,无法撤消以恢复到该字段的有效状态。

In this picture it is not possible to use undo : 在此图片中,无法使用undo

不太丰富的图像,希望使问题看起来更有趣。


Tools that are used in the application: 该应用程序中使用的工具:

  • Eclipse data binding Eclipse数据绑定
  • UpdateValueStrategy s on the bindings for validation 绑定上的UpdateValueStrategy进行验证
  • Undo is implemented using standard UndoAction that calls CommandStack.undo 使用调用CommandStack.undo标准UndoAction实现撤消
  • A MessageManagerSupport class that connects the validation framework to the Eclipse Forms based GUI. 一个MessageManagerSupport类,它将验证框架连接到基于Eclipse Forms的GUI。

The data bindings look like this: 数据绑定如下所示:

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

The problem is this: 问题是这样的:

  • The undo system works on the commands that change the model. 撤消系统适用于更改模型的命令。
  • The validation system stops updates from reaching to model when there are validation errors. 当存在验证错误时,验证系统将停止更新到达模型。

To make undos work when there are validation errors I think I could do one of these different things: 为了在出现验证错误时使撤消正常工作,我想我可以做以下其中一种不同的事情:

  1. Make undo system work on the GUI layer. 使撤消系统在GUI层上工作。 (This would be a huge change, it's probably not possible to use EMF for this at all.) (这将是一个巨大的变化,可能根本无法使用EMF。)
  2. Make the invalid data in the GUI trigger commands that change the model data, in the same way as valid data does. 以与有效数据相同的方式,在用于更改模型数据的GUI触发命令中创建无效数据。 (This would be okay as long as the data can not be saved to disk. But I can't find a way to do this.) (只要数据不能保存到磁盘上就可以了。但是我找不到解决办法。)
  3. Make the validation work directly on the model, maybe triggered by a content listener on the Resource . 使验证直接在模型上起作用,可能由Resource上的内容侦听器触发。 (This is a big change of validation strategy. It doesn't seem possible to track the source GUI control in this stage.) (这是验证策略的重大变化。在此阶段似乎无法跟踪源GUI控件。)

These solutions either seem impossible or have severe disadvantages. 这些解决方案似乎是不可能的,或者具有严重的缺点。

What is the best way to make undo work even when there are validation errors? 即使存在验证错误,使撤消工作的最佳方法是什么?


NOTE : I accept Mad Matts answer because their suggestions lead me to my solution. 注意 :我接受Mad Matts的回答,因为他们的建议将我引向了我的解决方案。 But I'm not really satisfied with that and I wish there was a better one. 但是我对此并不满意,我希望有更好的解决方案。

If someone at some time finds a better solution I'd be happy to consider to accept it instead of the current one! 如果某人在某个时候找到了更好的解决方案,我很乐意考虑接受它而不是当前的解决方案!

It makes sense that the Validator protects your Target value from invalid values. 验证程序可以保护您的目标值免受无效值的影响。 Therefor the target commandstack remains untouched in case of an invalid value. 因此,在无效值的情况下,目标命令堆栈保持不变。 Why would you like to force invalid values being set? 为什么要强制设置无效值? Isn't ctrl + z in the GUI enough to reset the last valid state? GUI中的ctrl + z是否足以重置上一个有效状态?

If you still want to set these values to your actual Target model, you can play around with the UpdateValueStrategy . 如果您仍想将这些值设置为实际的Target模型,则可以使用UpdateValueStrategy

The update phases are: 更新阶段为:

  1. Validate after get - validateAfterGet(Object) 获取后验证-validateAfterGet(Object)

  2. Conversion - convert(Object) 转换-convert(Object)

  3. Validate after conversion - validateAfterConvert(Object) 转换后验证-validateAfterConvert(Object)

  4. Validate before set - validateBeforeSet(Object) 设置之前验证-validateBeforeSet(Object)

  5. Value set - doSet(IObservableValue, Object) 值集-doSet(IObservableValue,Object)

I'm not sure where the validation error ( Status.ERROR ) occurs exactly, but you could check where and then force a SetCommand manually. 我不确定验证错误( Status.ERROR )的确切位置,但是您可以检查位置,然后手动强制执行SetCommand You can set custom IValidator for each step to your UpdateValueStrategy to do that. 您可以为IValidator的每个步骤设置自定义UpdateValueStrategy来执行此操作。

NOTE: This is the solution I ended up using in my application. 注意:这是我最终在应用程序中使用的解决方案。 I'm not really satisfied with it. 我对此并不满意。 I think it is a little bit of a hack. 我认为这有点骇人听闻。

I accept Mad Matts answer because their suggestions lead me to this solution. 我接受Mad Matts的回答,因为他们的建议使我找到了这个解决方案。

If someone at some time finds a better solution I'd be happy to consider to accept it instead of the current one! 如果某人在某个时候找到了更好的解决方案,我很乐意考虑接受它而不是当前的解决方案!


I ended up creating an UpdateValueStratety sub-class which runs a validator after a value has been set on the model object. 我最终创建了一个UpdateValueStratety子类,该子类在模型对象上设置了值之后运行验证程序。 This seems to be working fine. 这似乎工作正常。

I create this answer to post the code I ended up using. 我创建此答案以发布最终使用的代码。 Here it is: 这里是:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM