Blazor Hybrid 實戰體驗:那些你可能沒預料到的坑沒預料到的坑

程序设计实验室發表於2024-10-11

前言

昨天寫了一篇介紹 Blazor Hybrid 技術的文章,但限於篇幅,一些問題未能深入探討。今天,我想繼續記錄使用 Blazor Hybrid 過程中遇到的幾個問題,以及這個技術目前的一些侷限性。

檔案拖放事件的侷限

Blazor Hybrid 的執行環境是 WebView,這導致了在處理檔案拖放時出現了一些限制。在傳統桌面應用中(如 WinForms 或 WPF),開發者可以直接捕獲拖放事件,並獲得檔案的完整路徑。但在 Blazor 中,拖放事件只能像瀏覽器中一樣處理,意味著我們只能獲得上傳檔案的流,而無法獲取檔案的實際路徑。

這對於那些需要直接訪問檔案路徑的功能(如Clipify中把影片拖進去處理)帶來了很大的不便。

冗餘程式碼(不是)

看了專案程式碼的同學可能會發現,FormMain.cs裡還有處理拖放事件的程式碼,不過實際上並沒有生效。

// 處理拖動進入事件,檢測是否為檔案
private void blazorWebView1_DragEnter(object sender, DragEventArgs e) {
  Console.WriteLine("drag enter");
  if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
    // 改變滑鼠圖示,表示可以拖放
    e.Effect = DragDropEffects.Copy;
  }
  else {
    e.Effect = DragDropEffects.None;
  }
}

// 處理拖放事件,獲取檔案路徑
private void blazorWebView1_DragDrop(object sender, DragEventArgs e) {
  Console.WriteLine("drag drop");
  if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
    var files = (string[]?)e.Data.GetData(DataFormats.FileDrop);

    // 這裡只處理單個檔案,當然你也可以處理多個檔案
    if (files?.Length > 0) {
      var filePath = files[0]; // 獲取拖放的檔案路徑
      MessageBox.Show($"檔案路徑: {filePath}");

      // 在這裡你可以將檔案路徑傳遞給 Blazor 或其他處理邏輯
    }
  }
}

// 處理 DragOver 事件,防止系統預設行為
private void blazorWebView1_DragOver(object sender, DragEventArgs e) {
  if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
    e.Effect = DragDropEffects.Copy; // 明確允許拖放檔案
  }
  else {
    e.Effect = DragDropEffects.None;
  }
}

解決方案

目前的解決辦法有限,根據查詢到的資料和我自己的探索,有以下幾種:

  1. 在需要拖放的時候,使用一個WinForms原生控制元件覆蓋webview
  2. 使用hook技術,攔截webview的拖放事件
  3. 重寫微軟提個的這個 Blazor Webview 控制元件,自己實現 WndProc 方法

第3種方法的程式碼大概是這樣(未驗證)

public class CustomBlazorWebView : BlazorWebView {
  protected override void WndProc(ref Message m) {
    const int WM_DROPFILES = 0x233; // 拖放檔案訊息

    if (m.Msg == WM_DROPFILES) {
      // 處理檔案拖放邏輯
      // 你可以在這裡呼叫你的拖放事件處理邏輯

      // 阻止訊息傳遞,避免系統預設處理檔案
      return;
    }

    base.WndProc(ref m);
  }
}

PS:我嫌麻煩就還沒去折騰實現這個拖放功能,目前只做了開啟對話方塊選擇檔案。

社群反饋

同樣的問題我在 Github issues 和 Stack Overflow 之類的平臺也有看到很多人提出,不過看起來微軟並不想解決這些問題。

相關連結:

  • https://github.com/MicrosoftEdge/WebView2Feedback/issues/2805
  • https://github.com/dotnet/maui/issues/2205
  • https://stackoverflow.com/questions/73197778/net-maui-blazor-app-drag-and-drop-impossible

桌面應用體驗差異

Blazor Hybrid 儘管以桌面應用的形式執行,但表現更接近於網頁應用。

瀏覽器的快捷鍵

一個明顯的例子是,在 WebView 中按下 F5 鍵時,頁面會像瀏覽器一樣重新整理,這種行為顯然不符合傳統桌面軟體的使用者體驗。

在類似的技術中,如 Electron,也存在類似的侷限。但不同的是,Electron 提供了更多對瀏覽器行為的控制手段,可以阻止或重定義這些行為,而 Blazor Hybrid 目前則沒有這些更細粒度的控制能力。

從桌面應用的角度來看,使用者希望獲得一致且原生的操作體驗,因此這些細微的差異可能會影響開發者對 Blazor Hybrid 應用的期望。

視窗大小調整的表現

在使用 Blazor Hybrid 時,我還注意到視窗大小調整的流暢度問題。相比起原生的桌面應用,Blazor Hybrid 的表現不盡如人意。當使用者調整視窗大小時,介面偶爾會出現黑邊或畫面撕裂的現象。

這種問題不僅在 Blazor Hybrid 中出現,實際上,在瀏覽器(chrome)和 Electron 應用(QQ)中,我也觀察到類似的問題。

為了更深入地理解這個現象,我還測試了 C++ 原生應用,結果發現原生應用在調整視窗大小時相對來說更流暢,沒有出現黑邊或撕裂的問題。

我猜測造成這種差異的原因可能在於,Blazor Hybrid 和 Electron 依賴 WebView 作為渲染引擎,而 WebView 的渲染機制在處理視窗大小調整時不如原生 UI 渲染引擎高效。

小結

Blazor Hybrid 是一個非常有潛力的技術,它讓 C# 開發者能夠輕鬆地構建跨平臺桌面應用。

然而,在使用過程中,我發現了一些需要關注的問題,尤其是在拖放事件、桌面應用行為一致性和視窗大小調整表現上。

這些問題目前可能對開發者造成一定的困擾,也影響了使用者體驗的流暢性。

接下來我會找時間試一下 Electron 和 wails 的開發體驗,進一步探索 Blazor Hybrid 在桌面軟體開發中的優勢。

相關文章