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