.NET混合開發解決方案13 自定義WebView2中的上下文菜單
- 2022 年 5 月 11 日
- 筆記
- .NET:WebView2, WebView2
WebView2控制項應用詳解系列部落格
.NET混合開發解決方案2 WebView2與Edge瀏覽器的區別
.NET混合開發解決方案5 WebView2運行時與分發應用
.NET混合開發解決方案7 WinForm程式中通過NuGet管理器引用集成WebView2控制項
.NET混合開發解決方案8 WinForm程式中通過設置固定版本運行時的BrowserExecutableFolder屬性集成WebView2控制項
.NET混合開發解決方案9 WebView2控制項的導航事件
Edge瀏覽器中的網頁,點擊滑鼠右鍵,出現上下文菜單及子菜單,如下圖
WebView2控制項載入網頁後,滑鼠在網頁上點擊右鍵,也會出現上下文菜單,如下圖
對比可以看出WebView2控制項中的右鍵上下文菜單內容比Edge瀏覽器中網頁的右鍵右鍵上下文菜單的數量少。結合我的部落格《.NET混合開發解決方案2 WebView2與Edge瀏覽器的區別》可知,WebView2控制項中對於網頁右鍵上下文菜單做了裁剪。
在企業級應用軟體開發中,可能有以下幾種種常有且實用的需求
使用 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進行控制,一方面不改變網頁本身的功能,另一方面可以統一控制網頁右鍵菜單的啟用與禁用。
通過WebView2能禁用右鍵菜單,理論上也可以自定義右鍵菜單。WebView2提供了豐富的API供開發者使用,參考微軟官方文檔《自定義 WebView2 中的上下文菜單》,可以實現自定義的右鍵菜單。
關於右鍵菜單的術語
- 菜單項 包括複選框、命令、單選按鈕、分隔符和子菜單。
- 命令 五種類型的菜單項之一。
- 上下文菜單 屬於 WebView2 控制項的默認上下文菜單 (右鍵單擊菜單) 或自定義上下文菜單 (右鍵單擊菜單) 屬於主機應用。
與WebView2控制項右鍵菜單相關的事件、類、屬性與枚舉
-
CoreWebView2ContextMenuItemKind 枚舉
CoreWebView2ContextMenuItemKind.Command
CoreWebView2ContextMenuItemKind.Separator
指示為目標屬性創建上下文菜單的上下文種類。此枚舉將始終表示導致上下文菜單請求的活動元素。例如,如果有一個包含多個影像、音頻和文本的選擇,最終用戶在此選擇中右鍵單擊的元素將是此枚舉表示的選項。
-
- Audio 指示上下文菜單是為音頻元素創建的。
- Image 指示上下文菜單是為影像元素創建的。
- Page 指示上下文菜單是為頁面創建的,沒有任何其他內容。
- SelectedText 指示上下文菜單是為所選文本創建的。
- Video 指示上下文菜單是為影片元素創建的。
-
CoreWebView2ContextMenuItemKind 枚舉
CheckBox
Radio
Separator
Submenu
首先獲取WebView2控制項的默認右鍵菜單列表,查看每一項的具體屬性資訊。通過註冊WebView2的ContextMenuRequested事件,使用事件參數CoreWebView2ContextMenuRequestedEventArgs中提供的數據來顯示包含所選條目的自定義上下文菜單。
默認提供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 }
測試效果如下圖
現在只有【返回】、【前進】、【刷新】菜單項了,但是最後還有一條分割線。
調試程式碼可知目前還有7個菜單項,其中第4,5,6,7項都是分割線。在12個原始菜單項中就包含有四個分割線,所以此處需要刪除這4個分割線
修改邏輯程式碼
再次測試,效果如下圖
如果是清空所有的菜單項就比較簡單了,直接清空右鍵菜單列表
IList<CoreWebView2ContextMenuItem> allMenuList = args.MenuItems; allMenuList.Clear();//清空所有的默認菜單項
上述第二個場景中保留了【返回】、【前進】、【刷新】三個菜單項,滿足大多數場景的需求。考慮一些極端情況,系統需要統一實現自定義的右鍵菜單功能。
通過一個簡單的示例來演示如何實現自定義WebView2 中的上下文菜單。
場景:在第二個場景的基礎之上,增加2個自定義右鍵菜單項。
先看下實現效果
同樣需要在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
事件,開發者在該事件中可以自定義業務邏輯。