說明:在同一視窗打開鏈接,只要稍加改造就可以實作,這里實作的是在新Tab頁打開鏈接,并且支持帶type="POST" target="_blank"的鏈接
github和bitbucket上相關問題:
1、WPF empty POST data when using custom popup https://github.com/cefsharp/CefSharp/issues/1267
2、CefLifeSpanHandler, customized OnBeforePopup problem https://bitbucket.org/chromiumembedded/cef/issues/1949/
解決(CefSharp版本75.1.143.0):
一、實作IRequestHandler介面
using CefSharp; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Security.Cryptography.X509Certificates; namespace CefSharpDemo { public class RequestHandler : IRequestHandler { private ExtChromiumBrowser _browser; public RequestHandler(ExtChromiumBrowser browser) { _browser = browser; } public bool GetAuthCredentials(IWebBrowser chromiumWebBrowser, IBrowser browser, string originUrl, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback) { return false; } public IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling) { if (request.Method.ToUpper() == "POST" && request.PostData != null) { if (request.PostData.Elements.Count > 0) { _browser.PostData = new byte[request.PostData.Elements[0].Bytes.Length]; request.PostData.Elements[0].Bytes.CopyTo(_browser.PostData, 0); } } return null; } public bool OnBeforeBrowse(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect) { return false; } public bool OnCertificateError(IWebBrowser chromiumWebBrowser, IBrowser browser, CefErrorCode errorCode, string requestUrl, ISslInfo sslInfo, IRequestCallback callback) { return false; } public bool OnOpenUrlFromTab(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture) { return false; } public void OnPluginCrashed(IWebBrowser chromiumWebBrowser, IBrowser browser, string pluginPath) { } public bool OnQuotaRequest(IWebBrowser chromiumWebBrowser, IBrowser browser, string originUrl, long newSize, IRequestCallback callback) { return false; } public void OnRenderProcessTerminated(IWebBrowser chromiumWebBrowser, IBrowser browser, CefTerminationStatus status) { } public void OnRenderViewReady(IWebBrowser chromiumWebBrowser, IBrowser browser) { } public bool OnSelectClientCertificate(IWebBrowser chromiumWebBrowser, IBrowser browser, bool isProxy, string host, int port, X509Certificate2Collection certificates, ISelectClientCertificateCallback callback) { return false; } } }View Code
二、實作ILifeSpanHandler介面
using CefSharp; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Interop; using Utils; namespace CefSharpDemo { public class CefLifeSpanHandler : CefSharp.ILifeSpanHandler { private static LimitedTaskScheduler _scheduler = new LimitedTaskScheduler(2); public CefLifeSpanHandler() { } public bool DoClose(IWebBrowser browserControl, CefSharp.IBrowser browser) { if (browser.IsDisposed || browser.IsPopup) { return false; } return true; } public void OnAfterCreated(IWebBrowser browserControl, IBrowser browser) { } public void OnBeforeClose(IWebBrowser browserControl, IBrowser browser) { } public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser) { var chromiumWebBrowser = (ExtChromiumBrowser)browserControl; chromiumWebBrowser.Dispatcher.Invoke(new Action(() => { BrowserPopupWin win = new BrowserPopupWin(); win.ShowInTaskbar = false; win.Height = 0; win.Width = 0; win.Show(); IntPtr handle = new WindowInteropHelper(win).Handle; windowInfo.SetAsChild(handle); _scheduler.Run(() => { WaitUtil.Wait(() => chromiumWebBrowser.PostData); IRequest request = null; if (chromiumWebBrowser.PostData != null) { request = frame.CreateRequest(); request.Url = targetUrl; request.Method = "POST"; request.InitializePostData(); var element = request.PostData.CreatePostDataElement(); element.Bytes = chromiumWebBrowser.PostData; request.PostData.AddElement(element); chromiumWebBrowser.PostData = null; } chromiumWebBrowser.Dispatcher.Invoke(new Action(() => { NewWindowEventArgs e = new NewWindowEventArgs(targetUrl, request); chromiumWebBrowser.OnNewWindow(e); })); chromiumWebBrowser.Dispatcher.Invoke(new Action(() => { win.Close(); })); }); })); newBrowser = null; return false; } } }View Code
三、擴展ChromiumWebBrowser
using CefSharp.Wpf; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CefSharpDemo { public class ExtChromiumBrowser : ChromiumWebBrowser { public byte[] PostData { get; set; } public ExtChromiumBrowser() : base() { this.LifeSpanHandler = new CefLifeSpanHandler(); this.DownloadHandler = new DownloadHandler(this); this.MenuHandler = new MenuHandler(); this.KeyboardHandler = new KeyboardHandler(); this.RequestHandler = new RequestHandler(this); } public event EventHandler<NewWindowEventArgs> StartNewWindow; public void OnNewWindow(NewWindowEventArgs e) { if (StartNewWindow != null) { StartNewWindow(this, e); } } public void ClearHandlers() { //如果不清理Handler,會導致子行程CefSharp.BrowserSubprocess.exe無法釋放 this.LifeSpanHandler = null; this.DownloadHandler = null; this.MenuHandler = null; this.KeyboardHandler = null; } } }View Code
四、封裝ExtChromiumBrowser(BrowserCtrl控制元件)
using CefSharp; using CefSharp.Wpf; using System; using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using Utils; namespace CefSharpDemo { /// <summary> /// 瀏覽器用戶控制元件 /// </summary> public partial class BrowserCtrl : UserControl, IDisposable { #region 外部方法 /* [DllImport("user32.dll", SetLastError = true)] private static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent); [DllImport("user32.dll", SetLastError = true)] public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string className, string windowTitle); [DllImport("user32.dll", SetLastError = true)] public static extern int MoveWindow(IntPtr hWnd, int x, int y, int nWidth, int nHeight, bool BRePaint); [DllImport("user32.dll", SetLastError = true)] public static extern int CloseWindow(IntPtr hWnd); [DllImport("User32.dll", EntryPoint = "GetWindowText")] private static extern int GetWindowText(IntPtr hwnd, StringBuilder lpString, int nMaxCount); */ #endregion #region 變數屬性事件 private static bool _isCefInited = false; private static object _lockObject = new object(); private JSObject _jsObject; private bool _firstLoad = true; /// <summary> /// 在此事件中設定URL(此事件已在執行緒中執行,此事件已對錯誤情況進行處理) /// </summary> public event EventHandler SetUrlEvent; /// <summary> /// URL /// </summary> public string Url { get; set; } public IRequest Request { get; set; } /// <summary> /// 瀏覽器FrameLoadEnd事件 /// </summary> public event EventHandler FrameLoadEnd; private ExtChromiumBrowser _browser; public ExtChromiumBrowser Browser { get { WaitUtil.Wait(() => this._browser != null && this._browser.IsInitialized && _isCefInited); return this._browser; } } private static LimitedTaskScheduler _scheduler = new LimitedTaskScheduler(2); #endregion #region 建構式 public BrowserCtrl() { InitializeComponent(); if (DesignerProperties.GetIsInDesignMode(this)) return; this.Loaded += BrowserCtrl_Loaded; lock (_lockObject) { if (!_isCefInited) { _isCefInited = true; InitCef();//初始化CefSharp } } _browser = new ExtChromiumBrowser(); BindBrowser(_browser); grid.Children.Add(_browser); } #endregion #region BrowserCtrl_Loaded private void BrowserCtrl_Loaded(object sender, RoutedEventArgs e) { } #endregion #region SetMapCtrl /// <summary> /// 設定Map控制元件介面,用于C#和JS互操作 /// </summary> public void SetMapCtrl(IMapCtrl mapCtrl) { _jsObject.MapCtrl = mapCtrl; } #endregion #region Dispose 釋放資源 /// <summary> /// 釋放資源 /// </summary> public void Dispose() { //如果有彈出視窗則先釋放它 //foreach (UIElement item in grid.Children) //{ // if (item is BrowserContainer) // { // (item as BrowserContainer).ClearResource(); // } //} _browser.ClearHandlers(); if (_browser != null && !_browser.IsDisposed) { _browser.Dispose(); } } #endregion #region Load public void Load(string url) { if (!string.IsNullOrWhiteSpace(url)) { loadingWait.Visibility = Visibility.Visible; Url = url; _scheduler.Run(() => { #region Wait WaitUtil.Wait(() => { if (this._browser == null) return false; if (!this._browser.IsInitialized) return false; if (!_isCefInited) return false; bool isBrowserInitialized = false; this.Dispatcher.Invoke(() => { isBrowserInitialized = this._browser.IsBrowserInitialized; }); if (!isBrowserInitialized) return false; return true; }); #endregion _browser.Load(Url); }); } } #endregion #region LoadUrl private void LoadUrl() { if (_firstLoad) { _firstLoad = false; _scheduler.Run(() => { #region Wait WaitUtil.Wait(() => { if (this._browser == null) return false; if (!this._browser.IsInitialized) return false; if (!_isCefInited) return false; bool isBrowserInitialized = false; this.Dispatcher.Invoke(() => { isBrowserInitialized = this._browser.IsBrowserInitialized; }); if (!isBrowserInitialized) return false; return true; }); #endregion if (Url == null && SetUrlEvent != null) { try { SetUrlEvent(this, null); } catch (Exception ex) { LogUtil.Error(ex, "BrowserCtrl LoadUrl error 獲取URL失敗"); } } else { this.Dispatcher.Invoke(new Action(() => { loadingWait.Visibility = Visibility.Collapsed; })); } if (Url != null) { try { if (Request == null) { _browser.Load(Url); } else { _browser.Load(Url); _browser.GetMainFrame().LoadRequest(Request); Request = null; } } catch (Exception ex) { LogUtil.Error(ex, "BrowserCtrl LoadUrl error Load URL失敗"); } } else { this.Dispatcher.Invoke(new Action(() => { loadingWait.Visibility = Visibility.Collapsed; })); } }); } } #endregion #region BindBrowser private void BindBrowser(ExtChromiumBrowser browser) { _jsObject = new JSObject(); browser.RegisterJsObject("jsObj", _jsObject, new CefSharp.BindingOptions { CamelCaseJavascriptNames = false }); browser.IsBrowserInitializedChanged += (ss, ee) => { LoadUrl(); }; browser.FrameLoadStart += (ss, ee) => { this.Dispatcher.BeginInvoke(new Action(() => { (ss as ExtChromiumBrowser).Focus(); })); }; browser.FrameLoadEnd += (ss, ee) => { this.Dispatcher.BeginInvoke(new Action(() => { loadingWait.Visibility = Visibility.Collapsed; })); if (FrameLoadEnd != null) { FrameLoadEnd(null, null); } }; browser.KeyDown += (ss, ee) => { if (ee.Key == Key.F5) { try { browser.Reload(); } catch (Exception ex) { LogUtil.Error(ex, "ExtChromiumBrowser Reload error"); } } }; browser.PreviewTextInput += (o, e) => { foreach (var character in e.Text) { // 把每個字符向瀏覽器組件發送一遍 browser.GetBrowser().GetHost().SendKeyEvent((int)WM.CHAR, (int)character, 0); } // 不讓cef自己處理 e.Handled = true; }; browser.LoadError += (s, e) => { this.Dispatcher.BeginInvoke(new Action(() => { loadingWait.Visibility = Visibility.Collapsed; })); }; } #endregion #region RegisterJsObject public void RegisterJsObject(string name, object objectToBind, BindingOptions options = null) { try { if (_browser != null) { _browser.RegisterJsObject(name, objectToBind, options); } } catch (Exception ex) { LogUtil.Error(ex, "BrowserCtrl RegisterJsObject 錯誤"); } } #endregion #region 初始化CefSharp public static void InitCef() { string cefsharpFolder = "CefSharp"; var settings = new CefSettings(); //The location where cache data will be stored on disk. If empty an in-memory cache will be used for some features and a temporary disk cache for others. //HTML5 databases such as localStorage will only persist across sessions if a cache path is specified. settings.CachePath = cefsharpFolder + "/cache"; //設定cache目錄 settings.MultiThreadedMessageLoop = true; CefSharpSettings.FocusedNodeChangedEnabled = true; CefSharpSettings.LegacyJavascriptBindingEnabled = true; CefSharpSettings.ShutdownOnExit = true; CefSharpSettings.SubprocessExitIfParentProcessClosed = true; string logDir = AppDomain.CurrentDomain.BaseDirectory + cefsharpFolder + "/log/"; if (!Directory.Exists(logDir)) { Directory.CreateDirectory(logDir); } settings.BrowserSubprocessPath = AppDomain.CurrentDomain.BaseDirectory + cefsharpFolder + "/CefSharp.BrowserSubprocess.exe"; settings.LogFile = logDir + DateTime.Now.ToString("yyyyMMdd") + ".log"; settings.LocalesDirPath = AppDomain.CurrentDomain.BaseDirectory + cefsharpFolder + "/locales"; settings.CefCommandLineArgs.Add("disable-gpu", "1"); settings.CefCommandLineArgs.Add("enable-media-stream", "1"); if (!Cef.Initialize(settings, performDependencyCheck: true, browserProcessHandler: new BrowserProcessHandler())) { throw new Exception("Unable to Initialize Cef"); } } #endregion } }View Code
五、MainWindow測驗代碼
using CefSharp; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; using Utils; namespace CefSharpDemo { /// <summary> /// CefSharp Demo 表單 /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); tabControl.AddTabItemEvent += tabControl_AddTabItemEvent; Application.Current.MainWindow = this; } private void tabControl_AddTabItemEvent(object sender, EventArgs e) { //CreateTabItem("https://www.cnblogs.com/"); CreateTabItem("file:///D:/_程式/CefSharpDemo/post.html"); } /// <summary> /// 新增Tab頁 /// </summary> private void CreateTabItem(string url = null, IRequest request = null) { TabItem tabItem = new TabItem(); tabItem.Header = "新標簽頁"; BrowserDemoCtrl ctrl = new BrowserDemoCtrl(); ctrl.browserCtrl.Browser.StartNewWindow += (s, e) => { CreateTabItem(e.TargetUrl, e.Request); }; ctrl.browserCtrl.SetUrlEvent += (s, e) => { ctrl.browserCtrl.Url = url; ctrl.browserCtrl.Request = request; }; tabItem.Content = ctrl; tabControl.Items.Add(tabItem); tabControl.SelectedItem = tabItem; ScrollViewer scrollViewer = tabControl.Template.FindName("scrollViewer", tabControl) as ScrollViewer; scrollViewer.ScrollToRightEnd(); } private void Window_Closed(object sender, EventArgs e) { tabControl.CloseAllTabItem(); //關閉表單清理資源 //程式退出時洗掉cache CefSharp.Cef.Shutdown(); string cachePath = AppDomain.CurrentDomain.BaseDirectory + "CefSharp\\cache"; if (Directory.Exists(cachePath)) { foreach (string path in Directory.GetDirectories(cachePath)) { Directory.Delete(path, true); } foreach (string file in Directory.GetFiles(cachePath)) { if (!file.ToLower().Contains("cookies")) { File.Delete(file); } } } } } }View Code
六、測驗html代碼post.html
<!DOCTYPE html> <html> <head> <title>CefSharpDemo</title> <meta charset="utf-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <style type="text/css"> </style> <script type="text/javascript"> </script> </head> <body> <!--enctype="multipart/form-data"--> <form method="post" action="http://localhost:1209/netcms/" target="_blank"> <span>name:</span><input type="text" name="name" value="測驗名稱" /> <span>code:</span><input type="text" name="code" value="測驗編碼" /> <button type="submit">Post提交</button> </form> </body> </html>View Code
七、測驗后臺代碼
public ActionResult index() { string name = Request.Params["name"]; string code = Request.Params["code"]; ViewBag.name = name; ViewBag.code = code; return View(); }View Code
八、測驗前臺cshtml代碼
@using Models; @{ Layout = "~/Views/Shared/_SiteLayout.cshtml"; } <div style="font-size:50px; height:1200px;"> <span>name:</span><span>@ViewBag.name</span><br /><span>code:</span><span>@ViewBag.code</span> </div>View Code
九:關鍵代碼段:
1、RequestHandler類中獲取并保存PostData
public IResourceRequestHandler GetResourceRequestHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling) { if (request.Method.ToUpper() == "POST" && request.PostData != null) { if (request.PostData.Elements.Count > 0) { _browser.PostData = new byte[request.PostData.Elements[0].Bytes.Length]; request.PostData.Elements[0].Bytes.CopyTo(_browser.PostData, 0); } } return null; }View Code
2、CefLifeSpanHandler類中創建IRequest
public bool OnBeforePopup(IWebBrowser browserControl, IBrowser browser, IFrame frame, string targetUrl, string targetFrameName, WindowOpenDisposition targetDisposition, bool userGesture, IPopupFeatures popupFeatures, IWindowInfo windowInfo, IBrowserSettings browserSettings, ref bool noJavascriptAccess, out IWebBrowser newBrowser) { var chromiumWebBrowser = (ExtChromiumBrowser)browserControl; chromiumWebBrowser.Dispatcher.Invoke(new Action(() => { BrowserPopupWin win = new BrowserPopupWin(); win.ShowInTaskbar = false; win.Height = 0; win.Width = 0; win.Show(); IntPtr handle = new WindowInteropHelper(win).Handle; windowInfo.SetAsChild(handle); _scheduler.Run(() => { WaitUtil.Wait(() => chromiumWebBrowser.PostData); IRequest request = null; if (chromiumWebBrowser.PostData != null) { request = frame.CreateRequest(); request.Url = targetUrl; request.Method = "POST"; request.InitializePostData(); var element = request.PostData.CreatePostDataElement(); element.Bytes = chromiumWebBrowser.PostData; request.PostData.AddElement(element); chromiumWebBrowser.PostData = null; } chromiumWebBrowser.Dispatcher.Invoke(new Action(() => { NewWindowEventArgs e = new NewWindowEventArgs(targetUrl, request); chromiumWebBrowser.OnNewWindow(e); })); chromiumWebBrowser.Dispatcher.Invoke(new Action(() => { win.Close(); })); }); })); newBrowser = null; return false; }View Code
說明:OnBeforePopup方法要return false,用BrowserPopupWin和windowInfo.SetAsChild方法彈出一個不可見的表單,這樣才能拿到PostData
3、在BrowserCtrl控制元件中用LoadRequest方法打開新的URL,并把post資料帶過去
if (Request == null) { _browser.Load(Url); } else { _browser.Load(Url); _browser.GetMainFrame().LoadRequest(Request); Request = null; }View Code
十、效果圖:

完整代碼下載:https://files-cdn.cnblogs.com/files/s0611163/CefSharpDemo.zip
原始碼說明:為了減少原始碼壓縮包的大小,代碼中沒有依賴的CefSharp檔案,請自己下載(使用x86版本),用于測驗的網頁后臺代碼也沒有,請自己制作測驗后臺
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/91536.html
標籤:C#
上一篇:C# 設定、洗掉、讀取Word檔案背景——基于Spire.Cloud.SDK for .NET
下一篇:高德離線地圖瓦片坐標偏移糾偏
