Use Preview in a Separate C++ (Child-) Window

Valid from List & Label 24
The preview of List & Label is basically available in a separate modal window. Often, however, you would like to have these displayed in a window of your own application. For .NET there is already a separate control for this - see the class ListLabelPreviewControl.

The following steps are necessary to display the preview in a separate window. The example codes are based on Visual C++ with MFC classes. But the principle and the procedure should also be transferable to other mechanisms for unmanaged code. Prerequisites for an own implementation are:
  • the handling with Windows window handles
  • the creating of new windows
  • the sending of window messages
  • the handling with callbacks (optional)
1. First, the List & Label module responsible for the preview must be loaded. For x86 applications the module cmls??.dll is required and for x64 applications cxls??.dll. For loading, the two options dynamic and static are available dynamically or statically.

dynamic:
#include "cmbtls??.hx"
// ...

// load LS module/dll
LS??xLoad();

// do some stuff...

// unload LS module/dll
LS??xUnload()


static:
Add an additional dependency in the project to the cmls??.lib (x86) or cxls??.lib (x64) library and add the following code:

#include "cmbtls??.h"
// ...

// load LS module/dll
LS??Load();

// do some stuff...

// unload LS module/dll
LS??Unload()


2. In order to host the preview in a separate window, a window or control must be created accordingly. The window handle is the basis for further use to control the preview:

CRect rcClient;
GetClientRect(rcClient);

DWORD dwStyle = WS_VISIBLE | WS_CHILD | WS_TABSTOP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

/*HWND*/ m_hWndLLPreviewCtrl = CreateWindow(
										::LsGetViewerControlClassName(), // e.g. "LlStgsysViewerControl??W"
										_T(""),
										dwStyle,
										0, 0, rcClient.Width(), rcClient.Height(),
										GetSafeHwnd(),
										(HMENU)1,
										AfxGetInstanceHandle(),
										NULL
										);
ASSERT(m_hWndLLPreviewCtrl != NULL);


3. With the help of various options, properties can be determined for the preview. Possible values can be taken from the header file. In order not to display the toolbar of the preview, the following message can be sent to the window handle:

::SendMessage(m_hWndLLPreviewCtrl, LS_VIEWERCONTROL_SET_OPTION, LS_OPTION_TOOLBAR, 0 /* 0 means no toolbar */);


4. In order to be able to react to messages or events of the preview itself, e.g. a certain button in the toolbar was clicked (see LS_VIEWERCONTROL_NTFY_BTNPRESSED), an own progress indicator is to be used (see LS_VIEWERCONTROL_NTFY_PROGRESS) etc., a callback can be registered for the new preview window:

// attach Callback to preview with used defined data
::SendMessage(m_hWndLLPreviewCtrl, LS_VIEWERCONTROL_SET_OPTION, LS_OPTION_USERDATA, (LPARAM)this);
::SendMessage(m_hWndLLPreviewCtrl, LS_VIEWERCONTROL_SET_NTFYCALLBACK, NULL, (LPARAM)CBNtfyViewerCallback);
// ...
/*static*/ LRESULT CALLBACK MyPreviewClass::CBNtfyViewerCallback(UINT nMsg, WPARAM wParam,LPARAM lUserParam)
{
	MyPreviewClass* pThis = (MyPreviewClass*)lUserParam;
	return(pThis->NtfyViewerCallback(nMsg, wParam));
}
// ...
LRESULT CALLBACK MyPreviewClass::NtfyViewerCallback(UINT nMsg, LPARAM lParam)
{
	switch(nMsg)
	{
		case LS_VIEWERCONTROL_NTFY_BTNPRESSED:
		{
			// lParam contains the control ID - see also MenuID.txt
			// ...
		}
		break;

		case LS_VIEWERCONTROL_NTFY_PROGRESS:
		{
			// lParam = percentage (-1=finished). 
			// return: 1 if internal progress bar shall be suppressed
			// ...
			
			return 1;
        }
		break;
	}
	
	return (0);
}


5. In order to be able to print in the new preview window, the print job of List & Label has to be informed:

// attach preview window
::LlAssociatePreviewControl(m_hJob, m_hWndLLPreviewCtrl, 1);

// execute print/export...

// detach preview window
::LlAssociatePreviewControl(m_hJob, NULL, 1);
IDKBTE001373 KBTE001373