关闭窗口前提示保存/退出

29
  • 我正在开发一款写作应用程序。用户可以打开应用程序,编写一些文字,保存他们的工作等。
  • 我试图让点击“关闭窗口”按钮时提示用户(a)保存他们的工作(如果必要)或(b)直接退出。
  • 我尝试使用 window.beforeunload 来实现这一点,但是发现我陷入了一个循环中,尝试“退出”会导致同样的提示无限出现。

以下是一些代码:

windowCloseCheck() {
window.onbeforeunload = function(e) {
  e.returnValue = false;
// window.alert('try to close me');    
if(file.isUnsaved() || file.hasChanged()) {
  // prompt - save or just quit?
  file.fileWarning('You have unsaveeed work.', 'Save', 'Quit', function(){
    // OPTION A - save
    file.save();
  }, function() {
    // OPTION B: Quit.
    ipcRenderer.send('quitter')
  })
} 

else {
  // file is saved and no new work has been done:
  ipcRenderer.send('quitter')
}

windowCloseCheck在应用程序设置时被调用,启动一个关闭窗口的事件监听器。我的条件是检查文件是否未保存或已经与原始文件不同。

fileWarning是一个函数,只是封装了electron对话框,使得弹出一个带有两个选择和各自函数的选项框。

如果我漏掉了必要的信息,其余代码可以在这里找到。如果我表述不够清晰,我很乐意进行澄清。

7个回答

28
请在您定义浏览器窗口的函数内添加以下代码块(在我的情况下,这是在main.js中声明的createWindow()函数中)。
  // Create the browser window.
  mainWindow = new BrowserWindow({width: 400, height: 400})


  mainWindow.on('close', function(e){
    var choice = require('electron').dialog.showMessageBox(this,
        {
          type: 'question',
          buttons: ['Yes', 'No'],
          title: 'Confirm',
          message: 'Are you sure you want to quit?'
       });
       if(choice == 1){
         e.preventDefault();
       }
    });

2
这绝对是解决这个问题最好的方式。我已经尝试了几天不同的方法,但总是有需要被绕过的东西。然而,这种方法完美地发挥作用,并且在Electron版本1.4.15中几乎是唯一的没有遇到问题的方法。 - David
这很好,但如果我想在对话框中显示一个复选框,例如“不再显示”,那么我必须将回调传递给showMessageBox,这会导致非阻塞行为,使得此代码无用。 - pouya
完美的答案,干得好 :) - Mayank Vadiya
我尝试在渲染进程中使用onbeforeunload并失败了。我还尝试过使用app.on('before-quit'),但也没有成功。使用BrowserWindow是最好的解决方案! - Renan Borges
19
由于 dialog.showMessageBox 是一个异步方法,在 Electron 7 中,建议的解决方案似乎已经不再适用了。只需将调用替换为 dialog.showMessageBoxSync 即可修复该问题。 - Sergio Mazzoleni

17

Electron 7+ 的答案

以下是基于 awijeet 的答案和 Sergio Mazzoleni 的评论,适用于最新版本的 Electron。

createWindow() 的底部,在你的 win = new BrowserWindow(...) 下面使用:

win.on('close', function(e) {
  const choice = require('electron').dialog.showMessageBoxSync(this,
    {
      type: 'question',
      buttons: ['Yes', 'No'],
      title: 'Confirm',
      message: 'Are you sure you want to quit?'
    });
  if (choice === 1) {
    e.preventDefault();
  }
});

关闭 Electron 应用程序确认对话框


4

最终我选择使用window.destroy()来关闭渲染进程(在主进程中):

ipcMain.on('quitter', (e) => {
  mainWindow.destroy(); // necessary to bypass the repeat-quit-check in the render process.
  app.quit()
})

不太确定这是否被推荐,因为有更好的方法来正确退出一个electron应用程序。但如果您知道更好的答案,仍然很乐意听取任何反馈!

/耸肩!


虽然很奇怪,但 BrowswerWindow.destroy() 仍然需要确保它不必被关闭两次。这对我很有帮助。非常感谢。 - Abel Callejo

3
 // Create the browser window.
  mainWindow = new BrowserWindow({width: 400, height: 400})


  mainWindow.on('close', function(e){
         e.preventDefault();
    var choice = require('electron').dialog.showMessageBox(mainWindow,
        {
          type: 'question',
          buttons: ['Yes', 'No'],
          title: 'Confirm',
          message: 'Are you sure you want to quit?'
       });
      choice.then(function(res){
         // 0 for Yes
        if(res.response== 0){
         // Your Code
        }
         // 1 for No
        if(res.response== 1){
         // Your Code
        }
      }
    });

请注意,dialog.showMessageBox根据Electron文档返回Promise<Object>

this更改为mainWindow

这是对原答案给Awijeet的修改后回答


2
您可以添加一个本地变量来避免mainWindow.destroy(),例如:
  let showExitPrompt = true;

  // ask renderer process whether should close the app (mainWindow)
  mainWindow.on('close', e => {
    if (showExitPrompt) {
      e.preventDefault(); // Prevents the window from closing
      mainWindow.webContents.send('on-app-closing');
    }
  });

  // renderer allows the app to close
  ipcMain.on('quitter', (e) => {
    showExitPrompt = false;
    mainWindow.close();
  })

1

Awijeet的解决方案非常完美!顺便说一句,谢谢Awijeet:它节省了我几个小时!;-)

此外,如果您需要更进一步,请注意e.preventDefault()正在代码中广泛传播。一旦您正确地处理了preventDefault(),您需要将变量e.defaultPrevented = false设置回应用程序的自然行为。

实际上,似乎e.preventDefault()函数会将变量e.defaultPrevented的值转换为true,直到您改变其值。


1
如果用户选择保存按钮,那么我们应该保存数据并使用window.close()关闭窗口。 但是,采用这种方法会导致无限循环,因为window.close()将再次触发窗口的close事件,要解决这个问题,我们必须声明一个新的布尔变量forceQuit,最初设置为false,然后在保存数据或用户决定不保存数据而直接关闭时将其设置为true
import { app, BrowserWindow, dialog } from 'electron';

let win = null;
let forceQuit = false;

app.on('ready', () => {
    win = new BrowserWindow({
        // Your window options..
    });

    mainWindow.on('close', e => {
        if (!forceQuit) {
            const clickedButtonId = dialog.showMessageBox(mainWindow, {
                type: 'warning',
                buttons: ['Save', 'Cancel', `Don't save`],
                cancelId: 1,
                title: 'Confirm',
                message: 'You have unsaved work!'
            });
            if (clickedButtonId === 0) {
                e.preventDefault();

                console.log('Saving...');
                /** Here do your data saving logic */

                forceQuit = true;
                win.close();
            } else if (clickedButtonId === 1) {
                e.preventDefault();
            } else if (clickedButtonId === 2) {
                forceQuit = true;
                win.close();
            }
        }
    });
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接