Print Dialog customization using PrintDlg function in Visual C++/Win32


Customized Printing Dialog using PrintDlg:

This article is about how to create a customized Print Dialog. Customized dialog means the customization done with the PrintDlg(..) win32 api in Microsoft Visual C++. This article will demonstrate how to add a paper control, an Owner-Draw button that displays the paper and a ComboBox, which lists the PaperSizes, supported by the given printer. When you choose a Paper from the Paper Size list, the corresponding relative paper size is shown on the left. At the end of the article you would be able to add controls of your own and have the user input them and the application would make use of the user inputs to do the rendering. An example of an application which does customizing of the standard print dialog is Acrobat.

PrintDlg function:

PrintDlg(..) API is exported by the comdlg32.dll.The Parameter to the API is LPPRINTDLG, a pointer to PRINTDLG.

Below is the PRINTDLG structure taken from commdlg.h:-

typedef struct tagPDW {
..
HGLOBAL hDevMode;
..
LPPRINTHOOKPROC lpfnPrintHook;
LPCWSTR lpPrintTemplateName;
..
} PRINTDLGW, *LPPRINTDLGW;

hDevMode is a handle to memory which contains the DEVMODE, this can be sent as input to the function in which case the PrintDlg api would use it to do the initialization of the Printer Property sheets. If left initialized to NULL, it would contain the DEVMODE with the user inputs made in the property sheets.

lpfnPrintHook, this contains the Hook Window Proceedure. This function will be hit whenever the user works on the Printer Dialog. This is enabled via the the PD_ENABLEPRINTHOOK flag in Flags member.

lpPrintTemplateName, this contains the name of the dialog template resource.

Steps to Create a Custom Dialog using PrintDlg

As a first step, copy the dlgs.h from the vc++ include directory to a file named printproc.rc.

The resource file has been modified to look like this:-

 

 

The below is the running version of the program.

Lets have a look at the PrintHook Proceedure:-


UINT_PTR CALLBACK PrintHookProc(
	HWND hdlg, // handle to dialog box
	UINT uiMsg, // message identifier
	WPARAM wParam, // message parameter
	LPARAM lParam // message parameter
)
{
	switch(uiMsg)
	{
	case WM_COMMAND:
	switch(LOWORD(wParam))
	{
	case 1139:
		if(HIWORD(wParam) == CBN_SELCHANGE)
		FillPaperList(hdlg , 1139 , IDC_COMBO1);
		return FALSE;
	case IDC_COMBO1:
		{
			if(HIWORD(wParam) == CBN_SELCHANGE)
			{
				int sel = (int)SendMessage((HWND)GetDlgItem(hdlg , IDC_COMBO1 ), CB_GETCURSEL, (WPARAM)0, (LPARAM)0);
				HDC hdc = GetDC(GetDlgItem(hdlg , IDC_PAPERPREVIEW));
				SetMapMode(hdc , MM_ISOTROPIC);
				SetWindowExtEx(hdc , MaxPaperSize , MaxPaperSize , NULL);
				RECT rect;
				GetWindowRect(GetDlgItem(hdlg , IDC_PAPERPREVIEW) , &rect);
				int width = rect.right - rect.left;
				int height = rect.bottom - rect.top;
				SetViewportExtEx(hdc , width , height , NULL);
				DrawPaper(hdc , pPaperSizes[sel]);
				SetMapMode(hdc, MM_TEXT);
				ReleaseDC( GetDlgItem(hdlg , IDC_PAPERPREVIEW), hdc);
			}
		}
		return TRUE;
		}
		break;
	case WM_INITDIALOG:
		FillPaperList(hdlg , 1139 , IDC_COMBO1);
		return TRUE;
	case WM_DRAWITEM:
		switch(wParam)
		{
			case IDC_PAPERPREVIEW:
			{
				DRAWITEMSTRUCT *pdis = (DRAWITEMSTRUCT *)lParam;
				if(pdis->itemAction & ODA_DRAWENTIRE)
				{
					SetMapMode(pdis->hDC , MM_ISOTROPIC);
					SetWindowExtEx(pdis->hDC , MaxPaperSize , MaxPaperSize , NULL);
					int width = pdis->rcItem.right - pdis->rcItem.left;
					int height = pdis->rcItem.bottom - pdis->rcItem.top;
					SetViewportExtEx(pdis->hDC , width , height , NULL);
					POINT pt;
					pt.x = 2125;
					pt.y = 2794;
					int sel = (int)SendMessage((HWND)GetDlgItem(hdlg , IDC_COMBO1 ), CB_GETCURSEL, (WPARAM)0, (LPARAM)0);
					if(pPaperSizes)
					DrawPaper(pdis->hDC , pPaperSizes[sel]);
					else
					DrawPaper(pdis->hDC , pt);
					SetMapMode(pdis->hDC , MM_TEXT);
					return TRUE;
				}
			}
		}
		return FALSE;
}

What the above Proceedure does:

It does three things:-

1) WM_INITDIALOG: The Paper DropDown List is filled.

2) Whenever, the user changes the Printer in the PrinterList,it will keep the PaperList in the dropdown to the ones supported by the printer. case 1139:

if(HIWORD(wParam) == CBN_SELCHANGE)
FillPaperList(hdlg , 1139 , IDC_COMBO1); return FALSE;

3) Whenever the user changes the PaperName in the PaperList, it would set the Window and ViewPort Extents in the MM_ISOTROPIC mode and then draw the relative size of the Paper. This is done in

case IDC_COMBO1:

4) WM_DRAWITEM and the ODA_DRAWENTIRE notification for the IDC_PAPERPREVIEW control, the control for displaying the relative PaperSize. The functionality is here:-

case WM_DRAWITEM: switch(wParam) { case IDC_PAPERPREVIEW: {

That almost covers 90% of what you need to customize the print dialog using printdlg function. Download the Sample code. To compile and run the program, do the following in the command line:

1) rc printproc.rc – This compiles the resource file

2) cl printproc.cpp printproc.res gdi32.lib comdlg32.lib user32.lib winspool.lib – this compiles and links the exe as printproc.exe.

Now we can Run the printproc.exe.