vs2017開發ActiveX(主講OCX)(三)、MFC ActiveX控件向導中的控件設置


前言

先把圖片上上來:
控件設置
官方稱之為:MFC ActiveX控件:優化
官方是這么介紹的:
可見時關閉激活和在非活動時提供鼠標交互在激活之前不創建窗口的控件。無窗口激活永遠不會創建窗口的控件,即使它們被激活也是如此。

Windows對OLE對象有兩個主要缺點:它們在活動時防止對象變為透明或非矩形,並且它們在實例化和控件顯示中增加了大量開銷。通常,創建窗口需要60%的控件創建時間。使用單個共享窗口(通常是容器)和一些調度代碼,控件接收相同的窗口服務,通常不會降低性能。擁有一個窗口對於對象來說幾乎是不必要的開銷。

在某些容器中使用控件時,某些優化不一定會提高性能。例如,1996年之前發布的容器不支持無窗口激活,因此實現此功能不會在舊容器中提供好處。但是,幾乎每個容器都支持持久性,因此優化控件的持久性代碼可能會提高其在任何容器中的性能。如果您的控件專門用於某種特定類型的容器,您可能需要研究該容器支持哪些優化。但是,一般情況下,您應該嘗試實現適用於您的特定控件的許多這些技術,以確保您的控件在各種容器中都能執行。

MFC ActiveX控件向導

您可以通過“ 控制設置”頁面上的“ 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標志。

無窗口激活

窗口創建代碼(即,調用時發生的所有事情CreateWindow)執行成本很高。維護屏幕窗口的控件必須管理窗口的消息。因此,無窗口控件比帶窗口的控件更快。

無窗控制的另一個優點是,與窗口控件不同,無窗口控件支持透明繪畫和非矩形屏幕區域。透明控件的常見示例是具有透明背景的文本控件。控件繪制文本而不是背景,因此文本下的任何內容都顯示出來。較新的表單通常使用非矩形控件,例如箭頭和圓形按鈕。

通常,控件不需要自己的窗口,而是可以使用其容器的窗口服務,前提是已編寫容器以支持無窗口對象。無窗口控件向后兼容舊容器。在未編寫為支持無窗口控件的舊容器中,無窗口控件在活動時創建窗口。

由於無窗口控件沒有自己的窗口,因此容器(具有窗口)負責提供本來由控件自己的窗口提供的服務。例如,如果您的控件需要查詢鍵盤焦點,捕獲鼠標或獲取設備上下文,則這些操作由容器管理。容器使用該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控件。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM