I'm having a problem closing my JavaFX application. The application keeps running in the background even after closing. I think the actual problem is caused due to the alert box which will popup while closing the application for confirmation, if the alert doesn't pop up (on certain conditions) then the application exits normally.
Actually, I want the user to confirm whether to save the file or not before closing the application so I have added an alert box that will show only if the file is not saved manually (using Ctrl+S or File->Save option) before closing the application. When the popup appears, if the user selects to save the file it will save as usual if it chooses to cancel then the application continues to run.
But the problem is that if the alert box appears for confirmation and the user chooses to save or don't save option then the application doesn't close completely (even after completing all the task related to saving if the user chooses to save the file) it only hides the stage and keeps running in the background but if the alert box doesn't appear for confirmation then the application closes normally.
Here is the code for widow.setOnCloseRequest(); event:-
Platform.runLater(() -> {
window = App.getStage();
window.setOnCloseRequest(event -> {
if (!saveIfFileEdited()) {
event.consume();
} else {
scheduledService.shutdown();
// Platform.exit(); //I have tried this also but it dosen't work.
}
});
});
Here it will call the saveIfFileEdited() method which will handle the alert popup for confirmation & and here is its code:-
private boolean saveIfFileEdited() {
if (isEdited.get()) { // this will check if the file was edited but not saved.
Alert fileEditedAlert = new Alert(Alert.AlertType.CONFIRMATION);
fileEditedAlert.setTitle("Notepad");
fileEditedAlert.setHeaderText(null);
fileEditedAlert.setContentText("Do you want to save the changes to " + windowTitle.get() + "?");
ButtonType save = new ButtonType("Save");
ButtonType dontSave = new ButtonType("Don't Save");
fileEditedAlert.getButtonTypes().setAll(save, dontSave, ButtonType.CANCEL);
Optional<ButtonType> result = fileEditedAlert.showAndWait();
if (result.isPresent() && result.get().equals(save)) {
handleSaveFile(null); // this func will save the file, it may open save file dialog if the file is going to be saved for first time.
return true;
} else if (result.isPresent() && result.get().equals(dontSave)) {
return true;
} else if (result.isPresent() && result.get().equals(ButtonType.CANCEL)) {
return false;
} else {
return false;
}
} else {
return true;
}
}
Please help me to resolve this issue.
I cannot close the JVM forcefully using System.exit(0); because there may be some task in another thread which has to be completed even after closing the application.
Thanks.
Related
I have a small application in JavaFX running on Windows 10, I've changed many things inside the application and now I am facing the problem that my InfoBox is showing up with header and Picture but no text is wiritten inside the box.
I am calling this inside a part of my app:
infoBox("Karte wurde gelesen... bitte warten ", "Information-NFC", "Information-NFC!");
and here is the function itself:
public void infoBox(String infoMessage, String titleBar, String headerMessage)
{
Alert alert = new Alert(AlertType.INFORMATION);
alert.setResizable(true);
alert.getDialogPane().setPrefSize(480, 320);
alert.getDialogPane().setStyle("-fx-text-fill: red;-fx-font-size: 15px; -fx-font: 10px Tahoma; ");
alert.setTitle(titleBar);
alert.setHeaderText(headerMessage);
alert.setContentText(infoMessage);
System.out.println(infoMessage+"here");
//Logo für Programm Fenster
//////////// Logo ProgrammFesnter end
Image img2 = new Image(getClass().getResourceAsStream("logo.PNG"));
Stage dialogStage = (Stage) alert.getDialogPane().getScene().getWindow();
dialogStage.getIcons().add(img2);
alert.show();
try {
Thread.sleep(8000);
} catch (InterruptedException ex) {
System.out.println(ex);
}
alert.close();
}
I can see in the log window that the variable "infoMessage" is set but the Alert Box itself is empty.
And I don't know why.
Anyone of you out there had this problem before?
Everything is running fine except this small little box... ???
Thanks to all out there and have nice holidays....
Stev
Most likely the problem is the Thread.sleep(8000) call. That sleeps the JavaFX Application Thread which prevents user interaction with the UI and also prevents any render "pulses" from being scheduled. In other words, your application is unresponsive for those 8 seconds.
If you want to delay an action on the FX thread then you should use the animation API. And since you appear to want to wait for the alert to close before allowing the code to proceed you should also make use of the onShown property and showAndWait() method of Dialog. For example:
Alert alert = ...;
// configure alert
PauseTransition closeDelay = new PauseTransition(Duration.seconds(8));
closeDelay.setOnFinished(e -> alert.close());
alert.setOnShown(e -> closeDelay.playFromStart());
alert.showAndWait();
I'm writing an app using Xamarin Forms and I have an issue I was hoping someone can help with.
My app contains a screen which has multiple icons that can be pressed which would then open a new screen.
My issue is that if you press the icon twice really fast, the app opens up 2 instances of the same screen (it's not just related to a double press, if you press the icon 6 times very fast it will open up 6 duplicate screens). Pressing the Back button, closes the top screen to reveal the duplicate screen underneath. Pressing the Back button again navigates you back to the original screen.
This issue seems to occur on any screen within my app so I'm hoping other people will have experienced it and know of a solution to prevent duplicate screens being displayed.
This is a known issue in TapEvents.
My hack is, in the code-behind, have a bool variable _canTap.
Inside the method you are calling to push new page, first you check if canTap, then set to false, and only set to true after navigating to another page. This way all taps will be disregarded.
Example:
private bool _canTap = true;
public void YourMethod()
{
if(_canTap)
{
_canTap = false;
YourMethodToNavigate();
_canTap = true;
}
}
In the Icon_Pressed method add this,
this.IsEnabled = false;
await Navigation.PushAsync(new MyPage());
this.IsEnabled = true;
It disables the page until the current Icon pressed event is finished
This is known problem with Xamarin apps. I've used a private variable combined with a try-finally pattern to solve this. Ex:
bool allowTap = true;
public void ButtonTapped()
{
try
{
if(allowTap)
{
allowTap = false;
// Do whatever...
}
}
finally
{
allowTap = true;
}
}
The finally makes sure allowTap gets set back to true no matter what happens, short of a complete crash. Note that you can also use a catch block between the try and finally blocks to grab any errors if needed.
I have a dialog where user selects file(s). I added QCompleter to the line edit, which automatically suggests next file name:
However if user clicks on file, the suggestions disappear:
I want them to reappear if directory is selected and display the files in that directory. I tried to do this inside the QLineEdit::textChanged signal. I connected it to such slot:
void ImportFromExcelDialog::pathChanged( const QString& path )
if(path.length()>0) {
QFileInfo info(path);
if( info.exists() && info.isFile() && info.isReadable() ) {
// File selected, do stuff with it
}
else {
// If a directory
if((info.exists() && info.isDir())) {
if(!path.endsWith("/"))
ui->fileLineEdit->setText(path + "/");
// Assume QCompleter* completer_; which is set in constructor
if(completer_!=nullptr)
completer_->complete();
}
}
}
}
The problem is that calling complete() shows the old list of files, the one for parent directory:
I can click telemetrie as many times as I want and the display won't change.
So how to force QCompleter to reappear and handle the new value of the text field?
I have an AIR application running on a Mac and I want to have the behavior of hiding the window when someone "closes" the app (e.g. hits the red "x" button or cmd-w). However, if someone hits cmd-q or chooses "Quit" from the dock context menu or top level menu, I want the app to actually close.
I can preventDefault on the "closing" event sent by the application, however, this causes all "close" methods to just hide the window. The only way for someone to close the application at that point is ForceQuit (or through a separate interface I provide, like a context menu option on the dock icon).
I have also tried capturing the cmd-q keyDown event manually, but it doesn't get sent. Also, this wouldn't help for the case when people try to quit the app using the menu options.
Furthermore, if I preventDefault on the closing method, it causes my application to cancel a shut down process immediately (which is a terrible user experience).
Is there a way to detect different methods of closing an AIR application? I want to be able to tell the difference between these closing methods and react to the appropriately.
Try this for the closing, from what I understand there was / is a bug in the framework so that if you include the AIR updater it breaks cmd-q support, the thread used to be here: http://www.adobe.com/cfusion/webforums/forum/messageview.cfm?forumid=72&catid=670&threadid=1373568
This may or may not be applicable to your situation.
NativeApplication.nativeApplication.addEventListener(Event.EXITING,
function(e:Event):void {
var opened:Array = NativeApplication.nativeApplication.openedWindows;
for (var i:int = 0; i < opened.length; i ++) {
opened[i].close();
}
});
Try this, I am sure there must be a better way of handling this but this has worked for me.
<?xml version="1.0" encoding="utf-8"?>
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="onCreationComplete()">
<mx:Script>
<![CDATA[
import mx.core.Application;
import mx.events.AIREvent;
import mx.core.Window;
private function onCreationComplete():void {
addMacSupport();
}
private var macsupport_allowExit:Boolean = false;
private function addMacSupport():void {
if ( Capabilities.os.indexOf("Mac") == 0 ) {
//open a hidden window that will prevent the application from
//exiting when the user presses Cmd+W
var win:Window = new Window();
win.visible = false;
win.open(false);
//add a closing listener on the hidden window, this event will only
//be fired when the user pressed Cmd+Q or selects quit from the menu
//then set macsupport_allowExit to true
win.addEventListener(Event.CLOSING, function(e:Event):void {
macsupport_allowExit = true;
});
//add an event listener to this window on closing
addEventListener(Event.CLOSING, function(e:Event):void {
//always preventDefault
e.preventDefault();
//wait one frame, then check the macsupport_allowExit variable
//if it is true, we nedd to exit the app, otherwise just hide
//the app window
callLater(function():void {
if ( macsupport_allowExit ) {
nativeApplication.exit();
}
else {
nativeWindow.visible = false;
}
});
});
//add an event listener for INVOKE to show our main app window
//when the dock icon is clicked.
addEventListener(InvokeEvent.INVOKE, function(e:InvokeEvent):void {
if ( nativeWindow && !nativeWindow.visible ) {
nativeWindow.visible = true;
nativeWindow.activate();
}
});
}
}
]]>
</mx:Script>
</mx:WindowedApplication>
Goal:
Allow the user to delete a record by dragging a row from an AdvancedDataGrid, dropping it onto a trash-can icon and verify the user meant to do that via a popup alert with "OK" and "Cancel" buttons.
What is working:
Dragging/Dropping a row onto the trash icon.
If the user clicks the "OK" button, the record is deleted.
If the user clicks the "Cancel" button, the operation is canceled.
Problem:
After the user clicks the "Cancel" button and the popup alert closes, no rows in the ADG can be dragged. I've discovered that after sorting the ADG, by clicking on a column header, the user can begin dragging rows again.
Code: (changed from original post)
<mx:Image source="{trashImage}" buttonMode="true"
toolTip="drag a participant here to delete them from the project"
dragDrop="deleteParticipantDrop(event)" dragEnter="deleteParticipantEnter(event)"
dragExit="deleteParticipantDragExit(event)" top="4" right="122" id="image2" />
// trashImage Event Handlers:
private function deleteParticipantEnter(event:DragEvent):void
{
var component:IUIComponent = IUIComponent(event.currentTarget);
dragComponent = component;
DragManager.acceptDragDrop(component);
DragManager.showFeedback(DragManager.MOVE);
deleteParticipantDragEvent = event;
}
private function deleteParticipantDrop(event:DragEvent):void
{
var selectedKitNum:String = memberRpt.selectedItem.KitNum;
var selectedName:String = memberRpt.selectedItem.ParticipantName;
var component:IUIComponent = IUIComponent(event.currentTarget);
dragComponent = component;
DragManager.acceptDragDrop(component);
isEditingParticipantInfo = false;
isDeletingParticipant = true;
deleteParticipantDropEvent = event;
event.stopImmediatePropagation(); // Added as per mrm
alert.confirm("Are you sure you want to delete this participant, Kit #" + memberRpt.selectedItem.KitNum + " (" +
memberRpt.selectedItem.ParticipantName + ") from the project? This cannot be reversed!! An email will be " +
"sent to notify this participant and you will receive a copy of it for your records.", confirmRemoveParticipant);
}
private function deleteParticipantDragExit(event:DragEvent):void
{
var component:IUIComponent = IUIComponent(event.currentTarget);
dragComponent = component;
DragManager.acceptDragDrop(component);
DragManager.showFeedback(DragManager.NONE);
}
private function confirmRemoveParticipant(event:CloseEvent):void
{
if (event.detail == Alert.YES)
{
deleteReason = DeleteParticipantTitleWindow(PopUpManager.createPopUp( this, DeleteParticipantTitleWindow , true));
dispatchEvent(deleteParticipantDropEvent); // Added as per mrm
PopUpManager.centerPopUp(deleteReason);
deleteReason.showCloseButton = true;
deleteReason.title = "Reason for removal from project";
deleteReason.addEventListener("close", cleanupRemoveParticipant);
deleteReason["cancelButton"].addEventListener("click", cleanupRemoveParticipant);
deleteReason["okButton"].addEventListener("click", finalizeDeleteParticipant);
isDeletingParticipant = false;
}
else
{
cleanupRemoveParticipant();
}
}
private function cleanupRemoveParticipant(event:Event = null):void
{
memberRpt.invalidateDisplayList();
memberRpt.executeBindings();
if (deleteReason != null)
{
PopUpManager.removePopUp(deleteReason);
deleteReason = null;
}
}
public function finalizeDeleteParticipant(event:Event):void
{
if (deleteReason.reason.text != null)
{
selectedReportItem = memberRpt.selectedItem;
selectedReportItemIndex = memberRpt.selectedIndex;
memberReportData.removeItemAt(selectedReportItemIndex);
}
else
{
alert.info("You must provide a reason for removing a participant from your project!!");
}
cleanupRemoveParticipant();
}
Thanks in advance for all helpful suggestions.
Have you tried running the validateNow() method on the ADG after the cancel event?
Here is some more information on the validateNow() method.
Why you need to know about validateNow...
I really do think this is what you're looking for! Please let us know if that is the case...
Try refreshing the data bindings on the datagrid using executeBindings and/or invalidateDisplayList in the enclosing control.
To be honest this sounds a bit like a bug. Have you posted this on flexcoders? The Adobe guys hang out on there (probably here too, but definitely there)
Hang on... just spotted that between the drop event and the cancel button of the popup there is an asynchronous web service call which appears to be handled by GetParticipantOrderInformation. Is that correct?
If yes, then have you tried offering a simpler dialog for Cancel before you do that? I wonder whether the combination of layers of events is causing a problem.
I didn't have any success with refreshing the data bindings on the datagrid via the executeBindings and invalidateDisplayList methods. I also didn't have any luck by showing the confirmation alert before making the webservice call. In fact, I discovered that making the webservice call was completely unnecessary and removed it. So now the code flows like this:
Drag/drop ADG row onto trash icon.
Display confirmation Alert box.
If user clicked "Cancel" button, redisplay the ADG.
But the same problem persists. I'll update the Code section with the latest code.
Here's an idea:
- Just before you create the alert window, stop the DragEvent
event.stopImmediatePropagation();
store the event so we can resume if the user clicks the Yes button
queuedEvent = event as DragEvent;
show the alert window
if the user clicks the yes button, resume the queued event
dispatchEvent(queuedEvent);
DragManager.showFeedback(DragManager.NONE);