Visual C++ Workshop
Session 10
R. Eckert

MORE MFC DIALOG BOXES AND CONTROLS, DIALOG-BASED APPLICATIONS 

MFC Dialog Box with a Combo Box Control

Sketch Application Color selection:
   Menu choices==>many handler functions
   Dialog Box w/ Edit control==>ambiguity
   Better to use a listbox or combo box
   Put color choices inside the box

Need:
   CColorDlg class with member variables:
      m_Combo (control derived from CComboBox)
         Use AddString() member function to fill Combo box list
      m_drawColor (string derived from CString)
         Will contain the user selection

SktchComboColor Example <Show the program in action>



Start out with Basic Sketch MFC application (no color menu choices)
   (Or remove Red/Green/Blue Drawing Color menu items from sketch)

Add "Drawing Color" menu item (IDM_DRAWINGCOLOR)
   (Or remove Red/Green/Blue Drawing Color menu items from sketch)

"Drawing Color" Dialog Box--

   Insert/Resource/Dialog/New
   Label: "Drawing Colors", IDD_COLORDLG
   Controls:
      Static label ("Drawing Color", default ID)
      Dropdown Combo Box control (IDC_DCOLOR)
   
Use ClassWizard to add--

   New Class: CColorDlg

      (1) Add IDC_DCOLOR member variables:
            m_Combo (Category Control, type CComboBox)
            m_drawColor (Category Value, type CString)

      (2) Add a New Message Map for WM_INITDIALOG
            Member Function: OnInitDialog()
            Edit the code by adding the following:
           // TODO: Add extra initialization here
           m_Combo.AddString("Red");
           m_Combo.AddString("Green");
           m_Combo.AddString("Blue");
           m_Combo.AddString("Black");  

   Add OnDrawingColor() Menu Command Message Map handler
           Class: CSketchView, 
           ObjectID: IDM_DRAWINGCOLOR
           Messages: Command
           Edit the code by adding the following:
            // TODO: Add your command handler code here
            CColorDlg colDlg;
            if(colDlg.DoModal()==IDOK)
            {
                if (colDlg.m_drawColor=="Red")
                        nColor=RGB(255,0,0);
                if (colDlg.m_drawColor=="Green")
                        nColor=RGB(0,255,0);
                if (colDlg.m_drawColor=="Blue")
                        nColor=RGB(0,0,255);
                if (colDlg.m_drawColor=="Black")
                        nColor=RGB(0,0,0);
            }  

#include "ColorDlg.h" in the SketchView.cpp file

Build and run the application


Using the Common Color Dialog Box

CColorDialog--a class derived from CDialog
Encapsulates the "Common Color" dialog box
Allows user to select colors

Construct a CColorDialog object
Call its DoModal() function to start the dialog box
Invoke its GetColor() member function-->RGB value chosen

Use this in the sketching application
(Combo box above is limited to small number of colors)

SketchComColor Example <Show program in action>



Start out with Basic Sketch MFC application (no color menu choices)

Add "Drawing Color" menu item (IDM_DRAWINGCOLOR)

Use ClassWizard to Add OnDrawingColor() Menu command handler
       Class: CSketchView, 
       ObjectID: IDM_DRAWINGCOLOR
       Messages: Command
       Edit the code by adding the following:
          // TODO: Add your command handler code here
          CColorDialog dlgColor;
          if (dlgColor.DoModal()==IDOK)
              nColor=dlgColor.GetColor();

Build and run the application


Text Fonts and the Common Font Dialog Box

FONT: Typeface, style, size of characters in a character set

Under Windows Fonts are "drawing objects"--
     Must be selected into a Device Context to be used

There are three basic kinds of fonts-

Stock fonts-
     Built into Windows
     Always available


Logical or GDI fonts-
     Defined in separate font resource files on disk-
          .fon (stroke or raster) file 
          .fot/.ttf (TrueType) file 






Stroke fonts-
     Consist of line/curve segments
     Continuously scalable
     Slow to draw
     Legibility not good

Raster fonts-
     Bitmaps
          Scaling by non-integer scaling factors difficult
          Fast to display
          Legibility very good

TrueType fonts-
     Rasterized stroke fonts
          Stored as strokes with hints to convert to bitmap
     Continuously scalable
     Fast to display
     Legibility very good
     Combine best of both stroke and raster fonts

Device fonts-
     Native to output device (e.g., built-in printer fonts).


Using stock fonts--

 Access with GetStockObject();
     Returns a handle to a font 
     Select into DC with SelectObject();

Using Logical Fonts-

     Can specify and create your own "Custom" font from available font files
     In MFC, Font specification/creation is encapsulated in the CFont class
     Two ways of using it:
          (1) Call CFont::CreateFont(), passing 14 font description parameters
          (2) Describe the font with a LOGFONT sturcture & pass ptr to
               that structure in a call to CFont::CreateFontIndirect()
     In each case, use SelectObject(&fnt) to select new font into the DC
     
CreateFont() can make new fonts by interpolating data in a font file
     New sizes, bold/underlined, rotated/distorted characters
     Called logical since generated by program logic

CFont  fnt;
fnt.CreateFont(Ht, Width, Escapement, Orientation, Weight, Italic,
                   Underline, StrikeOut, CharSet, OutputPrecision,
                   ClipPrecision, Quality, PitchAndFamily, Facename);

Heights and average Widths--measured in logical units
     Default logical unit of a screen DC: pixels

Escapement--Angle, in tenths of a degree, between line through the origins of first 
and last characters on a line and the x-axis of the screen surface

Orientation--how much a character should be rotated, in tenths of degree 
     (900 == character is lying on its back)

Weight--How thickly to print character strokes 
     (FW_NORMAL=400, FW_BOLD=700, others)

Italic--TRUE/FALSE

Underline--TRUE/FALSE

StrikeOut--TRUE/FALSE
     Characters with line through the center

CharSet--ANSI_CHARSET, SYMBOL_CHARSET, OEM_CHARSET, SHIFTJIS_CHARSET

OutputPrecision--How close output must match requested font height/width
orientation/escapement/pitch
     Several possibilities

ClipPrecision--How to clip characters partially outside clipping region

Quality--DRAFT_QUALITY, PROOF_QUALITY , DEFAULT_QUALITY

PitchAndFamily-2 bitwise OR values
     First (low order 2 bits): font pitch-
          (DEFAULT_PITCH, FIXED_PITCH, VARIABLE_PITCH)
     Second (high order 4 bits): font family-
     (FF_DECORATIVE, FF_DONTCARE, FF_MODERN, FF_ROMAN,  
      FF_SCRIPT, FF_SWISS)
     e.g., DEFAULT_PITCH | FF_ROMAN

Facename--String that specifies typeface name (style) of font data
     Identifies font resource data file, e.g.: 
          Courier, MS Serif, MS Sans Serif, Symbol, Small, Modern, Roman,  
          Script Courier New, Courier New Bold, Courier New Italic, Courier    
          New Bold, etc.
     Takes precedence over several other parameters

Example call to CreateFont()--

font.CreateFont(36, 0, 0, 0, FW_NORMAL, 0, 0, 0, ANSI_CHARSET,
OUT_DEFAULT_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,VARIABLE_PITCH|FF_ROMAN,"Roman");

Using CFont::CreateFontIndirect()--

1. Construct a CFont object
        CFont  font;

2. Set up a LOGFONT structure with desired font characteristics
        LOGFONT  lf;

typedef struct tagLOGFONT { /* lf */
   LONG lfHeight;
   LONG lfWidth;
   LONG lfEscapement;
   LONG lfOrientation;
   LONG lfWeight;
   BYTE lfItalic;
   BYTE lfUnderline;
   BYTE lfStrikeOut;
   BYTE lfCharSet;
   BYTE lfOutPrecision;
   BYTE lfClipPrecision;
   BYTE lfQuality;
   BYTE lfPitchAndFamily;
   CHAR lfFaceName[LF_FACESIZE];
} LOGFONT;

3. Call member function CreateFontIndirect(&lf)
       font.CreateFontIndirect(&lf);

Common Font Dialog Box-

Allows user to choose a logical font from all available on system
An easily-used dialog box
Encapsulated in the CFontDialog class
Construct a CFontDialog object
     Pass it a pointer to a LOGFONT structure
Call its DoModal() function to start the dialog box
     After User chooses a font, the LOGFONT structure will have the data
     LOGFONT m_logFont;
     CFontDialog dlgFont(&m_logFont);
     dlgFont.DoModal();  

TextComFont Example <Show program in action>





Use AppWizard to create the TextComFont SDI application template

Add menu items:
     "Text" (IDM_TEXT)
     "Font" (IDM_FONT)

Insert a new "Text Entry Dialog Box" resource (IDD_TEXTDLG)

Controls (most as in sketch3: the last Session-9 Example):
     "Text:" static control
     Single-line edit control (IDC_TEXTEDIT)
     "x:" and "y:" static controls
     Single-line edit controls for each (IDC_X and IDC_Y)
     "Angle" static control
     Single-line edit control for text angle (IDC_ANGLE)

Use ClassWizard to:
     Create the CTextDlg class (derived from CDialog)
     Attach member Value variables to the CTestDlg class controls:
          IDC_TEXTEDIT:  m_text, type CString
          IDC_ANGLE:  m_angle, type UINT
          IDC_X:  m_x, type UINT
          IDC_Y: m_y, type UINT

Add private member variables to the CTextComFontView class:
 (i.e., right click on the CTextComFontView class in ClassView):
     m_angle:  type UINT
     m_logFont:  type LOGFONT
     m_string:  type CString
     m_x:  type UINT
     m_y:  type UINT

Add initialization construction code to the CTextComFontView class:
   // TODO: add construction code here
   m_x=m_y=m_angle=0;
   m_string="";
   ZeroMemory(&m_logFont,sizeof(LOGFONT));
   lstrcpy(m_logFont.lfFaceName,"Times New Roman"); //Initial font

Add OnDraw() handler code to the CTextComFontView class:
   // TODO: add draw code for native data here
   CFont fnt;   //Create and select the chosen font/angle
   m_logFont.lfEscapement = m_angle*10;
   fnt.CreateFontIndirect(&m_logFont);
   CFont* pOldFont = pDC->SelectObject(&fnt);
   pDC->TextOut(m_x,m_y,m_string);
   pDC->SelectObject(pOldFont);
   fnt.DeleteObject();

Use ClassWizard to Add Message Map Command handler functions
to the CTextComFontView class menu items:
     (IDM_TEXT and IDM_FONT)

     Edit the code of the resulting OnText() handler function:
        // TODO: Add your command handler code here
        CTextDlg dlg;
        if (dlg.DoModal()==IDOK)   //invoke the "Text" dialog box
        {
           m_x=dlg.m_x; //get text dialog box control values
           m_y=dlg.m_y;
           m_string=dlg.m_text;
           m_angle=dlg.m_angle;
           Invalidate(); //force a window repaint
        }

     Edit the code of the resulting OnFont() handler function:
        // TODO: Add your command handler code here
        CFontDialog dlgFont(&m_logFont);
        dlgFont.DoModal();  //invoke the common font dialog box
        Invalidate();  //force a window repaint

Use ClassWizard to Add a Message Map handler function for
the CTextComView class's WM_LBUTTONDOWN message:

     Edit the resulting OnLButtondown handler function:
        // TODO: Add your message handler code here and/or call default
        m_x=point.x;
        m_y=point.y;

#include "TextDlg.h" in the TextComFontView.cpp file

Build and run the application

Note that if you choose a really big font, and select File/Print Preview--
     You're actually able to print the text in the window

     That's because when OnDraw() is called, the DC that's sent to it will
      be the appropriate one-- 
            OnPrint() and OnPaint() both call OnDraw()
            Correct DC is sent (screen or printer)
      
      But the text is probably too small--
           Printer dots are usually much smaller than screen pixels
           Check into Windows Mapping Modes!!!


FILES

Standard C Library File Functions--
     fopen(); fread(); fwrite(); fseek(); ftell(); fclose();

Opening a File--

FILE *fp;
fp = fopen("filename_string", "mode_string");

"mode_string":
     "r"--open file for reading; fails if file not found
     "w"--open empty file for writing; contents destroyed if file exists
     "a"--open or create file for appending (writing at the end)
     "r+"--open existing file for update (reading and writing); file must exist
     "w+"--open empty file for reading/writing; if file exists, contents are lost
     "a+"--open or create a file for reading and appending
Can be used with "b" suffix, meaning binary file; e.g., "rb", "wb"

If successful, returns a "file pointer"
     Used to identify file in subsequent accesses
If  not successful, returns a NULL pointer

Reading a File--

fread (buffer, size, number, fp);

     buffer--address of memory block where file data will be stored
     size--size in bytes of elements to be read (for char*, size is 1)
     number--number of elements to be read (for char*, use lstrlen())
     fp--file pointer identifying file

     Reads from file starting with element at "file position pointer" location
     File system (OS) keeps track of the "file position pointer location"
     When file is first opened ("r" or "w"), positioned at start of file
     When opened with "a" or "a+", positioned at end of file
     Returns number of elements actually read.

Writing a File--

fwrite (buffer, size, number, fp);

     buffer--address of memory block containing data to be written
     size--size in bytes of elements to be written
     number--number of elements to be written
     fp--file pointer identifying file

     Writes elements to file starting at location of the "file position pointer"
     Returns number of elements actually written

Moving to a Place in a File--

fseek(fp, offset, whence);

     fp--file pointer identifying file
     offset--number of bytes to move "file position pointer"
     whence--moved relative to what origin
          SEEK_SET(0): seek from beginning of file
          SEEK_CUR(1): seek from current position
          SEEK_END(2): seek from end of file

     Moves "file position pointer" to a new position in file
     Indicates byte position in file where next element is to be read/written
     Returns 0 if successful
     Returns an error code if not successful

Obtaining the Current File Position--

cur_posn = ftell(fp);

     fp--file pointer identifying file
     Returns current position of "file position pointer"

Closing a File--

fclose(fp);

     fp--file pointer identifying file to be closed
     Closes specified file (makes permanent)

Example Fragment 1--Create EX1.TXT & Copy a String to it:

FILE    *fp;
char      szData[] = "Text to copy to file"
fp = fopen ("EX1.TXT", "w");
if (fp != NULL)
{
      fwrite (szData, 1, lstrlen(szData), fp);
      fclose (fp);
}

Example Fragment 2--Read First 10 Characters from EX1.TXT into a Buffer:

FILE      *fp;
char      cBuf[128];
fp = fopen ("EX1.TXT", "r");
if (fp != NULL)
{
      fread (cBuf, 1, 10, fp);
      fclose (fp);
}

Example Fragment 3--Move 10 Bytes from start of EX1.TXT, Read next 5 Bytes:

FILE      *fp;
char      cBuf[128];
fp = fopen ("EX1.TXT", "r");
if (fp != NULL)
{
     fseek (fp, 10, SEEK_SET);
     fread (cBuf, 1, 5, fp);
     fclose (fp);
}

Use of Common File Dialog Boxes

Among the most useful of Windows common dialog boxes
Allows user to specify file to be opened or saved
Very familiar to Windows users
Common File Dialog Box encapsulated in the CFileDialog
Works with an OPENFILENAME member data structure

typedef struct tagOFN
    {
    DWORD         lStructSize;
    HWND          hwndOwner;
    HINSTANCE     hInstance;
    LPCTSTR       lpstrFilter;
    LPTSTR        lpstrCustomFilter;
    DWORD         nMaxCustFilter;
    DWORD         nFilterIndex;
    LPTSTR        lpstrFile;
    DWORD         nMaxFile;
    LPTSTR        lpstrFileTitle;
    DWORD         nMaxFileTitle;
    LPCTSTR       lpstrInitialDir;
    LPCTSTR       lpstrTitle;
    DWORD         Flags;
    WORD          nFileOffset;
    WORD          nFileExtension;
    LPCTSTR       lpstrDefExt;
    DWORD         lCustData;
    LPOFNHOOKPROC lpfnHook;
    LPCTSTR       lpTemplateName; }
    OPENFILENAME;


Steps to follow:

Construct a CFileDialog object
     param=TRUE==>Open (read file)
     param==FALSE==>Save (write file)
Call its DoModal() function to start the dialog box
     After User chooses a file name/path, 
          m_ofn member structure will have the data
          Use "Get" member functions to retrieve the data, e.g.:
             CFileDialog m_ldFile(TRUE);
             m_ldFile.DoModal();
             CString fn=m_ldFile.GetPathName();

           
Dialog-Box Based Applications

Dialog-Based Projects use a dialog box as the main window
Nice for simple applications with no complex menus
Fewer classes--most action occurs in a single CDialog-based class

The FileComDlg Application <Show program in action>



Use AppWizard to create a Dialog-based application template:
     File/New/MFC AppWizard(exe), name it FileComDlg
     Step 1: choose "Dialog based "
     Step 2 of 4: deselect "ActiveX Controls"
     Step 3 of 4: accept defaults
     Step 4 of 4: accept defaults
     Finish/OK

Delete the "TODO" static control in the dialog box template

Add the following Controls to the dialog box template:
     "Open File" Button (IDC_OPEN)
     "Save File" Button (IDC_SAVE)
     "Text:" Static Control
     A Large multiline Edit control (IDC_EDIT, style: multiline)

Add private variable m_sFileName to the CFileComDlgDlg class
     Type: CString
     (Right click on CFileComDlgDlg class) 

Use ClassWizard to attach m_string variable to the Edit Control:
      In CFlilComDlgDlg class, Control ID: IDC_EDIT:
         Variable name: m_string, Category: Value, Type: CString 

Use ClassWizard to add button-clicked Message Map handler functions
to each button for the CFileComDlgDlg class:
     IDC_OPEN:   BN_CLICKED -- OnOpen()
     IDC_SAVE:   BN_CLICKED -- OnSave()

Add following code to the OnOpen() function:

   // TODO: Add your control notification handler code here
   FILE *fp;
   int nFileLong;

   CFileDialog m_ldFile(TRUE);      //Construct for reading 
   if (m_ldFile.DoModal() == IDOK)  //Start File dlg box
   {
      m_sFileName=m_ldFile.GetPathName();  //Get file name
      fp=fopen(m_sFileName,"rb");   //Open file for reading
      fseek(fp,0,SEEK_END);         //Go to file end 
      nFileLong=ftell(fp);          //Get length
      char* sText = new char[nFileLong+1]; //reserve string space
      fseek(fp,0,SEEK_SET);         //Go to file start 
      int i=fread(sText,1,nFileLong,fp); //Read the characters
      sText[i]=0;              //Set string terminating null
      m_string=sText;          //Put text in Edit box's variable
      fclose(fp);              //Close file      

      UpdateData(FALSE);       //Force data to go to Edit control
   }

Add following code to the OnSave() function:

    // TODO: Add your control notification handler code here
    FILE *fp;
    int nFileLong;

    UpdateData(TRUE); //Force data from Edit box to variable

    CFileDialog m_ldFile(FALSE);     //Construct for writing
    if (m_ldFile.DoModal() == IDOK)  //Start file dlg box
    {
        m_sFileName=m_ldFile.GetPathName(); //Get file name
        nFileLong = m_string.GetLength();  //Length of text in Edit box
        fp=fopen(m_sFileName,"wb");       //Open file for writing
        fwrite(LPCTSTR(m_string),1,nFileLong,fp); //Write text to file
        fclose(fp);                       //Close file
    }

Build and Run the Application
   Try opening (reading) arbitray text file on system
   Try saving resulting text to a new file
   Try typing in text
       Note: To get a carriage control, use <Ctrl> <Enter>
   Try deleting/cutting/pasting
       Part of built-in Edit control functionality