基于CEF内核的简单浏览器实现(***)

来源:互联网 发布:电脑3d桌面软件 编辑:程序博客网 时间:2024/05/16 11:51

原文地址: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)启动默认打开百度


(2)访问CSDN


(3)回退到百度页面


(4)前进到CSDN页面


(5)打开链接



对应Demon下载链接: http://download.csdn.net/detail/lixiang987654321/9598429

0 0
原创粉丝点击