天天看點

基于CEF核心的簡單浏覽器實作(***)

原文位址:http://blog.csdn.net/lixiang987654321/article/details/52154857

看本人該文章之前,首先必須先了解libcef的下載下傳、編譯等過程以及如何在MFC中使用CEF浏覽器核心,如果不熟悉的夥伴們,請先看本人的前兩篇文章,有了對應的庫之後才能進行實作。

      前一篇文章中介紹了如何在MFC中加載使用CEF核心,這是基礎,我也在文章中提供了相應的Demon,但是對應的Demon功能不全,僅僅是将CEF核心加載進去并且使用了預設的URL(百度),而且我給出的Demon有個小問題就是對應的浏覽器視窗嵌入到MFC的時候位置不對,與上面的工具欄差了四五十個像素,這裡我糾正一下,原因是将CEF視窗設定為子視窗的時候,應該将對應的PICTURE控件的位置轉換成相對于主對話框中的相對位置,也就是要将螢幕坐标轉換成相對于主對話框的坐标(子視窗坐标),由于缺少改轉換步驟導緻了上面的問題,這篇文章提供的Demon已經解決了這個bug。當然本文提供的簡單浏覽器的功能有如下:

(1)主浏覽器浏覽的時候加載對應網頁的URL顯示到我們主視窗的CEdit控件中;

(2)實作浏覽器的回退與前進浏覽功能,并且前進或回退的網頁URL也能及時顯示;

(3)在浏覽了多個網頁之後,對應的回退和前進按鈕要根據使用者浏覽順序控制其能否回退或前進;

(4)主視窗最大化對應的主浏覽器也能最大化;

(5)浏覽網頁标題能顯示到我們視窗的标題中;

實作步驟如下(必須先看完我前一篇文章如何在MFC加載libcef一文章,這裡我直接步入主題):

(1)網頁标題的顯示

在加載文檔的時候,我們的事件處理器實作的CefDisplayHandler的接口,改接口中的有一個方法:

virtual void OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) OVERRIDE;

改方法就是在某浏覽器(一個網頁一個浏覽器視窗)标題發生變化的時候出發的回調,也就是我們在這裡能夠通過浏覽器的标題變化事件,将加載的網頁的标題顯示到對應的浏覽器的視窗中,由于主浏覽器視窗被嵌入到我們的主對話框中,是以主對話框的标題要設定為主浏覽器的标題,其他以連結形式打開的彈出式視窗則設定為自己浏覽器的标題;每一個浏覽器對應一個Frame視窗,也就是Browser->GetMainFrame擷取對應的主視窗,主視窗中IsMain方法标志了這個是根主視窗還是由根浏覽器産生的子浏覽器視窗;

代碼如下:

void CCefBrowserEventHandler::OnTitleChange(CefRefPtr<CefBrowser> browser, const CefString& title) OVERRIDE
{
CefWindowHandle hwnd = browser->GetHost()->GetWindowHandle();

// 如果為被嵌入到主對話框的主浏覽器視窗則更改我們對話框标題
if (GetParent(hwnd) == AfxGetApp()->m_pMainWnd->GetSafeHwnd()) 
{
AfxGetApp()->m_pMainWnd->SetWindowText(std::wstring(title).c_str());
}

// 否則有核心建立(連結形式打開)的視窗标題設定為自己的視窗标題
else
{
::SetWindowText(hwnd, std::wstring(title).c_str());
}
}
           

(2)主浏覽器浏覽的時候加載對應網頁的URL顯示到我們主視窗的CEdit控件中;

在浏覽器載入某網頁的時候會發生浏覽器位址變化消息,我們可以捕獲CefDisplayHandler處理器中的方法

void OnAddressChange(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,const CefString& url) ;

由于我們需要檢測主浏覽器視窗(主對話框的子視窗)的URL變化,是以主浏覽器URL變化的時候,我們隻需要将主浏覽器的URL顯示到對話框即可。主浏覽器判斷是否是主浏覽器視窗通過其視窗的IsMain方法判斷;

void CCefBrowserEventHandler::OnAddressChange(CefRefPtr<CefBrowser> browser,CefRefPtr<CefFrame> frame,const CefString& url) OVERRIDE
{
if (frame->IsMain())
{
AfxGetApp()->m_pMainWnd->SendMessage(WM_URL_CHANGED, (WPARAM)(LPCTSTR)url.c_str(), 0);
}
}
           

(3)在浏覽了多個網頁之後,對應的回退和前進按鈕要根據使用者浏覽順序控制其能否回退或前進;浏覽器加載頁面的時候是一個過程,這個過程中是一個變化的過程,是以是一個狀态變化過程,過程變化事件我們可以通過加載處理器CefLoadHandler的

void OnLoadingStateChange(CefRefPtr<CefBrowser> browser,bool isLoading,bool canGoBack,boolcanGoForward)

回調事件捕獲,改事件參數中表明了目前通路頁是否可以回退或者先前翻頁;通過後兩個參數控制按鈕的可用性

void CCefBrowserEventHandler::OnLoadingStateChange(CefRefPtr<CefBrowser> browser,bool isLoading,bool canGoBack,bool canGoForward) OVERRIDE
{
if (browser->GetMainFrame()->IsMain())
{
AfxGetApp()->m_pMainWnd->SendMessage(WM_LOAD_CHANGED, canGoBack, canGoForward);
}

}
           

(4)實作浏覽器的回退與前進浏覽功能,并且前進或回退的網頁URL也能及時顯示;浏覽器中CBrowser控制浏覽的頁面的基本操作,包括回退、前進等,Frame裡則主要控制視窗中的事件,包括菜單、列印等等事件,是以知道之後(這裡我要說兩句,我知道這些就是檢視了對應接口的方法,見名知意,不知道的看英文就能明白,包括對應的處理器事件接口,我們可以讀一讀就能明白裡面接口的含義)我們隻需要調用對應的浏覽器的方法GoBack或GoForword即可達到回退和前進功能;

void CCef_DemonDlg::OnBnClickedButtonBack()
{
// TODO: 在此添加控件通知處理程式代碼
if (CCefBrowserEventHandler::GetInstance()->GetMainBrowser())
{
CCefBrowserEventHandler::GetInstance()->GetMainBrowser()->GoBack();
}
}

void CCef_DemonDlg::OnBnClickedButtonNext()
{
// TODO: 在此添加控件通知處理程式代碼
if (CCefBrowserEventHandler::GetInstance()->GetMainBrowser())
{
CCefBrowserEventHandler::GetInstance()->GetMainBrowser()->GoForward();
}
}
           

   其中,我們這裡的事件處理器中提供了擷取全局執行個體的方法GetInstance以及擷取主視窗的方法,拿到對應主浏覽器之後直接調用GoBack和GoForward即可。

(5)主視窗最大化對應的主浏覽器也能最大化;這個就很簡單了,捕獲Dialog的Onsize消息,處理對應位置即可;

 代碼簡單如下:

void CCef_DemonDlg::OnSize(UINT nType, int cx, int cy)
{
CDialog::OnSize(nType, cx, cy);
OnCtrlSize();
}

void CCef_DemonDlg::OnCtrlSize()
{
int nHeadHeight = 27;
int nBtnWidth = 55;

CRect rtClient;
GetClientRect(&rtClient);

CRect rtBtn;
if (::IsWindow(GetDlgItem(IDC_BUTTON_BACK)->GetSafeHwnd()))
{
rtBtn = rtClient;
rtBtn.left += 1;
rtBtn.top += 1;
rtBtn.right = rtBtn.left + nBtnWidth;
rtBtn.bottom = rtBtn.top + nHeadHeight;
GetDlgItem(IDC_BUTTON_BACK)->MoveWindow(&rtBtn);
}

if (::IsWindow(GetDlgItem(IDC_BUTTON_NEXT)->GetSafeHwnd()))
{
rtBtn.left = rtBtn.right + 1;
rtBtn.right = rtBtn.left + nBtnWidth;
GetDlgItem(IDC_BUTTON_NEXT)->MoveWindow(&rtBtn);
}

int nUrlLeft = rtBtn.right + 1;
if (::IsWindow(GetDlgItem(IDC_BUTTON_GO)->GetSafeHwnd()))
{
rtBtn = rtClient;
rtBtn.top += 1;
rtBtn.right -= 1;
rtBtn.left = rtBtn.right - nBtnWidth;
rtBtn.bottom = rtBtn.top + nHeadHeight;
GetDlgItem(IDC_BUTTON_GO)->MoveWindow(&rtBtn);
}
int nUrlRight = rtBtn.left - 1;

CRect rtEdit;
if (::IsWindow(GetDlgItem(IDC_EDIT_URL)->GetSafeHwnd()))
{
rtEdit = rtClient;
rtEdit.top += 1;
rtEdit.bottom = rtEdit.top + nHeadHeight;
rtEdit.left = nUrlLeft;
rtEdit.right = nUrlRight;
GetDlgItem(IDC_EDIT_URL)->MoveWindow(&rtEdit);
}

CRect rtBody;
if (::IsWindow(GetDlgItem(IDC_STATIC_BODY)->GetSafeHwnd()))
{
rtBody = rtClient;
rtBody.left += 1;
rtBody.right -= 1;
rtBody.top = rtEdit.bottom + 1;
GetDlgItem(IDC_STATIC_BODY)->MoveWindow(&rtBody);

::MoveWindow(CCefBrowserEventHandler::GetInstance()->GetMainBrowser()->GetHost()->GetWindowHandle(),
rtBody.left, rtBody.top, rtBody.Width(), rtBody.Height(), TRUE);
}
}
           

 好了,看了對應的幾個問題之後就是看看實作效果了:

(1)啟動預設打開百度

基于CEF核心的簡單浏覽器實作(***)

(2)通路CSDN

基于CEF核心的簡單浏覽器實作(***)

(3)回退到百度頁面

基于CEF核心的簡單浏覽器實作(***)

(4)前進到CSDN頁面

基于CEF核心的簡單浏覽器實作(***)

(5)打開連結

基于CEF核心的簡單浏覽器實作(***)

對應Demon下載下傳連結: http://download.csdn.net/detail/lixiang987654321/9598429