阻止JTree选择发生变化的最佳方法是什么?

时间:2008-11-04 10:19:09

标签: java swing design-patterns error-handling

我有一个对话框,其中JTree中的每个条目在不同的面板中都有相应的选项,当选择更改时会更新。如果其中一个条目的选项设置为无效状态,当用户尝试更改为树中的其他条目时,我希望有一个错误对话框并且选择不会更改。

我尝试在JTree上使用valueChangeListener执行此操作,但是如果出现错误,则当前必须将valueChanged方法调用“setSelectionRow”到旧选择。所以我没有获得StackOverflow,我在执行此操作之前将布尔“isError”设置为true,以便我可以忽略新的valueChanged事件。不知怎的,我有直觉,这不是最好的解决方案。 ; - )

我该怎么做呢?对于这样的情况,有一个好的设计模式吗?

7 个答案:

答案 0 :(得分:6)

我没有找到更好的方法,但这种方法对我来说很好。 我知道在Delphi中这是一个非常方便的事件:“在更改选择之前”你可以很容易地停止改变选择。

这是我的java代码,防止无限递归问题

    navTree.addTreeSelectionListener(new TreeSelectionListener() {

        boolean treeSelectionListenerEnabled = true;

        public void valueChanged(TreeSelectionEvent e) {
            if (treeSelectionListenerEnabled) {
                if (ok to change selection...) {
                    ...
                } else {
                    TreePath treePath = e.getOldLeadSelectionPath();
                    treeSelectionListenerEnabled = false;
                    try {
                        // prevent from leaving the last visited node
                        navTree.setSelectionPath(treePath);
                    } finally {
                        treeSelectionListenerEnabled = true;
                    }
                }
            }
        }
    });

始终记得删除您添加的所有侦听器,以防止内存泄漏。

这是另一种方法:

private class VetoableTreeSelectionModel extends DefaultTreeSelectionModel {
    public void setSelectionPath(TreePath path){
        if (allow selection change?) {
            super.setSelectionPath(path);
        }
    }
}
{
    navTree.setSelectionModel(new VetoableTreeSelectionModel());
}

答案 1 :(得分:3)

不确定这是最佳做法,但也许您可以将FocusListener放在要验证的组件上...在调用事件时调用验证然后如果您不想要焦点则使用then then事件要移动,因为验证失败了吗?

稍后编辑:

至少使用java 8(我没有检查早期版本),这个解决方案不起作用,因为FocusEvent似乎不是一个低级事件。因此它无法消费。请参见方法AWTEvent.consume()

答案 2 :(得分:3)

这是我的解决方案。

在JTree子类中:

protected void processMouseEvent(MouseEvent e) {
        TreePath selPath = getPathForLocation(e.getX(), e.getY());
        try {
            fireVetoableChange(LEAD_SELECTION_PATH_PROPERTY, getLeadSelectionPath(), selPath);
        }
        catch (PropertyVetoException ex) {
            // OK, we do not want change to happen
            return;
        }

        super.processMouseEvent(e);
}

然后在树中使用class:

VetoableChangeListener vcl = new VetoableChangeListener() {

        public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException {
            if ( evt.getPropertyName().equals(JTree.LEAD_SELECTION_PATH_PROPERTY) ) {
                try {
                    <some code logic that has to be satisfied>
                } catch (InvalidInputException e) {
                    throw new PropertyVetoException("", evt);
                }

            }
        }
    };
    tree.addVetoableChangeListener(vcl);

机制从最早的地方开始。拦截鼠标操作,将要选择的路径通告给VetoableChangeListeners。在具体的VCL中,检查更改属性,如果是前导选择,则检查否决逻辑。如果需要否决,则VCL抛出PropertyVetoException,否则鼠标事件处理将照常进行并进行选择。 简而言之,这使得潜在客户选择属性成为约束属性。

答案 3 :(得分:0)

设置一个实现相应语义的TreeSelectionModel。

答案 4 :(得分:0)

以下是实现TreeSelectionModel的示例,该TreeSelectionModel包装另一个TreeSelectionModel但允许选择被否决:

public class VetoableTreeSelectionModel implements TreeSelectionModel
{
   private final ListenerList<VetoableTreeSelectionListener> m_vetoableTreeSelectionListeners = new ListenerList<VetoableTreeSelectionListener>();

   private final DefaultTreeSelectionModel m_treeSelectionModel = new DefaultTreeSelectionModel();

   /**
    * {@inheritDoc}
    */
   public void addTreeSelectionListener(final TreeSelectionListener listener)
   {
      m_treeSelectionModel.addTreeSelectionListener(listener);
   }

   /**
    * {@inheritDoc}
    */
   public void removeTreeSelectionListener(final TreeSelectionListener listener)
   {
      m_treeSelectionModel.removeTreeSelectionListener(listener);
   }

   /**
    * Add a vetoable tree selection listener
    *
    * @param listener the listener
    */
   public void addVetoableTreeSelectionListener(final VetoableTreeSelectionListener listener)
   {
      m_vetoableTreeSelectionListeners.addListener(listener);
   }

   /**
    * Remove a vetoable tree selection listener
    *
    * @param listener the listener
    */
   public void removeVetoableTreeSelectionListener(final VetoableTreeSelectionListener listener)
   {
      m_vetoableTreeSelectionListeners.removeListener(listener);
   }

   /**
    * {@inheritDoc}
    */
   public void addPropertyChangeListener(final PropertyChangeListener listener)
   {
      m_treeSelectionModel.addPropertyChangeListener(listener);
   }

   /**
    * {@inheritDoc}
    */
   public void removePropertyChangeListener(final PropertyChangeListener listener)
   {
      m_treeSelectionModel.removePropertyChangeListener(listener);
   }

   /**
    * {@inheritDoc}
    */
   public void addSelectionPath(final TreePath path)
   {
      try
      {
         m_vetoableTreeSelectionListeners.fireVetoableEvent(new VetoableAction<VetoableTreeSelectionListener>() {
            public void fireEvent(final VetoableTreeSelectionListener listener) throws EventVetoedException
            {
               listener.aboutToAddSelectionPath(path);
            }});

         m_treeSelectionModel.addSelectionPath(path);
      }
      catch (final EventVetoedException e)
      {
         return;
      }
   }

   /**
    * {@inheritDoc}
    */
   public void addSelectionPaths(final TreePath[] paths)
   {
      try
      {
         m_vetoableTreeSelectionListeners.fireVetoableEvent(new VetoableAction<VetoableTreeSelectionListener>() {
            public void fireEvent(final VetoableTreeSelectionListener listener) throws EventVetoedException
            {
               listener.aboutToAddSelectionPaths(paths);
            }});

         m_treeSelectionModel.addSelectionPaths(paths);
      }
      catch (final EventVetoedException e)
      {
         return;
      }
   }

   /**
    * {@inheritDoc}
    */
   public void clearSelection()
   {
      try
      {
         m_vetoableTreeSelectionListeners.fireVetoableEvent(new VetoableAction<VetoableTreeSelectionListener>() {
            public void fireEvent(final VetoableTreeSelectionListener listener) throws EventVetoedException
            {
               listener.aboutToClearSelection();
            }});

         m_treeSelectionModel.clearSelection();
      }
      catch (final EventVetoedException e)
      {
         return;
      }
   }

   /**
    * {@inheritDoc}
    */
   public TreePath getLeadSelectionPath()
   {
      return m_treeSelectionModel.getLeadSelectionPath();
   }

   /**
    * {@inheritDoc}
    */
   public int getLeadSelectionRow()
   {
      return m_treeSelectionModel.getLeadSelectionRow();
   }

   /**
    * {@inheritDoc}
    */
   public int getMaxSelectionRow()
   {
      return m_treeSelectionModel.getMaxSelectionRow();
   }

   /**
    * {@inheritDoc}
    */
   public int getMinSelectionRow()
   {
      return m_treeSelectionModel.getMinSelectionRow();
   }

   /**
    * {@inheritDoc}
    */
   public RowMapper getRowMapper()
   {
      return m_treeSelectionModel.getRowMapper();
   }

   /**
    * {@inheritDoc}
    */
   public int getSelectionCount()
   {
      return m_treeSelectionModel.getSelectionCount();
   }

   public int getSelectionMode()
   {
      return m_treeSelectionModel.getSelectionMode();
   }

   /**
    * {@inheritDoc}
    */
   public TreePath getSelectionPath()
   {
      return m_treeSelectionModel.getSelectionPath();
   }

   /**
    * {@inheritDoc}
    */
   public TreePath[] getSelectionPaths()
   {
      return m_treeSelectionModel.getSelectionPaths();
   }

   /**
    * {@inheritDoc}
    */
   public int[] getSelectionRows()
   {
      return m_treeSelectionModel.getSelectionRows();
   }

   /**
    * {@inheritDoc}
    */
   public boolean isPathSelected(final TreePath path)
   {
      return m_treeSelectionModel.isPathSelected(path);
   }

   /**
    * {@inheritDoc}
    */
   public boolean isRowSelected(final int row)
   {
      return m_treeSelectionModel.isRowSelected(row);
   }

   /**
    * {@inheritDoc}
    */
   public boolean isSelectionEmpty()
   {
      return m_treeSelectionModel.isSelectionEmpty();
   }

   /**
    * {@inheritDoc}
    */
   public void removeSelectionPath(final TreePath path)
   {
      try
      {
         m_vetoableTreeSelectionListeners.fireVetoableEvent(new VetoableAction<VetoableTreeSelectionListener>() {
            public void fireEvent(final VetoableTreeSelectionListener listener) throws EventVetoedException
            {
               listener.aboutRemoveSelectionPath(path);
            }});

         m_treeSelectionModel.removeSelectionPath(path);
      }
      catch (final EventVetoedException e)
      {
         return;
      }
   }

   /**
    * {@inheritDoc}
    */
   public void removeSelectionPaths(final TreePath[] paths)
   {
      try
      {
         m_vetoableTreeSelectionListeners.fireVetoableEvent(new VetoableAction<VetoableTreeSelectionListener>() {
            public void fireEvent(final VetoableTreeSelectionListener listener) throws EventVetoedException
            {
               listener.aboutRemoveSelectionPaths(paths);
            }});

         m_treeSelectionModel.removeSelectionPaths(paths);
      }
      catch (final EventVetoedException e)
      {
         return;
      }
   }

   /**
    * {@inheritDoc}
    */
   public void resetRowSelection()
   {
      try
      {
         m_vetoableTreeSelectionListeners.fireVetoableEvent(new VetoableAction<VetoableTreeSelectionListener>() {
            public void fireEvent(final VetoableTreeSelectionListener listener) throws EventVetoedException
            {
               listener.aboutToResetRowSelection();
            }});

         m_treeSelectionModel.resetRowSelection();
      }
      catch (final EventVetoedException e)
      {
         return;
      }
   }

   /**
    * {@inheritDoc}
    */
   public void setRowMapper(final RowMapper newMapper)
   {
      m_treeSelectionModel.setRowMapper(newMapper);
   }

   /**
    * {@inheritDoc}
    */
   public void setSelectionMode(final int mode)
   {
      try
      {
         m_vetoableTreeSelectionListeners.fireVetoableEvent(new VetoableAction<VetoableTreeSelectionListener>() {
            public void fireEvent(final VetoableTreeSelectionListener listener) throws EventVetoedException
            {
               listener.aboutToSetSelectionMode(mode);
            }});

         m_treeSelectionModel.setSelectionMode(mode);
      }
      catch (final EventVetoedException e)
      {
         return;
      }
   }

   /**
    * {@inheritDoc}
    */
   public void setSelectionPath(final TreePath path)
   {
      try
      {
         m_vetoableTreeSelectionListeners.fireVetoableEvent(new VetoableAction<VetoableTreeSelectionListener>() {
            public void fireEvent(final VetoableTreeSelectionListener listener) throws EventVetoedException
            {
               listener.aboutToSetSelectionPath(path);
            }});

         m_treeSelectionModel.setSelectionPath(path);
      }
      catch (final EventVetoedException e)
      {
         return;
      }
   }

   /**
    * {@inheritDoc}
    */
   public void setSelectionPaths(final TreePath[] paths)
   {
      try
      {
         m_vetoableTreeSelectionListeners.fireVetoableEvent(new VetoableAction<VetoableTreeSelectionListener>() {
            public void fireEvent(final VetoableTreeSelectionListener listener) throws EventVetoedException
            {
               listener.aboutToSetSelectionPaths(paths);
            }});

         m_treeSelectionModel.setSelectionPaths(paths);
      }
      catch (final EventVetoedException e)
      {
         return;
      }
   }

   /**
    * {@inheritDoc}
    */
   @Override
   public String toString()
   {
      return m_treeSelectionModel.toString();
   }

}

以下是听众:

public interface VetoableTreeSelectionListener
{
   /**
    * About to add a path to the selection
    *
    * @param path the path to add
    *
    * @throws EventVetoedException
    */
   void aboutToAddSelectionPath(TreePath path) throws EventVetoedException;

   /**
    * About to add paths to the selection
    *
    * @param paths the paths to add
    *
    * @throws EventVetoedException
    */
   void aboutToAddSelectionPaths(TreePath[] paths) throws EventVetoedException;

   /**
    * About to clear selection
    *
    * @throws EventVetoedException
    */
   void aboutToClearSelection() throws EventVetoedException;

   /**
    * About to remove a selection path
    *
    * @param path the path
    *
    * @throws EventVetoedException
    */
   void aboutRemoveSelectionPath(TreePath path) throws EventVetoedException;

   /**
    * About to remove multiple selection paths
    *
    * @param paths the paths
    *
    * @throws EventVetoedException
    */
   void aboutRemoveSelectionPaths(TreePath[] paths) throws EventVetoedException;

   /**
    * About to reset the row selection
    *
    * @throws EventVetoedException
    */
   void aboutToResetRowSelection() throws EventVetoedException;

   /**
    * About to set the selection mode
    *
    * @param mode the selection mode
    *
    * @throws EventVetoedException
    */
   void aboutToSetSelectionMode(int mode) throws EventVetoedException;

   /**
    * About to set the selection path
    *
    * @param path the path
    *
    * @throws EventVetoedException
    */
   void aboutToSetSelectionPath(TreePath path) throws EventVetoedException;

   /**
    * About to set the selection paths
    *
    * @param paths the paths
    *
    * @throws EventVetoedException
    */
   void aboutToSetSelectionPaths(TreePath[] paths) throws EventVetoedException;
}

你可以使用你自己的ListenerList实现,但你明白了......

答案 5 :(得分:0)

为了防止选择,我只是将DefaultTreeSelectionModel子类化,并覆盖所有方法以检查我不想选择的对象(下面我的例子中的“DisplayRepoOwner”实例)。如果对象可以选择,我调用了super方法;否则我没有。我将JTree的选择模型设置为该子类的实例。

public class MainTreeSelectionModel extends DefaultTreeSelectionModel {
public void addSelectionPath(TreePath path) {
    if (path.getLastPathComponent() instanceof DisplayRepoOwner) {
        return;
    }
    super.addSelectionPath(path);
}
public void addSelectionPaths(TreePath[] paths) {
    for (TreePath tp : paths) {
        if (tp.getLastPathComponent() instanceof DisplayRepoOwner) {
            return;
        }
    }
    super.addSelectionPaths(paths);
}
public void setSelectionPath(TreePath path) {
    if (path.getLastPathComponent() instanceof DisplayRepoOwner) {
        return;
    }
    super.setSelectionPath(path);
}
public void setSelectionPaths(TreePath[] paths) {
    for (TreePath tp : paths) {
        if (tp.getLastPathComponent() instanceof DisplayRepoOwner) {
            return;
        }
    }
    super.setSelectionPaths(paths);
}

}

答案 6 :(得分:-1)

在调查同一问题的解决方案时偶然发现了这个问题。首先,让我告诉你一些不起作用的事情。我试图用树注册MouseListeners和所有这些。问题是TreeUI的鼠标监听器在我的JTree之前进入事件的过程,这意味着设置一个标志或类似的东西为时已晚。除此之外,这个解决方案产生了一些丑陋的代码,我通常会避免它。

现在为实际解决方案!
在使用一些Thread.dumpStack()调用来获取堆栈转储后,我找到了我想要覆盖的方法。我扩展了BasicTreeUI并覆盖了“protected void selectPathForEvent(TreePath path,MouseEvent event)”。

这将使您能够访问在选择实际发生之前导致选择的鼠标事件。然后,您可以使用event.consume()所需的任何逻辑,如果要停止选择,返回所需的任何选择,或通过调用super.selectPathForEvent(path,event)将其传递给默认处理;

请记住设置您在JTree中创建的UI。这个错误浪费了我生命中的几分钟; - )