📄 ch11.htm
字号:
<BR>
DECLARE_STANDARD_UNKNOWN(); <BR>
// IDispatch methods<BR>
<BR>
//<BR>
<BR>
DECLARE_STANDARD_DISPATCH(); <BR>
// ISupportErrorInfo methods<BR>
<BR>
//<BR>
<BR>
DECLARE_STANDARD_SUPPORTERRORINFO(); <BR>
// IPerPropertyBrowsing methods<BR>
<BR>
//<BR>
<BR>
DECLARE_STANDARD_PERPROPERTYBROWSING(); <BR>
// IBCFControl methods<BR>
<BR>
// <BR>
STDMETHOD(get_Alignment)(THIS_ long FAR* lRetValue);<BR>
</TT></FONT></P>
<P>When an application needs to use an interface in a control (or any component for
that matter), the application has to call <TT>QueryInterface</TT> to locate the correct
interface pointer within the component. This requirement is also true for the <TT>IPerPropertyBrowsing</TT>
interface. Listing 11.12 shows the change that you must make to your <TT>InternalQueryInterface</TT>
function in order to support the new interface. This change is required because the
control will not function correctly without it.
<H3><A NAME="Heading16"></A>Listing 11.12 <SPACER TYPE="HORIZONTAL" SIZE="10">BCFCONTROLCTL.CPP--QueryInterface
Implementation of IPerPropertyBrowsing</H3>
<P><FONT COLOR="#0066FF"><TT>HRESULT CBCFControlControl::InternalQueryInterface(REFIID
riid, void **ppvObjOut) <BR>
<BR>
{<BR>
<BR>
IUnknown *pUnk; <BR>
*ppvObjOut = NULL; <BR>
// TODO: if you want to support any additional interfaces, then you should<BR>
<BR>
// indicate that here. never forget to call COleControl's version in the<BR>
<BR>
// case where you don't support the given interface.<BR>
<BR>
//<BR>
<BR>
if(DO_GUIDS_MATCH(riid, IID_IBCFControl))<BR>
<BR>
pUnk = (IUnknown *)(IBCFControl *)this;<BR>
<BR>
else if(DO_GUIDS_MATCH(riid, IID_IPerPropertyBrowsing))<BR>
<BR>
pUnk = (IUnknown *)(IPerPropertyBrowsing *)this;<BR>
<BR>
else<BR>
<BR>
return COleControl::InternalQueryInterface(riid, ppvObjOut); <BR>
pUnk->AddRef();<BR>
<BR>
*ppvObjOut = (void *)pUnk;<BR>
<BR>
return S_OK; <BR>
}<BR>
</TT></FONT></P>
<P>Your last requirement is to implement the functions of the interface (see Listing
11.13).</P>
<P><TT>MapPropertyToPage</TT> is not required for your implementation, so you just
return the constant <TT>E_NOTIMPL</TT>. <TT>MapPropertyToPage</TT> is used to connect
the property to a property page that is implemented either in the container or in
the control.</P>
<P><TT>GetPredefinedStrings</TT> is the first function to be called. When this method
is called, the dispid of the property that is currently being referenced will be
passed in. This method is called for all properties that the control supports, so
take care when adding code. If the function is called and it is determined that the
correct property is in context, the control is required to create an array of strings
and cookies. A cookie is any 32-bit value that has meaning to the control implementation.
The strings are placed in a list from which the user of the control can select the
appropriate value to set the property to. In this case, the cookie value that is
supplied is also the value that will be stored in the control's property.</P>
<P><TT>GetPredefinedValue</TT> is the method that is called when the property browser
of the container needs the value that is associated with the particular dispid and
cookie. The value that is returned will be the actual value that is stored in the
property and not the string that was used to represent it.</P>
<P>After the property has been set with its value, the property browser calls the
function <TT>GetDisplayString</TT> to get the string that is associated with the
current property setting. <BR>
<BR>
<IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0"></P>
<BLOCKQUOTE>
<P><B>NOTE:</B> It may seem a little redundant to have the method <TT>GetDisplayString</TT>
when the property browser already has the string for the value from the <TT>GetPredefinedStrings</TT>
function. The <TT>GetDisplayString</TT> function can be implemented without implementing
the other methods. Implementing <TT>GetDisplayString</TT> without implementing the
other functions in the <TT>IPerPropertyBrowsing</TT> interface is for those property
types that do not use the standard property selection mechanism, for example, font
selection, which uses a color selection dialog rather than a list of choices. The
name of the font is retrieved via the <TT>GetDisplayString</TT> function, but the
property selection facility is provided through a standard font dialog.
</BLOCKQUOTE>
<P><IMG SRC="bar.gif" WIDTH="480" HEIGHT="6" ALIGN="BOTTOM" BORDER="0">
<H3><A NAME="Heading17"></A>Listing 11.13 <SPACER TYPE="HORIZONTAL" SIZE="10">BCFCONTROLCTL.CPP--IPerPropertyBrowsing
Implementation</H3>
<P><FONT COLOR="#0066FF"><TT>STDMETHODIMP CBCFControlControl::MapPropertyToPage(DISPID,
LPCLSID)<BR>
<BR>
{<BR>
<BR>
return E_NOTIMPL;<BR>
<BR>
} <BR>
STDMETHODIMP CBCFControlControl::GetPredefinedStrings(DISPID Dispid, <BR>
<BR>
CALPOLESTR * lpcaStringsOut, CADWORD * lpcaCookiesOut)<BR>
<BR>
{<BR>
<BR>
HRESULT hResult = S_FALSE; <BR>
// we should have gotten two pointers if we didn't<BR>
<BR>
if((lpcaStringsOut == NULL) || (lpcaCookiesOut == NULL))<BR>
<BR>
// we are out of here<BR>
<BR>
return E_POINTER; <BR>
// if this is the property that we are looking for<BR>
<BR>
if(Dispid == dispidAlignment)<BR>
<BR>
{<BR>
<BR>
ULONG ulElems = 3; <BR>
// allocate the memory for our string array<BR>
<BR>
lpcaStringsOut->pElems = <BR>
<BR>
(LPOLESTR *) ::CoTaskMemAlloc(sizeof(LPOLESTR) * ulElems); <BR>
// if we couldn't allocate the memory<BR>
<BR>
if(lpcaStringsOut->pElems == NULL)<BR>
<BR>
// were out of here<BR>
<BR>
return E_OUTOFMEMORY; <BR>
// allocate the memory for our cookie array<BR>
<BR>
lpcaCookiesOut->pElems = <BR>
<BR>
(DWORD*) ::CoTaskMemAlloc(sizeof(DWORD*) * ulElems); <BR>
// if we couldn't allocate the memory<BR>
<BR>
if (lpcaCookiesOut->pElems == NULL)<BR>
<BR>
{<BR>
<BR>
// free the string array<BR>
<BR>
::CoTaskMemFree(lpcaStringsOut->pElems); <BR>
// exit the function<BR>
<BR>
return E_OUTOFMEMORY;<BR>
<BR>
} <BR>
// store the number of elements in each array<BR>
<BR>
lpcaStringsOut->cElems = ulElems;<BR>
<BR>
lpcaCookiesOut->cElems = ulElems; <BR>
// allocate the strings<BR>
<BR>
lpcaStringsOut->pElems[0] = OLESTRFROMANSI(EALIGN_LEFT_TEXT);<BR>
<BR>
lpcaStringsOut->pElems[1] = OLESTRFROMANSI(EALIGN_CENTER_TEXT);<BR>
<BR>
lpcaStringsOut->pElems[2] = OLESTRFROMANSI(EALIGN_RIGHT_TEXT); <BR>
// assign the cookie value<BR>
<BR>
lpcaCookiesOut->pElems[0] = EALIGN_LEFT;<BR>
<BR>
lpcaCookiesOut->pElems[1] = EALIGN_CENTER;<BR>
<BR>
lpcaCookiesOut->pElems[2] = EALIGN_RIGHT; <BR>
hResult = S_OK;<BR>
<BR>
} <BR>
return hResult;<BR>
<BR>
} <BR>
STDMETHODIMP CBCFControlControl::GetPredefinedValue(DISPID Dispid, DWORD dwCookie,
<BR>
<BR>
VARIANT* lpvarOut)<BR>
<BR>
{<BR>
<BR>
BOOL bResult = FALSE; <BR>
// which property is it<BR>
<BR>
switch(Dispid)<BR>
<BR>
{<BR>
<BR>
case dispidAlignment:<BR>
<BR>
// clear the variant<BR>
<BR>
::VariantInit(lpvarOut);<BR>
<BR>
// set the type to a long<BR>
<BR>
lpvarOut->vt = VT_I4;<BR>
<BR>
// set the value to the value that was stored with the string<BR>
<BR>
lpvarOut->lVal = dwCookie;<BR>
<BR>
// set the return value<BR>
<BR>
bResult = TRUE;<BR>
<BR>
break;<BR>
<BR>
} <BR>
return bResult;<BR>
<BR>
} <BR>
STDMETHODIMP CBCFControlControl::GetDisplayString(DISPID Dispid, BSTR* lpbstr)<BR>
<BR>
{<BR>
<BR>
HRESULT hResult = S_FALSE; <BR>
// which property is it<BR>
<BR>
switch(Dispid)<BR>
<BR>
{<BR>
<BR>
case dispidAlignment:<BR>
<BR>
{<BR>
<BR>
switch(m_state.lAlignment)<BR>
<BR>
{<BR>
<BR>
case EALIGN_LEFT:<BR>
<BR>
*lpbstr = BSTRFROMANSI(EALIGN_LEFT_TEXT);<BR>
<BR>
break;<BR>
<BR>
case EALIGN_CENTER:<BR>
<BR>
*lpbstr = BSTRFROMANSI(EALIGN_CENTER_TEXT);<BR>
<BR>
break;<BR>
<BR>
case EALIGN_RIGHT:<BR>
<BR>
*lpbstr = BSTRFROMANSI(EALIGN_RIGHT_TEXT);<BR>
<BR>
break;<BR>
<BR>
} <BR>
// set the return value<BR>
<BR>
hResult = S_OK;<BR>
<BR>
}<BR>
<BR>
break;<BR>
<BR>
} <BR>
return hResult; <BR>
}<BR>
</TT></FONT></P>
<H2><A NAME="Heading18"></A>Drawing the Control</H2>
<P>Optimized drawing allows you to create drawing objects, such as pens or brushes.
Rather than remove them when you are finished drawing, you can store them as control
member variables and use them the next time your control draws itself. The benefit
is that you create a pen once for the drawing lifetime of your control, instead of
every time it draws. One thing to remember, though, is that optimized drawing does
not guarantee performance improvements. It simply supplies a framework for how drawing
should be performed and how drawing resources should be used. A poorly written control
is still poorly written, no matter how you slice it.</P>
<P>Standard and optimized drawings have a single tradeoff, and that is size versus
speed. Standard drawing does not require member variables for the drawing objects
that are created and used-- thus requiring less instance data but more processing
time--whereas optimized code is the opposite.</P>
<P>An additional drawback to optimized drawing is that a container may not support
it. A control must, at the very least, support standard drawing functionality, deferring
to optimized only if it is available.</P>
<P>For BaseCtl (like MFC and ATL), the scope of optimized drawing is very narrow
compared to the OC 96 specification, but its use can nonetheless result in performance
improvements. The OC 96 specification further breaks optimized drawing into what
is known as <I>aspects</I>. For more information on aspect drawing, please see the
OC 96 specification that ships with the ActiveX SDK.
<H3><A NAME="Heading19"></A>Optimized Drawing</H3>
<P>In <A HREF="ch10.htm">chapter 10</A>, you learn how to implement standard drawing.
In this chapter, you will enhance the original implementation to take advantage of
drawing optimization.</P>
<P>Listing 11.14 shows the optimized portion of your drawing implementation. If the
container doesn't support optimized drawing, you select the original brush back into
the Device Context (DC), and you destroy the brush you created. The next time that
the <TT>OnDraw</TT> function is executed, you re-create the brush. When using optimized,
you simply reuse the existing brush.
<H3><A NAME="Heading20"></A>Listing 11.14<SPACER TYPE="HORIZONTAL" SIZE="10"> BCFCONTROLCTL.CPP--Optimized
Drawing</H3>
<P><FONT COLOR="#0066FF"><TT>. . . <BR>
// **<BR>
<BR>
if(hOldFont)<BR>
<BR>
// select the old object<BR>
<BR>
::SelectObject(hdcDraw, hOldFont); <BR>
// increment the ref count so the font doesn't drop<BR>
<BR>
// out from under us<BR>
<BR>
if(m_pFont && hFont)<BR>
<BR>
m_pFont->ReleaseHfont(hFont);<BR>
<BR>
// **<BR>
<BR>
// ****** Get the text font ****** <BR>
// The container does not support optimized drawing.<BR>
<BR>
if(!fOptimize)<BR>
<BR>
{<BR>
<BR>
// select the old brush back<BR>
<BR>
::SelectObject(hdcDraw, hOldBrush); <BR>
// destroy the brush we created<BR>
<BR>
::DeleteObject(hBrush); <BR>
// clear the brush handles<BR>
<BR>
hBrush = hOldBrush = NULL;<BR>
<BR>
} <BR>
return S_OK;<BR>
<BR>
}</TT></FONT></P>
<P>If the container supports optimized drawing, the final implementation detail is
to destroy any resources that may still be active, which you do in the <TT>BeforeDestroyWindow</TT>
function. Listing 11.15 shows the implementation that restores the original brush
and destroys the brush that you created.
<H3><A NAME="Heading21"></A>Listing 11.15 <SPACER TYPE="HORIZONTAL" SIZE="10">BCFCONTROLCTL.CPP--BeforeDestroyWindow
Implementation</H3>
<P><FONT COLOR="#0066FF"><TT>void CBCFControlControl::BeforeDestroyWindow(void)<BR>
<BR>
{<BR>
<BR>
// if there is an old brush<BR>
<BR>
if(hOldBrush)<BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -