This article explains how one ATL full control (contained control) can be used dynamically inside another ATL composite control. This control is built in ATL 3.0.
The contained Control is "Static - subclassed" ATL full control. I have used it to display any image but on top of another background image/color which can act as a border to the image. The control contains two
LPPICTURE data members
m_lpPictureBk to hold the image and background image streams. Images can be displayed using this control by two ways.
- Either any image file physical path can be specified OR
- Any image
IPICTURE stream can be directly assigned. (which may be used to directly display images stored in database in binary format
The main interfaces of this control used are:
HRESULT BorderColor([in]OLE_COLOR clr);
HRESULT BorderColor([out, retval]OLE_COLOR* pclr);
USED TO SET A BACKGROUND BORDER COLOR
HRESULT BorderWidth([in]long width);
HRESULT BorderWidth([out, retval]long* width);
USED TO SPECIFY THE BORDER WIDTH.
HRESULT Enabled([in]VARIANT_BOOL vbool);
HRESULT Enabled([out,retval]VARIANT_BOOL* pbool);
USED TO ENABLE THE CONTROL.
HRESULT SetParameters([in]BSTR ImagePath, [in] BSTR ImageBkPath, [in]
VARIANT_BOOL Proportional, [in] VARIANT_BOOL Transparent);
//USED TO SET IMAGE FILE PHYSICAL PATH,
//BACKGROUND IMAGE PATH FOR BORDER.
The proportionality can be specified as true if the image should be resized as per the control size proportionally. I have not coded it yet but hook is provided to do that.
HRESULT SetImageID([in] BSTR ImageID);
HRESULT ID([out, retval] BSTR *pVal);
HRESULT ID([in] BSTR newVal);
HRESULT DisplayName([out, retval] BSTR *pVal);
HRESULT DisplayName([in] BSTR newVal);
//USING THESE INTERFACES THE CONTROL
//CAN BE MADE TO HOLD SOME NAME AND ID
[HRESULT SetBkIPicture([in] LPUNKNOWN IBkPicture);
//USED TO SET BACKGROUND IMAGE STREAM DIRECTLY
HRESULT SetParentWnd([in] DWORD hParent);
//USED TO SET THE PARENT WINDOW (It is used to send
//messages to parent window as I was not able to figure
//out how to source events in the Composite control in case
//this control will be dynamically generated.
HRESULT SetIPicture([in] LPUNKNOWN Picture);
//USED TO SET IMAGE STREAM DIRECTLY
HRESULT BorderColorSelected([out, retval] OLE_COLOR *pVal);
HRESULT BorderColorSelected([in] OLE_COLOR newVal);
//USED TO SET BORDER COLOR IF THIS CONTROL IS SET ACTIVE (Selected)
HRESULT Selected([out, retval] VARIANT_BOOL *pVal);
HRESULT Selected([in] VARIANT_BOOL newVal);
//USED TO SET THIS CONTROL AS ACTIVE (Currently selected,
//this will change to border of the image to the color specified in
//BorderSelectedColor or image specified in BorderImgSelected)
HRESULT BorderImgSelected([out, retval] BSTR *pVal);
HRESULT BorderImgSelected([in] BSTR newVal);
//USED TO SET BORDER IMAGE IF THIS CONTROL IS SET ACTIVE (Selected)
HRESULT BorderImage([out, retval] BSTR *pVal);
HRESULT BorderImage([in] BSTR newVal);
//USED TO SET BORDER IMAGE IF THIS CONTROL IS INACTIVE (Not Selected)
HRESULT ImagePath([out, retval] BSTR *pVal);
HRESULT ImagePath([in] BSTR newVal);
//ALTERNATIVE TO SET IMAGE PHYSICAL PATH
Compile the project StaticX.dsp and build the StaticX.dll.
The composite control
This control used the above StaticX control and a
Recordset to dynamically create the StaticX controls and display the image directly from database. To access database I am using a wrapper class of ADO which I downloaded from Code Project. Thanks to the author of the class.
This also incorporates paging. But I don't know how to add scrolling functionality. Need to find out. Any way, I used STL map to keep lists of the images in the
RecordSet and also the dynamic instances of StaticX control.
To host the control,
CAxWindow window instance is used.
The following are the interfaces used to interact with the main composite control.
HRESULT SetPagingDetails([in] int ItemsPerPage,
[in] int NumberOfColumns, [in] int PageSlotSize);
HRESULT GetPagingDetails([out] int *ItemsPerPage,
[out] int *NumberOfColumns, [out] int *PageSlotSize);
//USED TO SET PAGING DETAILS AS NUMBER OF IMAGES
//PER PAGE, NUMBER OF COLUMNS.
//Note: PageSlotSize is provided to have a funtionality
like 1,2…10. And then on clicking NEXT -> 11,12,…20.
//In this case the PageSlotSize is 10. Normally keep it 1.
HRESULT ConnectionString([out, retval] BSTR *pVal);
HRESULT ConnectionString([in] BSTR newVal);
//USED TO SET THE CONNECTION STRING FOR THE DATABASE CONNECTIVITY.
//(Connection Provider String).
HRESULT Init([in] int nMode);
//USED TO INITIALIZE THE CONTROL AFTER ALL PARAMETERS ARE SET.
HRESULT Query([out, retval] BSTR *pVal);
HRESULT Query([in] BSTR newVal);
//QUERY FOR THE RECORD SET.
//(Note that your recordset should contain the following columns(fields)
//1- ImageID : To contain an Image ID
//2- Image : To contain image in binary format.
HRESULT BackImage([out, retval] BSTR *pVal);
HRESULT BackImage([in] BSTR newVal);
//USED TO SET IMAGE FOR BACK BUTTON
//(Physical path of image)
HRESULT NextImage([out, retval] BSTR *pVal);
HRESULT NextImage([in] BSTR newVal);
//USED TO SET IMAGE FOR NEXT BUTTON
//(Physical path of image)
//I used OleCreatePictureIndirect to create a
//IPICTURE from a bmp that is created for the
//Background Border Image(of some specified color).
PICTDESC //Pointer to the structure of parameters for picture
REFIID //Reference to the identifier of the interface
BOOL, //Whether the picture is to be destroyed
VOID** //Address of output variable that receives the
// interface pointer requested in riid
Rest all is pretty easy.
To display the image directly from database, I again used
IPICTURE stream. I read the image in binary format in a buffer which then I converted into stream and passed to StaticX control. Also to easily have stream operations, I have used
CCOMString, again a wrapper class provided by Paul E. Bible. Thanks for that implementation.