.NET混合開發解決方案13 自定義WebView2中的上下文選單

張傳寧發表於2022-05-11

WebView2控制元件應用詳解系列部落格

.NET桌面程式整合Web網頁開發的十種解決方案 

.NET混合開發解決方案1 WebView2簡介

.NET混合開發解決方案2 WebView2與Edge瀏覽器的區別

.NET混合開發解決方案3 WebView2的程式模型

.NET混合開發解決方案4 WebView2的執行緒模型

.NET混合開發解決方案5 WebView2執行時與分發應用

.NET混合開發解決方案7 WinForm程式中通過NuGet管理器引用整合WebView2控制元件

.NET混合開發解決方案8 WinForm程式中通過設定固定版本執行時的BrowserExecutableFolder屬性整合WebView2控制元件

.NET混合開發解決方案9 WebView2控制元件的導航事件

.NET混合開發解決方案10 WebView2控制元件呼叫網頁JS方法

.NET混合開發解決方案11 網頁JS呼叫C#方法

.NET混合開發解決方案12 網頁JS呼叫C#方法訪問WinForm或WPF窗體

Edge瀏覽器中的網頁,點選滑鼠右鍵,出現上下文選單及子選單,如下圖

.NET混合開發解決方案13 自定義WebView2中的上下文選單

WebView2控制元件載入網頁後,滑鼠在網頁上點選右鍵,也會出現上下文選單,如下圖

.NET混合開發解決方案13 自定義WebView2中的上下文選單

對比可以看出WebView2控制元件中的右鍵上下文選單內容比Edge瀏覽器中網頁的右鍵右鍵上下文選單的數量少。結合我的部落格《.NET混合開發解決方案2 WebView2與Edge瀏覽器的區別》可知,WebView2控制元件中對於網頁右鍵上下文選單做了裁剪。

在企業級應用軟體開發中,可能有以下幾種種常有且實用的需求

1、禁用網頁右鍵選單

使用 webView2.CoreWebView2.ExecuteScriptAsync() 方法執行JS指令碼即可實現禁用右鍵選單

await webView.CoreWebView2.ExecuteScriptAsync("window.addEventListener('contextmenu', window => {window.preventDefault();});");

或者通過C#編碼禁用右鍵選單

 webView2.CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;

有的開發者小夥伴會說,我在網頁中寫JS也可以禁用右鍵上選單,確實如此

function document.oncontextmenu()
{
   return false;
}
 
 
function nocontextmenu()
{
  if(document.all) {
     event.cancelBubble=true;
     event.returnvalue=false;
     return false;
   }
}

但是通過WebView2進行控制,一方面不改變網頁本身的功能,另一方面可以統一控制網頁右鍵選單的啟用與禁用。

2、從預設上下文選單中刪除選單項

  通過WebView2能禁用右鍵選單,理論上也可以自定義右鍵選單。WebView2提供了豐富的API供開發者使用,參考微軟官方文件《自定義 WebView2 中的上下文選單》,可以實現自定義的右鍵選單。

關於右鍵選單的術語

  • 選單項  包括核取方塊、命令、單選按鈕、分隔符和子選單。
  • 命令  五種型別的選單項之一。
  • 上下文選單 屬於 WebView2 控制元件的預設上下文選單 (右鍵單擊選單) 或自定義上下文選單 (右鍵單擊選單) 屬於主機應用。

.NET混合開發解決方案13 自定義WebView2中的上下文選單

與WebView2控制元件右鍵選單相關的事件、類、屬性與列舉

  指示為目標屬性建立上下文選單的上下文種類。此列舉將始終表示導致上下文選單請求的活動元素。例如,如果有一個包含多個影像、音訊和文字的選擇,終端使用者在此選擇中右鍵單擊的元素將是此列舉表示的選項。

    • Audio  指示上下文選單是為音訊元素建立的。
    • Image 指示上下文選單是為影像元素建立的。
    • Page 指示上下文選單是為頁面建立的,沒有任何其他內容。
    • SelectedText 指示上下文選單是為所選文字建立的。
    • Video 指示上下文選單是為視訊元素建立的。

首先獲取WebView2控制元件的預設右鍵選單列表,檢視每一項的具體屬性資訊。通過註冊WebView2的ContextMenuRequested事件,使用事件引數CoreWebView2ContextMenuRequestedEventArgs中提供的資料來顯示包含所選條目的自定義上下文選單。

.NET混合開發解決方案13 自定義WebView2中的上下文選單

預設提供12個右鍵選單項(包含分隔符),除錯程式碼檢視每個選單項資訊,如下

觀察12個選單項,可以發現以下規律

  • 分割線的Kind值為Separator,其餘選單項的Kind值為Command。
  • 分割線的CommandId值為-1,label值為空字串,name值為other。
  • Kind值為Command的選單項CommandId、label、name值不同且唯一。

一般的應用程式保留【返回】、【前進】、【重新整理】三個選單項即可滿足。此時就需要刪除其他的選單項。實現邏輯如下

 1 private void CoreWebView2_ContextMenuRequested(object? sender, CoreWebView2ContextMenuRequestedEventArgs args)
 2 {
 3     IList<CoreWebView2ContextMenuItem> allMenuList = args.MenuItems;
 4 
 5     var itemOfSaveAs = allMenuList.FirstOrDefault(x => x.Name == "saveAs");
 6     if (itemOfSaveAs != null)
 7         allMenuList.Remove(itemOfSaveAs);
 8 
 9     var itemOfPrint = allMenuList.FirstOrDefault(x => x.Name == "print");
10     if (itemOfPrint != null)
11         allMenuList.Remove(itemOfPrint);
12 
13     var itemOfCreateQRCode = allMenuList.FirstOrDefault(x => x.Label == "為此頁面建立 QR 程式碼");
14     if (itemOfCreateQRCode != null)
15         allMenuList.Remove(itemOfCreateQRCode);
16 
17     var itemOfShare = allMenuList.FirstOrDefault(x => x.CommandId == 50460);
18     if (itemOfShare != null)
19         allMenuList.Remove(itemOfShare);
20 
21     var itemOfSaveInspectElement = allMenuList.FirstOrDefault(x => x.Name == "inspectElement");
22     if (itemOfSaveInspectElement != null)
23         allMenuList.Remove(itemOfSaveInspectElement);
24 }

測試效果如下圖

.NET混合開發解決方案13 自定義WebView2中的上下文選單

現在只有【返回】、【前進】、【重新整理】選單項了,但是最後還有一條分割線。

除錯程式碼可知目前還有7個選單項,其中第4,5,6,7項都是分割線。在12個原始選單項中就包含有四個分割線,所以此處需要刪除這4個分割線

修改邏輯程式碼

再次測試,效果如下圖

.NET混合開發解決方案13 自定義WebView2中的上下文選單

如果是清空所有的選單項就比較簡單了,直接清空右鍵選單列表

IList<CoreWebView2ContextMenuItem> allMenuList = args.MenuItems;
allMenuList.Clear();//清空所有的預設選單項
3、自定義 WebView2 中的上下文選單

上述第二個場景中保留了【返回】、【前進】、【重新整理】三個選單項,滿足大多數場景的需求。考慮一些極端情況,系統需要統一實現自定義的右鍵選單功能。

通過一個簡單的示例來演示如何實現自定義WebView2 中的上下文選單。

場景:在第二個場景的基礎之上,增加2個自定義右鍵選單項。

先看下實現效果

.NET混合開發解決方案13 自定義WebView2中的上下文選單

同樣需要在WebView2控制元件的ContextMenuRequested事件中實現

private void CoreWebView2_ContextMenuRequested(object? sender, CoreWebView2ContextMenuRequestedEventArgs args)
{
   IList<CoreWebView2ContextMenuItem> allMenuList = args.MenuItems;

   PopulateContextMenu(args, allMenuList);
}

其中新增選單項的邏輯如下

CoreWebView2ContextMenuItem 類不能直接例項化,需要使用 webView2.CoreWebView2.Environment.CreateContextMenuItem() 類建立一個選單物件。CreateContextMenuItem() 方法中傳遞三個引數

1、選單項的名稱。如果是分割線,則設定為空字串。

2、選單項的圖示,是檔案流物件。如果不設定,則賦值為null。

3、選單項的型別,包含Command(命令按鈕)、CheckBox(核取方塊)、Radio(單選框)、Separator(分割線)、Submenu(子選單)。

程式中我設定了CheckBox,但是執行後沒有生效,暫時不知道什麼原因。如有小夥伴研究出來了,可以分享一下。

選單項還有Label、CommandId屬性,但是隻讀,無法賦值

除錯程式可以看到,建立選單時,CommandId的值是自動分配的,Label的值與Name相同。

使用者何時請求上下文選單
當使用者請求開啟上下文選單 ((例如右鍵單擊) )時,應用需要偵聽 ContextMenuRequested 事件。當應用檢測到此事件時,應用應執行以下操作的一些組合:將自定義選單項新增到預設上下文選單。
  • 從預設上下文選單中刪除自定義選單項。
  • 開啟自定義上下文選單。

該 ContextMenuRequested 事件指示使用者請求開啟上下文選單。

WebView2 控制元件引發此事件,指示使用者請求在 WebView2 控制元件中開啟上下文選單,例如右鍵單擊。

僅當前網頁允許顯示上下文選單時,WebView2 控制元件才會引發 ContextMenuRequested 事件,即 AreDefaultContextMenusEnabled true 時引發該事件。

CoreWebView2ContextMenuRequestedEventArgs 包含以下資訊:

  • 要填充自定義上下文選單的 ContextMenuItem 物件的有序列表。 已排序列表包括以下內容:

    • 選單項的內部名稱。
    • 選單項的 UI 標籤,顯示給 UI 中的使用者。
    • 選單項的型別。
    • 鍵盤快捷方式說明(如有 Alt+C)。
    • 自定義選單項的任何其他屬性。
  • 請求上下文選單的座標,以便應用可以檢測使用者右鍵單擊的 UI 項。 座標是根據 WebView2 控制元件的左上角定義的。

  • 包含所選上下文型別的選擇物件 和相應的上下文選單引數資料。

當使用者在上下文選單上選擇自定義選單項時,WebView2 控制元件將觸發 CustomItemSelected 事件,開發者在該事件中可以自定義業務邏輯。

相關文章