參與貢獻

編輯此文件

翻譯文件,或在您的電腦上開啟完整專案。

在您選擇提交成果之前,不會發布任何內容。

視窗基礎

在 Wails 中建立及管理應用程式視窗

視窗管理

Wails 提供可跨所有平台運作的統一視窗管理 API。您可以建立視窗、控制其行為,並完整掌控多個視窗的建立、外觀、行為及生命週期。

快速開始

go
package main

import "github.com/wailsapp/wails/v3/pkg/application"

func main() {
    app := application.New(application.Options{
        Name: "My App",
    })

    // Create a window
    window := app.Window.New()
    
    // Configure it
    window.SetTitle("Hello Wails")
    window.SetSize(800, 600)
    window.Center()
    
    // Show it
    window.Show()

    app.Run()
}

就這麼簡單!您現在已有一個跨平台視窗。

建立視窗

基本視窗

建立視窗最簡單的方式:

go
window := app.Window.New()

您會得到:

  • 預設大小(800x600)
  • 預設標題(應用程式名稱)
  • 可供前端使用的 WebView
  • 平台原生外觀

含選項的視窗

使用自訂設定建立視窗:

go
window := app.Window.NewWithOptions(application.WebviewWindowOptions{
    Title:  "My Application",
    Width:  1200,
    Height: 800,
    X:      100,   // Position from left
    Y:      100,   // Position from top
    AlwaysOnTop: false,
    Frameless: false,
    Hidden: false,
    MinWidth: 400,
    MinHeight: 300,
    MaxWidth: 1920,
    MaxHeight: 1080,
})

常用選項:

選項 型別 說明
Title string 視窗標題
Width int 視窗寬度(像素)
Height int 視窗高度(像素)
X int X 位置(從左側起算)
Y int Y 位置(從頂端起算)
AlwaysOnTop bool 讓視窗保持在其他視窗上方
Frameless bool 移除標題列和邊框
Hidden bool 啟動時隱藏
MinWidth int 最小寬度
MinHeight int 最小高度
MaxWidth int 最大寬度
MaxHeight int 最大高度

如需完整清單,請參閱視窗選項。

具名視窗

為視窗命名,以便輕鬆擷取:

go
window := app.Window.NewWithOptions(application.WebviewWindowOptions{
    Name:  "main-window",
    Title: "Main Application",
})

// Later, find it by name
if mainWindow, ok := app.Window.GetByName("main-window"); ok {
    mainWindow.Show()
}

使用情境:

  • 多個視窗(主視窗、設定、關於)
  • 從程式碼的不同部分尋找視窗
  • 視窗間通訊

控制視窗

顯示與隱藏

go
// Show window
window.Show()

// Hide window
window.Hide()

// Check if visible
if window.IsVisible() {
    fmt.Println("Window is visible")
}

使用情境:

  • 啟動畫面(先顯示,再隱藏)
  • 設定視窗(不需要時隱藏)
  • 快顯視窗(依需求顯示)

位置與大小

go
// Set size
window.SetSize(1024, 768)

// Set position
window.SetPosition(100, 100)

// Centre on screen
window.Center()

// Get current size
width, height := window.Size()

// Get current position
x, y := window.Position()

座標系統:

  • (0, 0)是主要螢幕的左上角
  • X 軸正向朝右
  • Y 正值向下

視窗狀態

go
// Minimise
window.Minimise()

// Maximise
window.Maximise()

// Fullscreen
window.Fullscreen()

// Restore to normal
window.Restore()

// Check state
if window.IsMinimised() {
    fmt.Println("Window is minimised")
}

if window.IsMaximised() {
    fmt.Println("Window is maximised")
}

if window.IsFullscreen() {
    fmt.Println("Window is fullscreen")
}

狀態轉換:

text
Normal ←→ Minimised
Normal ←→ Maximised
Normal ←→ Fullscreen

標題與外觀

go
// Set title
window.SetTitle("My Application - Document.txt")

// Set background colour — RGBA value (helper for RGB)
window.SetBackgroundColour(application.NewRGBA(0, 0, 0, 255))

// Set always on top
window.SetAlwaysOnTop(true)

// Set resizable
window.SetResizable(false)

關閉視窗

go
// Close window — dispatches WindowClosing; a RegisterHook can call e.Cancel().
window.Close()

v3 中沒有 window.Destroy() 方法——請使用 Close(),並以 OnWindowEvent 監聽(無法取消),或以 RegisterHook 掛接處理常式(可呼叫 e.Cancel() 讓視窗保持開啟)。

尋找視窗

依名稱

go
if window, ok := app.Window.GetByName("settings"); ok {
    window.Show()
}

依 ID

每個視窗都有唯一的 ID:

go
id := window.ID()
fmt.Printf("Window ID: %d\n", id)

// Find by ID
if found, ok := app.Window.GetByID(id); ok {
    found.Focus()
}

目前視窗

取得目前具有焦點的視窗:

go
current := app.Window.Current()
if current != nil {
    current.SetTitle("Active Window")
}

所有視窗

取得所有視窗:

go
windows := app.Window.GetAll()
fmt.Printf("Total windows: %d\n", len(windows))

for _, w := range windows {
    fmt.Printf("Window: %s (ID: %d)\n", w.Name(), w.ID())
}

視窗生命週期

建立

go
app.Window.OnCreate(func(window application.Window) {
    fmt.Printf("Window created: %s\n", window.Name())

    // Configure new windows
    window.SetMinSize(400, 300)
})

關閉

若要防止視窗關閉,請搭配 WindowClosing 事件使用 RegisterHook:

go
window.RegisterHook(events.Common.WindowClosing, func(event *application.WindowEvent) {
    if hasUnsavedChanges() {
        // Ask user for confirmation
        result := showConfirmDialog("Unsaved changes. Close anyway?")
        if result != "yes" {
            // Cancel the close event
            event.Cancel()
        }
    }
})

重要:RegisterHook 會在關閉事件發生前攔截該事件。呼叫 event.Cancel() 可防止視窗關閉。這適用於使用者主動發起的關閉操作(按一下 X 按鈕)。

銷毀

若要在視窗關閉時執行清理,請搭配 WindowClosing 事件使用 OnWindowEvent:

go
window.OnWindowEvent(events.Common.WindowClosing, func(event *application.WindowEvent) {
    fmt.Println("Window is closing")
    // Cleanup resources
})

多個視窗

建立多個視窗

go
// Main window
mainWindow := app.Window.NewWithOptions(application.WebviewWindowOptions{
    Name:   "main",
    Title:  "Main Application",
    Width:  1200,
    Height: 800,
})

// Settings window
settingsWindow := app.Window.NewWithOptions(application.WebviewWindowOptions{
    Name:   "settings",
    Title:  "Settings",
    Width:  600,
    Height: 400,
    Hidden: true,  // Start hidden
})

// Show settings when needed
settingsWindow.Show()

視窗間通訊

視窗可透過事件互相通訊:

go
// In main window
app.Event.Emit("data-updated", map[string]interface{}{
    "value": 42,
})

// In settings window
app.Event.On("data-updated", func(event *application.CustomEvent) {
    data := event.Data.(map[string]interface{})
    value := data["value"].(int)
    fmt.Printf("Received: %d\n", value)
})

如需更多資訊,請參閱事件。

父子視窗

WebviewWindowOptions沒有Parent欄位。請將子視窗建立為一般視窗,並以表單式模態視窗的形式附加至父視窗:

go
// Create child window
childWindow := app.Window.NewWithOptions(application.WebviewWindowOptions{
    Title: "Child Window",
})

// Attach to the parent — presents as a sheet on macOS.
mainWindow.AttachModal(childWindow)

行為:

  • 子視窗會保持在父視窗上方。
  • 子視窗是模態視窗——會阻止與父視窗互動。

平台支援:

  • macOS:完整支援(以表單式對話框顯示)。
  • Windows:不支援。
  • Linux:不支援。

平台特定功能

Windows 特定功能:

go
// Flash taskbar button
window.Flash(true)  // Start flashing
window.Flash(false) // Stop flashing

// Trigger Windows 11 Snap Assist (Win+Z)
window.SnapAssist()

沒有個別視窗專用的 SetIcon——應透過 app.SetIcon([]byte) 設定應用程式圖示(若要設定 Linux 特定的視窗圖示,則在建立視窗時使用 application.LinuxWindow.Icon 欄位)。

貼齊小幫手: 透過系統捷徑路徑顯示 Windows 11 貼齊配置選項。若自訂 HTML 最大化按鈕需要原生的懸停貼齊配置,請改用 Windows 上的原生非用戶端區域。

工作列閃爍: 適合在視窗最小化時發出通知。

常見模式

啟動畫面

go
// Create splash screen
splash := app.Window.NewWithOptions(application.WebviewWindowOptions{
    Title:     "Loading...",
    Width:     400,
    Height:    300,
    Frameless: true,
    AlwaysOnTop: true,
})

// Show splash
splash.Show()

// Initialise application
time.Sleep(2 * time.Second)

// Hide splash, show main window
splash.Close()
mainWindow.Show()

設定視窗

go
var settingsWindow *application.WebviewWindow

func showSettings() {
    if settingsWindow == nil {
        settingsWindow = app.Window.NewWithOptions(application.WebviewWindowOptions{
            Name:   "settings",
            Title:  "Settings",
            Width:  600,
            Height: 400,
        })
    }
    
    settingsWindow.Show()
    settingsWindow.Focus()
}

關閉前確認

go
window.RegisterHook(events.Common.WindowClosing, func(event *application.WindowEvent) {
    if hasUnsavedChanges() {
        // Show dialog
        result := showConfirmDialog("Unsaved changes. Close anyway?")
        if result != "yes" {
            // Cancel the close event
            event.Cancel()
        }
    }
})

最佳實務

✅ 建議做法

  • 為重要視窗命名——日後更容易尋找
  • 設定最小尺寸——避免版面無法使用
  • 將視窗置中——使用者體驗比隨機位置更好
  • 處理關閉事件——避免資料遺失
  • 在所有平台上測試——行為因平台而異
  • 使用適當的尺寸——考量不同的螢幕尺寸

❌ 請勿這樣做

  • 不要建立過多視窗——會讓使用者感到困惑
  • 不要忘記關閉視窗——否則會造成記憶體洩漏
  • 不要將位置寫死——螢幕尺寸各不相同
  • 不要忽略平台差異——請徹底測試
  • 不要阻塞 UI 執行緒——長時間執行的作業請使用 goroutine

疑難排解

視窗未顯示

可能原因:

  1. 建立視窗時將其設為隱藏
  2. 視窗位於螢幕範圍之外
  3. 視窗位於其他視窗後方

解決方法:

go
window.Show()
window.Center()
window.Focus()

視窗尺寸不正確

原因:Windows/Linux 上的 DPI 縮放

解決方法:

go
// Wails handles DPI automatically
// Just use logical pixels
window.SetSize(800, 600)

視窗立即關閉

原因:最後一個視窗關閉時,應用程式會結束

解決方法:

go
app := application.New(application.Options{
    Mac: application.MacOptions{
        ApplicationShouldTerminateAfterLastWindowClosed: false,
    },
})

後續步驟

視窗選項

所有視窗選項的完整參考資料。

深入瞭解 →

多視窗

多視窗應用程式的常見模式。

深入瞭解 →

無邊框視窗

建立自訂視窗框架。

深入瞭解 →

視窗事件

處理視窗生命週期事件。

深入瞭解 →


有問題嗎?請到Discord提問,或查看視窗範例。

編輯頁面

上次更新: