使用MFC開發ActiveX控制項

來源:互聯網
上載者:User
使用MFC開發ActiveX控制項 轉截:http://www.cnblogs.com/jyz/archive/2008/04/11/1148476.html   摘要: 本文對COM組件中的ActiveX控制項的MFC開發方法進行了介紹,講述了使用者自訂和庫存屬性、方法以及事件的添加方法和屬性頁面的製作過程。使讀者能夠掌握基本的MFC ActiveX開發方法。

  前言

  ActiveX控制項是一種實現了一系列特定介面而使其在使用和外觀上更象一個控制項的COM組件。ActiveX控制項這種技術涉及到了幾乎所有的COM和OLE的技術精華,如可連結化物件、統一資料轉送、OLE文檔、屬性頁面、永久儲存以及OLE自動化等。

  ActiveX控制項作為基本的介面單元,必須擁有自己的屬性和方法以適合不同特點的程式和向包容器程式提供功能服務,其屬性和方法均由自動化服務的IDispatch介面來支援。除了屬性和方法外,ActiveX控制項還具有區別於自動化服務的一種特性--事件。事件指的是從控制項發送給其包容程式的一種通知。與視窗控制項通過發送訊息通知其擁有者類似,ActiveX控制項是通過觸發事件來通知其包容器的。事件的觸發通常是通過控制項包容器提供的IDispatch介面來調用Automation 物件的方法來實現的。在設計ActiveX控制項時就應當考慮控制項可能會發生哪些事件以及包容器程式將會對其中的哪些事件感興趣並將這些事件包含進來。與自動化服務不同,ActiveX控制項的方法、屬性和事件均有自訂(custom)和庫存(stock)兩種不同的類型。自訂的方法和屬性也就是是普通的自動化方法和屬性,自訂事件則是自己選取名字和Dispatch ID的事件。而所謂的庫存方法、屬性和事件則是使用了ActiveX控制項規定了名字和Dispatch ID的"標準"方法、屬性和事件。

  ActiveX控制項可以使COM組件從外觀和使用上能與普通的視窗控制項一樣,而且還提供了類似於設定Windows標準控制項屬性的屬性頁面,使其能夠在包容器程式的設計階段對ActiveX控制項的屬性進行可視化設定。ActiveX控制項提供的這些功能使得對其的使用將是非常方便的。本文下面即以MFC為工具對ActiveX控制項的開發進行介紹。

建立工程架構

  通過"MFC ActiveX ControlWizard"嚮導可以非常容易的建立一個MFC ActiveX控制項工程架構。按照預設的選項將建立1所示的工程結構:


圖1 使用預設選項建立的ActiveX控制項工程結構

  其中,_DSample68和_DSample68Events這兩個介面將為客戶程式提供本控制項的屬性、方法以及可能響應的事件。全域函數DllRegisterServer()和DllUnregisterServer()分別用於控制項在註冊表的註冊和登出,一般不需要對其進行改動。

  應用程式類從COleControlModule繼承。而COleControlModule有是從CWinApp派生,提供了初始化控制項模組的功能。CSample68PropPage的基類是COlePropertyPage,CDialog類的衍生類別,主要負責對屬性頁面中對圖形介面下使用者控制項屬性的顯示。控制項類CSample68Ctrl類是這幾個類中比較重要的一個類,大部分實質性工作都在該類完成,其基類為COleControl,從CWnd和CCmdTarget繼承,因此能夠為控制項對象提供與MFC視窗對象相同的功能同時也提供了一系列事件觸發函數和一個分發映射表,使ActiveX控制項能夠同包容器程式有效地進行互動。該類的衍生類別將可以在滿足特定的條件時向控制項的包容器發送訊息或是觸發事件,以通知包容器程式在控制項內有一些重要的事件發生。分發映射表是其中很重要的一個部分,負責向包容器程式暴露控制項提供的方法和屬性。圖2展示了COleControl類在控制項與包容器通訊中所起的作用。可以看出,ActiveX控制項與其包容器之間的所有通訊過程都是由COleControl來完成的:


圖2 COleControl在ActiveX控制項與包容器通訊中的作用

  控制項類對基類COleControl的OnDraw()函數進行了重載,嚮導產生了如下預設代碼,其作用是在控制項的客戶區繪製一個橢圓。在編程過程中通常要對其進行替換:

void CSample68Ctrl::OnDraw(
CDC* pdc, const CRect& rcBounds, const CRect& rcInvalid)
{
 // TODO: Replace the following code with your own drawing code.
 pdc->FillRect(rcBounds, CBrush::FromHandle((HBRUSH)GetStockObject(WHITE_BRUSH)));
 pdc->Ellipse(rcBounds);
}


圖3 插入ActiveX控制項


圖4 插入的待測試控制項

  對嚮導產生的程式碼進行編譯後,將產生副檔名為ocx的ActiveX控制項。ActiveX控制項並不能獨立運行,只能在包容器程式中才能夠運行。通常,為了調試方便而多使用VC++附帶的ActiveX Control Test Container工具以在測試階段對ActiveX控制項進行調試。在測試載入器的客戶區點擊滑鼠右鍵,並選中快顯功能表的"Insert New Control…"功能表項目,將彈出圖3所示的對話方塊,左側的列表框中列出了當前系統中所有註冊的ActiveX控制項,選中要測試的控制項並將其插入到測試程式即可通過"Control"菜單下的各功能表項目對控制項的方法、屬性以及事件等進行測試。在位於下方的分割視圖中將跟蹤顯示出調試記錄(參見圖4)。 屬性、方法以及事件的添加


圖5 屬性的添加


圖6 方法的添加

  對ActiveX控制項屬性、方法和事件的添加均有庫存和自訂兩種。其中對屬性和方法的添加在MFC ClassWizard對話方塊的Automation頁中通過按鈕"Add Property…"和"Add Method…"彈出5和圖6所示的添加屬性和添加方法的對話方塊來完成。對於庫存屬性和方法,可以直接從External name組合框的下拉式清單中選取,Implementation項將自動化佈建為Stock。對於自訂屬性和方法的添加與在Automation 物件中為介面添加屬性和方法的過程一樣,ClassWizard將在.odl檔案和控制項類產生相應的代碼,下面給出的是在控制項類中實現的部分分發映射代碼:

……
// Dispatch maps
//{{AFX_DISPATCH(CSample68Ctrl)
CString m_message;
afx_msg void OnMessageChanged();
afx_msg short GetXPos();
afx_msg void SetXPos(short nNewValue);
afx_msg short GetYPos();
afx_msg void SetYPos(short nNewValue);
afx_msg short MessageLen();
//}}AFX_DISPATCH
DECLARE_DISPATCH_MAP()
// Dispatch and event IDs
public:
enum {
//{{AFX_DISP_ID(CSample68Ctrl)
dispidMessage = 1L,
dispidXPos = 2L,
dispidYPos = 3L,
dispidMessageLen = 4L,
//}}AFX_DISP_ID
};
……
BEGIN_DISPATCH_MAP(CSample68Ctrl, COleControl)
//{{AFX_DISPATCH_MAP(CSample68Ctrl)
DISP_PROPERTY_NOTIFY(CSample68Ctrl, "Message", m_message, OnMessageChanged, VT_BSTR)
DISP_PROPERTY_EX(CSample68Ctrl, "XPos", GetXPos, SetXPos, VT_I2)
DISP_PROPERTY_EX(CSample68Ctrl, "YPos", GetYPos, SetYPos, VT_I2)
DISP_FUNCTION(CSample68Ctrl, "MessageLen", MessageLen, VT_I2, VTS_NONE)
DISP_STOCKPROP_BACKCOLOR()
DISP_STOCKPROP_CAPTION()
DISP_STOCKPROP_FORECOLOR()
//}}AFX_DISPATCH_MAP
END_DISPATCH_MAP()
……

  在這裡共添加了一個自訂方法MessageLen()和三種庫存屬性BackColor、Caption和ForeColor(分別表示控制項的背景色、標題和前台色)、兩個以Get/Set方式擷取的自訂屬性XPos、YPos和一個以成員變數方式實現的自訂屬性Message。這幾個自訂屬性分別表示要顯示字串的x、y座標和要顯示的內容。對於採取Get/Set方式擷取的屬性,應當在控制項類中為其添加相應的成員函數,並修改其Get、Set成員函數的實現過程:

short m_nYPos;
short m_nXPos;
……
short CSample68Ctrl::GetXPos()
{
 return m_nXPos;
}
void CSample68Ctrl::SetXPos(short nNewValue)
{
 m_nXPos = nNewValue;
 SetModifiedFlag();
}
short CSample68Ctrl::GetYPos()
{
 return m_nYPos;
}
void CSample68Ctrl::SetYPos(short nNewValue)
{
 m_nYPos = nNewValue;
 SetModifiedFlag();
}

  對於以成員變數方式建立的屬性Message,嚮導還為其產生了一個訊息響應函數:

void CSample68Ctrl::OnMessageChanged()
{
SetModifiedFlag();
}

  只要該屬性的值被更改,OnMessageChanged()函數即會被調用。

  為了使上述屬性設定如背景色、前景色彩等能夠與控制項實際聯絡起來,需要替換控制項類OnDraw()函數中由嚮導產生的那部分代碼。例如,下面這段代碼即以前面添加的屬性設定作為參數值,在控制項中顯示一串字元:

// 用背景色設定畫刷
CBrush Brush(TranslateColor(GetBackColor()));
// 用前台色設定字型顏色
pdc->SetTextColor(TranslateColor(GetForeColor()));
// 繪製背景
pdc->FillRect(rcBounds, &Brush);
// 設定字型背景透明
pdc->SetBkMode(TRANSPARENT);
// 顯示字元
pdc->TextOut(m_nXPos, m_nYPos, m_message);

  為了使屬性設定更改後,其效果能夠立即在控制項上顯示出來,應當在與屬性設定相關的函數實現中調用InvalidateControl()以更新控制項的顯示。

  可以編譯器並在ActiveX Control Test Container工具中對其進行測試。在插入控制項後,通過"Invoke Methods…"功能表項目彈出7所示的對話方塊。在Method Name組合框中可以選擇要測試的屬性和方法。其中,對於屬性的測試分別有ProgGet和ProgSet的說明以指出是對屬性值的擷取與設定。在Parameter編輯框中輸入要設定的參數及其對應的參數類型,點擊SetValue按鈕將把該參數值添加到參數列表框,最後點擊Invoke按鈕將在控制項應用設定的屬性並執行指定的方法。對於有傳回值的方法,其執行結果將在Return編輯框中顯示。如果出現了異常操作,在Exception編輯框中將會顯示出相應的異常錯誤資訊。圖8給出了經過屬性設定的控制項介面。


圖7 對屬性、方法的測試


圖8 設定了屬性後的控制項

  對於控制項事件的添加,在MFC ClassWizard對話方塊的ActiveX Events頁中通過"Add Event…"按鈕彈出9所示的"Add Event"事件添加對話方塊。與方法、屬性的添加類似,在External name組合框中可以輸入要添加的自訂事件名稱,也可以從下拉式清單選擇庫存事件。Implementation項將根據所要添加的事件類型而自動化佈建Stock或Custom選項。ActiveX控制項將通過添加的事件來通知容器程式有特定的事件發生,庫存事件多為鍵盤、滑鼠事件,將由COleControl自動進行處理。對於自訂事件,則只是在.odl檔案和控制項類中添加了事件映射表等必要的代碼(代碼附下),至於應當在何種條件下觸發該事件須由開發人員自行編寫代碼。


圖9 事件的添加

dispinterface _DSample68Events
{
 properties:
  // Event interface has no properties
 methods:
  // NOTE - ClassWizard will maintain event information here.
  // Use extreme caution when editing this section.
  //{{AFX_ODL_EVENT(CSample68Ctrl)
  [id(1)] void MsgOut();
  //}}AFX_ODL_EVENT
};
……
// Event maps
//{{AFX_EVENT(CSample68Ctrl)
void FireMsgOut()
{FireEvent(eventidMsgOut,EVENT_PARAM(VTS_NONE));}
//}}AFX_EVENT
DECLARE_EVENT_MAP()
// Dispatch and event IDs
public:
enum {
 //{{AFX_DISP_ID(CSample68Ctrl)
 ……
 eventidMsgOut = 1L,
 //}}AFX_DISP_ID
};
……
BEGIN_EVENT_MAP(CSample68Ctrl, COleControl)
//{{AFX_EVENT_MAP(CSample68Ctrl)
EVENT_CUSTOM("MsgOut", FireMsgOut, VTS_NONE)
//}}AFX_EVENT_MAP
END_EVENT_MAP()

  上述代碼添加了一個MsgOut的自訂事件,可以在通過調用FireMsgOut()來激發。下面對Message屬性的OnMessageChanged()訊息響應函數進行修改,每當Message屬性內容被更改都會調用該函數,在該函數中調用此前添加的MessageLen()方法以確定更改後的Message屬性的字串長度,在長度大於10時調用FireMsgOut()觸發MsgOut事件:

void CSample68Ctrl::OnMessageChanged()
{
 InvalidateControl();
 if (MessageLen() >= 10)
  FireMsgOut();
  SetModifiedFlag();
}


圖10 選擇要記錄的事件

  在用ActiveX Control Test Container對剛添加的事件進行測試時,首先通過"Control"菜單下的"Logging…"功能表項目彈出10所示的對話方塊,並從"Events"屬性頁面中選中要追蹤記錄的事件。當通過Invoke Methods對話方塊設定Message屬性的內容超過10個字元後,位於程式架構下方的分割視圖將記錄控制項所觸發的MsgOut事件(11所示)。


圖11 對事件的測試

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.