孫鑫VC學習筆記:ActiveX 控件

孫鑫VC學習筆記:ActiveX 控件

基本概念:

容器和服務器程序

      容器應用程序時可以嵌入或鏈接對象的應用程序。Word 就是容器應用程序。服務器應用程序是創建對象並且當對象被雙擊時,可以被啓動的應用程序。Excel 就是服務器應用程序。ActiveX 控件不能獨立運行,它必須被嵌入容器應用程序中,和容器應用程序一起運行。
 
---------------------------------------------------------------------------------
編寫一個 ActiveX 時鐘控件

      1.利用 MFC ActiveX ContrlWizard 新建一個 Clock 工程

      2.在新建的工程中有三個類,其中 CClockApp 從 COleControlModule 中派生出來的,

可以將其看作是應用程序類,它的一個實例表示控件程序本身。
 
      COleControl 從 CWnd 派生得到,也是一個窗口類,
      CClockCtrl 相當於單文檔程序的主窗口類。
      這個類包含重繪用的 OnDraw 函數,也包含了一些消息映射,包括調度映射。
      Dispatch maps 調度映射,主要是 MFC 提供讓外部應用程序可以訪問控件的屬性和方法。
      Event maps 事件映射,控件向包含它的容器發送事件通知。
 
CClockPropPage 類由 COlePropertyPage 派生而來:
 
      COlePropertyPage
 
      The COlePropertyPage class is used to display the properties of a custom control in a graphical interface, similar to a dialog box. 被用來顯示一個自定義控件的屬性,類似於一個對話框。它是對話框類,於是 enum { IDD = IDD_PROPPAGE_CLOCK }給它關聯了一個對話框資源。
 
      在工程中,還有兩個全局函數:STDAPI DllRegisterServer(void);將控件信息寫入註冊表中 STDAPI DllUnregisterServer(void)卸載註冊信息。

      3.在三個類之上,還有類似小勺的圖標 Dclock,_DClockEvents,它們表示接口。

      接口是外部程序和控件進行通信的協議,可以把接口看作是函數的集合,外部程序通過接口提供的方法,去訪問控件的屬性和方法。也可以將接口看作抽象基類,接口中所定義的所有函數都是純虛函數, 這些函數的實現都是在 CClockCtrl 類中實現,MFC 通過底層的封裝使 CClockCtrl 類繼承 Dclock 接口。所以調用接口,事實上調用的是 CClockCtrl 類中的函數。由於封裝,底層的通信細節我們看不到,如果對這個感興趣,可以看一些 COM 編程的資料。
 
      4.如果些時編譯一下工程,會生成一個 Clock.ocx 文件,它就是 ActiveX 控件的文件。

       要用控件的時候,只需要把這個文件傳遞給對方。注意:ActiveX 控件不能單獨使用,必須嵌入到一個窗口當中一直運行。當我們用 VB 添加控件時,發現了我們剛纔編譯的控件,但是 VB 怎麼知道我們新建的控件的位置呢? 我們發現編譯時,輸出窗口的最後一行 “Registering ActiveX Control...”說明編譯時會註冊控件。事實上編譯之後,VC 會調用 regsvr32 註冊控件,並將信息寫入到註冊表中,VB 在加載 ActiveX 控件時,會從註冊表中搜尋相關的 ActiveX 控件信息。注意:ActiveX 控件在使用之前都需要註冊。

      如果想卸載控件,可以在“運行”中輸入命令:
      “regsvr32 /u 控件文件完整路徑名”,事實上是調用工程中全局函數 DllUnregisterServer 來完成卸載的。
      如果想再次註冊控件,可以在“運行”中輸入下面命令:
      “regsvr32 控件文件完整路徑名”事實上是調用工程中全局函數 DllRegisterServer 來完成註冊的。
 
      5.下面實現在控件上輸出當時系統時間。

      可以在 OnDraw 函數中完成這個功能。
 
      這樣就能做出一個靜態的時間控件,如果我們想使控件實時顯示時間,需要添加兩個消息響應函數  WM_CREATE,WM_TIMER.要使時間可以每秒更新,我們先在 CClockCtrl 類中添加 WM_CREATE 消息處理,在其響應函數 OnCreate()中設置一個計時器:SetTimer(1,1000,NULL);接着增加一個 WM_TIMER 消息響應處理,在 OnTimer 中寫上 Invalidate(); 使窗口發生重繪。也可以調用 InvalidateControl()強制控件重繪自身。
 
      6.在 VB 測試中發現,其他很多控件可以修改控件的背景色、前景色和字體顏色,而我們還不行。

      ActiveX 控件有四種屬性:
      Stock:爲每個控件提供的標準屬性,如字體或顏色。
      Ambient:圍繞控件的環境屬性——已被置入容器的屬性。
      這些屬性不能被修改,但控件可以使用它們調整自己的屬性。
      Extended:這些是由容器處理的屬性,一般包括大小和在屏幕上的位置。
      Custom:由控件開發者添加的屬性。
 
      7.這時在 VB 測試中,我們的控件也可以修改控件的背景色和前景色了,但是設置完以後沒有效果,因爲還要 OnDraw 函數中自己編寫代碼來完成這些改變。要得到控件標準屬性,要通過一些函數來完成,如COleControl::GetForeColor 得到前景色,COleControl::GetBackColor 得到背景色,不過要注意的是它們得到的是 OLE_COLOR 的顏色,還需要通過 TranslateColor 方法轉換成 COLORREF。
 
      8.控件一般都會有屬性頁,當我們右鍵點擊控件時會彈出這個屬性頁方便對控件屬性的設置,我們的控件已經有一個屬性頁,通過 CClockPropPage 類來實現的,顯示的面容是對話框資源的內容,下面修改控件屬性頁:
      屬性頁之所以可以在窗口中看得到,是因爲在 ClockCtl.cpp 中的 BEGIN_PROPPAGEIDS(CClockCtrl, 1)與 END_PROPPAGEIDS(CClockCtrl)之間調用了 PROPPAGEID(CClockPropPage::guid),其中的 guid 表示全局唯一標識符,它是一個128位的整數,用來唯一地標識一個組件或者一個接口。我們可以用 PROPPAGEID 宏增加屬性頁。
 
      增加顏色屬性頁
      首先在 BEGIN_PROPPAGEIDS(CClockCtrl, 1)與END_PROPPAGEIDS(CClockCtrl)之間添加代碼 PROPPAGEID(CLSID_CColorPropPage)來添加屬性頁。
 
      CLSID_CColorPropPage 屬性表單是控件自身提供的, 我們添加之後不用作任何處理,就可以使用,效果如下:
 
      注意:
      BEGIN_PROPPAGEIDS(CClockCtrl, 2)中的數字2表示有多少個屬性頁代碼要顯示,
      如果增加了屬性頁數字一定要加1,不然如果沒有修改或修改錯誤,會產生不可預料錯誤。
 
      9.增加一個自定義屬性也是通過 MFC ClassWizard 來完成,與第6步增加前景色與背景色的步驟相同。

      1)下面增加設置時間間隔的屬性,用這個屬性來控件時間刷新頻率:
 
      屬性添加成功以後,在_DClock 接口中增加了 Interval 屬性,同時在 CClockCtrl 類中增加了一個
 成員變量 m_interval 和 OnIntervalChanged()方法(當外部屬性修改之後就會調用這個方法)。
 而且它們都在調度映射當中:
 
      2)在 CClockCtrl::OnIntervalChanged()中添加屬性處理程序代碼
 
      10.使增加的自定義屬性在屬性表單中設置

      在對話框資源中添加一個編輯框,再爲這個編輯框關聯一個變量, 注意,我們在爲編輯框關聯一個變量 m_updateInterval 的同時也關聯了一個屬性是,這樣我們不需要增加代碼就能把控件和自定義屬性相關聯。在 void CClockPropPage::DoDataExchange(CDataExchange* pDX)中會生成下面代碼:
 
      這樣,我們就可以在屬性頁裏面設置時間間隔了。
 
      11.爲控件添加一個方法:

      爲控件增加函數,MFC ClassWizard-->Automation-->Add Method Class Name 要選擇CClockCtrl 輸入函數名,之後就可以在 CClockCtrl 類中找到了。方法添加成功以後,在_DClock 接口中增加了 Hello 方法,同時在 CClockCtrl 類中增加了 Hello 方法。接下來,我們可以在 CClockCtrl 類中增加了 Hello 方法添加自己的代碼就可以了。

      12.爲控件添加一個標準事件

      我們選擇 MFC ClassWizard-->ActiveX Events--->Add Event。事件添加成功以後,會在_DClockEvents 中增加一個事件 Click,DClockEvents 接口是源接口,控件將用這個接口發送通知事件,它不是控件本身實現的接口,這個接口是通過容器來實現的
 
      13.增加一個自定義事件:當秒數爲0時,發出一個 NewMinute 事件。

      1)增加一個自定義事件的過程與增加一個標準事件的步驟相同,也可以這樣添加在事件接口_DClockEvents 上點擊右鍵,選擇增加事件,效果一樣,都會彈出 Add Event 對話框。事件添加成功以後,會在_DClockEvents 中增加一個事件NewMinute事件,同時在在 CClockCtrl 類中增加了void FireNewMinute(),也就是在控件內部可以調用 FireNewMinute()向容器發送事件通知,而這個函數內部會調用接口的 void NewMinute()向容器發出事件通知。
 
      2)在 OnDraw()方法添加代碼 if(0 == time.GetSecond())FireNewMinute();使當秒數爲0時,向容器發出一個 NewMinute 事件通知。標準消息不需要另外寫代碼向容器發出事件通知,VC在底層代碼內部實現了這個過程。

      14.將我們自定義的控件屬性在修改之後永久保存下來,用戶打開程序之後,控件的屬性都是使用原先保存的值。需要在 void CClockCtrl::DoPropExchange(CPropExchange* pPX)加入如下代碼 PX_Short(pPX,"Interval",m_interval,1000);之後再在程序中 OnCreate()方法中將 SetTimer(1,1000,NULL); 修改代碼爲 SetTimer(1,m_interval,NULL);
 
      15.在屬性頁中修改自定義控件屬性的時候,通知容器做相應的調整,從而使容器屬性列表中實時地顯示我們對屬性所作的修改。在 void CClockCtrl::OnIntervalChanged() 中加入如下代碼:BoundPropertyChanged(0x1);   //通知容器調度 ID 爲1的屬性發生了改變
 
      16.前面都是讓控件在容器設計時改變控件屬性,如果希望用戶在設計模式時時鐘控件停止運行,而在用戶模式下時間會跳動,在控件內部可以通過 AmbientUserMode()方法得到當前控件是處於設計狀態還是運行狀態。在 void CClockCtrl::OnTimer(UINT nIDEvent)下修改代碼如下:
      if(AmbientUserMode())  InvalidateControl();

      17.編寫完控件以後,我們可以選擇 Win32 Release 方式進行編譯,生成發行版 ActiveX 控件。在開發的時候通常是以 Win32 Debug 模式下編譯的,這種模式下開發有助於我們開發過程的產生的錯誤,例如非法內存訪問錯誤;還可以幫助我們調試程序,跟蹤程序進而排查錯誤。但是在調試模式下生成的文件比較大,因爲在文件中包含了調試的信息。

      而當我們開發完成後,在 Release 模式下進行編譯時,VC 編譯器會在代碼生成上、執行速度上做一些優化,同時生成的可執行程序或控件文件會比較小。
-------------------------------------------------------------------

在 VC 中編寫一個客戶端調用 ActiveX 控件:

1.新建一個基於 MFC 對話框的 ClockTest 工程項目

2.點擊右鍵,選擇“插入 ActiveX 控件”,然後在彈出的對話框中選擇剛纔我們創建的控件。

也可以通過菜單的方式增加控件:
 “工程”->增加到工程->Componets and Controls
 選擇已註冊控件"Registered ActiveX Contrlos",找到我們自己控件,再按下插入。
 
通過這種方式插入 ActiveX 控件時,會在工程中自動生成一個類 CCock,其基類爲 CWnd。這個類是一個封裝類,封裝了對 ActiveX 控件進行訪問的一些操作。同時在 VC 的工具箱上面也增加一個時鐘控件,可以直接將一個時鐘控件拖放到窗體上。

3.用第二種方法插入的控件,除了將控件手動插入到窗體以外,我們也可以通過代碼動態生成一個控件。

1)CClockTestDlg 增加一個成員變量:CClock m_clock;
2)在 CClockTestDlg 的頭文件中包含一個 clock.h
3)接下來就要以在一個按鈕的單擊事件中增加創建控件的代碼:
4)在設計時,可以點擊右鍵爲控件添加事件響應。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章