1. 程式人生 > >CefSharp.v49.0.1瀏覽器控制元件完全WPF版,實現禁止彈出新視窗,在同一視窗開啟連結,並且支援帶type="POST" target="_blank"的連結

CefSharp.v49.0.1瀏覽器控制元件完全WPF版,實現禁止彈出新視窗,在同一視窗開啟連結,並且支援帶type="POST" target="_blank"的連結

    需求場景:在查詢頁面,填寫查詢條件,查詢條件包括上傳的圖片,根據圖片的特徵查詢,這就需要在提交的時候,使用POST提交,因為GET提交無法提交圖片資料,提交查詢條件之後,在新的視窗展示查詢結果。(當然查詢結果頁面可能不支援F5重新整理頁面)

表單HTML程式碼示意(注意method="post" target="_blank" action指向新頁面):

<!DOCTYPE html>
<html>
<head>
    <title>提交表單查詢</title>
    <
script type="text/javascript" src='jquery.js'></script> <script type="text/javascript"> //儲存 function save() { $("#frm").submit(); } </script> </head> <body> <form id="frm" action="searchResult" enctype="multipart/form-data"
method="post" target="_blank"> <input type="text" class="input-text" id="title" name="title" value="測試title" style="width: 300px;" /> <input type="text" class="input-text" id="name" name="name" value="測試name" style="width: 300px;" /> <a href="javascript:void(0);"
onclick="save()">儲存</a> </form> </body> </html>
View Code

請先大致看下Winform版的CefSharp瀏覽器控制元件實現方式:

https://www.cnblogs.com/s0611163/p/7716692.html

下面是WPF版的CefSharp瀏覽器控制元件的實現方式,與Winform版相同的程式碼這裡不再貼上:

using CefSharp;
using log4net;
using SunCreate.Vipf.Base;
using SunCreate.Vipf.Client.Bussiness;
using System;
using System.Collections.Generic;
using System.ComponentModel;
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;

namespace SunCreate.Vipf.Client.UI
{
    /// <summary>
    /// 瀏覽器使用者控制元件
    /// </summary>
    public partial class BrowserCtrl : UserControl, IDisposable
    {
        [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);

        private ILog _log = LogManager.GetLogger(typeof(BrowserCtrl));

        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; }

        /// <summary>
        /// 瀏覽器FrameLoadEnd事件
        /// </summary>
        public event EventHandler FrameLoadEnd;

        private ExtChromiumBrowser _browser;

        public ExtChromiumBrowser Browser
        {
            get
            {
                return this._browser;
            }
        }

        public BrowserCtrl()
        {
            InitializeComponent();
            if (DesignerProperties.GetIsInDesignMode(this)) return;

            this.Loaded += BrowserCtrl_Loaded;

            lock (_lockObject)
            {
                if (!_isCefInited)
                {
                    _isCefInited = true;
                    InitCef(true);//初始化CefSharp
                }
            }

            _browser = new ExtChromiumBrowser();
            BindBrowser(_browser);
            grid.Children.Add(_browser);
        }

        /// <summary>
        /// 設定Map控制元件介面,用於C#和JS互操作
        /// </summary>
        public void SetMapCtrl(IMapCtrl mapCtrl)
        {
            _jsObject.MapCtrl = mapCtrl;
        }

        /// <summary>
        /// 釋放資源
        /// </summary>
        public void Dispose()
        {
            //如果有彈出視窗則先釋放它
            foreach (UIElement item in grid.Children)
            {
                if (item is BrowserContainer)
                {
                    (item as BrowserContainer).ClearResource();
                }
            }

            if (_browser != null && !_browser.IsDisposed)
            {
                _browser.Dispose();
            }
        }

        private void BrowserCtrl_Loaded(object sender, RoutedEventArgs e)
        {

        }

        private void LoadUrl()
        {
            if (_firstLoad)
            {
                _firstLoad = false;
                System.Threading.Tasks.Task.Factory.StartNew(() =>
                {
                    Thread.Sleep(100);

                    if (Url == null && SetUrlEvent != null)
                    {
                        try
                        {
                            SetUrlEvent(this, null);
                        }
                        catch (Exception ex)
                        {
                            _log.Error("BrowserCtrl LoadUrl error 獲取URL失敗", ex);
                        }
                    }

                    this.Dispatcher.Invoke(new Action(() =>
                    {
                        _browser.Load(Url);
                    }));
                });
            }
        }

        private void BindBrowser(ExtChromiumBrowser browser)
        {
            _jsObject = new JSObject();
            browser.RegisterJsObject("jsObj", _jsObject, false);
            browser.StartNewWindow += (s, e) =>
            {
                try
                {
                    IntPtr hwndChild = IntPtr.Zero; //瀏覽器彈出視窗控制代碼

                    BrowserContainer browserContainer = new BrowserContainer();
                    System.Windows.Forms.Control control = new System.Windows.Forms.Control();
                    control.Dock = System.Windows.Forms.DockStyle.Fill;
                    control.CreateControl();
                    browserContainer.host.Child = control;
                    browserContainer.ClearResourceEvent += (ss, ee) =>
                    {
                        CloseWindow(hwndChild);
                        control.Dispose();
                        browserContainer.host.Dispose();
                    };

                    //釋放上一個彈出視窗
                    foreach (UIElement item in grid.Children)
                    {
                        if (item is BrowserContainer)
                        {
                            (item as BrowserContainer).ClearResource();
                        }
                    }

                    grid.Children.Clear();
                    grid.Children.Add(browserContainer);

                    e.WindowInfo.SetAsChild(control.Handle, 0, 0, (int)browserContainer.ActualWidth, (int)browserContainer.ActualHeight);

                    browserContainer.SizeChanged += (ss, ee) =>
                    {
                        hwndChild = FindWindowEx(control.Handle, IntPtr.Zero, null, null);
                        MoveWindow(hwndChild, 0, 0, (int)browserContainer.ActualWidth, (int)browserContainer.ActualHeight, true);
                    };
                }
                catch (Exception ex)
                {
                    _log.Error("BrowserCtrl BindBrowser error", ex);
                }
            };
            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.D)
                {
                    //_browser.ExecuteScriptAsync("dayOrNightMap", 0);
                }
                if (ee.Key == Key.N)
                {
                    //_browser.ExecuteScriptAsync("dayOrNightMap", 1);
                }
            };
            browser.LoadError += (ss, ee) =>
            {
                _log.Error("ExtChromiumBrowser LoadError 錯誤碼:" + ee.ErrorCode + ",錯誤資訊:" + ee.ErrorText + ",錯誤URL:" + ee.FailedUrl);

                return; //下面程式碼不執行
                System.Threading.Tasks.Task.Factory.StartNew(() =>
                {
                    if (Url == null && SetUrlEvent != null)
                    {
                        try
                        {
                            SetUrlEvent(this, null);
                        }
                        catch (Exception ex)
                        {
                            _log.Error("BrowserCtrl LoadUrl error 獲取URL失敗", ex);
                        }
                    }

                    Thread.Sleep(500);
                    this.Dispatcher.BeginInvoke(new Action(() =>
                    {
                        (ss as ExtChromiumBrowser).Load(Url);
                    }));
                });
            };
        }

        #region 初始化CefSharp
        public static void InitCef(bool multiThreadedMessageLoop)
        {
            string cefsharpFolder = "CefSharp.v49.0.1";

            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 = multiThreadedMessageLoop;
            settings.FocusedNodeChangedEnabled = true;

            Cef.OnContextInitialized = delegate
            {
                var cookieManager = Cef.GetGlobalCookieManager();
                cookieManager.SetStoragePath(cefsharpFolder + "/cookies", true);
                cookieManager.SetSupportedSchemes("custom");
            };

            settings.BrowserSubprocessPath = AppDomain.CurrentDomain.BaseDirectory + cefsharpFolder + "/CefSharp.BrowserSubprocess.exe";
            settings.LogFile = cefsharpFolder + "/debug.log";
            settings.CefCommandLineArgs.Add("disable-gpu", "1");
            settings.CefCommandLineArgs.Add("enable-media-stream", "1");

            if (!Cef.Initialize(settings, shutdownOnProcessExit: true, performDependencyCheck: true))
            {
                throw new Exception("Unable to Initialize Cef");
            }
        }
        #endregion

    }
}
View Code

 核心程式碼(StartNewWindow):

browser.StartNewWindow += (s, e) =>
{
    try
    {
        IntPtr hwndChild = IntPtr.Zero; //瀏覽器彈出視窗控制代碼

        BrowserContainer browserContainer = new BrowserContainer();
        System.Windows.Forms.Control control = new System.Windows.Forms.Control();
        control.Dock = System.Windows.Forms.DockStyle.Fill;
        control.CreateControl();
        browserContainer.host.Child = control;
        browserContainer.ClearResourceEvent += (ss, ee) =>
        {
            CloseWindow(hwndChild);
            control.Dispose();
            browserContainer.host.Dispose();
        };

        //釋放上一個彈出視窗
        foreach (UIElement item in grid.Children)
        {
            if (item is BrowserContainer)
            {
                (item as BrowserContainer).ClearResource();
            }
        }

        grid.Children.Clear();
        grid.Children.Add(browserContainer);

        e.WindowInfo.SetAsChild(control.Handle, 0, 0, (int)browserContainer.ActualWidth, (int)browserContainer.ActualHeight);

        browserContainer.SizeChanged += (ss, ee) =>
        {
            hwndChild = FindWindowEx(control.Handle, IntPtr.Zero, null, null);
            MoveWindow(hwndChild, 0, 0, (int)browserContainer.ActualWidth, (int)browserContainer.ActualHeight, true);
        };
    }
    catch (Exception ex)
    {
        _log.Error("BrowserCtrl BindBrowser error", ex);
    }
};
View Code

核心程式碼(資源釋放):

/// <summary>
/// 釋放資源
/// </summary>
public void Dispose()
{
    //如果有彈出視窗則先釋放它
    foreach (UIElement item in grid.Children)
    {
        if (item is BrowserContainer)
        {
            (item as BrowserContainer).ClearResource();
        }
    }

    if (_browser != null && !_browser.IsDisposed)
    {
        _browser.Dispose();
    }
}
View Code

 難點:彈出視窗和原來的視窗似乎共用同一個ChromiumWebBrowser例項的某些東西,但是彈出的視窗本身又獲取不到ChromiumWebBrowser例項,某些操作不便。