zoukankan      html  css  js  c++  java
  • CListView, CListCtrl, CListBox


    1. http://forum.eviloctal.com/simple/index.php?t18780.html
    微软的MFC在Visual Studio 6.0中提供了一个新类CHtmlView,利用这个类,我们可以实现在基于文档视图结构的程序中显示HTML文件。 但是它是否可以用来在对话框中实现这一功能呢?我们不妨拿CHtmlView和CListView做一个比较,通过比较这两个类,我们会发现一些有趣的差别,MFC中CListView有一个对应的CListCtrl类用来在对话框中使用,而CHtmlView却没有一个CHtmlCtrl类与之对应。所以为了实现在对话框的控制中显示HTML文件,我们不得不为CHtmlView创建一个对应的子类CHtmlCtrl。为了演示该类的使用方法,本实例在程序的About对话框中显示一个名为"about.htm"的HTML文件。更有趣的是,程序所用到的HTML源文件是作为资源存储在EXE文件中的。该程序编译运行后的效果如图一所示:
    ......................................

    图一、显示HTML文件的对话框

      一、实现方法

      为了在对话框中显示HTML文件,我们必须将CHtmlCtrl类与对话框中的一个静态控制(也可以是其它控制)关联起来,这样才能为显示HTML文件提供一个窗口,为此我们在CHtmlCtrl类中定义了CreateFromStatic()函数,具体代码如下:

    BOOL CHtmlCtrl::CreateFromStatic(UINT nID, CWnd* pParent)
    {
     CStatic wndStatic; //静态控件对象;
     if (!wndStatic.SubclassDlgItem(nID, pParent))
      return FALSE;
     // 获取静态控制的矩形区域并转换为父窗口的客户区坐标
     CRect rc;
     wndStatic.GetWindowRect(&rc);
     pParent->ScreenToClient(&rc);
     wndStatic.DestroyWindow();
     // 创建 HTML 控制 (CHtmlView)
     return (Create(NULL, // 类名;
      NULL, // 标题;
      (WS_CHILD | WS_VISIBLE ), // 风格;
      rc, // 矩形区域;
      pParent, // 父窗口;
      nID, // 控制的ID号;
      NULL)); //取消文档框架支持;
    }

      为了避免主控程序将CHtmlView对象看作是文档/视图框架,需要重载CView::OnMouseActivate()和CView::OnDestroy()函数。此外,当用户在控制中单击时,OnMouseActivate要负责响应(WM_MOUSEACTIVATE)。

    int CHtmlCtrl::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT msg)
    {
     //旁路 CView 文档/框架
     return CWnd::OnMouseActivate(pDesktopWnd, nHitTest, msg);
    }

    void CHtmlCtrl::OnDestroy()
    {
     if (m_pBrowserApp)
     {
      m_pBrowserApp->Release();
      m_pBrowserApp = NULL;
     }
     CWnd::OnDestroy(); // 旁路 CView 文档/框架
    }

      通常,CHtmlView是在virtual void PostNcDestroy()中释放空间,但对话框中的控制常常是作为堆栈对象实现的,所以,在PostNcDestroy()中不必在做什么。

      为了播放资源中的HTML文件,需要重载导航处理器OnBeforeNavigate2(), 实现"app:" 伪协议,。传递"app:"链接到一个虚拟协议处理器。因为app:是假协议,所以需要设置pbCancel参数为"TRUE",以停止掉这个导航。

    void CHtmlCtrl::OnBeforeNavigate2( LPCTSTR lpszURL,
    DWORD nFlags,
    LPCTSTR lpszTargetFrameName,
    CByteArray& baPostedData,
    LPCTSTR lpszHeaders,
    BOOL* pbCancel )
    {
     const char APP_PROTOCOL[] = "app:";
     int len = _tcslen(APP_PROTOCOL);
     if (_tcsnicmp(lpszURL, APP_PROTOCOL, len)==0)
     {
      OnAppCmd(lpszURL + len);
      *pbCancel = TRUE;
     }
    }

      定义一个虚函数OnAppCmd(),处理app:命令,例如当浏览器准备导航到"app:foo"时,这个函数被调用,参数lpszWhere的值为"foo"。

    void CHtmlCtrl::OnAppCmd(LPCTSTR lpszWhere){ // default: do nothing}

      对于作为资源的HTML文件和其中的嵌入的图片和音乐文件,用文件的实际名字作为资源名很重要,以便浏览器能够找到他们。在一个普通的Web页面中,我们使用图像是用下列语法:

    <IMG src="pd.jpg">

      此代码假设图像文件"pd.jpg"存在当前目录(页面文件所在目录)中。如果图像文件是作为资源存在EXE文件中,我们如何引用呢?方法一样,此时,我们必须告诉浏览器Web页面文件的位置。为此要在Web页面文件的开头加上如下代码:

    <BASE url="res://ShowHtml.exe/about.htm">

      这一行代码告诉浏览器当前目录是"res://ShowHtml.exe",当浏览器遇到代码<IMG src="pd.jpg">时,它会按照路径res://ShowHtml.exe/pd.jpg查找。否则,它会在程序文件的路径查找。通常用res://modulename可以访问动态库或可执行文件中的资源。这里res:的意思与http:,ftp:,file:,及mailto的意思相同。即:"在这个路径中的第一个名字是一个文件名,第二个名字是文件中的资源名"。其余的工作由浏览器完成。

      为了在对话框中加载web页面,调用CHtmlCtrl::LoadFromResource函数,它是由CHtmlView继承而来的。也可以用全路径res://ShowHtml.exe/about.htm作为参数。除此之外,还有一个问题就是:CAboutDialog对话框中"OK"按钮的处理,其实,它根本就不是一个按钮,而是一个在HTML文件中嵌入的图像,用JScript来控制图像被按下时和弹起时的状态。处理"OK"按钮的技巧主要是解决对话框与主控程序之间的通讯。利用动态HTML文档层(COM)技术可以处理用户单击图像或链接,方法是获得图像元素,然后侦听OnClick事件。但这是一种非常非常麻烦的方法。还有一种更简单的方法。假设HTML有如下的图像链接:

    <A href="ok"><IMG ...></A>

      当用户单击它时,浏览器显示这个"OK"文件,但是在显示之前,控制先执行CHtmlView::OnBeforeNavigate2()函数,为此可以定义CHtmlCtrl类的子类CMyHtmlCtrl,重载这个函数,在这里面实现想做的任何事情。下面的代码实现了当用户点击HTML文件上的"OK"图片时,关闭对话框。

    void CHtmlCtrl::OnBeforeNavigate2(
     LPCTSTR lpszURL,
     ...,
     BOOL* pbCancel)
    {
     if (_tcscmp(lpszURL,_T("ok"))==0)
     {
      // "ok" clicked:
      *pbCancel=TRUE; // abort
      GetParent()->SendMessage(WM_COMMAND,IDOK); // will close dialog
     }
    }

      其实"OK"并不是什么文件;它只是一个很特殊的名字,可以定义一个CHtmlCtrl类的子类CMyHtmlCtrl,该类将"OK"图片看作是"OK"按钮。为了实现这个想法,程序中创建了一个叫app:的冒充协议来代替"OK",在about.htm中定义实际的链接是app:ok。每当浏览器导航到app:somewhere的时候,CMyHtmlCtrl都以"somewhere"为参数调用一个虚函数:CMyHtmlCtrl::OnAppCmd。

    void CMyHtmlCtrl::OnAppCmd( LPCTSTR lpszWhere )
    {
     if (_tcsicmp(lpszWhere, _T("ok"))==0)
     {
      GetParent()->SendMessage(WM_COMMAND,IDOK);
     }
    }


      二、编程步骤

      1、启动Visual C++6.0,生成一个单文档的应用程序,命名为"ShowHtml";

      2、修改程序中的"About"对话框资源,在其中放置一个Static控件,设置它的ID为IDC_HTMLVIEW;

      3、向程序中添加HTML文件资源,其ID设置为"About.htm";

      4、向程序中添加CHtmlCtrl、CMyHtmlCtrl类文件;

      5、在CAbout类中增加一个CMyHtmlCtrl类的对象m_page,并使用CLASSWIZARD重载CAbout类的OnInitDialog()函数;

      6、编译运行程序。

    三、程序代码

    ////////////////////////////////////////////////////CHtmlCtrl类的头文件;
    #include "afxhtml.h"
    class CHtmlCtrl : public CHtmlView {
     public:
      CHtmlCtrl() { }
      ~CHtmlCtrl() { }
      //使CHtmlCtrl控件与静态控件建立关联;
      BOOL CreateFromStatic(UINT nID, CWnd* pParent);
      virtual void PostNcDestroy() { }
      //重载下面两个函数,旁路ChtmlView类的文档视图结构;
      afx_msg void OnDestroy();
      afx_msg int OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT msg);
      // 实现"app"伪协议;
      virtual void OnBeforeNavigate2( LPCTSTR lpszURL,
       DWORD nFlags,
       LPCTSTR lpszTargetFrameName,
       CByteArray& baPostedData,
       LPCTSTR lpszHeaders,
       BOOL* pbCancel );
       virtual void OnAppCmd(LPCTSTR lpszWhere);
       DECLARE_MESSAGE_MAP();
       DECLARE_DYNAMIC(CHtmlCtrl)
    };

    //////////////////////////////////////////////CHtmlCtrl类的实现文件;
    #include "StdAfx.h"
    #include "HtmlCtrl.h"
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endif
    IMPLEMENT_DYNAMIC(CHtmlCtrl, CHtmlView)
    BEGIN_MESSAGE_MAP(CHtmlCtrl, CHtmlView)
    ON_WM_DESTROY()
    ON_WM_MOUSEACTIVATE()
    END_MESSAGE_MAP()
    BOOL CHtmlCtrl::CreateFromStatic(UINT nID, CWnd* pParent)
    {
     CStatic wndStatic;
     if (!wndStatic.SubclassDlgItem(nID, pParent))
      return FALSE;
     // 获取静态控件的尺寸,并销毁该控件的窗口;
     CRect rc;
     wndStatic.GetWindowRect(&rc);
     pParent->ScreenToClient(&rc);
     wndStatic.DestroyWindow();
     // 创建一个HtmlView控件;
     return Create(NULL, // class name
       NULL, // title
      (WS_CHILD | WS_VISIBLE ), // style
      rc, // rectangle
      pParent, // parent
      nID, // control ID
      NULL); // frame/doc context not used
    }

    void CHtmlCtrl::OnDestroy()
    {
     if (m_pBrowserApp) {释放浏缆器的m_pBrowserApp成员变量;
      m_pBrowserApp->Release();
      m_pBrowserApp = NULL;
     }
     CWnd::OnDestroy(); // bypass CView doc/frame stuff
    }

    int CHtmlCtrl::OnMouseActivate(CWnd* pDesktopWnd, UINT nHitTest, UINT msg)
    {
     //旁路文档视图结构;
     return CWnd::OnMouseActivate(pDesktopWnd, nHitTest, msg);
    }

    //////////////////实现"app"伪协议;
    void CHtmlCtrl::OnBeforeNavigate2( LPCTSTR lpszURL,
     DWORD nFlags,
     LPCTSTR lpszTargetFrameName,
     CByteArray& baPostedData,
     LPCTSTR lpszHeaders,
     BOOL* pbCancel )
    {
     const char APP_PROTOCOL[] = "app:";
     int len = _tcslen(APP_PROTOCOL);
     if (_tcsnicmp(lpszURL, APP_PROTOCOL, len)==0) {
      OnAppCmd(lpszURL + len);
      *pbCancel = TRUE;
     }
    }

    void CHtmlCtrl::OnAppCmd(LPCTSTR lpszWhere)
    {
     // default: do nothing
    }

    /////////////////////////////////////////////////////
    class CMyHtmlCtrl : public CHtmlCtrl {
     virtual void OnAppCmd(LPCTSTR lpszWhere);
    };

    /////////////////// 处理HTML文件上的 "app:ok"链接,关闭对话框;
    void CMyHtmlCtrl::OnAppCmd(LPCTSTR lpszWhere)
    {
     if (_tcsicmp(lpszWhere,_T("ok"))==0) {
      GetParent()->SendMessage(WM_COMMAND,IDOK);
     }
    }
    ////////////////////////////////////////
    class CAboutDlg : public CDialog
    {
     public:
      CAboutDlg();
      CMyHtmlCtrl m_page;
      // Dialog Data
      //{{AFX_DATA(CAboutDlg)
       enum { IDD = IDD_ABOUTBOX };
      //}}AFX_DATA
      // ClassWizard generated virtual function overrides
      //{{AFX_VIRTUAL(CAboutDlg)
     protected:
      virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
      //}}AFX_VIRTUAL
      // Implementation
     protected:
      //{{AFX_MSG(CAboutDlg)
       virtual BOOL OnInitDialog();
      //}}AFX_MSG
     DECLARE_MESSAGE_MAP()
    };

    CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
    {
     //{{AFX_DATA_INIT(CAboutDlg)
     //}}AFX_DATA_INIT
    }

    void CAboutDlg::DoDataExchange(CDataExchange* pDX)
    {
     CDialog::DoDataExchange(pDX);
     //{{AFX_DATA_MAP(CAboutDlg)
     //}}AFX_DATA_MAP
    }

    BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
     //{{AFX_MSG_MAP(CAboutDlg)
     //}}AFX_MSG_MAP
    END_MESSAGE_MAP()

    // App command to run the dialog
    void CShowHtmlApp::OnAppAbout()
    {
     CAboutDlg aboutDlg;
     aboutDlg.DoModal();
    }

    BOOL CAboutDlg::OnInitDialog()
    {
     CDialog::OnInitDialog();
     VERIFY(CDialog::OnInitDialog());
     VERIFY(m_page.CreateFromStatic(IDC_HTMLVIEW, this));
     m_page.LoadFromResource(_T("ABOUT.HTM"));
     return TRUE;
    }

      四、小结

      在上述实例中,读者朋友还可以在HTML文件中作其他的链接,诸如:app:cancel, app:refresh, 或 app:whatever等等,并且在OnAppCmd中编写自己的代码来处理 "cancel"、"refresh"、和"whatever"等字符串。参照例子程序,将自己的About对话框改进一番吧。如果有兴趣的话,您甚至可以利用这个技术来实现复活节彩蛋。
  • 相关阅读:
    leetcode 268. Missing Number
    DBSCAN
    python二维数组初始化
    leetcode 661. Image Smoother
    leetcode 599. Minimum Index Sum of Two Lists
    Python中的sort() key含义
    leetcode 447. Number of Boomerangs
    leetcode 697. Degree of an Array
    滴滴快车奖励政策,高峰奖励,翻倍奖励,按成交率,指派单数分级(1月3日)
    北京Uber优步司机奖励政策(1月2日)
  • 原文地址:https://www.cnblogs.com/cy163/p/529652.html
Copyright © 2011-2022 走看看