Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Tools

Undocumented Corner


JUN96: UNDOCUMENTED CORNER

UNDOCUMENTED CORNER

Customizing MFC

Scot Wingo and George Shepherd

Scot is a cofounder of Stingray Software, an MFC extension company. He can be contacted at [email protected]. George is a senior computer scientist with DevelopMentor where he develops and delivers courseware for developers using MFC and OLE. George can be contacted at [email protected]. They are the coauthors of MFC Internals (Addison-Wesley, 1996).


Customization is one of the best uses of undocumented MFC information. In our April column, for instance, we examined the undocumented MFC print-preview class CPreviewView. While CPreviewView's default print-preview support is a good start for most applications, you'll have to customize it for enhanced support. This month, we show you how to customize the MFC print-preview and print-status dialog. Along the way we'll point out some techniques for working with undocumented MFC classes that weren't really designed to be customized. To its credit, Microsoft does outline print-preview customization in a tech note; as you'll see, however, it's not the simple three-step task Microsoft describes.

A Generic CPreviewView Derivative

By customizing the appearance of CPreviewView, you can create and plug in a generic CPreviewView derivative that serves as a jumping-off point for print-preview customization. Since CPreviewView is an undocumented class, however, you have to create a derivative by hand instead of using a Wizard or Expert. Our CPreviewView derivative, called CDDJPrintPreview (see Listings One and Two), is a plain-vanilla derivative that overrides CPreviewView:: OnDraw() with CDDJPrintPreview::OnDraw(). In the overriding CDDJPrintPreview::OnDraw(), there's a TRACE statement and a call to the overridden CPreviewView::OnDraw(). The TRACE statement lets you know that your application is using the new print-preview class.

Plugging in CDDJPrintPreview

Print previewing starts when the user selects File/Print Preview, thus invoking the CView member function OnFilePrintPreview(), which is in MFC\SRC\ VIEWPREV.CPP. OnFilePrintPreview() does some error checking, then calls DoPrintPreview(), passing along CRuntimeClass information for the print-preview class.

To make MFC use a view other than CPreviewView, override OnFilePrintPreview() in your application's view. Next, copy MFC's CView::OnFilePrintPreview() code into your OnFilePrintPreview() and change the DoPrintPreview() call to use the CPreviewView derivative; see Examples 1(a) and 1(b).

Finally, change the message map in your application's view to call your view's OnFilePrintPreview() instead of CView:: OnFilePrintPreview(). After this step, you can compile and run your application and the new print-preview view will be displayed instead of CPreviewView (double check with your debugger, or by looking for TRACE output).

In any CPreviewView derivative, you will need to customize the way the view looks by changing how OnDraw() works. The best way to do this is to copy the CPreviewView::OnDraw() code from MFC into your CPreviewView derivative's OnDraw() routine.

Unfortunately there's a snag: OnDraw() calls CView::OnPrint(), which is a protected CView member function, through a CView pointer, m_pPrintView. Your CPreviewView derivative cannot call this protected CView member function, but CPreviewView can call CView::OnPrint(), since it is a "friend" of CView. There are several ways around the problem. You could add your CPreviewView derivative to the list of MFC's CView friends by changing the MFC source code and rebuilding your MFC library. (We discourage tweaking MFC source code since there are numerous version problems, build issues, and the like.)

To get around this particular problem, we recommend the following steps:

    1. Override CView::OnPrint() in your application's view. Instead of making it protected, make it a public member function. You can also keep it protected, then make CDDJPrintPreview a friend--the choice is up to you.However, we also discourage friends because they usually indicate a class design problem. If a friend class needs to get to a protected/private member, others most likely will too, so it is more flexible to make the member public.

    2. Copy the code from CView::OnPrint() into the override (this code is in MFC\SRC\VIEWCORE.CPP).

    3. Make sure that the new OnPrint() is called instead of CView::OnPrint(). OnDraw() calls OnPrint() through a CView pointer: m_pPrintView->OnPrint(m_pPreviewDC, m_pPreviewInfo);.

This will always call CView::OnPrint() instead of your application view's OnPrint(). To fix the problem, you have to typecast (or "up-cast") the CView into a CDDJPrintPreview. Whenever you do this, it's wise to ASSERT that you are performing a safe typecast; see Example 2.

With this final OnDraw() adjustment, you have a fully customizable CPreviewView derivative. Depending on how much you want to customize your application's print preview, you will probably have to override or substitute some other CPreviewView member functions.

For example, if you want to change the number of pages displayed by print preview from two to four, you'll need to change the CalcPageDisplaySize() member function, since it makes calculations that are hard coded for a two-page display. Since CalcPageDisplaySize() is not a virtual function, you can't just override it. Instead, you have to override its caller, PositionPage(), which is virtual, then copy the MFC code into your PositionPage() override, and change the function to call your own version of CalcPageDisplaySize() instead of CPreviewView::CalcPageDisplaySize().

The Notepad Example

We thought it would be interesting to customize our generic customizable print preview to display something other than a white piece of paper. For example, what if your application prints checks, labels, or forms that don't look like the default print preview? Consequently, we decided to create three-hole punched, lined notebook paper.

The first step is to create red and blue pens for drawing the vertical and horizontal lines; see Example 3(a). Since OnDraw() already creates a black pen to draw the three-dimensional page outlines (called rectPen), there's no need to create a pen for drawing the three holes. The best point in the existing OnDraw() for the creation of the new pens is at the top, where the other pens are created. Listing Three implements the complete notebook paper OnDraw() routine.

Once the pens are created, the next step is to draw the lines and holes. OnDraw() has a for loop that iterates through each page and draws it. New per-page drawing logic should be added to this loop after the FillRect call that paints the page white.

In the page-drawing for loop, the variable pRect contains the current print-preview page's rectangle. You can use this (with some small offsets so that you don't draw over the page's border) in your own drawing. Example 3(b) is code for drawing the red line.

After selecting the pen, we set the x-coordinate of the red line to 1/8 of the page size on the left side of the page. Once x is calculated, draw the red line from the top of the page to the bottom of the page.

The algorithm for drawing the blue lines is trickier, since there are 30 lines per page. Example 4 is what we came up with. nBlueCurrentY starts at 1/8 of the page from the top. After the initial line, each subsequent line is 1/30 of the remaining 7/8 page away from the previous blue line (that's the nBlueYIncrement calculation). Once the starting y and y increment are calculated, a for loop draws the lines down the page.

After the blue lines, it's time to draw the holes. Since there are only three, you can take the brute-force approach and draw each hole individually instead of using a loop; see Listing Three. After selecting a black pen with a black brush, we calculate the left- and right-hole rectangle, which will be the same for each of the three holes. We reused the already-calculated nBlueYIncrement as the diameter of the hole in our print-preview algorithm. The first hole is drawn 1/8 down the page; the second hole, the 1/2 page mark; and the last hole is 1/8 of the page from the bottom of the page.

Figure 1 shows this algorithm in action. Listing Three presents the complete customized OnDraw() routine. Code we added is marked with //DDJ -- New and //DDJ -- End new.

Undocumented Printing-Status Dialog

When you print a view through MFC, a standard printing-status dialog displays the name of the document, printer name, and current page being printed. Figure 2 shows the default MFC printing-status dialog. Admittedly, this is a dull dialog. Let's look at how it is implemented in MFC and see how to customize the printing-status dialog.

The printing-status dialog is implemented by the undocumented class CPrintingDialog. The definition of CPrintingDialog is embedded in the MFC source file VIEWPRNT.CPP.

Listing Four includes the class declaration. From this listing, you can see that CPrintingDialog is a CDialog derivative that uses the resource ID AFX_ IDD_PRINTDLG. You can view this resource by opening FC\INCLUDE\ MFCDLL.RC.

The implementation of CPrintingDialog doesn't do much. The constructor calls CDialog::Create() to create the modeless dialog. CView does all of the CPrintingDialog creation and updating in OnFilePrint(). Example 5(a) creates the CPrintingDialog.

After creating a local CPrintingDialog variable on the stack called dlgPrintStatus, OnFilePrint() sets the document, printer, and port name. The AfxFormatString1() call is an internal MFC convenience function that performs a sprintf-like operation. After setting the text in the dialog, OnFilePrint() shows and displays the dialog.

Next, CView::OnFilePrint() has a loop that iterates through the pages of a document. Example 5(b) contains the logic that updates the CPrintingDialog page number. Finally, when MFC is done printing, it cleans up by destroying the CPrintingDialog using dlgPrintStatus.DestroyWindow();.

Customizing the MFC Printing-Status Dialog

You can now customize the MFC printing-status dialog. For example, we'll add a progress bar indicating the percentage complete to the user. There are five steps to creating a custom MFC print-status dialog:

    1. Create a dialog resource with a progress bar in it. We copied the CPrintingDialog resource and added a progress control with IDC_PROGRESS.

    2. Create a CDialog derivative that uses the resource created in step 1.

    3. Add an OnFilePrint() override to your application's CView derivative and copy MFC's CView::OnFilePrint() code into your override. Delete all references to CPrintingDialog so you do not have to copy the class out of MFC. (CView::OnFilePrint() is in VIEWPRNT.CPP.)

Several complications are caused by copying this code out of its usual MFC source home. First, OnFilePrint() calls AfxGetFileTitle() and other implementation-specific MFC routines. To get over this hurdle, include the MFC implementation file MFC\SRC\AFXIMPL.H. This file requires several other internal MFC OLE header files. To get around this problem, wrap the include in ifdef/undefs to turn off the need for the extra OLE header files. For example:

#define _AFX_NO_OLE_SUPPORT
#include "..\src\afximpl.h"
#undef _AFX_NO_OLE_SUPPORT

The second problem is that OnPrint() uses the undocumented callback function _AfxAbortProc() to cancel a print job. The easiest solution would be to declare the function extern and call the version of the function that lives in MFC. Unfortunately, _AfxAbortProc() is not exported in the MFC DLLs, so you have to be more creative. Another potential solution would be to copy the routine from MFC into your code. However, this doesn't work because _AfxAbortProc() accesses a private MFC global (also not exported in the DLL), called _afxWinState, to set and check a Boolean value that is set to TRUE when the user presses the CPrintingDialog cancel button.

To get around this, you need to get rid of the afxWinState dependence:

    a. In your view source file, duplicate the _AfxAbortProc() callback function and give it a new name. We renamed it _DDJAbortProc().

    b. Create a global BOOL variable to act as the replacement for the _afxWinState variable.

    c. Replace all instances of _afxWinState.m_bUserAbort with your new global variable.

    d. In your printing-dialog constructor, be sure to initialize this variable to FALSE.

    e. Override CDialog::OnCancel() to set this variable to TRUE.

    f. Change all of the _AfxAbortProc() references in OnPrint() to use the new abort procedure.

Now that all of the OnFilePrint() issues are resolved, we can continue the customization.

    4. Change your application's view message map to use the overridden OnFilePrint() instead of CView::OnFilePrint().

    5. Change OnFilePrint() to use the CDialog derivative created in step 2 instead of CPrintingDialog. Example 6(a) illustrates how to initialize the dialog with the progress bar, while Example 6(b) shows how to update the progress bar dialog along with the page number.

Conclusion

That's it. A few easy steps (well, step 3 was tricky) and you have created your own customized printing-status dialog! The customized printing-status dialog (see Figure3) is also part of the sample available electronically (see "Availability," page 3). In our next column, we'll take a look at some undocumented areas of MFC's OLE support.

Learn from Microsoft's Mistakes

There's an important lesson on MFC class design to be learned by customizing MFC's print preview. When designing an MFC class, be sure to make virtual any member functions that could ever conceivably be customized. With this approach, you dramatically increase the possibilities for your class users. Assume that fellow developers will try to override behavior you never thought they'd be interested in.

Also, break long segments of implementation-specific code into fine-grained member functions. For example, if CPreviewView::OnDraw() made several function calls like: PrepareView(), DrawOutline(), DrawPages(), CleanView(), and the like, it would be easy to change the way a page is drawn by overriding DrawPages(), instead of having to override all of OnDraw() and copy the code into your derivative.

Another common problem in MFC involves embedded classes. Embedding a class makes it hard to customize (see Example 5). CDocument has a good solution to this problem. In older versions of MFC, CDocument had an embedded CFile object, so making CDocument use a CFile derivative instead of CFile was very difficult. In MFC 4.0, Microsoft changed CDocument so that it calls the virtual function GetFile(). The default implementation of this creates a CFile (really CMirrorFile, as we exposed in the last article) and returns it. To customize the CFile used by CDocument, all the CDocument user has to do is override GetFile() to return a CFile derivative. Wouldn't it be nice if CView had a GetPrintingDialog() routine that let you customize the printing dialog?

--S.W. & G.S.

Figure 1: Customized notebook paper example.

Figure 2: Default MFC printing-status dialog.

Figure 3: Customized printing-status dialog.

Example 1: (a) Copying MFC's CView::OnFilePrintPreview() into your OnFilePrintPreview(); (b) change the DoPrintPreview() call to use the CPreviewView derivative.

(a)
DoPrintPreview(AFX_IDD_PREVIEW_TOOLBAR, this,
        RUNTIME_CLASS(CPreviewView), pState));

(b)
DoPrintPreview(AFX_IDD_PREVIEW_TOOLBAR, this,
        RUNTIME_CLASS(CDDJPrintPreview), pState));

Example 2: Using ASSERT for a safe typecast.

CDdjsampView * pMyView;
ASSERT(m_pPrintView->IsKindOf(RUNTIME_CLASS(CDdjsampView)));
pMyView = (CDdjsampView *)m_pPrintView;
pMyView->OnPrint(m_pPreviewDC,m_pPreviewInfo);

Example 3: (a) Creating red and blue pens for drawing the vertical and horizontal lines; (b) drawing the red line.

(a)
CPen redPen, bluePen;


redPen.CreatePen(PS_SOLID,2,RGB(255,0,0)); //thickness 2 pixels


bluePen.CreatePen(PS_SOLID,1,RGB(0,0,255)); //thickness 1 pixel




(b)
pDC->SelectObject(&redPen);


int nRedX = pRect->left + pRect->Width()/8; 


pDC->MoveTo(nRedX,pRect->top + 2);


pDC->LineTo(nRedX,pRect->bottom - 2);

Example 4: Algorithm for drawing the blue lines.

int nBlueCurrentY = pRect->top + pRect->Height()/8;
int nBlueYIncrement = (pRect->bottom - nBlueCurrentY)/30;
pDC->SelectObject(&bluePen);
for (int nBlueCount = 0; nBlueCount < 30; nBlueCount++){
  pDC->MoveTo(pRect->left + 3,nBlueCurrentY);
  pDC->LineTo(pRect->right - 2,nBlueCurrentY);
  nBlueCurrentY += nBlueYIncrement;
}//end blue line loop.

Example 5: (a) Creating CPrintingDialog; (b) logic that updates the CPrintingDialog page number.

(a)
CPrintingDialog dlgPrintStatus(this);


CString strTemp;


dlgPrintStatus.SetDlgItemText(AFX_IDC_PRINT_DOCNAME, strTitle);


dlgPrintStatus.SetDlgItemText(AFX_IDC_PRINT_PRINTERNAME,


                printInfo.m_pPD->GetDeviceName());


AfxFormatString1(strTemp, nFormatID, strPortName);


dlgPrintStatus.SetDlgItemText(AFX_IDC_PRINT_PORTNAME, strTemp);


dlgPrintStatus.ShowWindow(SW_SHOW);


dlgPrintStatus.UpdateWindow();




(b)
// write current page


TCHAR szBuf[80];





wsprintf(szBuf, strTemp, printInfo.m_nCurPage);


dlgPrintStatus.SetDlgItemText(AFX_IDC_PRINT_PAGENUM, szBuf);

Example 6: (a) Initializing custom dialog with progress bar; (b) updating the progress bar.

(a)
CDDJPrintDialog ddjPrintDlg(this);


CString strTemp;


//Now initialize the progress control


CProgressCtrl * pProgress =


  (CProgressCtrl*)ddjPrintDlg.GetDlgItem(IDC_PROGRESS);


ASSERT(pProgress != NULL);





pProgress->SetPos(0); //start at 0


pProgress->SetRange(0,printInfo.GetMaxPage());//stop at max


pProgress->SetStep(1); //increment by 1 (not 10)




(b)
TCHAR szBuf[80];


wsprintf(szBuf, strTemp, printInfo.m_nCurPage);


ddjPrintDlg.SetDlgItemText(AFX_IDC_PRINT_PAGENUM,szBuf); 


pProgress->StepIt();

Listing One

class CDDJPrintPreview : public CPreviewView
{
// Construction
protected:
    // protected constructor used by dynamic creation
    CDDJPrintPreview(); 
    DECLARE_DYNCREATE(CDDJPrintPreview)
// Attributes - none
// Operations - none
// Overrides
protected:
    virtual void OnDraw(CDC* pDC); // overridden to draw this view
// Implementation
protected:
    virtual ~CDDJPrintPreview();
#ifdef _DEBUG
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;
#endif
protected:
    //Message map in case you want to handle any user
    //interaction.
    DECLARE_MESSAGE_MAP()
};

Listing Two

IMPLEMENT_DYNCREATE(CDDJPrintPreview, CPreviewView)

CDDJPrintPreview::CDDJPrintPreview()
{
//Do any custom print preview initialization here.    
}
CDDJPrintPreview::~CDDJPrintPreview()
{ //Destroy any custom print preview stuff here.
}
BEGIN_MESSAGE_MAP(CDDJPrintPreview, CPreviewView)

//Insert any custom print preview message handlers here.
END_MESSAGE_MAP()

//OnDraw override just calls TRACE and overriden OnDraw().
void CDDJPrintPreview::OnDraw(CDC* pDC)
{
    TRACE0("Called CDDJPrintPreview::OnDraw()!!");
    CPreviewView::OnDraw(pDC);
}
#ifdef _DEBUG
void CDDJPrintPreview::AssertValid() const
{
//Add any checks for customized print preview members here.
    CView::AssertValid();
}
void CDDJPrintPreview::Dump(CDumpContext& dc) const
{
//Add debug output for any customized print preview members here.
    CView::Dump(dc);
}
#endif //_DEBUG

Listing Three

void CDDJPrintPreview::OnDraw(CDC* pDC)
{
    // don't do anything if not fully initialized
    if (m_pPrintView == NULL || m_dcPrint.m_hDC == NULL)
        return;
    //DDJ - New
    CDdjsampView * pMyView; 
        //DDJ - End New
    CPoint ViewportOrg = pDC->GetViewportOrg();
    CPen rectPen;
    rectPen.CreatePen(PS_SOLID, 2, GetSysColor(COLOR_WINDOWFRAME));
    CPen shadowPen;
    shadowPen.CreatePen(PS_SOLID, 3, GetSysColor(COLOR_BTNSHADOW));
    //DDJ - New 
    CPen redPen;
    redPen.CreatePen(PS_SOLID,2,RGB(255,0,0));
    CPen bluePen;
    bluePen.CreatePen(PS_SOLID,1,RGB(0,0,255));
    //DDJ - End New
    m_pPreviewInfo->m_bContinuePrinting = TRUE;    
    for (UINT nPage = 0; nPage < m_nPages; nPage++)
    {
        int nSavedState = m_dcPrint.SaveDC();       
        // Use paint DC for print preview output
       m_pPreviewDC->SetOutputDC(pDC->GetSafeHdc());
        m_pPreviewInfo->m_nCurPage = m_nCurrentPage + nPage;
    // Only call PrepareDC if within page range, otherwise use default
    // rect to draw page rectangle
        if (m_nCurrentPage + nPage <= m_pPreviewInfo->GetMaxPage())
            m_pPrintView->OnPrepareDC(m_pPreviewDC,                     m_pPreviewInfo);
        m_pPreviewInfo->m_rectDraw.SetRect(0, 0,
            m_pPreviewDC->GetDeviceCaps(HORZRES),
            m_pPreviewDC->GetDeviceCaps(VERTRES));
        m_pPreviewDC->DPtoLP(&m_pPreviewInfo->m_rectDraw);
        // Draw empty page on screen
        pDC->SaveDC();          // save the output dc state
        CSize* pRatio = &m_pPageInfo[nPage].sizeScaleRatio;
        CRect* pRect = &m_pPageInfo[nPage].rectScreen;
        if (pRatio->cx == 0)
        {   // page position has not been determined
            PositionPage(nPage);    // compute page position
            if (m_nZoomState != ZOOM_OUT)
                ViewportOrg = -GetDeviceScrollPosition();
        }
        //This section draws the page and outline and 3-d shadow 
        pDC->SetMapMode(MM_TEXT);// Page Rectangle is in screen 
        pDC->SetViewportOrg(ViewportOrg);
        pDC->SetWindowOrg(0, 0);
        pDC->SelectStockObject(HOLLOW_BRUSH);
        pDC->SelectObject(&rectPen); 
        pDC->Rectangle(pRect);
        pDC->SelectObject(&shadowPen);
        pDC->MoveTo(pRect->right + 1, pRect->top + 3);
        pDC->LineTo(pRect->right + 1, pRect->bottom + 1);
        pDC->MoveTo(pRect->left + 3, pRect->bottom + 1);
        pDC->LineTo(pRect->right + 1, pRect->bottom + 1);
        // erase background to white (most paper is white)
        CRect rectFill = *pRect;
        rectFill.left += 1;
        rectFill.top += 1;
        rectFill.right -= 2;
        rectFill.bottom -= 2;
        ::FillRect(pDC->m_hDC, rectFill,                                (HBRUSH)GetStockObject(WHITE_BRUSH));
        //DDJ - New 
        //Now that the page is white we can draw our notebook paper!
        //If you want yellow legal paper, change the FillRect above.
        //Draw the red line
        pDC->SelectObject(&redPen);
        int nRedX = pRect->left + pRect->Width()/8; 
        pDC->MoveTo(nRedX,pRect->top + 2);
        pDC->LineTo(nRedX,pRect->bottom - 2);
        // Draw 30 blue lines - start 1/8th page from top
        int nBlueCurrentY = pRect->top + pRect->Height()/8;
        int nBlueYIncrement = (pRect->bottom - nBlueCurrentY)/30;
                pDC->SelectObject(&bluePen);
        
        for (int nBlueCount = 0; nBlueCount < 30; nBlueCount++){
           pDC->MoveTo(pRect->left + 3,nBlueCurrentY);
            pDC->LineTo(pRect->right - 2,nBlueCurrentY);
            nBlueCurrentY += nBlueYIncrement;
        }//end blue line loop.
        //Now let's do some three-hole-punching!
        //Draw one every 1/4 page except for last one.
        //Make it the size of a blue increment which looks good.
        pDC->SelectObject(rectPen);
        pDC->SelectObject(GetStockObject(BLACK_BRUSH));
        //All holes have same left/right, different top/bottom
        CRect rectHole;     
        rectHole.left = pRect->left + pRect->Width()/16;
        rectHole.right = rectHole.left + nBlueYIncrement;
        //Hole 1 
        rectHole.top = pRect->Height()/8;
        rectHole.bottom = rectHole.top + nBlueYIncrement;
        pDC->Ellipse(rectHole);
        //Hole 2
        rectHole.top = pRect->Height()/2;
        rectHole.bottom = rectHole.top + nBlueYIncrement;
        pDC->Ellipse(rectHole);
        //Hole 3
        rectHole.top = pRect->Height()/8 * 7;
        rectHole.bottom = rectHole.top + nBlueYIncrement;
        pDC->Ellipse(rectHole);
        //DDJ - End New
        pDC->RestoreDC(-1);     // restore to synchronized state
        // Some old OnDraw() code removed for brevity.
    }  //end of the page for loop.
    rectPen.DeleteObject();
    shadowPen.DeleteObject();
    //DDJ - New Nuke our pens..
    bluePen.DeleteObject();
    redPen.DeleteObject();
    //DDJ - end new
}

Listing Four

class CPrintingDialog : public CDialog
{
public:
    enum { IDD = AFX_IDD_PRINTDLG };
    CPrintingDialog::CPrintingDialog(CWnd* pParent)
        {
            Create(CPrintingDialog::IDD, pParent
            _afxWinState->m_bUserAbort = FALSE;
        }
    virtual ~CPrintingDialog() { }
    virtual BOOL OnInitDialog();
    virtual void OnCancel();
protected:
};


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.