6 Replies Latest reply: Oct 28, 2013 6:09 AM by b4ceeb49-2ff0-4b97-a177-d1cfc200f750 Branched from an earlier discussion. RSS

    Re: MenuContextuel

    b4ceeb49-2ff0-4b97-a177-d1cfc200f750

      Not really, I want to have a popup that opens with different actions to perform 'Delete', 'Add' .....

      for this example, lorque I right click, I have a PopupMenu "Add", "Edit" and "Delete".

      I want to use this mechanism in a table if possible

       

       

      
      

       

      <AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="277.0000999999975" prefWidth="371.0" styleClass="background" xmlns:fx="http://javafx.com/fxml">

        <children>

          <Label layoutX="72.0" layoutY="146.0" styleClass="item-title" text="Right click here to bring up the context menu">

            <contextMenu>

              <ContextMenu>

                <items>

                  <MenuItem mnemonicParsing="false" text="Add" />

                  <SeparatorMenuItem mnemonicParsing="false" />

                  <MenuItem mnemonicParsing="false" text="Edit" />

                  <MenuItem mnemonicParsing="false" text="Delete" />

                </items>

              </ContextMenu>

            </contextMenu>

          </Label>

          <Label layoutX="35.0" layoutY="22.0" styleClass="header" text="Context Menu" />

        </children>

      </AnchorPane>

        • 1. Re: MenuContextuel
          James_D

          UPDATE:

          This message disappeared briefly, but now seems to be back. I don't think this approach works, however, as the same ContextMenu instance is shared by all rows in the table. Consequently, you'll either get runtime errors (same context menu in various places in the scene graph), or contextMenu.getOwnerNode() will be undefined or will simply return the wrong thing. I think you need the more convoluted approach below.

           

          "Add" is easy enough to do the same way, as the context for that functionality is the table. For "Edit" and "Delete", however, the context is the TableRow, and that's pretty tricky to do in FXML as you don't have access to the TableRow in fxml. I would just take the code from the answer to your previous question and put it in the initialize method of the controller.

           

          If you really want to design the look of the context menu in FXML, you could use a define block to define it outside of the scene graph and then reference it in the controller to associate it with the table row factory. Your event handlers might be a bit convoluted though.

           

          The FXML would look something like

          <fx:define>
          <ContextMenu fx:id="tableRowContextMenu">
          <items>
          ...
          </items>
          </ContextMenu>
          </fx:define>
          
          

           

          Then your initialize method can do something like

          @FXML private ContextMenu tableRowContextMenu ;
          public void initialize() {
               // ...
               table.setRowFactory(new Callback<TableView<...>, TableRow<...>>() {
                    @Override
                    public TableRow<...> call(TableView<...> table) {
                         TableRow<...> row = new TableRow<...>();
                         row.setContextMenu(tableRowContextMenu);
                    }
               });
          }
          
          

           

          and I think then your event handlers could do

           

          @FXML
          private void handleDelete() {
               TableRow<...> row = (TableRow<...>) tableRowContextMenu.getOwnerNode();
               // row.getItem() will now give you the item in the corresponding row
          }
          
          

          (I haven't tried this...)

           

          Message was edited by: James_D

          • 2. Re: MenuContextuel
            James_D

            Well, I don't know what happened to my previous reply, but it disappeared. I think it was flawed anyway...

             

            The code I posted in response to your previous question does exactly what you asked for. The easiest thing would be to put this code in the initialize() method of your controller class.

            If the issue is that you want to create the ContextMenu in FXML, that's difficult. The problem is that the context (for the "Edit" and "Delete" functionality) is the table row, not the table itself; hence you need one ContextMenu for each (non-empty) row in the table.

             

            So I suppose one way to do this is to write a top level class for the row factory; in that class you can reference an FXML file which defines your ContextMenu. So something like this:

             

            Tableau.fxml:

            ...
            <TableView>
            ...
            <rowFactory>
            <ContextMenuRowFactory />
            </rowFactory>
            </TableView>
            ...
            
            

             

            ContextMenuRowFactory.java:

            // imports omitted
            public class ContextMenuRowFactory implements Callback<TableView<T>, TableRow<T>> {
                 @Override
                 public TableRow<T> call(TableView<T> table) {
                      final TableRow<T> row = new TableRow<T>();
                      final ContextMenu contextMenu = (ContextMenu) FXMLLoader.load(getClass().getResource("TableRowContextMenu.fxml"));
                      row.contextMenuProperty().bind(Bindings.when(row.emptyProperty()).then((ContextMenu)null).otherwise(contextMenu);
                      return row ;
                 }
            }
            
            

             

            TableRowContextMenu.fxml:

            <ContextMenu fx:controller="TableRowContextMenuController" fx:id="contextMenu"/>
                 <items>
                      ...
                      <MenuItem text="Delete" onAction="#deleteTableRow" />
                      ...
                 <items>
            </ContextMenu>
            
            

             

            TableRowContextMenuController.java:

            // imports omitted
            public class TableRowContextMenuController {
                 @FXML
                 private ContextMenu contextMenu ;
                 @FXML
                 private void deleteTableRow() {
                      TableRow<T> row= (TableRow<T>) contextMenu.getOwnerNode();
                      TableView<T> table = row.getTableView();
                      table.getItems().remove(row.getItem());
                 }
                 //...
            }
            
            

             

            Obviously you replace "T" with the data type for your table throughout.

             

            I haven't tried any of this, and there are probably typos, but I think the general idea would work. Like I said, the easier way would be just to use the code I previously provided directly in your main controller's initialize() method. If you are a real stickler for separating the presentation from the logic, then this avoids creating the context menu in a controller class.

             

            Message was edited by: James_D

            • 3. Re: MenuContextuel
              b4ceeb49-2ff0-4b97-a177-d1cfc200f750

              Thank you for your help, by against I get errors when starting the application:

               

              Caused by: javax.xml.stream.XMLStreamException: ParseError at [row,col]:[1,81]

              Message: http://www.w3.org/TR/1999/REC-xml-names-19990114#AttributePrefixUnbound?ContextMenu&fx:controller&fx

              at com.sun.org.apache.xerces.internal.impl.XMLStreamReaderImpl.next(Unknown Source)

              at javax.xml.stream.util.StreamReaderDelegate.next(Unknown Source)

              ... 92 more

              javax.xml.stream.XMLStreamException: ParseError at [row,col]:[1,81]

              Message: http://www.w3.org/TR/1999/REC-xml-names-19990114#AttributePrefixUnbound?ContextMenu&fx:controller&fx

              /D:/Users/ambaye1/workspace/SmurfitIHM/bin/presentation/fxml/TableRowContextMenu.fxml

                at javafx.fxml.FXMLLoader.load(Unknown Source)

                at javafx.fxml.FXMLLoader.load(Unknown Source)

                at javafx.fxml.FXMLLoader.load(Unknown Source)

                at javafx.fxml.FXMLLoader.load(Unknown Source)

                at javafx.fxml.FXMLLoader.load(Unknown Source)

                at javafx.fxml.FXMLLoader.load(Unknown Source)

                at javafx.fxml.FXMLLoader.load(Unknown Source)

                at presentation.composants.TableRowWithContextMenu.<init>(TableRowWithContextMenu.java:14)

                at presentation.factories.ContextMenuRowFactory.extracted(ContextMenuRowFactory.java:17)

                at presentation.factories.ContextMenuRowFactory.call(ContextMenuRowFactory.java:13)

                at presentation.factories.ContextMenuRowFactory.call(ContextMenuRowFactory.java:1)

                at com.sun.javafx.scene.control.skin.TableViewSkin.createCell(Unknown Source)

                at com.sun.javafx.scene.control.skin.TableViewSkin$1.call(Unknown Source)

                at com.sun.javafx.scene.control.skin.TableViewSkin$1.call(Unknown Source)

                at com.sun.javafx.scene.control.skin.VirtualFlow.getAvailableCell(Unknown Source)

                at com.sun.javafx.scene.control.skin.VirtualFlow.addTrailingCells(Unknown Source)

                at com.sun.javafx.scene.control.skin.VirtualFlow.layoutChildren(Unknown Source)

                at javafx.scene.Parent.layout(Unknown Source)

                at javafx.scene.Parent.layout(Unknown Source)

                at javafx.scene.Parent.layout(Unknown Source)

               

              TableRowWithContextMenu.java

               

              package presentation.composants;
              import java.io.IOException;
              import javafx.beans.binding.Bindings;
              import javafx.fxml.FXMLLoader;
              import javafx.scene.control.ContextMenu;
              import javafx.scene.control.TableRow;
              public class TableRowWithContextMenu extends TableRow {
               public TableRowWithContextMenu() {
                ContextMenu contextMenu = new ContextMenu();
                try {
                 contextMenu = (ContextMenu) FXMLLoader.load(getClass().getResource(
                   "/presentation/fxml/TableRowContextMenu.fxml"));
                } catch (IOException e) {
                 // TODO Auto-generated catch block
                 e.printStackTrace();
                }
                contextMenuProperty().bind(
                  Bindings.when(emptyProperty()).then((ContextMenu) null)
                    .otherwise(contextMenu));
               }
              }
              
              

              ContextMenuRowFactory.java

               

               

              package presentation.factories;
              import javafx.scene.control.TableRow;
              import javafx.scene.control.TableView;
              import javafx.util.Callback;
              import metier.vo.CoupeVO;
              import presentation.composants.TableRowWithContextMenu;
              public class ContextMenuRowFactory implements
                Callback<TableView<CoupeVO>, TableRow<CoupeVO>> {
               @Override
               public TableRow<CoupeVO> call(TableView<CoupeVO> table) {
                return new TableRowWithContextMenu();
               }
              }
              
              
              • 4. Re: MenuContextuel
                James_D

                TableRowContextMenu.fxml was just the core of the file, it needs the usual FXML header and namespacing stuff.

                 

                <?xml version="1.0" encoding="UTF-8"?>
                <?import javafx.scene.control.ContextMenu?>
                <?import javafx.scene.control.MenuItem?>
                
                <ContextMenu xmlns:fx="http://javafx.com/fxml" fx:id="contextMenu" fx:controller="TableRowContextMenuController" >
                 <items>
                  <MenuItem text="Add"/>
                  <MenuItem text="Edit"/>
                  <MenuItem text="Delete" onAction="#deleteRow"/>
                 </items>
                </ContextMenu>
                
                
                



                • 5. Re: MenuContextuel
                  b4ceeb49-2ff0-4b97-a177-d1cfc200f750

                  With this code, when I do right click on a label, I have a pop "Add" menu, "Edit", "Delete", I just want to do it on a table without care functions

                   

                   

                  <AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="277.0000999999975" prefWidth="371.0" styleClass="background" xmlns:fx="http://javafx.com/fxml">
                    <children>
                      <Label layoutX="72.0" layoutY="146.0" styleClass="item-title" text="Right click here to bring up the context menu">
                        <contextMenu>
                          <ContextMenu>
                            <items>
                              <MenuItem mnemonicParsing="false" text="Add" />
                              <SeparatorMenuItem mnemonicParsing="false" />
                              <MenuItem mnemonicParsing="false" text="Delete" />
                              <MenuItem mnemonicParsing="false" text="Edit" />
                            </items>
                          </ContextMenu>
                        </contextMenu>
                      </Label>
                      <Label layoutX="35.0" layoutY="22.0" styleClass="header" text="Context Menu" />
                    </children>
                  </AnchorPane>
                  
                  
                  

                   

                  • 6. Re: MenuContextuel
                    b4ceeb49-2ff0-4b97-a177-d1cfc200f750

                    Now I created my popup menu, but when I send the event (example remove) the reception the event is null, here is my code: Opening popup by right click :

                     

                     @FXML
                     private void handleTableRightClicAction(MouseEvent event) throws Exception {
                      if (event.getButton().name().equals("SECONDARY")) {
                       popupMenu.show(table, event.getScreenX(), event.getScreenY());
                      }
                     }
                    
                    

                     

                     

                    My classe PopupMenu

                     

                    public class PopupMenu extends ContextMenu {
                     public PopupMenu() {
                      this.getItems().addAll(
                        MenuItemBuilder.create().text("Delete")
                          .graphic(supprimeIcone).onAction(new EventHandler() {
                           @Override
                           public void handle(Event event) {
                            coupeItem = (MenuItem) event.getSource();
                            deleteTableRow(coupeItem);
                            System.out.println(coupeItem);
                           }
                          }).build());
                      this.setAutoHide(true);
                      this.setHideOnEscape(true);
                     }
                     @FXML
                     private void deleteTableRow(MenuItem coupeItem) {
                      TableRow<CoupeVO> row = (TableRow<CoupeVO>) coupeItem
                        .getOnMenuValidation();
                       TableView<CoupeVO> table = row.getTableView();
                       table.getItems().remove(row.getItem());
                     }
                    }