0

语境

我已经实现了一个 NatTable (v1.1.0.201405012245) - 请考虑这个简化的例子:

package testproject;

import java.util.ArrayList;

import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.nebula.widgets.nattable.NatTable;
import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
import org.eclipse.nebula.widgets.nattable.config.EditableRule;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.data.IColumnAccessor;
import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
import org.eclipse.nebula.widgets.nattable.edit.config.DefaultEditBindings;
import org.eclipse.nebula.widgets.nattable.edit.config.DefaultEditConfiguration;
import org.eclipse.nebula.widgets.nattable.edit.editor.TextCellEditor;
import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
import org.eclipse.nebula.widgets.nattable.layer.LabelStack;
import org.eclipse.nebula.widgets.nattable.layer.cell.IConfigLabelAccumulator;
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
import org.eclipse.nebula.widgets.nattable.selection.config.DefaultSelectionStyleConfiguration;
import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;

public class ViewPart1 extends ViewPart
{
  @Override
  public void createPartControl(final Composite parent)
  {
    final ArrayList<String> list = new ArrayList<>();
    list.add("one");
    list.add("two");
    list.add("three");

    final IColumnAccessor<String> columnAccessor = new IColumnAccessor<String>()
    {
      @Override
      public void setDataValue(final String rowObject, final int columnIndex, final Object newValue)
      {
        if (!(newValue instanceof String) || ((String) newValue).contains("x"))
        {
          MessageDialog.openError(getSite().getShell(), "Error", "Invalid Input");
          return;
        }
        list.set(list.indexOf(rowObject), (String) newValue);
      }

      @Override
      public Object getDataValue(final String rowObject, final int columnIndex)
      {
        return rowObject;
      }

      @Override
      public int getColumnCount()
      {
        return 1;
      }
    };

    final IDataProvider dataProvider = new ListDataProvider<>(list, columnAccessor);
    final DataLayer dataLayer = new DataLayer(dataProvider);
    final SelectionLayer selectionLayer = new SelectionLayer(dataLayer);
    final ViewportLayer viewportLayer = new ViewportLayer(selectionLayer);

    final NatTable table = new NatTable(parent, viewportLayer, false);
    GridDataFactory.fillDefaults().grab(true, true).applyTo(table);

    viewportLayer.addConfiguration(new DefaultEditConfiguration());
    viewportLayer.addConfiguration(new DefaultEditBindings());
    viewportLayer.setRegionName(GridRegion.BODY);

    viewportLayer.setConfigLabelAccumulator(new IConfigLabelAccumulator()
    {
      @Override
      public void accumulateConfigLabels(final LabelStack configLabels, final int columnPosition, final int rowPosition)
      {
        configLabels.addLabel("myLabel");
      }
    });

    table.setConfigRegistry(new ConfigRegistry());
    table.addConfiguration(new DefaultNatTableStyleConfiguration());
    table.addConfiguration(new DefaultSelectionStyleConfiguration());
    table.addConfiguration(new AbstractRegistryConfiguration()
    {
      @Override
      public void configureRegistry(final IConfigRegistry registry)
      {
        registry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR, new TextCellEditor(true), DisplayMode.NORMAL, "myLabel");
        registry.registerConfigAttribute(EditConfigAttributes.CELL_EDITABLE_RULE, EditableRule.ALWAYS_EDITABLE, DisplayMode.NORMAL, "myLabel");
      }
    });
    table.configure();
  }

  @Override
  public void setFocus()
  {
  }
}

当然,这不是真正的代码,但我的问题也可以用这段代码来演示。

重要的是,在我的实际项目中,当用户修改一个值时,我需要更新一个模型(包括复杂的依赖树),如果失败(例如在数值计算中,更改会导致除以零) ,我需要显示一个错误(并恢复到以前的值)。

为了显示我的核心问题,在此处显示的代码中,我检查了一个简单的条件IColumnAccessor#setDataValue(输入包含“x”)并相应地显示错误。

问题

我的实际问题是,如果您在 TextCellEditor 中输入一个 x,则会弹出两次错误对话框(按顺序 - 这意味着只要我为第一个单击“确定”,就会显示第二个)。

分析

我的分析表明,原因是 setDataValue 被调用了两次:

  1. 因为按下了 ENTER 键 - Stacktrace

    • TextCellEditor(AbstractCellEditor).commit(SelectionLayer$MoveDirectionEnum, boolean) line: 331
    • TextCellEditor(AbstractCellEditor).commit(SelectionLayer$MoveDirectionEnum) line: 326
    • TextCellEditor$1.keyPressed(KeyEvent) line: 246
  2. 因为 TextCellEditor 失去焦点 - Stacktrace

    • TextCellEditor(AbstractCellEditor).commit(SelectionLayer$MoveDirectionEnum, boolean, boolean) line: 341
    • TextCellEditor(AbstractCellEditor).commit(SelectionLayer$MoveDirectionEnum, boolean) line: 331
    • AbstractCellEditor$InlineFocusListener.focusLost(FocusEvent) line: 462

所以,我的主要问题是:我怎样才能防止(或至少检测)第二个事件?

4

1 回答 1

1

您的实现的问题是,您正在 IColumnAccessor 中执行转换并打开一个对话框以通知用户该错误。但由于各种用例,这不是使用 NatTable 执行此操作的方法。

如果您需要执行转换和/或验证,您应该注册一个适当的 IDisplayConverter 和一个 IDataValidator。由于您需要一个字符串,因此您不需要注册不同的转换器,因为通过 DefaultEditConfiguration 注册的默认转换器已经这样做了。因此,您需要一个 IDataValidator 来检查值 x 并在这种情况下抛出 ValidationFailedException。如果您将 DialogErrorHandling 注册为验证错误处理程序,则带有异常消息的错误将显示在对话框中。并且不打开对话框两次的检查是在内部完成的。

这在(目前很小的)文档http://www.eclipse.org/nattable/documentation.php?page=editing中有解释

顺便说一句,我建议更新到最新的 NatTable 版本 1.3.0,因为它还包含几个错误修复。

于 2015-08-07T15:26:09.133 回答