先把圖片上上來:
官方稱之爲:MFC ActiveX控件:優化
官方是這麼介紹的:
可見時關閉激活和在非活動時提供鼠標交互在激活以前不建立窗口的控件。無窗口激活永遠不會建立窗口的控件,即便它們被激活也是如此。web
Windows對OLE對象有兩個主要缺點:它們在活動時防止對象變爲透明或非矩形,而且它們在實例化和控件顯示中增長了大量開銷。一般,建立窗口須要60%的控件建立時間。使用單個共享窗口(一般是容器)和一些調度代碼,控件接收相同的窗口服務,一般不會下降性能。擁有一個窗口對於對象來講幾乎是沒必要要的開銷。less
在某些容器中使用控件時,某些優化不必定會提升性能。例如,1996年以前發佈的容器不支持無窗口激活,所以實現此功能不會在舊容器中提供好處。可是,幾乎每一個容器都支持持久性,所以優化控件的持久性代碼可能會提升其在任何容器中的性能。若是您的控件專門用於某種特定類型的容器,您可能須要研究該容器支持哪些優化。可是,通常狀況下,您應該嘗試實現適用於您的特定控件的許多這些技術,以確保您的控件在各類容器中都能執行。svg
您能夠經過「 控制設置」頁面上的「 MFC ActiveX控件嚮導」實現許多這些優化。函數
控件有兩種基本狀態:激活和非激活。傳統上,這些狀態的區別在於控件是否有窗口。一個激活的控件有一個窗口; 一個非激活的控件則沒有。隨着無窗激活的引入,這種區別再也不廣泛,但仍適用於許多控件。性能
與一般由ActiveX控件執行的其他初始化相比,窗口的建立是很是消耗資源的操做。理想狀況下,控件會推遲建立窗口,直到絕對必要。字體
許多控件在容器中可見的整個時間內不須要處於激活狀態。一般,控件能夠保持在非激活狀態,直到用戶執行要求其變爲激活狀態的操做(例如,用鼠標單擊或按TAB鍵)。要使控件在容器須要激活以前保持非激活狀態,請從控件的其餘標誌中刪除OLEMISC_ACTIVATEWHENVISIBLE標誌:優化
static const DWORD BASED_CODE _dwNVC_MFC_AxOptOleMisc = OLEMISC_SETCLIENTSITEFIRST | OLEMISC_INSIDEOUT | OLEMISC_CANTLINKINSIDE | OLEMISC_RECOMPOSEONRESIZE;
若是在建立控件時關閉MFC ActiveX控件嚮導的「控件設置」頁面中的「當可見時激活」選項,則會自動省略OLEMISC_ACTIVATEWHENVISIBLE標誌。spa
窗口建立代碼(即,調用時發生的全部事情CreateWindow)執行成本很高。維護屏幕窗口的控件必須管理窗口的消息。所以,無窗口控件比帶窗口的控件更快。指針
無窗控制的另外一個優勢是,與窗口控件不一樣,無窗口控件支持透明繪畫和非矩形屏幕區域。透明控件的常見示例是具備透明背景的文本控件。控件繪製文本而不是背景,所以文本下的任何內容都顯示出來。較新的表單一般使用非矩形控件,例如箭頭和圓形按鈕。code
一般,控件不須要本身的窗口,而是可使用其容器的窗口服務,前提是已編寫容器以支持無窗口對象。無窗口控件向後兼容舊容器。在未編寫爲支持無窗口控件的舊容器中,無窗口控件在活動時建立窗口。
因爲無窗口控件沒有本身的窗口,所以容器(具備窗口)負責提供原本由控件本身的窗口提供的服務。例如,若是您的控件須要查詢鍵盤焦點,捕獲鼠標或獲取設備上下文,則這些操做由容器管理。容器使用該IOleInPlaceObjectWindowless接口將發送到其窗口的用戶輸入消息路由到適當的無窗口控件。(有關此接口的說明,請參閱ActiveX SDK。)COleControl成員函數從容器中調用這些服務。
要使您的控件使用無窗口激活,請在COleControl :: GetControlFlags返回的標誌集中包含windowlessActivate標誌。例如:
DWORD CMyAxOptCtrl::GetControlFlags() { DWORD dwFlags = COleControl::GetControlFlags(); // The control can activate without creating a window. dwFlags |= windowlessActivate; return dwFlags; }
若是在MFC ActiveX控件嚮導的「 控制設置」頁面上選擇「 無窗口激活」選項,則會自動生成包含此標誌的代碼。
啓用無窗口激活後,容器會將輸入消息委託給控件的IOleInPlaceObjectWindowless界面。COleControl在適當調整鼠標座標後,此接口的實現經過控件的消息映射調度消息。您能夠經過將相應的條目添加到消息映射來處理消息,如普通窗口消息。在這些消息的處理程序中,避免使用m_hWnd成員變量(或使用它的任何成員函數),而不首先檢查其值是否爲NULL。
若是您徹底肯定您的控件不會在其客戶端矩形以外繪製,則能夠經過禁用對COleControl進行的IntersectClipRect調用來實現小但可檢測的速度增益。 爲此,請從COleControl :: GetControlFlags返回的標誌集中刪除clipPaintDC標誌。 例如:
DWORD CMyAxOptCtrl::GetControlFlags() { DWORD dwFlags = COleControl::GetControlFlags(); dwFlags &= ~clipPaintDC; return dwFlags; }
若是在使用MFC ActiveX控件嚮導建立控件時在「 控制設置」頁面上選擇「未剪輯的設備上下文」選項,則會自動生成刪除此標誌的代碼。
若是您使用無窗口激活,則此優化不起做用。
若是您的控件在非活動狀態和活動狀態下繪製相同(而且不使用無窗口激活),則能夠消除在非活動狀態和活動狀態之間進行轉換時一般發生的繪製操做和伴隨的視覺閃爍。爲此,請在COleControl :: GetControlFlags返回的標誌集中包含noFlickerActivate標誌。例如:
DWORD CMyAxOptCtrl::GetControlFlags() { DWORD dwFlags = COleControl::GetControlFlags(); dwFlags |= noFlickerActivate; return dwFlags; }
若是在使用MFC ActiveX控件嚮導建立控件時在「 控制設置」頁面上選擇「無閃爍」激活選項,則會自動生成包含此標誌的代碼。
若是您使用無窗口激活,則此優化不起做用。
若是未當即激活控件,您可能仍但願它處理WM_SETCURSOR和WM_MOUSEMOVE消息,即便控件沒有本身的窗口。這能夠經過啓用接口COleControl的實現來IPointerInactive實現,默認狀況下禁用該接口。(有關此接口的說明,請參閱ActiveX SDK。)要啓用它,請在COleControl :: GetControlFlags返回的標誌集中包含pointerInactive標誌:
DWORD CMyAxOptCtrl::GetControlFlags() { DWORD dwFlags = COleControl::GetControlFlags(); // The control can receive mouse notifications when inactive. dwFlags |= pointerInactive; return dwFlags; }
若是在使用MFC ActiveX控件嚮導建立控件時在「 控制設置」頁面上選擇「 非活動時鼠標指針通知」選項,則會自動生成包含此標誌的代碼。
IPointerInactive啓用該接口後,容器會向其委派WM_SETCURSOR和WM_MOUSEMOVE消息。在適當調整鼠標座標後,經過控件的消息映射調度消息COleControl的實現IPointerInactive。您能夠經過將相應的條目添加到消息映射中來處理消息,就像普通窗口消息同樣。在這些消息的處理程序中,避免使用m_hWnd成員變量(或使用它的任何成員函數),而不首先檢查其值是否爲NULL。
您可能還但願非活動控件成爲OLE拖放操做的目標。這須要在用戶在其上拖動對象時激活控件,以便控件的窗口能夠註冊爲放置目標。要在拖動期間激活,請覆蓋COleControl :: GetActivationPolicy,並返回POINTERINACTIVE_ACTIVATEONDRAG標誌:
DWORD CMyAxOptCtrl::GetActivationPolicy() { return POINTERINACTIVE_ACTIVATEONDRAG; }
啓用IPointerInactive界面一般意味着您但願控件始終可以處理鼠標消息。要在不支持IPointerInactive接口的容器中獲取此行爲,您須要在可見時始終激活控件,這意味着控件應在其雜項標誌中包含OLEMISC_ACTIVATEWHENVISIBLE標誌。可是,要防止此標誌在支持的容器中生效IPointerInactive,您還能夠指定OLEMISC_IGNOREACTIVATEWHENVISIBLE標誌:
static const DWORD BASED_CODE _dwMyOleMisc = OLEMISC_ACTIVATEWHENVISIBLE | OLEMISC_IGNOREACTIVATEWHENVISIBLE | OLEMISC_SETCLIENTSITEFIRST | OLEMISC_INSIDEOUT | OLEMISC_CANTLINKINSIDE | OLEMISC_RECOMPOSEONRESIZE;
當指示控件將自身繪製到容器提供的設備上下文中時,它一般會將GDI對象(如筆,畫筆和字體)選擇到設備上下文中,執行其繪製操做,並恢復之前的GDI對象。若是容器具備要在同一設備上下文中繪製的多個控件,而且每一個控件選擇它所需的GDI對象,則若是控件不單獨還原先前選定的對象,則能夠節省時間。繪製完全部控件後,容器能夠自動恢復原始對象。
要檢測容器是否支持此技術,控件能夠調用COleControl :: IsOptimizedDraw成員函數。若是此函數返回TRUE,則控件能夠跳過恢復先前選定對象的正常步驟。
考慮具備如下(未優化)OnDraw功能的控件:
void OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& /*rcInvalid*/) { CPen pen(PS_SOLID, 0, TranslateColor(GetForeColor())); CBrush brush(TranslateColor(GetBackColor())); CPen* pPenSave = pdc->SelectObject(&pen); CBrush* pBrushSave = pdc->SelectObject(&brush); pdc->Rectangle(rcBounds); pdc->SelectObject(pPenSave); pdc->SelectObject(pBrushSave); }
此示例中的筆和畫筆是局部變量,這意味着當它們超出範圍時(OnDraw函數結束時)將調用它們的析構函數。析構函數將嘗試刪除相應的GDI對象。可是若是您計劃在返回時將它們選擇到設備上下文中,則不該刪除它們OnDraw。
要防止CPen和CBrush對象在OnDraw完成時被銷燬,請將它們存儲在成員變量而不是局部變量中。在控件的類聲明中,添加兩個新成員變量的聲明:
class CMyAxOptCtrl : public COleControl { CPen m_pen; CBrush m_brush; };
而後,該OnDraw函數能夠重寫以下:
void OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& /*rcInvalid*/) { CPen pen(PS_SOLID, 0, TranslateColor(GetForeColor())); CBrush brush(TranslateColor(GetBackColor())); CPen* pPenSave = pdc->SelectObject(&pen); CBrush* pBrushSave = pdc->SelectObject(&brush); pdc->Rectangle(rcBounds); pdc->SelectObject(pPenSave); pdc->SelectObject(pBrushSave); }
這種方法避免了每次OnDraw調用時都會產生筆和畫筆。速度的提升是以維護額外的實例數據爲代價的。
若是ForeColor或BackColor屬性更改,則須要再次建立筆或畫筆。爲此,請覆蓋OnForeColorChanged和OnBackColorChanged成員函數:
void CMyAxOptCtrl::OnForeColorChanged() { m_pen.DeleteObject(); } void CMyAxOptCtrl::OnBackColorChanged() { m_brush.DeleteObject(); }
最後,爲了消除沒必要要的SelectObject調用,修改OnDraw以下:
void CMyAxOptCtrl::OnDraw(CDC* pdc, const CRect& rcBounds, const CRect& /*rcInvalid*/) { if (m_pen.m_hObject == NULL) m_pen.CreatePen(PS_SOLID, 0, TranslateColor(GetForeColor())); if (m_brush.m_hObject == NULL) m_brush.CreateSolidBrush(TranslateColor(GetBackColor())); CPen* pPenSave = pdc->SelectObject(&m_pen); CBrush* pBrushSave = pdc->SelectObject(&m_brush); pdc->Rectangle(rcBounds); if (! IsOptimizedDraw()) { pdc->SelectObject(pPenSave); pdc->SelectObject(pBrushSave); } }
下一篇開始介紹繪製ActiveX控件。