我想为JavaFX创建上下文菜单。这是我测试的代码。但由于某些原因,当我右键单击树节点时,没有上下文菜单。你能帮我找错吗?
import java.util.Arrays;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.ContextMenuBuilder;
import javafx.scene.control.MenuItemBuilder;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.TextFieldTreeCell;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Callback;
public class MainApp extends Application
{
List<Employee> employees = Arrays.<Employee>asList(
new Employee("New Chassi", "New Datacenter"),
new Employee("New Battery", "New Rack"),
new Employee("New Chassi", "New Server"),
new Employee("Anna Black", "Sales Department"),
new Employee("Rodger York", "Sales Department"),
new Employee("Susan Collins", "Sales Department"),
new Employee("Mike Graham", "IT Support"),
new Employee("Judy Mayer", "IT Support"),
new Employee("Gregory Smith", "IT Support"),
new Employee("Jacob Smith", "Accounts Department"),
new Employee("Isabella Johnson", "Accounts Department"));
TreeItem<String> rootNode = new TreeItem<>("MyCompany Human Resources");
public static void main(String[] args)
{
Application.launch(args);
}
TreeView<String> treeView = new TreeView<>(rootNode);
@Override
public void start(Stage stage)
{
rootNode.setExpanded(true);
for (Employee employee : employees)
{
TreeItem<String> empLeaf = new TreeItem<>(employee.getName());
boolean found = false;
for (TreeItem<String> depNode : rootNode.getChildren())
{
if (depNode.getValue().contentEquals(employee.getDepartment()))
{
depNode.getChildren().add(empLeaf);
found = true;
break;
}
}
if (!found)
{
TreeItem<String> depNode = new TreeItem<>(
employee.getDepartment()//,new ImageView(depIcon) // Set picture
);
rootNode.getChildren().add(depNode);
depNode.getChildren().add(empLeaf);
}
}
stage.setTitle("Tree View Sample");
VBox box = new VBox();
final Scene scene = new Scene(box, 400, 300);
scene.setFill(Color.LIGHTGRAY);
treeView.setCellFactory(new Callback<TreeView<String>, TreeCell<String>>()
{
@Override
public TreeCell<String> call(TreeView<String> arg0)
{
// custom tree cell that defines a context menu for the root tree item
return new MyTreeCell();
}
});
box.getChildren().add(treeView);
stage.setScene(scene);
stage.show();
}
public static class Employee
{
private final SimpleStringProperty name;
private final SimpleStringProperty department;
private Employee(String name, String department)
{
this.name = new SimpleStringProperty(name);
this.department = new SimpleStringProperty(department);
}
public String getName()
{
return name.get();
}
public void setName(String fName)
{
name.set(fName);
}
public String getDepartment()
{
return department.get();
}
public void setDepartment(String fName)
{
department.set(fName);
}
}
class MyTreeCell extends TextFieldTreeCell<String>
{
private ContextMenu rootContextMenu;
public MyTreeCell()
{
// instantiate the root context menu
rootContextMenu
= ContextMenuBuilder.create()
.items(
MenuItemBuilder.create()
.text("Menu Item")
.onAction(
new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent arg0)
{
System.out.println("Menu Item Clicked!");
}
}
)
.build()
)
.build();
}
@Override
public void updateItem(String item, boolean empty)
{
super.updateItem(item, empty);
// if the item is not empty and is a root...
if (!empty && getTreeItem().getParent() == null)
{
setContextMenu(rootContextMenu);
}
}
}
}
答案 0 :(得分:8)
您应该将上下文菜单分配给TreeView,而不是将其分配给单元工厂。以下是上下文菜单的代码:
import java.util.Arrays;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.ContextMenuBuilder;
import javafx.scene.control.MenuItemBuilder;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.TextFieldTreeCell;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
import javafx.util.Callback;
public class Test extends Application
{
List<Employee> employees = Arrays.<Employee>asList(
new Employee("New Chassi", "New Datacenter"),
new Employee("New Battery", "New Rack"),
new Employee("New Chassi", "New Server"),
new Employee("Anna Black", "Sales Department"),
new Employee("Rodger York", "Sales Department"),
new Employee("Susan Collins", "Sales Department"),
new Employee("Mike Graham", "IT Support"),
new Employee("Judy Mayer", "IT Support"),
new Employee("Gregory Smith", "IT Support"),
new Employee("Jacob Smith", "Accounts Department"),
new Employee("Isabella Johnson", "Accounts Department"));
TreeItem<String> rootNode = new TreeItem<>("MyCompany Human Resources");
public static void main(String[] args)
{
Application.launch(args);
}
TreeView<String> treeView = new TreeView<>(rootNode);
@Override
public void start(Stage stage)
{
// instantiate the root context menu
ContextMenu rootContextMenu
= ContextMenuBuilder.create()
.items(
MenuItemBuilder.create()
.text("Menu Item")
.onAction(
new EventHandler<ActionEvent>()
{
@Override
public void handle(ActionEvent arg0)
{
System.out.println("Menu Item Clicked!");
}
}
)
.build()
)
.build();
treeView.setContextMenu(rootContextMenu);
rootNode.setExpanded(true);
for (Employee employee : employees)
{
TreeItem<String> empLeaf = new TreeItem<>(employee.getName());
boolean found = false;
for (TreeItem<String> depNode : rootNode.getChildren())
{
if (depNode.getValue().contentEquals(employee.getDepartment()))
{
depNode.getChildren().add(empLeaf);
found = true;
break;
}
}
if (!found)
{
TreeItem<String> depNode = new TreeItem<>(
employee.getDepartment()//,new ImageView(depIcon) // Set picture
);
rootNode.getChildren().add(depNode);
depNode.getChildren().add(empLeaf);
}
}
stage.setTitle("Tree View Sample");
VBox box = new VBox();
final Scene scene = new Scene(box, 400, 300);
scene.setFill(Color.LIGHTGRAY);
box.getChildren().add(treeView);
stage.setScene(scene);
stage.show();
}
public static class Employee
{
private final SimpleStringProperty name;
private final SimpleStringProperty department;
private Employee(String name, String department)
{
this.name = new SimpleStringProperty(name);
this.department = new SimpleStringProperty(department);
}
public String getName()
{
return name.get();
}
public void setName(String fName)
{
name.set(fName);
}
public String getDepartment()
{
return department.get();
}
public void setDepartment(String fName)
{
department.set(fName);
}
}
}
答案 1 :(得分:4)
var saveFile = new FileSavePicker();
saveFile.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
saveFile.FileTypeChoices.Add("jpeg", new List<string> { ".jpeg" });
saveFile.SuggestedFileName = MasterMenuItem.Title + " - " + MasterMenuItem.AlbumArtistsString;
StorageFile sFile = await saveFile.PickSaveFileAsync();
try
{
if (sFile != null)
{
Guid bitmapEncoderGuid = BitmapEncoder.JpegEncoderId;
IRandomAccessStream stream = await sFile.OpenAsync(FileAccessMode.ReadWrite, StorageOpenOptions.None);
BitmapEncoder encoder = await BitmapEncoder.CreateAsync(bitmapEncoderGuid, stream);
BitmapImage bi = MasterMenuItem.Cover as BitmapImage;
WriteableBitmap wb = null;
wb = await BitmapFactory.New(1, 1).FromContent(bi.UriSource);
Stream pixelStream = wb.PixelBuffer.AsStream();
byte[] pixels = new byte[pixelStream.Length];
await pixelStream.ReadAsync(pixels, 0, pixels.Length);
encoder.SetPixelData(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Ignore,
(uint)wb.PixelWidth,
(uint)wb.PixelHeight,
96.0,
96.0,
pixels);
await encoder.FlushAsync();
stream.Dispose();
pixelStream.Dispose();
}
}
catch { }
和ContextMenuBuilder
(已在接受的答案中使用)在JavaFX 8中已弃用,将在以后的版本中删除。
在包含两个项目的JavaFX 8示例之后,一个带有图标,一个没有图标:
MenuItemBuilder
答案 2 :(得分:1)
我知道这是3年前的事,但是当我试图找到一个更简单的解决方案时,希望我刚刚找到的解决方案能够对像我这样的人有所帮助。我发现您可以仅创建一个Label并将其设置为TreeItem的图形。然后只需为该标签设置上下文菜单,下面就是使它起作用的代码。
//The "" is important, without it the tree item would not appear at all
TreeItem<String> childItem = new TreeItem<>("");
//Create a label with the string you would use for the tree item
childItem.setGraphic(new Label("TreeItem Label"));
//Add menu items for contextMenu
ContextMenu contextMenu = new ContextMenu();
childItem.getGraphic().setOnContextMenuRequested(e -> {
//Sets the context menu for the label.
contextMenu.show(childItem.getGraphic(), e.getScreenX(), e.getScreenY()));
}
答案 3 :(得分:1)
最小工作示例 下面没有不必要的代码和弃用的功能。 .setContextMenu
对整个树的解决方案实际上并不可用,因为不同的 TreeItem 可能具有不同的菜单。下面的代码使用在 JavaFX 中使用已实现的工厂模式的标准方式,并允许使用 JavaFX API 中的所有函数:
package javaapplication2;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.control.cell.TextFieldTreeCell;
import javafx.stage.Stage;
import javafx.util.Callback;
public class JavaApplication2 extends Application {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage stage) throws Exception {
//Start a new TreeView
TreeView<String> tv = new TreeView<>(new TreeItem<>("Root"));
//Set the cell factory
tv.setCellFactory(new Callback() {
@Override
public Object call(Object p) {
return new TreeCellWithMenu();
}
});
//Fill tree with some things
tv.getRoot().getChildren().add(new TreeItem<>("1"));
tv.getRoot().getChildren().add(new TreeItem<>("2"));
tv.getRoot().getChildren().add(new TreeItem<>("3"));
//Stage a new scene
stage.setScene(new Scene(tv));
//Show the stage
stage.show();
}
public class TreeCellWithMenu extends TextFieldTreeCell<String> {
ContextMenu men;
public TreeCellWithMenu() {
//ContextMenu with one entry
men = new ContextMenu(new MenuItem("Right Click"));
}
@Override
public void updateItem(String t, boolean bln) {
//Call the super class so everything works as before
super.updateItem(t, bln);
//Check to show the context menu for this TreeItem
if (showMenu(t, bln)) {
setContextMenu(men);
}else{
//If no menu for this TreeItem is used, deactivate the menu
setContextMenu(null);
}
}
//Deccide if a menu should be shown or not
private boolean showMenu(String t, boolean bln){
if (t != null && !t.equals("Root")) {
return true;
}
return false;
}
}
}