vs2017开发ActiveX(主讲OCX)(三)、MFC ActiveX控件向导中的控件设置

前言

先把图片上上来:
控件设置
官方称之为:MFC ActiveX控件:优化
官方是这么介绍的:
可见时关闭激活和在非活动时提供鼠标交互在激活以前不建立窗口的控件。无窗口激活永远不会建立窗口的控件,即便它们被激活也是如此。web

Windows对OLE对象有两个主要缺点:它们在活动时防止对象变为透明或非矩形,而且它们在实例化和控件显示中增长了大量开销。一般,建立窗口须要60%的控件建立时间。使用单个共享窗口(一般是容器)和一些调度代码,控件接收相同的窗口服务,一般不会下降性能。拥有一个窗口对于对象来讲几乎是没必要要的开销。less

在某些容器中使用控件时,某些优化不必定会提升性能。例如,1996年以前发布的容器不支持无窗口激活,所以实现此功能不会在旧容器中提供好处。可是,几乎每一个容器都支持持久性,所以优化控件的持久性代码可能会提升其在任何容器中的性能。若是您的控件专门用于某种特定类型的容器,您可能须要研究该容器支持哪些优化。可是,通常状况下,您应该尝试实现适用于您的特定控件的许多这些技术,以确保您的控件在各类容器中都能执行。svg

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标志。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控件。

相关文章
相关标签/搜索