WPF 应用迁移到 Electron 框架过程记录

  • WPF 应用迁移到 Electron 框架过程记录已关闭评论
  • 54 次浏览
  • A+
所属分类:.NET技术
摘要

前一段时间我用 WPF 开发了一个查看 emoji 表情的小工具 https://github.com/he55/EmojiViewer ,由于最近我使用 macOS 系统比较多,我想能在 macOS 系统上也能使用这个工具。于是我尝试将 WPF 应用迁移到 Electron 框架,感觉这个框架很强大,在这里记录一下应用迁移的过程。

前一段时间我用 WPF 开发了一个查看 emoji 表情的小工具 https://github.com/he55/EmojiViewer ,由于最近我使用 macOS 系统比较多,我想能在 macOS 系统上也能使用这个工具。于是我尝试将 WPF 应用迁移到 Electron 框架,感觉这个框架很强大,在这里记录一下应用迁移的过程。

WPF 应用迁移到 Electron 框架过程记录

安装 Electron 环境

  • 安装 nodejs。到官网 https://nodejs.org/en 下载最新的 nodejs,然后安装
    WPF 应用迁移到 Electron 框架过程记录
  • 打开命令行输入 git clone https://github.com/electron/electron-quick-start.git 命令克隆 Electron 模板项目,使用模板可以快速搭建应用。
  • 然后使用 cd electron-quick-start 目录进入到目录,接着运行 npm install 命令还原项目。
  • 使用 vscode 打开文件夹,项目文件如下
    WPF 应用迁移到 Electron 框架过程记录

编写代码

  • Electron 分为主进程和渲染进程,对文件、系统和窗口的操作需要在主线程,界面渲染在渲染进程。创建窗口属于主进程的工作,需要到 main.js 文件编写代码。创建窗口使用 BrowserWindow 对象,widthheight 分别设置窗口宽度和高度,autoHideMenuBar 设置是否隐藏菜单,最后使用 loadFile 加载页面文件并显示窗口。
function createWindow() {   const mainWindow = new BrowserWindow({     width: 915,     height: 560,     autoHideMenuBar: true,     webPreferences: {       preload: path.join(__dirname, 'preload.js')     }   })    mainWindow.loadFile('index.html') } 
  • 监听 whenReady 事件,等待应用初始化完成后显示窗口
app.whenReady().then(() => {   createWindow()    app.on('activate', function () {     if (BrowserWindow.getAllWindows().length === 0) createWindow()   }) }) 
  • 修改 index.html 文件,界面部分使用了 vue 进行渲染
<!DOCTYPE html> <html>  <head>   <meta charset="UTF-8">   <script src="vue.global.js"></script>   <link href="./styles.css" rel="stylesheet">   <title>EmojiViewer</title> </head>  <body>   <div id="app" class="container">     <ul class="left">       <li v-for="(item, key) in categories" :class="{active: item.isActive}" @click="catetoryItemClick(item)">{{ key }}</li>     </ul>     <ul class="main" ref="mainElement">       <li v-for="emoji in emojis" :class="{active: emoji.isActive}" @click="emojiItemClick(emoji)">         <img :src="emoji.previewImage" alt="">         <p>{{emoji.name}}</p>       </li>     </ul>     <div class="right">       <img :src="selectedEmoji.previewImage">       <p>{{ selectedEmoji.name }}</p>       <button @click="copyEmoji(selectedEmoji)" type="button">Copy Emoji</button>       <button @click="copyImage(selectedEmoji)" type="button">Copy Image</button>       <button @click="openFile(selectedEmoji)" type="button">Open File</button>     </div>   </div>    <script src="./renderer.js"></script> </body>  </html> 
  • renderer.js 文件中编写页面处理代码
window.addEventListener('DOMContentLoaded', async () => {     const { createApp, ref, onMounted } = Vue     let emojiData = await ipc.getData()      createApp({         setup() {             const mainElement = ref(null)              const categories = ref(emojiData)             const emojis = ref([])             const selectedEmoji = ref({})              function copyEmoji(emoji) {                 ipc.ipc('writeText', emoji.metadata.glyph)             }             function copyImage(emoji) {                 ipc.ipc('writeImage', emoji.previewImage)             }             function openFile(emoji) {                 ipc.ipc('showItemInFolder', emoji.previewImage)             }              let lastSelectedEmojis             function catetoryItemClick(items) {                 if (lastSelectedEmojis) {                     lastSelectedEmojis.isActive = false                 }                  items.isActive = true                 lastSelectedEmojis = items                  // const main = document.querySelector('.main')                 mainElement.value.scrollTop = 0                 emojis.value = items             }              function emojiItemClick(emoji) {                 if (selectedEmoji.value) {                     selectedEmoji.value.isActive = false                 }                  emoji.isActive = true                 selectedEmoji.value = emoji             }              onMounted(() => {                 catetoryItemClick(emojiData['Activities'])                 emojiItemClick(emojiData['Activities'][0])             })              return {                 mainElement,                 categories,                 emojis,                 selectedEmoji,                 catetoryItemClick,                 emojiItemClick,                 copyEmoji,                 copyImage,                 openFile,             }         }     }).mount('#app') }) 
  • 读取文件,node 提供了文件操作相关的 api 可以很方便的操作文件系统。
function loadData(assetPath) {   const dirs = fs.readdirSync(assetPath)   const data = []   const groupData = {}   for (const dir of dirs) {     const fullPath = path.resolve(assetPath, dir)     const metadata = require(path.resolve(fullPath, 'metadata.json'))     let previewImage      let imagePaths = [path.resolve(fullPath, '3D'), path.resolve(fullPath, 'Default', '3D')]     for (const imagePath of imagePaths) {       if (fs.existsSync(imagePath)) {         let files = fs.readdirSync(imagePath)         if (files.length === 0)           return         previewImage = path.resolve(imagePath, files[0])       }     }      const { unicode, group } = metadata     const obj = {       metadata,       id: unicode,       name: dir,       previewImage,     }     data.push(obj)      if (!groupData[group])       groupData[group] = []     groupData[group].push(obj)   }   return groupData } 

完整代码(WPF 版本) https://github.com/he55/EmojiViewer
完整代码(vue 版本) https://github.com/he55/web-learn/tree/main/9.electron-emoji-viewer(vue)
完整代码(js 原生版本) https://github.com/he55/web-learn/tree/main/6.electron-emoji-viewer