How do I make the "current directory" of the dialogs persist across
different invocations?
You could modify the Singleton Pattern approach for this
Whereby you would only use one FileChooser
and monitor/control the initial directory there, yet not directly exposing the instance to modifications outside of the class
For example:
public class RetentionFileChooser {
private static FileChooser instance = null;
private static SimpleObjectProperty<File> lastKnownDirectoryProperty = new SimpleObjectProperty<>();
private RetentionFileChooser(){ }
private static FileChooser getInstance(){
if(instance == null) {
instance = new FileChooser();
instance.initialDirectoryProperty().bindBidirectional(lastKnownDirectoryProperty);
//Set the FileExtensions you want to allow
instance.getExtensionFilters().setAll(new ExtensionFilter("png files (*.png)", "*.png"));
}
return instance;
}
public static File showOpenDialog(){
return showOpenDialog(null);
}
public static File showOpenDialog(Window ownerWindow){
File chosenFile = getInstance().showOpenDialog(ownerWindow);
if(chosenFile != null){
//Set the property to the directory of the chosenFile so the fileChooser will open here next
lastKnownDirectoryProperty.setValue(chosenFile.getParentFile());
}
return chosenFile;
}
public static File showSaveDialog(){
return showSaveDialog(null);
}
public static File showSaveDialog(Window ownerWindow){
File chosenFile = getInstance().showSaveDialog(ownerWindow);
if(chosenFile != null){
//Set the property to the directory of the chosenFile so the fileChooser will open here next
lastKnownDirectoryProperty.setValue(chosenFile.getParentFile());
}
return chosenFile;
}
}
This will set the initial directory of the FileChooser
to be the directory of the file the user last opened/saved when it is re-used
Example usage:
File chosenFile = RetentionFileChooser.showOpenDialog();
However there is a limitation here:
//Set the FileExtensions you want to allow
instance.getExtensionFilters().setAll(new ExtensionFilter("png files (*.png)", "*.png"));
As without providing any ExtensionFilter
's the FileChooser
becomes less user-friendly, requiring the user to manually append the file type, yet providing the filters when creating the instance with no way of updating them limits it to those same filters
One way to improve upon this is to expose optional filters within RetentionFileChooser
that can be provided using varargs, whereby you can choose when to modify the filters as you display the dialogs
For example, building on the previous:
public class RetentionFileChooser {
public enum FilterMode {
//Setup supported filters
PNG_FILES("png files (*.png)", "*.png"),
TXT_FILES("txt files (*.txt)", "*.txt");
private ExtensionFilter extensionFilter;
FilterMode(String extensionDisplayName, String... extensions){
extensionFilter = new ExtensionFilter(extensionDisplayName, extensions);
}
public ExtensionFilter getExtensionFilter(){
return extensionFilter;
}
}
private static FileChooser instance = null;
private static SimpleObjectProperty<File> lastKnownDirectoryProperty = new SimpleObjectProperty<>();
private RetentionFileChooser(){ }
private static FileChooser getInstance(FilterMode... filterModes){
if(instance == null) {
instance = new FileChooser();
instance.initialDirectoryProperty().bindBidirectional(lastKnownDirectoryProperty);
}
//Set the filters to those provided
//You could add check's to ensure that a default filter is included, adding it if need be
instance.getExtensionFilters().setAll(
Arrays.stream(filterModes)
.map(FilterMode::getExtensionFilter)
.collect(Collectors.toList()));
return instance;
}
public static File showOpenDialog(FilterMode... filterModes){
return showOpenDialog(null, filterModes);
}
public static File showOpenDialog(Window ownerWindow, FilterMode...filterModes){
File chosenFile = getInstance(filterModes).showOpenDialog(ownerWindow);
if(chosenFile != null){
lastKnownDirectoryProperty.setValue(chosenFile.getParentFile());
}
return chosenFile;
}
public static File showSaveDialog(FilterMode... filterModes){
return showSaveDialog(null, filterModes);
}
public static File showSaveDialog(Window ownerWindow, FilterMode... filterModes){
File chosenFile = getInstance(filterModes).showSaveDialog(ownerWindow);
if(chosenFile != null){
lastKnownDirectoryProperty.setValue(chosenFile.getParentFile());
}
return chosenFile;
}
}
Example usage:
//Note the previous example still holds
File chosenFile = RetentionFileChooser.showOpenDialog();
File file = RetentionFileChooser.showSaveDialog(FilterMode.PNG_FILES);
but this does not work if the dialog is closed or canceled.
Unfortunately FileChooser
doesn't expose information on what directory was being examined before it was closed / terminated. If you de-compile the class and trace through, it eventually hits a native
call. So even if FileChooser
wasn't final
allowing it to be subclassed, it is just as unlikely to be able to get this information
The only gain the above approach provides around this is that it doesn't change the initial directory if this scenario is hit
If you're interested, there are some very good answers on the native
key word within this question and those linked: