User Interface

Implement UI Navigation

Create& integrate toolbars

Handling menu & toolbar selections (02/04/2000)

MFC framework -> handlers placed inside class most closely associated with handler function, e.g. connection to remote DB may be best handled by application as whole, therefore by the CMyApp class.

Handlers added via ClassWizards Message Map tab - specify class that will contain handler, object id that acts as source and message to be handled.

Dynamic updates menus & toolbars

MFC framework provides mechanisms to update appearance & status of menu commands and toolbars via handlers for user interface update command messages.

Framework generates UI update messages to signal app to update status of UI in response to menu openings and toolbar button presses. Message maps of command target objects in command routing searched for ON_UPDATE_COMMAND_UI entries.

Implement handler functions to modify UI appearance. Framework passes function pointer to CCmdUI object giving access to UI element associated with handler. This object permits enabling, disabling, checking of menu items, setting of radio buttons, etc.

Implement and Write to Status Bar

Often updating menu / toolbar appearance not enough to reflect info. Status bar provides area for further info to be displayed. AppWizard by default provides CAPS LOCK, NUM LOCK and SCROLL LOC indicators.

Status bar support handed by CStatusBar.

Indicator info held in array, leftmost position at locn 0. Each entry in array = resource ID of string table entry.

Fist indicator is elastic - takes up space not used by other, right aligned indicators. Entries in string table, usually IDINDICATOR*, associated with corresponding framework indicators. Entry in table should be of maximum length indicator will be so framework can set out indicators.

To update text in status bar use ON_UPDATE_COMMAND_UI entry in message map to associate UI entry with handler function - must be added manually as not supported by ClassWizard. Without handler the framework will disable and blank out the indicator.

Indicator text updated via CStatusBar::SetPaneText().

Create new app using MFC AppWizard (05/03/2000)

Project is source code + associated resources and their relationships (build rules, etc.).

Independent settings (debug, release, etc.) for build tools (compiler, linker, etc.) via Project Settings dialog. AppWizard creates debug and release configurations initially - can add more.

Workspace is container for project(s). Default = 1 project, can add others. Project dependencies can be defined. Only active project at a time (name displayed in bold).

Project Types

MFC is class based framework Develop apps, dlls, ActivX controls,etc.. Save time + effort but size + performance overhead = not suitable for small apps. Requires access to MFC DLLs (either static or dynamic linking).

Win32 is for simple apps without MFC overhead.

Wizards:

  • create empty or skeleton Window apps - register window classes, create message loop, basic window processing.
  • create empty of skeleton dlls - sample code to export classes, functions& variables.
  • create empty or skeleton console app
  • create empty or skeleton static library for linking to executable
  • can include MFC support (take advantage of strings, collections, etc.

ATL is templates for small, fast, COM objects. Create COM server, dll or exes. Can add ATL com objects to project - include simple objects, transaction services, MMC, etc.

Miscellaneous = Not fit into other categories, e.g. DLL resources for IIS or Cluster Server, Visual Studio add-ins, custom AppWizards, etc.

Create application using MFC AppWizard (12/03/2000)

  1. Choose application type (SDI, MDI, Dialog)

  2. Chose DB support

  3. ActiveX technologies - ActiveX isuse of COM to enable component interactivity. OLE provides compound docs.

  4. Misc features such as toolbars, 3D controls, MAPI, Sockets, etc.

  5. Advanced options - associates a file extension with the app and specify display characteristics (width of border, etc.)

  6. Project style - type of presentation (standard or Explorer like), generation of comments and static / dynamic MFC link

  7. Adjust names of classes generated and their inheritance

Create and edit UI objects with resource editors (26/03/2000)

App& doc resources

App& doc icons

To meet certification requirements must provide both standard (3232) and small (1616) icons for application and associated document type.

MFC framework automatically provides default icons for you.

Application resources (icons, menus, etc.) have numeric resource identifier (resource ID).

Editors

Graphics Editor

Part of IDE - use to edit icons, plus other bitmaps, jpgs, etc

.

Application Menuspermit convenient + consistent grouping of commands. Wizard generates menus appropriate to selections chosen by developer. Associated with resource ID of IDR_MAINFRAME. Can change menus through menu editor. Use of '&' within menu caption specifies shortcut key used in conjunction with ALT key to quickly select menu.

Shortcut keys (previously accelerator) provide quick access to program commands available from menu / toolbar, e.g. associate CTRL+U with data upload. Held in accelerator table manipulated by accelerator editor that maps key combinations to message identifiers (e.g. ID_DATA_UPLOAD).

Toolbars provide easy access to common commands. AppWizard generates standard toolbar providing access to file and edit commands. Toolbar resource associated with bitmap containing button images, additional bitmap files created for each toolbar defined. Images in toolbar of same size, usually 16*15. Bitmap image contains buttons side by side in resource id order - for this reason use provided graphic editor (displays one button at a time). Each button has associated command identifier.

Add Message Handler for event using ClassWizard

OnOK() = example of control notification message handler for BN_CLICKED message of IDOK control.

Different sets of notification messages associated with different controls - Message Maps tab of ClassWizard provides reference to all those available.

Control notification messages prefixed by abbreviation for control type followed by an N:

Prefix   Description
BN_Button
CBN_Combo box
CLBN_Check list box
EN_Edit control
LBN_List box
STN_Static text control

 

ClassWizard add macros to message map to handle notification messages for above control types. Macros constructed by prefixing message ID with ON_, e.g. handler for EN_UPDATE for IDC_USERID results in:

ON_EN_UPDATE(IDC_USERID, OnUpdateUserid)

Note, BN_CLICKED for IDOK and IDCANCEL mapped directly by overridden versions of OnOK and OnCancel - no manipulation available.

Creating data input forms and dialog boxes

Creating Dialog Box using dialog editor (03/04/2000)

Child window of main app window - display or retrieve status info.

Contains controls (small, standard window objects) through which info displayed/manipulated.

Two types

  • Modal - take control of interface. Require closure of box before use of app can continue. Example = apps About dialog.
  • Modeless - do not take control of app interface, can work on other areas of app without closing dialog. Less common than modal. Example is Properties dialog box used within resource editor of IDE.

Property sheet = special dialog box. Presents user with tabbed, index card like selection of pages within single frame.

Creating dialog box template

Use dialog editor to create dialog box template - reusable app resource that is binary description of dialog box + its controls layout.

Template edited by resource editor.

Each control within template (including itself) has associated identifier - IDD* for dialog, IDC* for control.

When using controls within dialog box, only one item has focus - indicated by outline around control.

Select control with mouse or TAB key. Specify TAB order (sequence of control selection) within dialog editor.

Creating dialog box class

Class representing dialog box within your program, derived from CDialog. Contains member variables representing controls and data items within dialog box + methods to handle events arising from user interactions with dialog.

Creating dialog box class simplified by ClassWizard.

To create class open from dialog box template, launch ClassWizard that will prompt for new class to be created (as none is currently associated with template).

Displaying dialog box

Achieved by calling CDialog::DoModal(). Default handlers for OK and Cancel buttons via CDialog::OnOK() and CDialog::OnCancel().

Example of creation:

{  
    CAboutDlg aboutDlg;

    AboutDlg.DoModal();  
}

Modeless dialogs handled differently. Don't call CDialog::DoModal(), instead call CDialog::Create() passing in resource ID of dialog box template. Use ShowWindow() to show and hide the dialog box.

Modal dialogs usually created as required, typically on stack. Modeless dialogs (e.g. toolbars) usually last for the lifetime of the application and hence are created at application instantiation.

Can make window always appear above others by ensuring it has WS_EX_TOPMOST style set. Can achieve this through the CWnd::SetWindowPos() function (added into the Create() function). Has side effect of making dialog appear above all windows in system (not just those of the application). Resolve this by hiding dialog when application looses focus - achieve this by adding handler for WM_ACTIVATEAPP message.

Common dialog classes

MFC supplies several classes derived form CDialog to handle common tasks; CColorDialog, CFileDialog, CFindReplaceDialog, CFontDialog, CPrintDialog.

Creating property sheets by using ClassWizard

3 main parts; containing dialog box, 1+ property pages, tabs at top of each page used to select page.

Used when have number of groups of similar settings / options, e.g. property sheet in MSDEV IDE.

Implemented by two classes - CPropertySheet for containing dialog box and CPropertyPage for individual pages of property sheet.

Recipe:

  • Create dialog template for each property page. Aim for consistent layout and size. Ensure thin style, child and disabled properties set. Caption for dialog template appears on the page tab.
  • Derive classes' from CProperyPage for each dialog.
  • Create member variables +DDX/DDV to hold values for property pages.
  • Create CPropertySheet object in source code
  • Create objects for each of the property pages.
  • Add each page to the sheet by calling the CPropertySheet::AddPage() function
  • Bring in to use via DoModal or Create functions depending on modal nature.

For modal property sheets the framework provides OK, Cancel and Apply buttons and DDX/DDV for control on each page. If additional controls required on property sheet then use class wizard to generate class derived from CPropertySheet.

For modeless property sheets it is necessary to provide own CPropertySheet derived class as the Create() function does not create default controls for the sheet.

Handling Apply Button

The Apply button allows users to exchange and validate changes made without closing property sheet - useful if wanting to save changes made on one page before proceeding to next.

By default Apply button is unavailable. To make available after data changed, call to CPropertyPage::SetModifed(TRUE) is required.

Validating user input

Dialog data exchange (DDX) and validation (DDV)

DDX = easy way to set and retrieve control values within dialog.

DDV = easy validation of data entry within dialog box.

DDX and DDV implemented within dialog class, architecture similar to message maps.

DDX and DDV functions = global MFC functions. A number of standard DDX and DDV functions provided for common dialog components.

Member variables within dialog class represent controls with global MFC functions used to transfer values between the two.

Framework DDX/DDV

ClassWizard implements basic DDX/DDV architecture. It will:

  • Add member variables to class definition (within AFX comments)
  • Initialise member variables in constructor (within AFX comments)
  • Add DDX functions to transfer values between variables and controls (within AFX comments in DoDataExchange())
  • Optionally add DDV functions to validate data entry (within AFX comments in DoDataExchange())

Overriden version of DoDataExchange() provided by ClassWizard for dialog class.

Both DDX and DDV functions take a pointer to a CDataExhange object (represents data exchange context - including direction of exchange) as their first parameter.

DoDataExchange() called by CWnd::UpdateData(). UpdateData() takes parm indicating direction of exchange (set or read values) which is passed to DDX/DDV functions via CDataExchage object. Can call UpdateData() at any point to perform data exchange.

Custom DDX/DDV

Can define own DDX/DDV functions - first parm always pointer to CDataExchange object. Extra parms can be provided as required by function.

All custom DDX/DDV function definitions and calls must be placed outside AFX comment blocks.

DDV functions must always be placed directly after DDX functions to which they refer.

Control Initialisation

Some controls require more initialisation than can be easily achieved via DDX functions, e.g. filling list boxes with info determined at run time.

Can achieve by overloading CDialog::OnInitDialog(). Note, OnInitDialog calls UpdateData() and hence DDX/DDV functions.

OnInitDialog called by framework after control creation but before display - provide opportunity to populate with data.

Enabling / Disabling Controls

To minimise DDV complexity controls on dialogs should be disabled until they can be realistically used - for example when connecting to DB only allow connect button o be clicked when name of database supplied elsewhere on dialog.

Controls derived from CWnd, therefore use EnableWindow() to set control availability.

Use MFC AppWizard to create ISAPI DLL

ISAPI Server Extensions

Server-side program running in response to client request. Implemented as DLL loaded by IIS, browser can run program by specifying its name in the url, as in:

http://myserver/apps/charts.dll

Specific functions can be invoked by appending their name to the function name as follows:

http://myserver/apps/charts.dll?GetChart

All ISAPI dlls must define default function to be called when client does not specify one. Parameters passed in similar way:

http://myserver/apps/charts.dll?GetChart?Fund=ASRC

Can link ISAPI dlls into HTML forms via the ACTION statement for FORMS, which specifies what action to carry out when submitting a form.

ISAPI server extensions based on CGI. Without altering specifications, ISAP permits any browser to load and run extension dll. As DLLs execute in same process space as webserver they are quicker than CGI scripts which require separate processes.

Creating

Use MFC ISAPI Extension Wizard to create server extension object. Creates single class (name derived from project name, e.g. CMySampleExtension) derived from CHttpServer. Add member function to this class to implement DLL exported functionality.

MFC uses a parse map (declared in the .h and implemented in .cpp) to map DLL functions to member functions of the CHttpServer derived class. One of members marked as default by DEFAULT_PARSE_COMMAND macro.

All ISAPI extension functions take a pointer to CHttpServerContext as first parm - provides details on client / server connection (a separate class created for each connection). Use this class to obtain access to clients HTTP request, insert entries (using << operator) into file sent back to client, etc.

Sample parse map:

BEGIN_PARSE_MAP(CMyServerExtension, CHtpServer)

ON_PARSE_COMMAND(Default, CMyServerExtension, ITS_EMPTY)

DEFAULT_PARSE(CMyServerExtension)

ON_PARSE_COMMAND(GetColour, CMyServerExtension, ITS_PTR)

ON_PARSE_COMMAND_PARAMS("Colour")

END_PARSE_MAP(CMyServerExtension)

Note, the GetColour function takes a parameter named colour. The parameters and their types handled by the function are indicated by the third (and subsequent) entries of ON_PARSE_COMMAND, with their names specified on subsequent ON_PARSE_COMMAND_PARAMS lines.

Extension DLLs loaded by IIS at first client request.

ISAPI Filters

Sit between client and HTTP server. Enhance IIS with custom features such as HTTP request logging, encryption, compression, etc. Loaded when IIS service started. Can only configure IIS 4.0 to use filters, not supported by Personal Web Server (PWS).

Filters receive notification from server when selected events occur, such as starting to process client request, sending data to client, writing information to log files, etc. Filter will have access to data associated with event, e.g. if data being sent to client then filter has access to data about to be sent and can modify it (compress, encrypt, etc.).

Creating

Use MFC ISAPI Extension Wizard to create filter object - sits between network connection of client and HTTP server. Creates single class (name derived from project name, e.g. CMySampleFilter) derived from CHttpFilter. Member functions have been added for each notification specified in step 3 of the Wizard - these overload those specified by CHttpFilter. Extend these functions to define filter behaviour.

Class also defines GetFilterVersion() which specifies what notifications the class is interested in. The server uses this function to ensure it does not send unnecessary notifications to the filter.

If you add additional handler functions later, must update this function to receive them.

Functions receive data structures containing information about event. Also receive pointer to CHttpFilterContext object containing info about current client connection + methods to read and modify its values.

Incorporate Dynamic HTML in Application

DHTML = extension to HTML exposing web page + its elements as scriptable objects. Able to:

  • Modify web page text
  • Modify style sheet elements specifying how text displayed
  • Animate text and images
  • Respond to user actions such as mouse moves or clicks
  • Validate input before submitting to server

Automatically reformats document - no need to reload. Does not require additional application support - DHTML documents are self-contained, use style sheets to specify format / appearance and small blocks of script to manipulate HTML tags, attributes, styles, etc.

Based on existing HTML standards. Some DHTML features not visible in non-DHTML browsers. Follow basic guidelines to ensure contents always visible, even if formatting wrong.

DHTML Object Model

Any HTML page loaded by IE4 (or later) held in representation accessible via DHTML object model. Can access + manipulate anything within document via model. HTML elements represented as individual objects held in collections (similar to those provided by MFC). Iterate collections (such as anchors, applets, frames...) to gain access to individual elements.

Model exposes user actions (key press, mouse click, etc.) as events. Create event handlers to intercept and process them.

Can identify individual elements on page using ID attribute as in

<H3 ID=MyHeading>Welcome to Dynamic HTML!</H3>

which can then be modified by scripts within document, for example to turn text green the following piece of java could be used MyHeading.style.color = "green".

WebBrowser Control

ActiveX control used to incorporate web pages within application. It is component of Internet Explorer implementing the main window. Supports hyperlinks, URL navigation, maintains security, history and favourites list. Encapsulated within MFC by CHtmlView.

Inserting control in project creates CWebBrowser2 class, a wrapper for the IWebBrowser2 automation interface, through which properties and methods can be accessed in C++ like fashion.

Preferred way of using control is to derive view from CHtmlView (specify during AppWizard creation process). Provides web browser functionality within document/view architecture.

Access Object Model

Very easy from HTML - refer to object by name as in document.bgColor = "hotpink". This functionality provided by Internet Explorer interpreter parsing HTML script and calling apparopriate automation interfaces on WebBrowser control. In C++ access DHTML object model by using Automation interfaces directly.

DHTML object model exposed through interfaces beginning HTML, as in IHTMLDocument, IHTMLWindow, IHTMLElement, etc. The CHtmlView class provides GetHtmlDocument() which return IDispatch to HTML document currently being displayed. From here can access the IHTML* interfaces.

HTML Resources

Can include HTML resources in project - just like bitmaps, dialog templates, etc. Provides safe + convenient way to distribute HTML pages used by application.

While adding html resource to project get choice (a checkbox) of whether to leave it as an external file (in res subdirectory) or bring into the .rc file.

Bringing html files into project causes them to be compiled - hence changes to external file (if applicable) will not be picked up until application rebuilt. Links to other local html pages, as in <A HREF="SecondPage.htm" link to a local page </A>, will not work either - must use res: protocol.

The res: protocol is similar to HTTP. Permits URL to locate resources bound to executable or dll.Has format of res://resource_file/[resource_type]/resource_id where resource_file is the executable or dll containing the resource, resource_type is an optional numeric identifier of the resource type (e.g. 23 for HTML page, 2 for bitmap, etc.) and resource_id is the resource identifier. Note, if no resource type is specified 23 (HTML page) is used as a default.

Creating HTML Controls with ATL

ATL HTML Control hosts WebBrowser control and provides pointer to the IWebBrowser2 automation interface (provides access to DHTML object model).

Like any ATL control, the HTML control provides empty dispatch interface to which you add methods and properties to define how control interacts with container. HTML control provides additional dispatch interface to allow communications between C++ code and HTML user interface.

To use HTML Control use ObjectWizard to insert an HTML Control. A sample HTML document is also added to the project that shows three buttons, each of which call a function named OnClick() passing over an element id and string specifying a colour. This function is not part of DHTML object model - it is a method on the IMyHtmlCtrlUI interface defined for your project by the ObjectWizard. Methods and properties on this interface are callable from your controls DHTML code via the window.external object.

DHTML Scriplets

Permit easy development of reusable simple UI controls using DHTML. Expose well-defined public interface. Capable of firing events.

Can be used like ActiveX control - they run within the Microsoft Scriplet Component (an ActiveX control). This component hosts internal instance of the HTML paring engine to interpret scriplet and exposes a standard ActiveX control interface for use by clients. Provides internal interface for use by the DTML scriplet via the window.external object. This interface allows scriplet to obtain info on state of host and to its services, such as the forwarding of events fired by the scriplet.

Scriplets interpreted -> slower than standard ActiveX control.

Properties and methods

Scriplet defined in normal DHTML document. <BODY> defines visual interface while <SCRIPT> manages properties, methods and events of control. All properties and methods not explicitly declared public are private. Two methods of declaring public elements:

Add public_ prefix to properties and methods, as in var public_text = "Hello". Can also declare properties as public by declaring functions with public_get and public_put prefix - allows finer granularity as can make property read-only by only defining public_get function for it - like function public_get_colour(){...}.

Second method is to use JavaScript public_description object. Create JavaScript constructor function (of any name) declaring scriplets public properties and methods, assigning this function to the scriplets public_description object thus:

public_description = new CreateScriplet();

function CreateScriplet()  
{

    this.text = p1.innerText;

    this.get_colour = getcolour;  
}

function getcolor()  
{

}

Latter is preferred method as creates distinction between interface and implementation, with former being defined in a single location

Events

Can expose two event types, standard (including onclick and onkeypress) and custom.

One of services provided by Scriplet Component is to forward events fired by scriplet to container. If user clicks mouse in scriplet< BODY>, the DHTML onclick event is intercepted by Scriplet Component. It will only pass this on to container if instructed to do so by scriplet code.

To pass even on must provide handler for event to be passed, from within this function call bubbleEvent() provided by Scripet Component in order to pass it on. For example:

<BODY onClick = passthru()>

...

</BODY>

function passthu()
{
    if(!window.external.frozen)

    window.external.bubbleEvent();  
}

Note check for frozen state - must ensure Scriplet Component ready to handle events.

Can also fire custom events at any point in scriplet code using raiseEvent method. Takes two parms, first specifies name for event and second its value. All custom events routed through the onscripletevent event, query its name parameter to identify which event been fired (first parm to raiseEvet).

Within Web Pages

Like ActiveX controls, scriplets hosted in HTML by <OBJECT> tag. Unlike ActiveX, not identified by CLSID. Scriplets identified by assigning scriplet MIME (Multipurpose Internet Mail EXTENSIONS) type of "text/x-scriplet" to TYPE attribute, for example:

<OBJECT ID="MyScriplet" TYPE="text.x-scriplet" DATA="MyScriplet.htm"></OBJECT>

Above assumes scriplet held in MyScriplet.htm in same directory as current file.

Within C++ Application

Scriplets can be placed and used in VB forms like ActiveX controls. Functionality not yet available in VC++, instead use Scriplet Component as host. This generates a CWebBridge class (wrappers IWebBridge) through which the URL of the scriplets HTML document can be specified and access to the event property provided.

Scriplet Component cannot be currently hosted in a dialog box. Until scriplets properly supported developers better off placing Scriplet Component within HTML page and hosting this page in a CHtmlView based application or ATL control.

Display + Printing Data

Coordinate mapping

Output rendered across 2D coordinate system - screen output by pixels, printers with dots on the page.

GDI drawing functions also use 2D coordinate system, measured and scaled using abstract measure (logical units).

For consistency across devices GDI provides coordinate mapping system. Provide mapping mode that determines how logical units (of no intrinsic value) relate to the drawing space of the hardware device.

GDI defines eight mapping modes - default = MM_TEXT that maps logical unit to single pixel on output device, regardless of device resolution. Can cause problems,, e.g. when printing output is very small as resolution of printer much higher than screen (individual points much smaller).

Six fixed mapping modes defined:

Mapping modeOne unit maps to
MM_TEXT1 device pixel
MM_LOENGLSIH  0.01 inch
MM_HIENGLISH0.001 inch
MM_LOMETRIC0.1 millimetre
MM_HIMETRIC0.01 millimetre
MM_TWIPS1/1440 inch

 

Two unconstrained modes exist, MM_ISOTROPIC and MM_ANISOTROPIC. These don't provide predefined mapping modes - user must specify ratio between logical and device units. MM_ISOTROPIC ensures one unit maps to same physical distance in x and y direction, MM_ANISOTROPIC has no such restrictions. To specify ratio must describe two rectangles specifying the dimensions of the logical space (the window set via CDC::SetWindowExt()) and device space (the viewport set via CDC::SetViewportExt()).

By default coordinates have origin in upper left with y values increasing downward. This is different to standard mathematics where y increases up the screen. Can use latter by making its y extent in SetWindowExt() negative - must also move origin to lower left with CDC::SetViewportOrg() otherwise drawing will be off top of screen.

After mapping mode established use GDI functions to render using logical coordinates. System automatically converts between logical and physical coordinates. If need to do calculations yourself can convert using LPtoDP() and DPtoLP() - e.g. mouse click location provided as physical coordinates, need to convert to logical to determine if significant region of logical space.

Scrolling views (04/04/2000)

Output limited to logical drawing space (on Windows 9x -32768 to 32768) - depending on mapping can be a huge space - more than can be shown on output device. If window can not show all application output in single frame then must implement scroll bars. Show scroll bars when frame starts to clip output in client area.

MFC provides CScrollView to make scroll implementation easy. Can do yourself using scroll functions within CWnd, but hard. Framework initialises scroll view to 100 * 100 logical elements with MM_TEXT mapping - roughly an inch square via SetScrollSizes().

Cannot show data outside of this area. Should override these x and y values set by CView::OnInitialUpdate() to match the size of the data that will be displayed. Easy if size known up front, if data area increases (e.g. wordprocessor) then have to call SetScrollSizes() more frequently to reflect new size of data area (e.g. in CView::OnDraw()).

Example, to set up scrollable display area with the document at most representing a sheet of standard pper in landscape orientation (11 inch by 8.5 inch) set following:

sizeTotal.cx = 1100;

sizeTotal.cy = 850;

SerScrollSize(MM_LONGENGLISH, sizeTotal);

Drawing in a Device Context

Framework passes pointer to CDC (representing client area) to OnDraw(). CDC provides functions to draw lines, shapes, text, etc.

Drawing routines work with drawing tool classes such as CPen, CBrush, CFont, CBitmap, etc. Drawing tool of each type selected into CDC for use, should store previous settings for later restoration.

Steps to use drawing tool:

  • Create graphic object and initialise with appropriate function, e.g. CPen::CreatePen().
  • Use CDC::SelectObject() to load graphic object into CDC
  • Save pointer to previous graphic object returned by CDC::SelectObject()
    Draw
  • Restore stored graphic object by selecting it back into CDC.

Can use CDC::SaveDC() and CDC::RestoreCD() to save and restore entire CDC (don't need to repeat above process for each object).

Printing Process

Tasks performed by framework during printing:

  • Display print dialog
  • Create CDC object for printer
  • Call CDC::StartDoc() - notifies printer that all subsequent pages should be spooled under same job until CDC::EndDoc() encountered. Ensures jobs larger than one page are not interspersed.
  • Call CDC::StartPage() and CDC::EndPage() for the beginning and end of each page. Call overriden view functions at appropriate times.

Many functions, including pagination, GDI resource allocation and escape code generation can be overridden.

By default print dialog allows specification of page range to print. If only one page in app then can disable this behaviour by setting the maximum number of pages in CPrintInfo structure passed to CView::OnPrepearePrinting() to 1.

Print Preview

For print preview framework creates CPreviewDC.

Characteristics set in printer device context by app will be mirrored in CPreviewDC by framework.

Differs from printing in order pages of document are drawn.

  • During printing the framework loops until specified number of pages printed.
  • For Print Preview one or two pages displayed at a time and then framework waits for user to press previous or next buttons. App must still respond to WM_PAINT messages.

Print preview behaviour can be modified in various ways, including; using scroll bars to access any page, start previewing from current page, undertake different initialisation when printing or previewing.

Add asynchronous processing

Create secondary threads

Thread = path of execution through process. All processes have a primary thread, can create additional secondary ones.

Use multiple threads whenever performance improvement can be achieved by executing tasks concurrently. Possible situations include:

  • Scheduled (timer-driven) tasks - such as backing up document every five minutes in word processor, thread blocks while awaiting timer event. Can set schedules with millisecond precision.
  • Event driven activities - triggered by events from other threads, e.g. error logging thread inactive until another alerts it to error situation.
  • Distributed activities - collecting or distributing data to several other processes when handled by threads permits them to proceed in parallel at own pace
  • Prioritisation - individual threads can be assigned different priorities, for example the UI thread can be given higher priority than background worker thread.

CWinThread classes

All threads in MFC represented by CWinThread - including primary thread of MFC application (as derived from CWinApp which derives from CWinThread).

Win32 provides _bginthreadex() - should not be used to create threads using MFC functionality as does not initialise MFC specific thread local storage - instead use AfxBeginThread() to return a CWinThread object.

Start CWinThread execution by calling CWinThread::CreateThread(), suspension and resumption through SuspendThread() and ResumeThread().

MFC recognises 2 thread types - worker and user interface.

Worker threads used for background jobs such as DB lookups.

UI threads handle user input - implement message loop to respond to event messages. Best example is applications primary thread (represented by apps CWinApp derived class). Secondary threads permit user interaction without affecting performance of other application features.

2 overloaded versions of AfxBeginThread() permit different types of thread to be created.

Creating Worker Threads

Implement a function to perform task and pass its address to AfxBeginThread(). Should take the form:

UINT MyFunc (LPVOID pParm);

The parameter can hold any value, including a pointer to a data structure to be used by the thread. Note, this structure can be used to provide additional pass back data from the function (above that offered by the return code).

AfxBeginThread() provides additional default value parms that affect the behaviour of the thread, these include its priority, size of stack, creation state (suspended, etc.) and security context to use. It automatically calls CreateThread() once CWinThread object created.

Worker thread ends when function terminates, or it calls AfxEndThread().

Creating User Interface Threads

Derive own class from CWinThread and pass to AfxBeginThread(). Must ensure the member variable m_pfnThreadProc is NULL otherwise AfxBeginThread assumes it is creating a worker thread. Example call:

AfxBeginThread(RUNTIME_CLASS(CMyUIThread));

Must provide own implementation of InitInstance() for your CWinThread object.

Thread Synchronisation

Secondary threads usually used for asynchronous processing - consequently need synchronising with others. Example = shared data where threads reading data must be blocked whilst data being updated.

One mechanism = use of global object that intermediates between threads. MFC provides a number of synchronisation classes:

MechanismDescription
CCriticalSection  Only allows one thread from current process to access object
CMutexOnly allows one thread from any process to access object
CSemaphorePermits between 1 and specified maximum number of threads to access object
CEventNotifies application when event occurred

 

Synchronisation classes used with synchronisation access classes CSingleLock and CMultiLock to provide thread safe access to data / resources.

Recipe:

  • Wrap global data / resource inside class, regulating access through public functions.
  • Within class create appropriate synchronisation object.
  • Within public access functions create instance of synchronisation access object. CSingleLock when waiting on only one object, CMultiLock when there are multiple objects that could be used.
  • Before access function manipulates protected data / resource call Lock() on synchronisation access object - can specify timeout or infinite wait.
  • After finishing with resource call Unlock() function.

Helps protect against deadlocks - where 2+ threads waiting for resources held by each other. Notoriously difficult to reproduce / track down.

Implement online user assistance in application

Context-sensitive help

Provide help on particular app feature - such as dialog box, command, toolbar button, etc.

Two help environments - WinHelp based on RTF and HTML Help base on compiled HTML.

WinHelp

Apps using this form of help rely n Winhlp32.exe located in system folder. Displays pages from help file (extension .hlp) residing in app directory. Page displayed determined by help context - a parameter provided to Winhlp32 by the application.

Access help:

  • Press F1 key - app displays help related to currently active application component (dialog box, toolbar button, etc.)
  • Entering help mode - via help button or SHIFT+F1. Changes mouse pointer to question mark. Context sensitive help displayed for item clicked on while in this mode.
  • Via help menu - opens help file from which searches, menu navigation, etc. Are available.

AppWizard can provide basic framework for help system based around WinHelp - ensure appropriate selection made during wizard process. The wizard creates a hlp folder under main project folder containing files making up help system. Contains following types of files:

  • hpj - Help Project File used by Windows Help compiler to build help files. Maintained by Help Workshop application.
  • rtf - Rich text format file containing pages of the help file. May be several such files in project. Each page displayed begins with hard page break. May contain hyperlink to other pages, bitmaps, etc. Links formatted as double underlined text followed by link destination formatted as hidden text. Link destinations start with # footnote mark with footnote text containing context name (name internal to help file or one specified in the .hm file). Other special footnote marks include; K to denote keywords and $ for topic names.
  • bmp - Bitmaps referenced by the .rtf file.
  • hm - Created by MakeHm tool that maps Help context IDs to app resource IDs so each resource has corresponding help ID available. When adding new resource IDs to project follow standard prefix notation to ensure MakeHm operates correctly.
  • cnt - Contains information required for the help files contents screen. Edited by Help Workshop.
  • ph - Phrase file created by Help Compiler when compression option enabled.
  • hlp - Resultant help file created by the help compiler
  • log - Messages generated by help compiler.

Message map for applications main frame extended to handle various help invocation mechanisms - help menu, button bar, context sensitive, etc. Eventually call on CWnd::WinHelp() which starts WinHelp application.

HTML Help

Next generation of help systems, based on IE. Composed of HTML, ActiveX, Java, VBS and HTML image formats - power + appearance of web site. Viewer contains toolbar and contents/index control for navigation - used by VC++ 6.

Currently no support in MFC framework - must implement manually.

HTML workshop (used to generate files) can read + convert existing WinHelp projects. Output is a .chm file (compiled HTML file).

HTML functionality compiled into applications - workshop provides .h and .lib files for your use. To gain access to .chm from an application include htmlhelp.h file and use HtmlHelp() function to launch .chm files, HTML files, URLs or plain text within a pop-up window.

Display data from a data source

Implement persistence via CFile

CFile class provides direct, unbuffered access to binary disk files. Encapsulates OS file handling routines. Derived classes provide additional functionality, e.g. CStdioFile implements buffered streams.

Opening

Several ways to open. Can use constructor to specify file name, but risky - if file doesn't exists error will result.

Better to use two stage approach. First create CFile object - no parms to constructor, then associate it with a disk file using CFile::Open(). Allows errors to be handled and clarifies distinction between file object and disk file.

If no explicit path provided to Open() then the class searches the path for a match. Can specify paths directly, relatively or using UNC.

When opening specify bitmask detailing open and share options.

Errors

Opening files can result in many errors - for example trying to access a file already held exclusively by another application. MFC provides CFileException to provide additional information on why CFile error occurred . The CFileException class may be used in several ways, when constructing an instance of CFile of an error takes place an exception will be thrown that can be caught with a CFileExcpetion filter, when trying to open a file an optional third parameter can be used into which a pointer to a CFileException object is passed.

Closing

Use the Close() function to close an open disk file. Call not always necessary - when object is destroyed it will automatically close the file it wrappers. Can use to disassociate the CFile object with a particular file, in order to use it again with another.

Reading and Writing

Read and Write functions directly call ReadFile() and WriteFile(0 API calls - provide direct, unbuffered I/O. Can be tricky to use - hence stream I/O routines provided by C and C++.

Streams permit data to be accessed from disk in convenient forms, be it a character or large data structure at a time. Equivalent provided by MFC CStdioFile class. Can specify text (translation of new line and carriage return characters) or binary access (no translation).

Access data via Read() or ReadString(), former reads specified number of bytes, latter reads a string (delimitated by newline or null). Write() is similar to Read() taking a pointer to data to write and number of bytes to write from buffer. Can use WriteString() in text mode to insert newline character at end of each string.

Random Access

Text files usually read/written sequentially.

Binary files often accessed randomly.

File pointer of open file accessed via CFile::Seek() function.

Implement serialisation

MFC Support

Provided via CObject. Classes implementing serialisation must derive from CObject and override CObject::Serialize() - which saves or restores selected class data members from a CArchive object.

CArchive (associated with a CFile) is type safe, buffering intermediary between object to be serialised and storage medium. CFile usually represents a disk file, but not always - for example CSharedFile. By associating CArchive with CSharedFile can serialise object to/from the windows clipboard.

CArchive used to store or retrieve data - never both. Its lifetime limited to one pass through (either reading or writing). Can determine type of Archive (storing or loading) via CArchive::IsStoring().

CArchive defines insertion operator << and extraction operator>> for number of data types. Behave similarly to those in C++ streams. More complex items may implement own Serialize() function, pass their function a reference to the CArchive object currently being used note cal outside conditional branches as will contain its own code to determine whether storage or retrieval is taking place. Sample code:

class CTestAppDoc  
{

    CString m_string;

    DWORD m_dwVar;

    MyObj m_obg;

}

voice CTestAppDoc::Serialise(CArchive &ar)
{
    if(ar.IsStoring())     
    {     
        ar << m_string;

        ar << m_dwVar;      
    }      
    else      
    {      
        ar >> m_string;

        ar >> m_dwVar;

    }

    m_obj.Serialise(ar);  
}

Process

Application data stored in applications document object. Its contents serialised t document file on disk. Document object begins serialisation in response to user command. A CArchive (either for save or restore) is created and passed to documents Serialize() function. AppWizard creates stub Serialize() which you must implement.

Ensure both branches of serialisation code store and restore objects in same order.

All serialisable objects must implement default constructor (i.e. take no arguments).

When changing document data call the CDocument::SetModifedFlag() function - causes framework to prompt user to save before closing document.

MDI apps create new document object whenever document opened / created. SDI apps reuse current document object on open / restore - to clear out current document contents a call is made to CDocument::DeleteContents() which must be overridden by developer to clear out app specific data from document.

Make a serialisable class

  1. Derive from CObject
  2. Provide default constructor
  3. Add macro DECLARE_SERIAL to class declaration (in .h file)
  4. Add macro to IMPLEMENT_SERIAL to class implementation (.cpp file). Takes three parms - class name, parent class name and schema version number (latter used to indicate which version of application created file).
  5. Override CObject::Serialize()

Serialising collection classes

Collection classes implement own Serialize() function. This calls on SerialzeElements() which performs a bitwise copy of the object data - fine for simple types but has problems with complicated ones such as CStrings. Need to override the templates SerializeElements() function to cope with the complicated object (e.g. those containing pointers).

Accessing the registry

Central, hierarchical database holding persistent configuration info for OS and Apps. Secure alternative to .ini files.

Data

Typically contains several MB of data - to ease access viewed as hierarchy. Implementation on different windows platforms different, but hidden by Win32 API. Besides programmatic control, can also gain access via registry editor.

Organisation

Root = set of predefined keys corresponding to general categories of system organisation.

KeyDescription
HKEY_CLASSES_ROOT  System config info, e.g. file extension / application associations, print configs, COM configs, etc.
HKEY_CURRENT_USERUser specific settings. Created at logon from users entry in HKEY_USERS
HKEY_LOCAL_MACHINEComputer specifications, driver settings, etc.
HKE_USERSInformation on all users who log on to system
HKEY_CURRENT_CONFIGConfiguration data for hardware profile currently in use on computer.

 

Additionally Windows 9* has HKEY_DYN_DATA to store dynamic data - performance statistics, P&P info, etc.

Subtrees contain keys that act as containers for other keys and values. Value = terminating leaf node in registry hierarchy. Value has three components; name, data type and value. A key may contain single unnamed value that acts as default value for key - for clarity avoid this practice. Supports limited set of data types:

KeyDescription
REG_DWORD32 bit number
REG_BINARYBinary data of any form
REG_SZNull terminated string
REG_MULTI_SZArray of null terminated strings
REG_EXAPND_SZ  Null terminated string containing unexpanded references to environment variables

 

MFC Support

AppWizard creates following entries:

  • Application and extension (document type) association
  • Location of application user profile - used to store user specific application settings.

Creates .reg file that can be used in conjunction with a setup program to perform registry updates during installation. Also permits easy removal of entries from registry during uninstall.

Document Types

InitInstance() calls on CWinApp::RegisterShellFileTypes() that iterates through document templates maintained by application and adds association entries to HKEY_CLASSES_ROOT. Also adds entry specifying default icon for document types. If called with TRUE parameter the file type has entries added permitting printing to take place from the shell / dragging of icon to printer.

Application User Profile

InitInstance() calls on CWinApp::SetRegistryKey() to create key under HKEY_CURRENT_USER\Software key - the parameter to the function being the name of the key to create. Replace the default name provided with one suitable for your application, typically your company name. The function will then proceed to create an entry under this key of the form \MyApp\Settings where MyApp is the name of the application.

Access the Application User Profile using CWinApp functions WriteProfileString(), GetProfileString(), WriteProfileInt(), GetrofileInt().

Win32 Support

MFC functions make registry access easy, but are very simple. For more sophisticated manipulation (such as creating application settings common to all users) must use Win32 API calls.

Download