2008-11-04 8 views
2

J'ai une boîte de dialogue où chaque entrée dans un JTree a ses options correspondantes dans un panneau différent, qui est mis à jour lorsque la sélection change. Si les options d'une des entrées sont définies sur un état non valide, lorsque l'utilisateur tente de modifier une entrée différente dans l'arborescence, je souhaite qu'il y ait une boîte de dialogue d'erreur et que la sélection ne soit pas modifiée.La meilleure façon d'empêcher un changement de sélection JTree de se produire?

J'ai essayé de le faire avec un valueChangeListener sur le JTree, mais actuellement, la méthode valueChanged appelle "setSelectionRow" pour l'ancienne sélection s'il y a une erreur. Pour ne pas avoir de StackOverflow, je place un "isError" booléen sur true avant de faire ceci afin que je puisse ignorer le nouvel événement valueChanged. D'une certaine façon, j'ai l'intuition que ce n'est pas la meilleure solution. ;-)

Comment je m'y prendrais plutôt? Y a-t-il un bon modèle de conception pour des situations comme celle-ci?

Répondre

3

Pas sûr que ce soit la meilleure pratique, mais peut-être que vous pourriez mettre un FocusListener sur les composants que vous voulez valider ... appelez votre validation lorsque l'événement est appelé, puis consommez l'événement si vous ne voulez pas concentrer pour être déplacé car la validation échoue?

plus tard Edit:

au moins avec java 8 (je ne l'ai pas vérifié les versions antérieures) cette solution ne fonctionnera pas, parce que le FocusEvent ne semble pas être un événement à faible niveau. Par conséquent, il ne peut pas être consommé. Voir Méthode AWTEvent.consume()

+0

Telle est l'approche idéale, l'OMI –

+0

Peut-être est la meilleure façon de le faire alors. – cagcowboy

+0

je l'aime. Merci. – Epaga

0

Définissez un TreeSelectionModel qui implémente la sémantique appropriée.

6

Je n'ai pas trouvé de meilleur moyen, mais cette approche fonctionne bien pour moi. Je sais que dans Delphi c'était un événement très pratique: "avant de changer de sélection" où vous pouvez très facilement arrêter de changer de sélection.

ici est mon code java avec la prévention du problème récursion infinie

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

toujours se rappeler de supprimer tous les auditeurs ajoutés, afin d'éviter les fuites de mémoire.

ici est une autre approche:

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

Voici un exemple de mise en œuvre d'un TreeSelectionModel qui enveloppe une autre TreeSelectionModel mais permet de choisir d'opposer son veto:

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(); 
    } 

}

Et voici l'auditeur pour aller avec elle:

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; 
} 

Vous pouvez utiliser votre propre implémentation de ListenerList, mais vous obtenez l'idée ...

3

Voici ma solution.

Dans une sous-classe 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); 
} 

Puis dans l'arbre en utilisant la classe:

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); 

Le mécanisme commence au plus tôt endroit possible. Action de la souris interceptée, le chemin à sélectionner est annoncé dans VetoableChangeListeners. Dans la VCL concrète, la propriété changeante est examinée et, si c'est la sélection du lead, la logique de veto est vérifiée. Si le veto est nécessaire, la VCL lève PropertyVetoException, sinon la gestion des événements de souris se déroule comme d'habitude et la sélection se produit. En résumé, cela fait que la propriété de sélection de fil devient une propriété contrainte.

+0

Merci pour ça! – The111

-1

Trébuché à travers ce fil tout en recherchant une solution pour le même problème. D'abord, laissez-moi vous dire des choses qui n'ont pas marché. J'ai essayé d'enregistrer MouseListeners et tout cela avec l'arbre. Le problème était que les écouteurs de la souris de TreeUI étaient en train de traiter l'événement avant mon JTree, ce qui signifiait qu'il était trop tard pour mettre un drapeau ou quelque chose comme ça. En plus de cela cette solution a produit un code laid et je l'éviterais généralement.

Alors maintenant pour la solution actuelle!
Après avoir utilisé quelques appels Thread.dumpStack() pour obtenir un vidage de pile, j'ai trouvé la méthode que je cherchais à remplacer. J'ai étendu le BasicTreeUI et remplacer le "protected void selectPathForEvent (chemin TreePath, événement MouseEvent)". Cela vous donnera accès à l'événement de souris qui a causé la sélection avant que la sélection ne se produise réellement. Vous pouvez ensuite utiliser n'importe quelle logique pour event.consume() et retourner si vous voulez arrêter la sélection, faire la sélection que vous voulez ou la passer pour le traitement par défaut en appelant super.selectPathForEvent (path, event); N'oubliez pas de définir l'interface utilisateur créée dans JTree. Cette erreur a gâché quelques minutes de ma vie ;-)

0

Pour éviter la sélection, j'ai juste sous-classé DefaultTreeSelectionModel et ai passé outre toutes les méthodes pour vérifier les objets que je ne voulais pas sélectionner (instances de "DisplayRepoOwner" dans mon exemple au dessous de). Si l'objet pouvait être sélectionné, j'ai appelé la super méthode; sinon je ne l'ai pas fait. J'ai défini le modèle de sélection de mon JTree sur une instance de cette sous-classe.

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

}

Questions connexes