对话框
对话框是桌面应用与用户交互的重要方式。Electron 提供了多种对话框类型,包括文件选择、消息提示、错误提示等。本节介绍如何使用 dialog 模块创建各种对话框。
dialog 模块
dialog 模块只能在主进程中使用。它提供了多种对话框类型:
showOpenDialog:打开文件/目录选择对话框showSaveDialog:保存文件对话框showMessageBox:消息对话框showErrorBox:错误对话框
文件选择对话框
选择文件
const { dialog } = require('electron')
async function openFile() {
const result = await dialog.showOpenDialog({
title: '选择文件',
defaultPath: '/Users/username/Documents',
buttonLabel: '选择',
filters: [
{ name: '文本文件', extensions: ['txt', 'md'] },
{ name: '图片', extensions: ['jpg', 'png', 'gif'] },
{ name: '所有文件', extensions: ['*'] }
],
properties: ['openFile']
})
console.log(result)
}
返回结果:
{
canceled: false,
filePaths: ['/path/to/file.txt']
}
选择多个文件
const result = await dialog.showOpenDialog({
properties: ['openFile', 'multiSelections']
})
if (!result.canceled) {
result.filePaths.forEach(filePath => {
console.log('选中文件:', filePath)
})
}
选择目录
const result = await dialog.showOpenDialog({
title: '选择目录',
properties: ['openDirectory']
})
if (!result.canceled) {
console.log('选中目录:', result.filePaths[0])
}
配置选项
| 选项 | 类型 | 说明 |
|---|---|---|
title | String | 对话框标题 |
defaultPath | String | 默认路径 |
buttonLabel | String | 确认按钮文本 |
filters | Array | 文件类型过滤器 |
properties | Array | 对话框属性 |
message | String | macOS 上的额外消息 |
properties 选项
| 属性 | 说明 |
|---|---|
openFile | 允许选择文件 |
openDirectory | 允许选择目录 |
multiSelections | 允许多选 |
showHiddenFiles | 显示隐藏文件 |
createDirectory | 允许创建目录 |
promptToCreate | 不存在时提示创建 |
noResolveAliases | 不解析别名 |
treatPackageAsDirectory | 将包视为目录 |
文件过滤器
filters: [
{ name: '图片', extensions: ['jpg', 'png', 'gif'] },
{ name: '文档', extensions: ['doc', 'docx', 'pdf'] },
{ name: '所有文件', extensions: ['*'] }
]
保存对话框
基本用法
const result = await dialog.showSaveDialog({
title: '保存文件',
defaultPath: 'untitled.txt',
buttonLabel: '保存',
filters: [
{ name: '文本文件', extensions: ['txt'] },
{ name: '所有文件', extensions: ['*'] }
]
})
if (!result.canceled) {
console.log('保存路径:', result.filePath)
fs.writeFileSync(result.filePath, '文件内容')
}
返回结果
{
canceled: false,
filePath: '/path/to/save/file.txt'
}
配置选项
保存对话框支持与打开对话框相同的选项,以及额外的:
| 选项 | 类型 | 说明 |
|---|---|---|
showsTagField | Boolean | macOS 上显示标签字段 |
消息对话框
基本用法
const result = await dialog.showMessageBox({
type: 'info',
title: '提示',
message: '操作已完成',
detail: '文件已成功保存',
buttons: ['确定']
})
console.log('点击了按钮:', result.response)
消息类型
| 类型 | 说明 |
|---|---|
none | 无图标 |
info | 信息图标 |
error | 错误图标 |
question | 问号图标 |
warning | 警告图标 |
确认对话框
const result = await dialog.showMessageBox({
type: 'question',
title: '确认',
message: '确定要删除这个文件吗?',
buttons: ['取消', '删除'],
defaultId: 0,
cancelId: 0
})
if (result.response === 1) {
console.log('用户确认删除')
}
配置选项
| 选项 | 类型 | 说明 |
|---|---|---|
type | String | 消息类型 |
title | String | 标题 |
message | String | 主消息 |
detail | String | 详细信息 |
buttons | Array | 按钮文本数组 |
defaultId | Number | 默认选中按钮 |
cancelId | Number | 按 ESC 时的按钮 |
checkboxLabel | String | 复选框文本 |
checkboxChecked | Boolean | 复选框默认状态 |
icon | NativeImage | 自定义图标 |
noLink | Boolean | 禁用链接样式 |
带复选框的消息框
const result = await dialog.showMessageBox({
type: 'info',
message: '是否记住这个选择?',
checkboxLabel: '不再提示',
checkboxChecked: false,
buttons: ['确定', '取消']
})
console.log('按钮:', result.response)
console.log('复选框:', result.checkboxChecked)
同步方法
如果不需要等待用户响应,可以使用同步方法:
const response = dialog.showMessageBoxSync({
type: 'warning',
message: '这是一个警告',
buttons: ['确定']
})
错误对话框
dialog.showErrorBox('错误标题', '发生了错误,请检查输入')
错误对话框是同步的,会阻塞应用直到用户关闭它。适合显示严重错误。
通过渲染进程调用
由于 dialog 模块只能在主进程使用,需要通过 IPC 从渲染进程调用。
主进程
const { ipcMain, dialog } = require('electron')
ipcMain.handle('dialog:openFile', async (event, options) => {
const result = await dialog.showOpenDialog(options)
return result
})
ipcMain.handle('dialog:saveFile', async (event, options) => {
const result = await dialog.showSaveDialog(options)
return result
})
ipcMain.handle('dialog:showMessage', async (event, options) => {
const result = await dialog.showMessageBox(options)
return result
})
预加载脚本
const { contextBridge, ipcRenderer } = require('electron')
contextBridge.exposeInMainWorld('electronAPI', {
openFile: (options) => ipcRenderer.invoke('dialog:openFile', options),
saveFile: (options) => ipcRenderer.invoke('dialog:saveFile', options),
showMessage: (options) => ipcRenderer.invoke('dialog:showMessage', options)
})
渲染进程
const result = await window.electronAPI.openFile({
title: '选择文件',
filters: [{ name: '文本文件', extensions: ['txt'] }],
properties: ['openFile']
})
if (!result.canceled) {
console.log('选中文件:', result.filePaths[0])
}
完整示例
下面是一个文件编辑器的对话框使用示例:
main.js
const { app, BrowserWindow, ipcMain, dialog } = require('electron')
const path = require('node:path')
const fs = require('node:fs')
let mainWindow
let currentFilePath = null
let isModified = false
function createWindow() {
mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
preload: path.join(__dirname, 'preload.js')
}
})
mainWindow.loadFile('index.html')
mainWindow.on('close', async (event) => {
if (isModified) {
event.preventDefault()
await confirmClose()
}
})
}
async function confirmClose() {
const result = await dialog.showMessageBox(mainWindow, {
type: 'question',
title: '保存更改',
message: '文件已修改,是否保存?',
buttons: ['保存', '不保存', '取消'],
defaultId: 0,
cancelId: 2
})
if (result.response === 0) {
await saveFile()
mainWindow.destroy()
} else if (result.response === 1) {
mainWindow.destroy()
}
}
async function openFile() {
const result = await dialog.showOpenDialog(mainWindow, {
title: '打开文件',
filters: [
{ name: '文本文件', extensions: ['txt', 'md'] },
{ name: '所有文件', extensions: ['*'] }
],
properties: ['openFile']
})
if (!result.canceled && result.filePaths.length > 0) {
const filePath = result.filePaths[0]
const content = fs.readFileSync(filePath, 'utf-8')
currentFilePath = filePath
isModified = false
mainWindow.setRepresentedFilename(filePath)
mainWindow.setTitle(path.basename(filePath))
mainWindow.webContents.send('file-loaded', { filePath, content })
}
}
async function saveFile() {
if (!currentFilePath) {
return saveFileAs()
}
const content = await mainWindow.webContents.executeJavaScript('document.getElementById("editor").value')
fs.writeFileSync(currentFilePath, content)
isModified = false
mainWindow.setDocumentEdited(false)
}
async function saveFileAs() {
const result = await dialog.showSaveDialog(mainWindow, {
title: '保存文件',
defaultPath: currentFilePath || 'untitled.txt',
filters: [
{ name: '文本文件', extensions: ['txt'] },
{ name: '所有文件', extensions: ['*'] }
]
})
if (!result.canceled && result.filePath) {
const content = await mainWindow.webContents.executeJavaScript('document.getElementById("editor").value')
fs.writeFileSync(result.filePath, content)
currentFilePath = result.filePath
isModified = false
mainWindow.setRepresentedFilename(result.filePath)
mainWindow.setTitle(path.basename(result.filePath))
mainWindow.setDocumentEdited(false)
}
}
ipcMain.handle('file:open', openFile)
ipcMain.handle('file:save', saveFile)
ipcMain.handle('file:saveAs', saveFileAs)
ipcMain.on('content-changed', () => {
isModified = true
mainWindow.setDocumentEdited(true)
})
app.whenReady().then(createWindow)
平台差异
macOS
- 对话框通常是 sheet 形式(附着在窗口上)
- 支持
message属性显示额外信息 - 支持
showsTagField显示标签字段
Windows
- 对话框是独立的模态窗口
- 支持
defaultPath设置默认路径
Linux
- 使用 GTK 对话框
- 某些选项可能不被支持
最佳实践
提供合理的默认值
const result = await dialog.showSaveDialog({
defaultPath: path.join(app.getPath('documents'), 'untitled.txt')
})
使用合适的过滤器
filters: [
{ name: '支持的图片格式', extensions: ['jpg', 'jpeg', 'png', 'gif', 'webp'] },
{ name: '所有文件', extensions: ['*'] }
]
处理取消操作
const result = await dialog.showOpenDialog()
if (result.canceled) {
return
}
// 处理选中的文件
关联窗口
将对话框关联到特定窗口,可以获得更好的用户体验:
const result = await dialog.showOpenDialog(mainWindow, options)
总结
对话框是桌面应用的重要交互方式:
showOpenDialog用于选择文件或目录showSaveDialog用于选择保存位置showMessageBox用于显示消息和确认showErrorBox用于显示严重错误- 通过 IPC 让渲染进程也能调用对话框