This is a graphics class written for Windows Template Library (WTL) that uses GDI+ to draw images on an owner-drawn picture (static) control. It supports bitmap, gif, jpeg, png and tif image formats and allows reading images from disk files, saving images to disk files in the same or different format, reading image or varbinary database columns, and writing database images in the same or different format.


Development Environment

This class is compatible with WTL 8.0 and the sample was created in Visual Studio 2008.


CWtlPicture is designed to interface with a common Picture control with Type set to Owner Draw on an ordinary Windows dialog. A GDI+ Bitmap object is loaded with image data from disk or database and the GDI+ graphics object then draws upon the Picture control surface. Disk images are opened using CFileDialog with Bitmap::FromFile and saved using Bitmap::Save supplemented with Encoder selection logic capable of converting image type. In addition, there is an image scaling function as well as a helper class that implements an ISequentialStream for reading and writing OLE DB blob database columns.

The class can also be used in a standard view or other window by assigning the window handle to CWtlPicture and calling the Render() function from the window's OnPaint event handler.

Using GDI+

GDI+ is a Microsoft imaging library. The library and its header are added to the CWtlPicture source file and the library is started in the class constructor and stopped in the destructor.

CWtlPicture functionality

The code can open bitmap, gif, jpeg, png and tif images either from disk file or OLE DB compatible database. After loading, an image can be saved to disk or database in any of the 5 supported formats. However, image processing rules apply. Bitmaps and Jpegs do not support transparency, so they cannot be saved as gif, png or tif transparent image and transparency is lost when images are saved as Bitmaps or Jpegs. Also, GDI+ does not enable transparency even for image types that support it. For example, a gif image that is not transparent cannot be made transparent with a GDI+ function.

Using the Code without Database Support

If you don't need database support and only wish to work with disk files, comment out the #define __DATABASE_SUPPORT__ line in WtlPicture.h and the database-specific code sections will be ignored.

MainDlg additions

You need to add a picture control to your dialog using the resource editor. For simplicity, name the control IDC_PICTURE and set the Type to Owner Draw, then size the control to your desired dimensions. Using the scaling property (discussed later), you can control how the image is sized when displayed.

Next, add a member variable to MainDlg. For example, CWtlPicture m_odPicture;, and attach the picture control window handle in InitDialog like this: m_odPicture = GetDlgItem(IDC_PICTURE);.

After that, set up any desired command handlers such as file open and file save in your dialog's message map, and add a message handler for draw notifications: MESSAGE_HANDLER(WM_DRAWITEM, OnDrawItem). Finally, add event handlers for menu command and for drawing. The code for drawing is straightforward, as shown below.

	// Message handler for drawing the image on the picture control
	LRESULT OnDrawItem(UINT /*uMsg*/, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
		if (IDC_PICTURE == wParam)
			// Paint the background
			::GetClientRect(lpDis->hwndItem, &lpDis->rcItem);
			::FillRect(lpDis->hDC, &lpDis->rcItem, 
                            (HBRUSH)(COLOR_BTNFACE + 1));

			// Draw the image
			if (!m_odPicture.IsNull()) m_odPicture.Render();
		else bHandled = FALSE;

		return 0;

Adding database support

To add database support to your project, you need to create an OLE DB consumer class for your data. There is a wizard available in Visual Studio Professional or higher that (mostly) automates this task. For Express versions of VS, you can download the Windows Driver Kit 7.1 to obtain ATL features. Once you've created your consumer class, you'll need to make a few changes to support image/varbinary operations. I've detailed the procedure in previous OLE DB articles.

After your consumer class is prepared, add a member variable for your table (for example, CProductionProductPhoto m_dbTable;) and command handlers for database operations. The sample project includes handlers for move next and previous and database read/write.

Note: MS Access stores images as embedded OLE objects. The Northwind database for SQL Server has similar OLE photo objects. The code in CWtlPicture DOES NOT read or write OLE objects. If you need to read OLE objects, you can use code something like this to skip over the 78 byte OLE header:

	void SkipOleHeader(IStream* pstm, ULONG& ulStatus)
		LARGE_INTEGER liOffset = { 0, 0 };
		struct OLEOBJHDR { WORD Sig; WORD Size; } Hdr;

		// Set stream to 0 and load header structure
		pstm->Seek(liOffset, STREAM_SEEK_SET, NULL);
		pstm->Read(&Hdr, sizeof(Hdr), NULL);

		// Set stream past OLE bitmap header
		if (Hdr.Sig == 0x1C15 && Hdr.Size == 0x2F)
			liOffset.QuadPart = Hdr.Size + 1 + 30;
			ulStatus = DBSTATUS_S_OLE;
		pstm->Seek(liOffset, STREAM_SEEK_SET, NULL);


Get/SetScaleType - controls how the image is displayed in the picture control. Choices are Normal, AutoFit, Stretch and Zoom. AutoFit sizes the image to fit entirely within the picture control. Zoom allows a scaling factor to be set through the render function. Zoom factors less than 1.0 shrink the image (for example, 0.25 displays the image at 1/4 size) and greater than 1.0 expand the image.

About the Sample Application

The sample dialog application uses the Production.ProductPhoto table from the AdventureWorks database. You will need access to a database server with this databases to use the sample database functions or you can generate your own consumer class for your database table and add it to the project. You may also need to modify the connection string in the consumer classes to point to your database. It is currently set to localhost with integrated security.

推荐.NET配套的通用数据层ORM框架:CYQ.Data 通用数据层框架