Visual Programming Notes1 Kinindia
Visual Programming Notes1 Kinindia
fit on a single screen by using scroll bars. Some menu items invoke dialog boxes, into which the user enters additional information. One dialog box in particular, that used to open a file, can be found in almost every large Windows program. This dialog box looks the same (or nearly the same) in all of these Windows programs, and it is almost always invoked from the same menu option. From the programmer's perspective, the consistent user interface results from using the routines built into Windows for constructing menus and dialog boxes. All menus have the same keyboard and mouse interface because Windowsrather than the application programhandles this job. To facilitate the use of multiple programs, and the exchange of information among them, Windows supports multitasking. Several Windows programs can be displayed and running at the same time. Each program occupies a window on the screen. The user can move the windows around on the screen, change their sizes, switch between different programs, and transfer data from one program to another. Earlier versions of Windows used a system of multitasking called "nonpreemptive." This meant that Windows did not use the system timer to slice processing time between the various programs running under the system. The programs themselves had to voluntarily give up control so that other programs could run. Under Windows NT and Windows 98, multitasking is preemptive and programs themselves can split into multiple threads of execution that seem to run concurrently. Programs running in Windows can share routines that are located in other files called "dynamic-link libraries." Windows includes a mechanism to link the program with the routines in the dynamic-link libraries at run time. Windows itself is basically a set of dynamic-link libraries. Windows is a graphical interface, and Windows programs can make full use of graphics and formatted text on both the video display and the printer. A graphical interface not only is more attractive in appearance but also can impart a high level of information to the user Programs written for Windows do not directly access the hardware of graphics display devices such as the screen and printer. Instead, Windows includes a graphics programming language (called the Graphics Device Interface, or GDI) that allows the easy display of graphics and formatted text. Windows virtualizes display hardware. A program written for Windows will run with any video board or any printer for which a Windows device driver is available. The program does not need to determine what type of device is attached to the system. Dynamic Linking Windows provides a wealth of function calls that an application can take advantage of, mostly to implement its user interface and display text and graphics on the video display. These functions are implemented in dynamic-link libraries, or DLLs. When you run a Windows program, it interfaces to Windows through a process called "dynamic linking." A Windows .EXE file contains references to the various dynamiclink libraries it uses and the functions therein. When a Windows program is loaded
into memory, the calls in the program are resolved to point to the entries of the DLL functions, which are also loaded into memory if not already there. When you link a Windows program to produce an executable file, you must link with special "import libraries" provided with your programming environment. These import libraries contain the dynamic-link library names and reference information for all the Windows function calls. The linker uses this information to construct the table in the .EXE file that Windows uses to resolve calls to Windows functions when loading the program. Windows Programming Options APIs and Memory Models To a programmer, an operating system is defined by its API. An API encompasses all the function calls that an application program can make of an operating system, as well as definitions of associated data types and structures. Windows NT and Windows 98 are both considered to support the Win32 API. Language Options Using C and the native APIs is not the only way to write programs for Windows 98. However, this approach offers you the best performance, the most power, and the greatest versatility in exploiting the features of Windows. Executables are relatively small and don't require external libraries to run (except for the Windows DLLs themselves, of course). Visual Basic or Borland Delphi Microsoft Microsoft Visual C++ with the Microsoft Foundation Class Library (MFC) - MFC encapsulates many of the messier aspects of Windows programming in a collection of C++ classes.
Your First Windows Program /*------------------------------------------------------------HelloMsg.c -- Displays "Hello, Windows 98!" in a message box --------------------------------------------------------------*/ #include <windows.h> int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,PSTR szCmdLine, int iCmdShow) { MessageBox (NULL, TEXT ("Hello, Windows 98!"), TEXT ("HelloMsg"), 0) ; return 0 ; } The Header Files HELLOMSG.C begins with a preprocessor directive that you'll find at the top of virtually every Windows program written in C: #include <windows.h> WINDOWS.H - master include file - includes other Windows header files,
some of which also include other header files. The most important and most basic of these header files are: WINDEF.H Basic type definitions. WINNT.H Type definitions for Unicode support. WINBASE.H Kernel functions. WINUSER.H User interface functions. WINGDI.H Graphics device interface functions. These header files define all the Windows data types, function calls, data structures, and constant identifiers. Program Entry Point Just as the entry point to a C program is the function main, the entry point to a Windows program is WinMain, which always appears like this: int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) Hungarian Notation
Prefix b c or ch clr cx, cy dw h l n p sz w wnd str m_ Data Type BOOL char COLORREF Horizontal or vertical distance DWORD Handle LONG int Pointer Zero-terminated string WORD CWnd CString class member variable
The WinMain function is declared as returning an int. The WINAPI identifier is defined in WINDEF.H with the statement: #define WINAPI __stdcall This statement specifies a calling convention that involves how machine code is generated to place function call arguments on the stack. Most Windows function calls are declared as WINAPI. The first parameter to WinMain is called an "instance handle." - simply a number that an application uses to identify the program. The second parameter to WinMain is always NULL (defined as 0). The third parameter to WinMain is the command line used to run the program.
The fourth parameter to WinMain indicates how the program should be initially displayedeither normally or maximized to fill the window, or minimized to be displayed in the task list bar. The MessageBox Function The MessageBox function is designed to display short messages. The little window that MessageBox displays is actually considered to be a dialog box, although not one with a lot of versatility. The first argument to MessageBox is normally a window handle. The second argument is the text string that appears in the body of the message box, and the third argument is the text string that appears in the caption bar of the message box. In HELLMSG.C, each of these text strings is enclosed in a TEXT macro. The fourth argument to MessageBox can be a combination of constants beginning with the prefix MB_ that are defined in WINUSER.H. You can pick one constant from the first set to indicate what buttons you wish to appear in the dialog box: #define MB_OK 0x00000000L #define MB_OKCANCEL 0x00000001L #define MB_ABORTRETRYIGNORE 0x00000002L #define MB_YESNOCANCEL 0x00000003L #define MB_YESNO 0x00000004L #define MB_RETRYCANCEL 0x00000005L Windows Environment
CHAPTER 2 Windows and Messages Windows In Windows, the word "window" has a precise meaning. A window is a rectangular area on the screen that receives user input and displays output in the form of text and graphics. The MessageBox function creates a window, but it is a special-purpose window of limited flexibility. The message box window has a title bar with a close button, an optional icon, one or more lines of text, and up to four buttons. However, the icons and buttons must be chosen from a small collection that Windows provides for you. We can't display graphics in a message box, and we can't add a menu to a message box. For that we need to create our own windows. An Architectural Overview The user sees windows as objects on the screen and interacts directly with them using the keyboard or the mouse. Interestingly enough, the programmer's perspective is analogous to the user's perspective.
The window receives the user input in the form of "messages" to the window. A window also uses messages to communicate with other windows. Getting a good feel for messages is an important part of learning how to write programs for Windows. Every window that a program creates has an associated window procedure. This window procedure is a function that could be either in the program itself or in a dynamic-link library. Windows sends a message to a window by calling the window procedure. The window procedure does some processing based on the message and then returns control to Windows. More precisely, a window is always created based on a "window class." The window class identifies the window procedure that processes messages to the window. The use of a window class allows multiple windows to be based on the same window class and hence use the same window procedure. For example, all buttons in all Windows programs are based on the same window class. This window class is associated with a window procedure located in a Windows dynamic-link library that processes messages to all the button windows. Creating the Window The window class defines general characteristics of a window, thus allowing the same window class to be used for creating many different windows. When you go ahead and create a window by calling CreateWindow, you specify more detailed information about the window. Why all the characteristics of a window can't be specified in one shot. Actually, dividing the information in this way is quite convenient. For example, all push-button windows are created based on the same window class. The window procedure associated with this window class is located inside Windows itself, and it is responsible for processing keyboard and mouse input to the push button and defining the button's visual appearance on the screen. All push buttons work the same way in this respect. But not all push buttons are the same. They almost certainly have different sizes, different locations on the screen, and different text strings. These latter characteristics are part of the window definition rather than the window class definition. Creating the Window hwnd = CreateWindow (szAppName, // window class name TEXT ("The Hello Program"), // window caption WS_OVERLAPPEDWINDOW, // window style CW_USEDEFAULT, // initial x position CW_USEDEFAULT, // initial y position CW_USEDEFAULT, // initial x size CW_USEDEFAULT, // initial y size NULL, // parent window handle NULL, // window menu handle hInstance, // program instance handle NULL) ; // creation parameters Creating the Window Overlapped window
It will have a title bar; a system menu button to the left of the title bar; a thick window-sizing border; and minimize, maximize, and close buttons to the right of the title bar. - standard style for windows, In WINUSER.H, this style is a combination of several bit flags:
#define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | \ WS_CAPTION |\ WS_SYSMENU |\ WS_THICKFRAME | \ WS_MINIMIZEBOX | \ WS_MAXIMIZEBOX) By default, Windows positions successive newly created windows at stepped horizontal and vertical offsets from the upper left corner of the display. The CreateWindow call returns a handle to the created window. This handle is saved in the variable hwnd, which is defined to be of type HWND ("handle to a window"). Every window in Windows has a handle. Your program uses the handle to refer to the window. Many Windows functions require hwnd as an argument so that Windows knows which window the function applies to. If a program creates many windows, each has a different handle. The handle to a window is one of the most important handles that a Windows program handles. Displaying the Window After the CreateWindow call returns, the window has been created internally in Windows. What this means basically is that Windows has allocated a block of memory to hold all the information about the window that you specified in the CreateWindow call, plus some other information, all of which Windows can find later based on the window handle. However, the window does not yet appear on the video display. Two more calls are needed. ShowWindow (hwnd, iCmdShow) ; The first argument is the handle to the window just created by CreateWindow. The second argument is the iCmdShow value passed as a parameter to WinMain. This determines how the window is to be initially displayed on the screen, whether it's normal, minimized, or maximized. The user probably selected a preference when adding the program to the Start menu. The value you receive from WinMain and pass to ShowWindow is SW_SHOWNORMAL if the window is displayed normally, SW_SHOWMAXIMIZED if the window is to be maximized, and SW_SHOWMINNOACTIVE if the window is just to be displayed in the taskbar.
The ShowWindow function puts the window on the display. If the second argument to ShowWindow is SW_SHOWNORMAL, the client area of the window is erased with the background brush specified in the window class. UpdateWindow (hwnd) ; then causes the client area to be painted. It accomplishes this by sending the window procedure ( the WndProc function in HELLOWIN.C) a WM_PAINT message.
The Message Loop After the UpdateWindow call, the window is fully visible on the video display. The program must now make itself ready to read keyboard and mouse input from the user. Windows maintains a "message queue" for each Windows program currently running under Windows. When an input event occurs, Windows translates the event into a "message" that it places in the program's message queue. A program retrieves these messages from the message queue by executing a block of code known as the "message loop": while (GetMessage (&msg, NULL, 0, 0)) { TranslateMessage (&msg) ; DispatchMessage (&msg) ; } The Message Loop (2) The msg variable is a structure of type MSG, which is defined in the WINUSER.H header file like this: typedef struct tagMSG { HWND hwnd ; UINT message ; WPARAM wParam ; LPARAM lParam ; DWORD time ; POINT pt ; } MSG, * PMSG ; The POINT data type is yet another structure, defined in the WINDEF.H header file like this: typedef struct tagPOINT { LONG x ; LONG y ; } POINT, * PPOINT; The Message Loop TranslateMessage (&msg) ; Passes the msg structure back to Windows for some keyboard translation.
DispatchMessage (&msg) ; Again passes the msg structure back to Windows. Windows then sends the message to the appropriate window procedure for processing
The Window Procedure The window class has been registered, the window has been created, the window has been displayed on the screen, and the program has entered a message loop to retrieve messages from the message queue. The real action occurs in the window procedure. The window procedure determines what the window displays in its client area and how the window responds to user input. The Window Procedure LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) The four parameters to the window procedure are identical to the first four fields of the MSG structure Processing the Messages Every message that a window procedure receives is identified by a number, which is the message parameter to the window procedure. The Windows header file WINUSER.H defines identifiers beginning with the prefix WM ("window message") for each type of message. Generally, Windows programmers use a switch and case construction to determine what message the window procedure is receiving and how to process it accordingly. When a window procedure processes a message, it should return 0 from the window procedure. All messages that a window procedure chooses not to process must be passed to a Windows function named DefWindowProc. The value returned from DefWindowProc must be returned from the window procedure.
In HELLOWIN, WndProc chooses to process only three messages: WM_CREATE, WM_PAINT, and WM_DESTROY. The window procedure is structured like this: switch (iMsg) { case WM_CREATE : [process WM_CREATE message] return 0 ; case WM_PAINT : [process WM_PAINT message] return 0 ; case WM_DESTROY :
[process WM_DESTROY message] return 0 ; } return DefWindowProc (hwnd, iMsg, wParam, lParam) ; It is important to call DefWindowProc for default processing of all messages that your window procedure does not process. Otherwise behavior regarded as normal, such as being able to terminate the program, will not work.
CHAPTER 3 Text Output Introduction Client area is the part of the window on which a program is free to draw and deliver visual information to the user. You can do almost anything you want with your program's client areaanything, that is, except assume that it will be a particular size or that the size will remain constant while your program is running. When a program displays text or graphics in its client area, it is often said to be "painting" its client area. Windows programs can assume little about the size of their client areas or even the size of text characters. Instead, they must use the facilities that Windows provides to obtain information about the environment in which the program runs.
Painting and Repainting In Windows, you can draw text and graphics only in the client area of your window, and you cannot be assured that what you put will remain there until your program specifically writes over it. For instance, the user may move another program's window on the screen so that it partially covers your application's window. Windows will not attempt to save the area of your window that the other program covers.When the program is moved away, Windows will request that your program repaint this portion of your client area. Windows is a message-driven system. Windows informs applications of various events by posting messages in the application's message queue or sending messages to the appropriate window procedure. Windows informs a window procedure that part of the window's client area needs painting by posting a WM_PAINT message. The WM_PAINT Message Most Windows programs call the function UpdateWindow during initialization in WinMain shortly before entering the message loop. Windows takes this opportunity to send the window procedure its first WM_PAINT message. This message informs the window procedure that the client area must be painted. Thereafter, that window procedure should be ready at almost any time to process additional WM_PAINT messages and even to repaint the entire client area of the window if necessary.
A window procedure receives a WM_PAINT message whenever one of the following events occurs: A previously hidden area of the window is brought into view when a user moves a window or uncovers a window. The user resizes the window (if the window class style has the CS_HREDRAW and CW_VREDRAW bits set). The program uses the ScrollWindow or ScrollDC function to scroll part of its client area. The program uses the InvalidateRect or InvalidateRgn function to explicitly generate a WM_PAINT message. Windows may sometimes post a WM_PAINT message when: Windows removes a dialog box or message box that was overlaying part of the window. A menu is pulled down and then released. A tool tip is displayed. In a few cases, Windows always saves the area of the display it overwrites and then restores it. This is the case whenever: The mouse cursor is moved across the client area. An icon is dragged across the client area. Your program should be structured so that it accumulates all the information necessary to paint the client area but paints only "on demand"when Windows sends the window procedure a WM_PAINT message. If your program needs to update its client area at some other time, it can force Windows to generate this WM_PAINT message. Valid and Invalid Rectangles Although a window procedure should be prepared to update the entire client area whenever it receives a WM_PAINT message, it often needs to update only a smaller area, most often a rectangular area within the client area. This is most obvious when a dialog box overlies part of the client area. Repainting is required only for the rectangular area uncovered when the dialog box is removed. That area is known as an "invalid region" or "update region." The presence of an invalid region in a client area is what prompts Windows to place a WM_PAINT message in the application's message queue. Your window procedure receives a WM_PAINT message only if part of your client area is invalid. Windows internally maintains a "paint information structure" for each window. This structure contains, among other information, the coordinates of the smallest rectangle that encompasses the invalid region. This is known as the "invalid rectangle. If another region of the client area becomes invalid before the window procedure processes a pending WM_PAINT message, Windows calculates a new invalid region (and a new invalid rectangle) that encompasses both areas and stores this updated information in the paint information structure. Windows does not place multiple WM_PAINT messages in the message queue. A window procedure can invalidate a rectangle in its own client area by calling InvalidateRect.
If the message queue already contains a WM_PAINT message, Windows calculates a new invalid rectangle. Otherwise, it places a WM_PAINT message in the message queue. A window procedure can obtain the coordinates of the invalid rectangle when it receives a WM_PAINT message It can also obtain these coordinates at any other time by calling GetUpdateRect. After the window procedure calls BeginPaint during the WM_PAINT message, the entire client area is validated. A program can also validate any rectangular area within the client area by calling the ValidateRect function. If this call has the effect of validating the entire invalid area, then any WM_PAINT message currently in the queue is removed. Introduction The subsystem of Microsoft Windows responsible for displaying graphics on video displays and printers is known as the Graphics Device Interface (GDI). GDI is an extremely important part of Windows. Not only do the applications you write for Windows use GDI for the display of visual information, but Windows itself uses GDI for the visual display of user interface items such as menus, scroll bars, icons, and mouse cursors. The Device Context When you want to draw on a graphics output device such as the screen or printer, you must first obtain a handle to a device context (or DC). In giving your program this handle, Windows is giving you permission to use the device. You then include the handle as an argument to the GDI functions to identify to Windows the device on which you wish to draw. The device context contains many "attributes" that determine how the GDI functions work on the device. These attributes allow GDI functions to have just a few arguments, such as starting coordinates. The GDI functions do not need arguments for everything else that Windows needs to display the object on the device. For example, when you call TextOut, you need specify in the function only the device context handle, the starting coordinates, the text, and the length of the text. You don't need to specify the font, the color of the text, the color of the background behind the text, or the intercharacter spacing. These are all attributes that are part of the device context. When you want to change one of these attributes, you call a function that does so. Subsequent TextOut calls to that device context use the new attribute. Getting a Device Context Handle Windows provides several methods for obtaining a device context handle. If you obtain a video display device context handle while processing a message, you should release it before exiting the window procedure. After you release the handle, it is no longer valid.
The most common method for obtaining a device context handle and then releasing it involves using the BeginPaint and EndPaint calls when processing the WM_PAINT message: hdc = BeginPaint (hwnd, &ps) ; [other program lines] EndPaint (hwnd, &ps) ; Getting a Device Context Handle Windows programs can also obtain a handle to a device context while processing messages other than WM_PAINT: hdc = GetDC (hwnd) ; [other program lines] ReleaseDC (hwnd, hdc) ; Getting a Device Context Handle The BeginPaint, GetDC, and GetWindowDC calls obtain a device context associated with a particular window on the video display. A much more general function for obtaining a handle to a device context is CreateDC: hdc = CreateDC (pszDriver, pszDevice, pszOutput, pData) ; [other program lines] DeleteDC (hdc) ; Getting Device Context Information A device context usually refers to a physical display device such as a video display or a printer. Often, you need to obtain information about this device, including the size of the display, in terms of both pixels and physical dimensions, and its color capabilities. You can get this information by calling the GetDeviceCap ("get device capabilities") function: iValue = GetDeviceCaps (hdc, iIndex) ; The iIndex argument is one of 29 identifiers defined in the WINGDI.H header file. For example, the iIndex value of HORZRES causes GetDeviceCaps to return the width of the device in pixels; a VERTRES argument returns the height of the device in pixels. If hdc is a handle to a screen device context, that's the same information you can get from GetSystemMetrics. If hdc is a handle to a printer device context, GetDeviceCaps returns the height and width of the printer display area in pixels. You can also use GetDeviceCaps to determine the device's capabilities of processing various types of graphics. DEVCAPS1 display for a 256-color, 640-by-480 VGA. The Size of the Device
The GetDeviceCaps function helps you obtain information regarding the physical size of the output device, be it the video display or printer. Within a Windows program you can use the GetDeviceCaps function to obtain the assumed resolution in dots per inch that the user selected in the Display applet of the Control Panel
Finding Out About Color A video display capable of displaying only black pixels and white pixels requires only one bit of memory per pixel. Color displays require multiple bits per pixels. The more bits, the more colors; or more specifically, the number of unique simultaneous colors is equal to 2 to the number of bits per pixel. iBitsPixel = GetDeviceCaps (hdc, BITSPIXEL) ; iColors = GetDeviceCaps (hdc, NUMCOLORS) ; The Device Context Attributes Saving Device Contexts Normally when you call GetDC or BeginPaint, Windows gives you a device context with default values for all the attributes. Any changes you make to the attributes are lost when the device context is released with the ReleaseDC or EndPaint call. If your program needs to use non-default device context attributes, you'll have to initialize the device context every time you obtain a new device context handle: Saving Device Contexts Although this approach is generally satisfactory, you might prefer that changes you make to the attributes be saved when you release the device context so that they will be in effect the next time you call GetDC or BeginPaint. You can accomplish this by including the CS_OWNDC flag as part of the window class style when you register the window class: wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC ; Saving Device Contexts Now each window that you create based on this window class will have its own private device context that continues to exist when the window is destroyed. When you use the CS_OWNDC style, you need to initialize the device context attributes only once, perhaps while processing the WM_CREATE message: case WM_CREATE: hdc = GetDC (hwnd) ; [initialize device context attributes] ReleaseDC (hwnd, hdc) ; The attributes continue to be valid until you change them.
Saving Device Contexts In some cases you might want to change certain device context attributes, do some painting using the changed attributes, and then revert to the original device context. To simplify this process, you save the state of a device context by calling
idSaved = SaveDC (hdc) ; Now you can change some attributes. When you want to return to the device context as it existed before the SaveDC call, you use RestoreDC (hdc, idSaved) ; You can call SaveDC any number of times before you call RestoreDC.
Creating the Child Windows Class name Window text Window style x position y position Width Height Parent window Child window Instance handle Extra parameters Push Buttons TEXT ("button") button[i].szText WS_CHILD WS_VISIBLE button[i].iStyle cxChar cyChar * (1 + 2 * i) 20 * xChar 7 * yChar / 4 hwnd ID(HMENU) I ((LPCREATESTRUCT) lParam) -> hInstance NULL
A push button is a rectangle enclosing text specified in the window text parameter of the CreateWindow call. The rectangle takes up the full height and width of the dimensions given in the CreateWindow or MoveWindow call. The text is centered within the rectangle. Push-button controls are used mostly to trigger an immediate action without retaining any type of on/off indication. The two types of push-button controls have window styles called BS_PUSHBUTTON and BS_DEFPUSHBUTTON.
When used to design dialog boxes, BS_PUSHBUTTON controls and BS_DEFPUSHBUTTON controls function differently from one another. When used as child window controls, however, the two types of push buttons function the same way, although BS_DEFPUSHBUTTON has a heavier outline. A push button looks best when its height is 7/4 times the height of a text character, which is what BTNLOOK uses. The push button's width must accommodate at least the width of the text, plus two additional characters. When the mouse cursor is inside the push button, pressing the mouse button causes the button to repaint itself using 3D-style shading to appear as if it's been depressed. Releasing the mouse button restores the original appearance and sends a WM_COMMAND message to the parent window with the notification code BN_CLICKED. As with the other button types, when a push button has the input focus, a dashed line surrounds the text and pressing and releasing the Spacebar has the same effect as pressing and releasing the mouse button. You can simulate a push-button flash by sending the window a BM_SETSTATE message.
This causes the button to be depressed: SendMessage (hwndButton, BM_SETSTATE, 1, 0) ; This call causes the button to return to normal: SendMessage (hwndButton, BM_SETSTATE, 0, 0) ;
Check Boxes A check box is a square box with text; the text usually appears to the right of the check box. If you include the BS_LEFTTEXT style when creating the button, the text appears to the left; you'll probably want to combine this style with BS_RIGHT to right-justify the text. Check boxes are usually incorporated in an application to allow a user to select options. The check box commonly functions as a toggle switch: clicking the box once causes a check mark to appear; clicking again toggles the check mark off. The two most common styles for a check box are BS_CHECKBOX and BS_AUTOCHECKBOX. When you use the BS_CHECKBOX style, you must set the check mark yourself by sending the control a BM_SETCHECK message. The wParam parameter is set to 1 to create a check mark and to 0 to remove it. You can obtain the current check state of the box by sending the control a BM_GETCHECK message. You might use code like this to toggle the X mark when processing a WM_COMMAND message from the control: SendMessage ((HWND) lParam, BM_SETCHECK, (WPARAM) !SendMessage ((HWND) lParam, BM_GETCHECK, 0, 0), 0) ;
The other two check box styles are BS_3STATE and BS_AUTO3STATE. As their names indicate, these styles can display a third state as wella gray color within the check boxwhich occurs when you send the control a WM_SETCHECK message with wParam equal to 2. The gray color indicates to the user that the selection is indeterminate or irrelevant. The check box is aligned with the rectangle's left edge and is centered within the top and bottom dimensions of the rectangle that were specified during the CreateWindow call. Clicking anywhere within the rectangle causes a WM_COMMAND message to be sent to the parent. The minimum height for a check box is one character height. The minimum width is the number of characters in the text, plus two. Radio Buttons A radio button is named after the row of buttons that were once quite common on car radios. Each button on a car radio is set for a different radio station, and only one button can be pressed at a time. In dialog boxes, groups of radio buttons are conventionally used to indicate mutually exclusive options. Unlike check boxes, radio
buttons do not work as togglesthat is, when you click a radio button a second time, its state remains unchanged. The radio button looks very much like a check box except that it contains a little circle rather than a box. A heavy dot within the circle indicates that the radio button has been checked. The radio button has the window style BS_RADIOBUTTON or BS_AUTORADIOBUTTON, but the latter is used only in dialog boxes.
Group Boxes The group box, which has the BS_GROUPBOX style, is an oddity in the button class. It neither processes mouse or keyboard input nor sends WM_COMMAND messages to its parent. The group box is a rectangular outline with its window text at the top. Group boxes are often used to enclose other button controls. The Scroll Bar Class You add window scroll bars to a window by including the identifier WS_VSCROLL or WS_HSCROLL or both in the window style when creating the window. You create child window scroll bar controls by using the predefined window class "scrollbar" and one of the two scroll bar styles SBS_VERT and SBS_HORZ. Unlike the button controls, scroll bar controls do not send WM_COMMAND messages to the parent window. Instead, they send WM_VSCROLL and WM_HSCROLL messages, just like window scroll bars. When processing the scroll bar messages, you can differentiate between window scroll bars and scroll bar controls by the lParam parameter. It will be 0 for window scroll bars and the scroll bar window handle for scroll bar controls. The high and low words of the wParam parameter have the same meaning for window scroll bars and scroll bar controls. Although window scroll bars have a fixed width, Windows uses the full rectangle dimensions given in the CreateWindow call to size the scroll bar controls. You can make long, thin scroll bar controls or short, pudgy scroll bar controls. You can set the range and position of a scroll bar control with the same calls used for window scroll bars: SetScrollRange (hwndScroll, SB_CTL, iMin, iMax, bRedraw) ; SetScrollPos (hwndScroll, SB_CTL, iPos, bRedraw) ; SetScrollInfo (hwndScroll, SB_CTL, &si, bRedraw) ; The difference is that window scroll bars use a handle to the main window as the first parameter and SB_VERT or SB_HORZ as the second parameter.
When you create a child window using the class name "edit," you define a rectangle based on the x position, y position, width, and height parameters of the CreateWindow call. This rectangle contains editable text. When the child window control has the input focus, you can type text, move the cursor, select portions of text using either the mouse or the Shift key and a cursor key, delete selected text to the clipboard by pressing Ctrl-X, copy text by pressing Ctrl-C, and insert text from the clipboard by pressing Ctrl-V. One of the simplest uses of edit controls is for single-line entry fields. But edit controls are not limited to single lines, to use menus, dialog boxes (to load and save files), and printing. Create an edit control using "edit" as the window class in the CreateWindow call. The window style is WS_CHILD, plus several options. As in static child window controls, the text in edit controls can be left-justified, right-justified, or centered. You specify this formatting with the window styles ES_LEFT, ES_RIGHT, and ES_CENTER. By default, an edit control has a single line. You can create a multiline edit control with the window style ES_MULTILINE. To create an edit control that automatically scrolls horizontally, you use the style ES_AUTOHSCROLL. For a multiline edit control, text wordwraps unless you use the ES_AUTOHSCROLL style, in which case you must press the Enter key to start a new line. You can also include vertical scrolling in a multiline edit control by using the style ES_AUTOVSCROLL. To add scroll bars to the edit control.-use the same window style identifiers as for nonchild windows: WS_HSCROLL and WS_VSCROLL. By default, an edit control does not have a border. You can add one by using the style WS_BORDER. When you select text in an edit control, Windows displays it in reverse video. When the edit control loses the input focus, however, the selected text is no longer highlighted. If you want the selection to be highlighted even when the edit control does not have the input focus, you can use the style ES_NOHIDESEL. Style given in the CreateWindow call: WS_CHILD WS_VISIBLE WS_HSCROLL WS_VSCROLL WS_BORDER ES_LEFT ES_MULTILINE ES_AUTOHSCROLL ES_AUTOVSCROLL
A list box is a collection of text strings displayed as a scrollable columnar list within a rectangle. A program can add or remove strings in the list by sending messages to the list box window procedure. The list box control sends WM_COMMAND messages to its parent window when an item in the list is selected. The parent window can then determine which item has been selected. A list box can be either single selection or multiple selection. The latter allows the user to select more than one item from the list box. When a list box has the input focus, it displays a dashed line surrounding an item in the list box. This cursor does not indicate the selected item in the list box. The selected item is indicated by highlighting, which displays the item in reverse video. In a single-selection list box, the user can select the item that the cursor is positioned on by pressing the Spacebar. The arrow keys move both the cursor and the current selection and can scroll the contents of the list box. The Page Up and Page Down keys also scroll the list box by moving the cursor but not the selection. Pressing a letter key moves the cursor and the selection to the first (or next) item that begins with that letter. An item can also be selected by clicking or double-clicking the mouse on the item. In a multiple-selection list box, the Spacebar toggles the selection state of the item where the cursor is positioned. (If the item is already selected, it is deselected.) The arrow keys deselect all previously selected items and move the cursor and selection, just as in single-selection list boxes. However, the Ctrl key and the arrow keys can move the cursor without moving the selection. The Shift key and arrow keys can extend a selection. Clicking or double-clicking an item in a multiple-selection list box deselects all previously selected items and selects the clicked item. However, clicking an item
while pressing the Shift key toggles the selection state of the item without changing the selection state of any other item.
The Microsoft Foundation Class Library Application Framework Application framework An integrated collection of object-oriented software components that offers all that's needed for a generic An Application Framework vs. a Class Library - An application framework is a superset of a class library. - An ordinary library is an isolated set of classes designed to be incorporated into any program, but an application framework defines the structure of the program itself. Why Use the Application Framework? The MFC library is the C++ Microsoft Windows API. Application framework applications use a standard structure. Application framework applications are small and fast. The Visual C++ tools reduce coding drudgery The MFC library application framework is feature rich An Application Framework Example source code for the header and implementation files for our MYAPPapplication. MyApp.h header file for the MYAPP application: // application class class CMyApp : public CWinApp
1
application.
{ public: virtual BOOL InitInstance(); }; // frame window class class CMyFrame : public CFrameWnd { public: CMyFrame(); protected: // "afx_msg" indicates that the next two functions are part // of the MFC library message dispatch system afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnPaint(); DECLARE_MESSAGE_MAP() };
MyApp.cpp - implementation file for the MYAPP application: #include <afxwin.h> // MFC library header file declares base classes #include "myapp.h" CMyApp theApp; // the one and only CMyApp object BOOL CMyApp::InitInstance() { m_pMainWnd = new CMyFrame(); m_pMainWnd->ShowWindow(m_nCmdShow); m_pMainWnd->UpdateWindow(); return TRUE; } BEGIN_MESSAGE_MAP(CMyFrame, CFrameWnd) ON_WM_LBUTTONDOWN() ON_WM_PAINT()
2
END_MESSAGE_MAP()
CMyFrame::CMyFrame() { Create(NULL, "MYAPP Application"); } void CMyFrame::OnLButtonDown(UINT nFlags, CPoint point) { TRACE("Entering CMyFrame::OnLButtonDown - %lx, %d, %d\n", (long) nFlags, point.x, point.y); } void CMyFrame::OnPaint() { CPaintDC dc(this); dc.TextOut(0, 0, "Hello, world!"); }
The program elements: The WinMain function Windows requires your application to have a WinMain function. You don't see WinMain here because it's hidden inside the application framework. The CMyApp class An object of class CMyApp represents an application. The program defines a single global CMyApp object, theApp. The CWinApp base class determines most of theApp's behavior. Application startup When the user starts the application, Windows calls the application framework's builtin WinMain function, and WinMain looks for your globally constructed application object of a class derived from CWinApp. In a C++ program global objects are constructed before the main program is executed. The CMyApp::InitInstance member function When the WinMain function finds the application object, it calls the virtual InitInstance member function, which makes the calls needed to construct and display the application's main frame window. You must override InitInstance in your derived application
3
class because the CWinApp base class doesn't know what kind of main frame window you want.
The CWinApp::Run member function The Run function is hidden in the base class, but it dispatches the application's messages to its windows, thus keeping the application running. WinMain calls Run after it calls InitInstance. The CMyFrame class An object of class CMyFrame represents the application's main frame window. When the constructor calls the Create member function of the base class CFrameWnd, Windows creates the actual window structure and the application framework links it to the C++ object. The ShowWindow and UpdateWindow functions, also member functions of the base class, must be called in order to display the window. The program elements: The CMyFrame::OnLButtonDown function MFC library's message-handling capability. The function invokes the MFC library TRACE macro to display a message in the debugging window. The CMyFrame::OnPaint function - The application framework calls this important mapped member function of class CMyFrame every time it's necessary to repaint the window: at the start of the program, when the user resizes the window, and when all or part of the window is newly exposed. - The CPaintDC statement relates to the Graphics Device Interface (GDI) and is explained in later chapters. The TextOut function displays "Hello, world!" Application shutdown - The user shuts down the application by closing the main frame window. - This action initiates a sequence of events, which ends with the destruction of the CMyFrame object, the exit from Run, the exit from WinMain, and the destruction of the CMyApp object.
MFC Library Message Mapping The MFC library application framework doesn't use virtual functions for Windows messages. Instead, it uses macros to "map" specified messages to derived class member functions
Why the rejection of virtual functions? What about message handlers for menu command messages and messages from button clicks? An MFC message handler requires a function prototype, a function body, and an entry (macro invocation) in the message map. BEGIN_MESSAGE_MAP(CMyFrame, CFrameWnd) ON_WM_LBUTTONDOWN() ON_WM_PAINT() END_MESSAGE_MAP() Documents and Views Typically, MFC application will contain application and frame classes plus two other classes that represent the "document" and the "view." This document-view architecture is the core of the application framework The document-view architecture separates data from the user's view of the data. One obvious benefit is multiple views of the same data.
CHAPTER 2 The Visual C++ Components Microsoft Visual C++ is two complete Windows application development systems in one product. You can develop C-language Windows programs using only the Win32 API. You can use many Visual C++ tools, including the resource editors, to make low-level Win32 programming easier. Components: The Project The Resource EditorsWorkspace ResourceView The C/C++ Compiler The Source Code Editor The Resource Compiler The Linker The Debugger AppWizard Classwizard What is a project? A project is a collection of interrelated source files that are compiled and linked to make up an executable Windows-based program or a DLL. Source files for each project are generally stored in a separate subdirectory. A project depends on many files outside the project subdirectory too, such as include files and library files.
A makefile stores compiler and linker options and expresses all the interrelationships among source files. A make program reads the makefile and then invokes the compiler, assembler, resource compiler, and linker to produce the final output, which is generally an executable file.
In a Visual C++ 6.0 project, there is no makefile (with an MAK extension) unless you tell the system to export one.
6
A text-format project file (with a DSP extension) serves the same purpose. A separate text-format workspace file (with a DSW extension) has an entry for each project in the workspace. It's possible to have multiple projects in a workspace, but all the
Examples in this book have just one project per workspace. To work on an existing project, you tell Visual C++ to open the DSW file and then you can edit and build the project. VC++ Project Files
Visual C++ creates some intermediate files too File Extension APS BSC CLW DEP DSP *DSW *MAK NCB OPT PLG Description Supports ResourceView Browser information file Supports ClassWizard Dependency file Project file Workspace file External makefile Supports ClassView Holds workspace configuration Builds log file
The Resource Editors Workspace ResourceView Each project usually has one text-format resource script (RC) file that describes the project's menu, dialog, string, and accelerator resources. The RC file also has #include statements to bring in resources from other subdirectories. These resources include project-specific items, such as bitmap (BMP) and icon (ICO) files, and resources common to all Visual C++ programs, such as error message strings. Editing the RC file outside the resource editors is not recommended.
7
The resource editors can also process EXE and DLL files, so you can use the clipboard to "steal" resources, such as bitmaps and icons, from other Windows applications. The C/C++ Compiler The Visual C++ compiler can process both C source code and C++ source code. It determines the language by looking at the source code's filename extension. A C extension indicates C source code, and CPP or CXX indicates C++ source code. The compiler is compliant with all ANSI standards, including the latest recommendations of a working group on C++ libraries, and has additional Microsoft extensions. Templates, exceptions, and runtime type identification (RTTI) are fully supported in Visual C++ version 6.0. The C++ Standard Template Library (STL) is also included, although it is not integrated into the MFC library.
Visual C++ 6.0 includes a sophisticated source code editor that supports many features such as dynamic syntax coloring, auto-tabbing, keyboard bindings The Resource Compiler
The Visual C++ resource compiler reads an ASCII resource script (RC) file from the resource editors and writes a binary RES file for the linker. The Linker
The linker reads the OBJ and RES files produced by the C/C++ compiler and the resource compiler, and it accesses LIB files for MFC code, runtime library code, and Windows code. It then writes the project's EXE file. The Debugger The Visual C++ debugger has been steadily improving, but it doesn't actually fix the bugs yet. The debugger works closely with Visual C++ to ensure that breakpoints are saved on disk.
AppWizard AppWizard is a code generator that creates a working skeleton of a Windows application with features, class names, and source code filenames that you specify through dialog boxes. AppWizard code is minimalist code; the functionality is inside the application framework base classes. AppWizard gets you started quickly with a new application.
ClassWizard ClassWizard is a program (implemented as a DLL) that's accessible from Visual C++'s View menu.
ClassWizard takes the drudgery out of maintaining Visual C++ class code. Need a new class, a new virtual function, or a new message-handler function? ClassWizard writes the prototypes, the function bodies, and (if necessary) the code to link the Windows message to the function. ClassWizard can update class code that you write, so you avoid the maintenance problems common to ordinary code generators.
CHAPTER 3 Basic Event Handling, Mapping Modes, and a Scrolling View The Message Handler: void CMyView::OnLButtonDown(UINT nFlags, CPoint point) { // event processing code here } The Message Map: BEGIN_MESSAGE_MAP(CMyView, CView) ON_WM_LBUTTONDOWN() // entry specifically for OnLButtonDown // other message map entries END_MESSAGE_MAP() Finally, your class header file needs the statement DECLARE_MESSAGE_MAP() Invalid Rectangle Theory InvalidateRect triggers a Windows WM_PAINT message, which is mapped in the CView class to call to the virtual OnDraw function. If necessary, OnDraw can access the "invalid rectangle" parameter that was passed to InvalidateRect. Your OnDraw function could call the CDC member function GetClipBox to determine the invalid rectangle, and then it could avoid drawing objects outside it. OnDraw is being called not only in response to your InvalidateRect call but also when the user resizes or exposes the window. Thus, OnDraw is responsible for all drawing in a window, and it has to adapt to whatever invalid rectangle it gets. The Window's Client Area A window has a rectangular client area that excludes the border, caption bar, menu bar, and any toolbars. The CWnd member function GetClientRect supplies you with the client-area dimensions. Normally, you're not allowed to draw outside the client area, and most mouse messages are received only when the mouse cursor is in the client area.
10
CRect, CPoint, and CSize Arithmetic The CRect, CPoint, and CSize classes are derived from the Windows RECT, POINT, and SIZE structures, and thus they inherit public integer data members as follows: CRect left, top, right, bottom CPoint x, y CSize cx, cy Dialog : Using Appwizard and Classwizard The Modal Dialog and Windows Common Controls The two kinds of dialogs are modal and modeless. The CDialog base class supports both modal and modeless dialogs Modal Dialog Box: The user cannot work elsewhere in the same application (more correctly, in the same user interface thread) until the dialog is closed. Example: Open File dialog Modeless Dialog The user can work in another window in the application while the dialog remains on the screen Example: Microsoft Word's Find and Replace dialog is a good example of a modeless dialog; you can edit your document while the dialog is open. Controls. A dialog contains a number of elements called controls. Dialog controls include edit controls, buttons, list boxes, combo boxes, static text, tree views, progress indicators, sliders, and so forth. Programming a Modal Dialog 1. Use the dialog editor to create a dialog resource that contains various controls. -The dialog editor updates the project's resource script (RC) file to include your new dialog resource, and it updates the project's resource.h file with corresponding #define constants. 2. Use ClassWizard to create a dialog class that is derived from CDialog and attached to the resource created in step 1. -ClassWizard adds the associated code and header file to the Microsoft Visual C++ project. 3. Use ClassWizard to add data members, exchange functions, and validation functions to the dialog class.
11
4. Use ClassWizard to add message handlers for the dialog's buttons and other eventgenerating controls. 5. Write the code for special control initialization (in OnInitDialog) and for the message handlers. Be sure the CDialog virtual member function OnOK is called when the user closes the dialog (unless the user cancels the dialog). (Note: OnOK is called by default.) 6.Write the code in your view class to activate the dialog. This code consists of a call to your dialog class's constructor followed by a call to the DoModal dialog class member function. DoModal returns only when the user exits the dialog window. In the CPP file, the constructor implementation looks like this: CMyDialog::CMyDialog(CWnd* pParent) { // initialization code here } The use of enum IDD decouples the CPP file from the resource IDs that are defined in the project's resource.h The Windows Common Dialogs Windows provides a group of standard user interface dialogs, and these are supported by the MFC library classes. All the common dialog classes are derived from a common base class, CCommonDialog. Class CColorDialog CFileDialog Purpose Allows the user to select or create a color Allows the user to open or save a file pParent /*=NULL*/) : CDialog(CMyDialog::IDD,
CFindReplaceDialog Allows the user to substitute one string for another CPageSetupDialog CFontDialog CPrintDialog Allows the user to input page measurement parameters
Allows the user to select a font from a list of available fonts Allows the user to set up the printer and print a document
Using the CFileDialog Class Directly The following code opens a file that the user has selected through the dialog: CFileDialog dlg(TRUE, "bmp", "*.bmp"); if (dlg.DoModal() == IDOK) {
12
CFile file; VERIFY(file.Open(dlg.GetPathName(), CFile::modeRead)); } The first constructor parameter (TRUE) specifies that this object is a "File Open" dialog instead of a "File Save" dialog. The default file extension is bmp, and *.bmp appears first in the filename edit box. The CFileDialog::GetPathName function returns a CString object that contains the full pathname of the selected file. BITMAPS Windows bitmaps are arrays of bits mapped to display pixels. There are two kinds of Windows bitmaps: GDI bitmaps GDI bitmap objects are represented by the Microsoft Library version 6.0 and DIBs. Foundation Class (MFC)
Color Bitmaps and Monochrome Bitmaps Many color bitmaps are 16-color. A standard VGA board has four contiguous color planes, with 1 corresponding bit from each plane combining to represent a pixel. The 4-bit color values are set when the bitmap is created. With a standard VGA board, bitmap colors are limited to the standard 16 colors. Windows does not use dithered colors in bitmaps. A monochrome bitmap has only one plane. Each pixel is represented by a single bit that is either off (0) or on (1). The CDC::SetTextColor function sets the "off" display color, and SetBkColor sets the "on" color. You can specify these pure colors individually with the Windows RGB macro. Code to load a Bitmap
void OnPaint() { CBitmap mybm; CPaintDC d(this); mybm.LoadBitmap(IDB_BITMAP1); CBrush mybrush; mybrush.CreatePatternBrush(&mybm); d.SelectObject(&mybrush);
13
GDI Bitmaps There are two kinds of Windows bitmaps: GDI bitmaps and DIBs. GDI bitmap objects are represented by the Microsoft Foundation Class (MFC) Library version 6.0 CBitmap class. The GDI bitmap object has an associated Windows data structure, maintained inside the Windows GDI module, that is device-dependent. Your program can get a copy of the bitmap data, but the bit arrangement depends on the display hardware. GDI bitmaps can be freely transferred among programs on a single computer, but because of their device dependency, transferring bitmaps by disk or modem doesn't make sense. Device-Independent Bitmaps
Device-Independent Bitmaps DIBs offer many programming advantages over GDI bitmaps. Since a DIB carries its own color information, color palette management is easier. DIBs also make it easy to control gray shades when printing. Any computer running Windows can process DIBs, which are usually stored in BMP disk files or as a resource in your program's EXE or DLL file. The wallpaper background on your monitor is read from a BMP file when you start Windows. The primary storage format for Microsoft Paint is the BMP file, and Visual C++ uses BMP files for toolbar buttons and other images. Other graphic interchange formats are available, such as TIFF, GIF, and JPEG, but only the DIB format is directly supported by the Win32 API.
14
Global Application object | Execution begins- WinMain by MFC | AfxGetApp() Gets ptr to Applocation object | InitInstance Executed(Create Window &assigns | hInstance,nCmdshow appln object) | RUN()- Implements Message Loop | Terminate if WM_QUIT Message occurs | ExitInstance() Document View Architecture Need: Given set of data can have multiple representation- best to separate display from data. Three Objects: Document - Store the data (responsible for
Window - The canvas on which all display take place , Keyboard Accelerators
Menu
Application framework- controls interaction between frame and view MainFrm.h and MainFrm.cpp -application's main frame window class ex13aDoc.h and ex13aDoc.cpp-application's document class ex13aView.h and ex13aView.cpp-applications view class
Windows Menus A Microsoft Windows menu is a familiar application element that consists of a toplevel horizontal list of items with associated pop-up menus that appear when the user selects a top-level item. Menu items - grayed ,have check marks,separator bar. Multiple levels pop-up menus are possible. Each Menu item ID which is defined in resource.h Entire resource definition - .rc file(resource script file) Command Processing WM_COMMAND message menu selection, keyboard accelerators,toolbar and dialog button clicks by windows. Message Map Entry:
2
For example, Menu item is Line (ID is ID_LINE ) For handle it in the view class, In view.cpp BEGIN_MESSAGE_MAP(CMyView, CView) END_MESSAGE_MAP() void CMyView::OnLine() { // command message processing code } In MyView.h Give the definition afx_msg void OnZoom(); before DECLARE_MESSAGE_MAP() macro Command update handler function. In view.cpp BEGIN_MESSAGE_MAP(CMyView, CView) ON_UPDATE_COMMAND_UI(ID_LINE, OnUpdateLine) END_MESSAGE_MAP() void CMyView::OnUpdateLine() { // command message processing code } In MyView.h Give the definition afx_msg void OnUpdateLine(); MFC Text Editing features Edit Control and Rich Edit Control: CEditView and
3
ON_COMMAND(ID_LINE, OnLine)
Whenever a pop-up menu is first displayed or menu item is clicked ,MFC calls this function. The handler function's argument is a CCmdUI object, which contains a pointer to the corresponding menu item. this pointer to modify the menu item's appearance by such as enable , setcheck etc. its operations
CRichEditView
CEditView Class: CRichEditView Supports mixed format and large quantities of text. can include embedded OLE objects CRichEditView maintains the text and formatting characteristic of text. CRichEditDoc maintains the list of OLE client items which are in the view. CRichEditCntrItem provides container-side access to the OLE client item Example Maximum size is 64 KB, work in View and Edit classes cant mix font and cut copy paste is possible.
1. Add a CString data member to the CEx13aDoc class. In ex13aDoc.h public: CString m_strText; 2. Add a CRichEditCtrl data member to the CEx13aView class. In file ex13aView.h public: CRichEditCtrl m_rich;
(CCmdUI* pCmdUI) { pCmdUI->Enable(!m_strText.IsEmpty()); } In CEx13View.cpp : Creation RichEdit Control { int CEx13aView::OnCreate(LPCREATESTRUCT lpCreateStruct) CRect rect(0, 0, 0, 0);
if (CView::OnCreate(lpCreateStruct) == -1) return -1; m_rich.Create(ES_AUTOVSCROLL | ES_MULTILINEES_WANTRETURN | WS_CHILD | WS_VISIBLE | WS_VSCROLL, rect, this, 1); return 0; } 2. void CEx13aView::OnSize(UINT nType, int cx, int cy) { CRect rect; CView::OnSize(nType, cx, cy); GetClientRect(rect); m_rich.SetWindowPos(&wndTop, 0, 0, rect.right - rect.left, rect.bottom - rect.top, SWP_SHOWWINDOW); } position &structure 3. void CEx13aView::OnTransferGetData() { CEx13aDoc* pDoc = GetDocument(); m_rich.SetWindowText(pDoc- >m_strText); m_rich.SetModify(FALSE); } 4. void CEx13aView::OnTransferStoreData() { CEx13aDoc* pDoc = GetDocument(); m_rich.GetWindowText(pDoc->m_strText); m_rich.SetModify(FALSE);
5
} 5. void CEx13aView::OnUpdateTransferStoreData (CCmdUI* pCmdUI) { pCmdUI->Enable(m_rich.GetModify()); } 6. Build and Run the application Creating Floating Pop-Up Menus void CMyView::OnContextMenu(CWnd *pWnd, CPoint point) { CMenu menu; menu.LoadMenu(IDR_MYFLOATINGMENU); menu.GetSubMenu(0) ->TrackPopupMenu (TPM_LEFTALIGN | TPM_RIGHTBUTTON, point.x, point.y, this); } Extended Command Processing BEGIN_MESSAGE_MAP(CMyView, CView) ON_COMMAND_EX_RANGE(IDM_ZOOM_1, IDM_ZOOM_2, OnZoom) END_MESSAGE_MAP() ON_COMMAND_RANGE( id1, id2, Fxn ) ON_COMMAND_EX_RANGE ON_UPDATE_COMMAND_UI_RANGE
A toolbar consists of a number of horizontally (or vertically) arranged graphical buttons that might be clustered in groups Pressing a toolbar button is equivalent to choosing a menu item(WM_COMMAND messages). An update command UI message handler is used to update the button's state MFC toolbar can dock to any side of its parent window or float in its own miniframe window. you can change its size and drag it. A toolbar can also display tool tips as the user moves the mouse over the toolbars buttons.
ToolBar Bitmap: -Each button on a toolbar appears to have its own bitmap, but actually a single bitmap serves the entire toolbar. -has tile, 15 pixels high and 16 pixels wide -The toolbar bitmap is stored in the file Toolbar.bmp Button State: 0: Normal, unpressed state. Available for
TBSTATE_CHECKED Checked (down) state.TBSTATE_ENABLED use. Button is grayed and unavailable if this state is not set.
7
TBSTATE_HIDDEN
Not visible.
TBSTATE_INDETERMINATE Grayed. TBSTATE_PRESSED Currently selected (pressed) with the mouse. TBSTATE_WRAP Line break follows the button
Locating the Main FrameWindow The toolbar and status bar objects you'll be working with are attached to the application's main frame window, not to the view window find the main frame window through the application object.
RunAppWizard to create an SDI application & Use the resource editor to edit the application's main menu as follows
8
3. Use the resource editor to update the application's toolbar-Edit the IDR_MAINFRAME toolbar resource 4. Give the ID to each Button 5. USE ClassWizard to add command and update command UI messages for ID_CIRCLE,ID_SQUARE &ID_PATTERN 6. In the file ex14aView.h, private: CRect m_rect; BOOL m_bCircle; BOOL m_bPattern; 7. void CEx14aView::OnDrawCircle() { m_bCircle = TRUE; m_rect += CPoint(25, 25); InvalidateRect(m_rect); }
InvalidateRect(m_rect); } 9. void CEx14aView::OnDrawPattern() { m_bPattern ^= 1; } 10. void CEx14aView::OnUpdateDrawCircle (CCmdUI* pCmdUI) { pCmdUI->Enable(!m_bCircle); } 11.voidCEx14aView::OnUpdateDrawSquare(CCmdUI* pCmdUI) { pCmdUI->Enable(m_bCircle); } 12. void CEx14aView::OnUpdateDrawPattern(CCmdUI* pCmdUI) { pCmdUI->SetCheck(m_bPattern); } StatusBar neither accepts user input nor generates command messages to display text in panes under program control supports two types of text panes o o message line panes status indicator panes //toggles
10
The static indicators array that AppWizard generates in the MainFrm.cpp file defines the panes for the application's status bar. The constant ID_SEPARATOR identifies a message line pane; the other constants are string resource IDs that identify indicator panes
Get access to the status bar object CMainFrame* pFrame = (CMainFrame*) AfxGetApp()-> m_pMainWnd; CStatusBar* pStatus = &pFrame->m_wndStatusBar; Display String in Massage Line-SetPaneText pStatus->SetPaneText(0, "message line for first pane"); Pane No.: 0-leftmost pane 1-next pane to the right and so forth. The Status Indicator status indicator pane is linked to a single resource-supplied string that is displayed or hidden by logic in an associated update command UI message handler function. Contains Indicators . An indicator is identified by a string resource ID. same ID is used to route update command UI messages. For example Caps Lock indication by status bar
In Mainframe.cpp ON_UPDATE_COMMAND_UI(ID_INDICATOR_CAPS, OnUpdateKeyCapsLock) void MainFrame::OnUpdateKeyCapsLock(CCmdUI* pCmdUI) { pCmdUI->Enable(::GetKeyState(VK_CAPITAL) & 1); } For Left Button Status ON_UPDATE_COMMAND_UI(ID_LEFT, OnLeft) void MainFrame::OnLeft(CCmdUI* pCmdUI) { pCmdUI->Enable(::GetKeyState(VK_LBUTTON) & 1); }
11
Avoid Default status bar and have your own status bar To assign your own ID, you must replace this call
Example
1. Use the string editor to edit the application's string table resource. ID Caption ID_INDICATOR_LEFT -LEFT
ID_INDICATOR_RIGHT -RIGHT 2. Choose Resource Symbols from the View menu. Add the new status bar identifier, ID_MY_STATUS_BAR, and accept the default value.
12
3. In MainFrame.cpp void CMainFrame::OnUpdateLeft(CCmdUI* pCmdUI) { pCmdUI->Enable(::GetKeyState(VK_LBUTTON) < 0); } 4. void CMainFrame::OnUpdateRight(CCmdUI* pCmdUI) { pmdUI->Enable(::GetKeyState(VK_RBUTTON) < 0); } 5. In MainFrame.cpp MessageMap Entry ON_UPDATE_COMMAND_UI(ID_INDICATOR_LEFT, OnUpdateLeft) ON_UPDATE_COMMAND_UI(ID_INDICATOR_RIGHT, OnUpdateRight) 6. In MainFrm.h. afx_msg void OnUpdateLeft(CCmdUI* pCmdUI); afx_msg void OnUpdateRight(CCmdUI*pCmdUI); 7. Edit the MainFrm.cpp file. Replace the original indicators array with the following boldface code: static UINT indicators[] = {ID_SEPARATOR, // first message line pane ID_SEPARATOR, // second message line pane ID_INDICATOR_LEFT, ID_INDICATOR_RIGHT }; 8. Use ClassWizard to add View menu command handlers in the class CMainFrame. 1. ID_VIEW_STATUS_BAR - COMMAND -OnViewStatusBar 2. ID_VIEW_STATUS_BAR-UPDATE_COMMAND_UI -OnUpdateViewStatusBar void CMainFrame::OnViewStatusBar() { m_wndStatusBar.ShowWindow((m_wndStatusBar.GetStyle() & WS_VISIBLE) == 0); RecalcLayout(); } void CMainFrame::OnUpdateViewStatusBar(CCmdUI* pCmdUI)
9.In OnCreate member function Replace if (!m_wndStatusBar.Create(this) || !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) { TRACE0("Failed to create status bar\n"); return -1; // fail to create } with the statement shown here: if (!m_wndStatusBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, ID_MY_STATUS_BAR) || !m_wndStatusBar.SetIndicators(indicators, sizeof(indicators)/sizeof(UINT))) { TRACE0("Failed to create status bar\n"); return 1; // fail to create }
10. InView.cpp void CEx14bView::OnDraw(CDC* pDC) { pDC->TextOut(0, 0, "Watch the status bar while you move and click the mouse."); } 11. void CEx14bView::OnMouseMove(UINT nFlags, Cpoint point) { CString str; CMainFrame* pFrame = (CMainFrame*) AfxGetApp()->m_pMainWnd; CStatusBar* pStatus = &pFrame->m_wndStatusBar; if (pStatus) { str.Format("x = %d", point.x); pStatus->SetPaneText(0, str); str.Format("y = %d", point.y); pStatus->SetPaneText(1, str); }} 12 #include "MainFrm.h" A Reusable Frame Window Base Class CString class. Build your own reusable base class . Access to the Windows Registry. PreCreateWindow & ActiveFrame function.
14
CString class
dynamic memory allocation-const char*. CString strFirstName("Elvis"); CString strLastName("Presley"); CString strTruth = strFirstName + " " + strLastName;
int nError = 23; CString strMessageText; strMessageText.Format("Error number %d", nError); AfxMessageBox(strMessageText); CString strTest("test"); strncpy(strTest, "T", 1);
CString::GetBuffer - "locks down" the buffer with a specified size and returns a char*. ReleaseBuffer - to make the string dynamic again. CString strTest("test"); strncpy(strTest.GetBuffer(5), "T", 1); strTest.ReleaseBuffer(); Build your own reusable base class CPersistentFrame - derived from the CFrameWnd
- supports a persistent SDI (Single Document Interface) frame window that remembers the following characteristics. Window size ,Window position Maximized status ,Minimized status Toolbar and Status bar enablement and position
When you terminate an application that's built with the CPersistentFrame class, the above information is saved on disk in the Windows Registry. When the application starts again, it reads the Registry and restores the frame to its state at the previous exit. The Windows Registry is a set of system files, managed by Windows, in which Windows and individual applications can store and access permanent information. is organized as a kind of hierarchical database in which string and integer data is accessed by a multipart key.
15
The SetRegistryKey function's string parameter establishes the top of the hierarchy,
SetRegistryKey(" TEXTPROC "); Following Registry functions(CWinApp) define the bottom two levels: called heading name and entry name. GetProfileInt WriteProfileInt GetProfileString WriteProfileString
AfxGetApp()->WriteProfileString("Text formatting", "Font", "Times Roman"); AfxGetApp()->WriteProfileInt("Text formatting", "Points", 10); ActivateFrame Member Function CFrameWnd The key to controlling the frame's size The application framework calls this virtual function (declared in CFrameWnd) during the SDI main frame window creation process (and in response to the File New and File Open commands). The framework's job is to call the CWnd::ShowWindow function with the parameter nCmdShow. The nCmdShow parameter determines whether the window is maximized or minimized or both. Override ActivateFrame in your derived frame class, to change the value of nCmdShow before passing to it . CWnd::SetWindowPlacement function, which sets the size and position of the frame window, and you can set the visible status of the control bars. First time call ie CPersistentFrame::ActivateFrame function operates only when the application starts. The PreCreateWindow Member Function-CWnd to change the characteristics of your window before it is displayed calls this function before it calls ActivateFrame.
16
a CREATESTRUCT structure as a parameter, and two of the data members in this structure are style and dwExStyle. CREATESTRUCT member lpszClass is also useful to change the window's background brush, cursor, or icon.
BOOL MyView::PreCreateWindow(CREATESTRUCT& c) { .. . c.lpszClass =AfxRegisterWndClass(CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW, AfxGetApp()-> LoadCursor(IDC_MYCURSOR), ::CreateSolidBrush (RGB(255, 0, 0))); if (cs.lpszClass != NULL) { return TRUE; } else { return FALSE; } } Style flag -determines whether the window has a border, scroll bars, a minimize box, and so on. Extended style double border, left aligned border etc Document View Architecture Menu, Keyboard Accelerators Status Bar, ToolBar Reusable Frame Window Class Global Application object Execution begins- WinMain by MFC AfxGetApp() Gets ptr to Applocation object InitInstance Executed(Create Window &assigns hInstance,nCmdshow appln object) RUN()- Implements Message Loop Terminate if WM_QUIT Message occurs ExitInstance() Separating Document from its View Need: Given set of data can have multiple representation- best to separate display
17
from data. Three Objects: Document - Store the data (responsible for
reading and writing data) View Window - To display data(each view one document) - The canvas on which all display
take place Document-View Interaction Functions The CView::GetDocument Function-provides the document pointer that can be used to access document class member functions or public data embers. CEx13aDoc* pDoc = GetDocument(); m_rich.SetWindowText(pDoc- >m_strText); The CDocument::GetNextView -navigates from the document to the view, but because a document can have more than one view, it's necessary to call this member function once for each view, inside a loop CDocument::UpdateAllViews
-If the document data changes for any reason, all views must be notified so that they can update their representations of that data. -GetDocument()->UpdateAllViews(NULL): Update all associated view . -GetDocument()->UpdateAllViews(this): - Update all associated view except current. Syntax: void UpdateAllViews( CView* pSender, LPARAM lHint = 0L, CObject* pHint = NULL ); Parameters pSender-Points to the view that modified the document, or NULL if all views are to be updated.
18
lHint
pHint -Points to an object storing information about the modification. CView::OnUpdate: - virtual function is called by the application framework in response to your application's call to the CDocument::UpdateAllViews function. - view class's OnUpdate function accesses the document, gets the document's data, and then updates the view's data members or controls to reflect the changes. OnUpdate can invalidate a portion of the view, causing the view's OnDraw function to use document data to draw in the window. Same parameters as UpdateAllViews.
CView::OnInitialUpdate -virtual CView function is called when the application starts, when the user chooses New from the File menu, and when the user chooses Open from the File menu. -calls OnUpdate. -use your derived class's OnInitialUpdate function to initialize your view object -When the application starts, the application framework calls OnInitialUpdate immediately after OnCreate CDocument::OnNewDocument -The framework calls this virtual function after a document object is first constructed and when the user chooses New from the File menu in an SDI application. -to set the initial values of your document's data members. Application starts CMyDocument object constructed CMyView object constructed View window created CMyView::OnCreate called (if mapped) CMyDocument::OnNewDocument called CMyView::OnInitialUpdate called View object initialized View window invalidated
19
CMyView::OnDraw called
User edits data CMyView functions update CMyDocument data members User exits application CMyView object destroyed Frame Window - Definition An application or some parts of an application when framed by Windows are called Frame Windows Frame windows act as containers for other windows such as control bars or child controls. Basic types of Frame Windows Single Document Interface (SDI) frame windows Multiple Document Interface (MDI) frame windows. MDI frame windows can contain MDI child windows Serialization Serialization is an important concept in MFC programming because it is the basis for MFC's ability to open and save documents in document/view applications. Serialization Process when someone using a document/view application selects Open or Save from the application's File menu, MFC opens the file for reading or writing and passes the application a reference to a CArchive object. The application, in turn, serializes its persistent data to or from the archive and, by so doing, saves a complete document to disk or reads it back again. A document whose persistent data consists entirely of primitive data types or serializable objects can often be serialized with just a few lines of code. Serialization - Write Assume that a CFile object named file represents an open file, that the file was opened with write access, and that you want to write a pair of integers named a and b to that file. One way to accomplish this is to call CFile::Write once for each integer: file.Write (&a, sizeof (a)); file.Write (&b, sizeof (b)); An alternative method is to create a CArchive object, associate it with the CFile object, and use the << operator to serialize the integers into the archive: CArchive ar (&file, CArchive::store);
20
ar << a << b; Serialization Read (deserialize) Assuming file once again represents an open file and that the file is open with read access, the following code snippet attaches a CArchive object to the file and reads, or deserializes, the integers from the file: CArchive ar (&file, CArchive::load); ar >> a >> b; MFC allows a wide variety of primitive data types to be serialized this way, including BYTEs, WORDs, LONGs, DWORDs, floats, doubles, ints, unsigned ints, shorts, and chars. SDI and MDI MFC supports two types of document/view applications. Single document interface (SDI) applications support just one open document at a time. Multiple document interface (MDI) applications permit two or more documents to be open concurrently and also support multiple views of a given document. The WordPad applet is an SDI application; Microsoft Word is an MDI application. SDI SDI application frame windows are derived from the class CFrameWnd. The MFC library supports two distinct application types: Single Document Interface (SDI) and Multiple Document Interface (MDI). An SDI application has, only one window. If the application depends on disk-file "documents," only one document can be loaded at a time. The original Windows Notepad is an example of an SDI application. The standard SDI frame menus The child windows within an SDI main frame window SDI Document View Architecture SDI Application Startup steps in a Microsoft Windows MFC library application: Windows loads your program into memory. The global object theApp is constructed. (All globally declared objects are constructed immediately when the program is loaded.) Windows calls the global function WinMain, which is part of the MFC library. (WinMain is equivalent to the non-Windows main functioneach is a main program entry point.) Steps WinMain searches for the one and only instance of a class derived from CWinApp.
21
WinMain calls the InitInstance member function for theApp, which is overridden in your derived application class. Your overridden InitInstance function starts the process of loading a document and displaying the main frame and view windows. WinMain calls the Run member function for theApp, which starts the processes of dispatching window messages and command messages. Object Relationship Steps for processing SDI InitInstance 1. Create an SDI document template from MFC's CSingleDocTemplate class. 2. Adding to list of document templates 3. Initializing command line info values 4. Processing command line parameters 5. Displaying applications frame window InitInstance function for an SDI application generated by AppWizard CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate (IDR_MAINFRAME, RUNTIME_CLASS (CMyDoc), RUNTIME_CLASS (CMainFrame), RUNTIME_CLASS (CMyView) );
AddDocTemplate (pDocTemplate); .. .. CCommandLineInfo cmdInfo; ParseCommandLine (cmdInfo); if (!ProcessShellCommand (cmdInfo)) return FALSE; m_pMainWnd->ShowWindow (SW_SHOW); m_pMainWnd->UpdateWindow ();
22
1. create an SDI document template from CSingleDocTemplate class CSingleDocTemplate* pDocTemplate; pDocTemplate = new CSingleDocTemplate ( IDR_MAINFRAME, RUNTIME_CLASS (CMyDoc), RUNTIME_CLASS (CMainFrame), RUNTIME_CLASS (CMyView) ); The template's constructor was passed four parameters: an integer value equal to IDR_MAINFRAME and three RUNTIME_CLASS pointers. AppWizard uses the resource ID IDR_MAINFRAME in the code that it generates. The RUNTIME_CLASS macro surrounding the class names returns a pointer to a CRuntimeClass structure for the specified class, which enables the framework to create objects of that class at run time. 2. Adding to list of document templates After the document template is created, the statement AddDocTemplate (pDocTemplate); adds it to the list of document templates maintained by the application object. Each template registered in this way defines one document type the application supports. SDI applications register just one document type 3. Initialize command line info values The statements CCommandLineInfo cmdInfo; Par seCommandLine (cmdInfo); use CWinApp::ParseCommandLine to initialize a CCommandLineInfo object with values reflecting the parameters entered on the command line, which often include a document file name. 4. Process command line parameters The statements if (!ProcessShellCommand (cmdInfo)) return FALSE; "process" the command line parameters. Among other things, ProcessShellCommand calls CWinApp::OnFileNew to start the application with an empty document if no file name was entered on the command line, or CWinApp::OpenDocumentFile to load a document if a
23
document name was specified. It's during this phase of the program's execution that the framework creates the document, frame window, and view objects using the information stored in the document template. ProcessShellCommand returns TRUE if the initialization succeeds and FALSE if it doesn't. 5. Display applications frame window If initialization is successful, the statements m_pMainWnd->ShowWindow (SW_SHOW); m_pMainWnd->UpdateWindow (); display the application's frame window (and by extension, the view) on the screen. Routing of command messages sent to an SDI frame window. Example Program - The SdiSquares Application The program shown in Figure is an SDI document/view application that displays a grid of squares four rows deep and four columns wide. Initially, each square is colored white. However, you can change a square's color by clicking it with the left mouse button. By default, clicking changes a square's color to red. You can select alternate colors from the Color menu and thereby create a multicolored grid containing squares of up to six different colors. The SdiSquares Application Use AppWizard to create a new project named SdiSquares. In AppWizard's Step 1 dialog box, choose Single Document as the application type and check the Document/View Architecture Support box, as shown in Figure The SdiSquares Application In the Step 3 dialog box, uncheck the ActiveX Controls box. In Step 4, uncheck Docking Toolbar, Initial Status Bar, Printing And Print Preview, and 3D Controls. The SdiSquares Application Also in the Step 4 dialog box, click the Advanced button and type the letters sqr into the File Extension box (as shown in Figure) to define the default file name extension for SdiSquares documents. The SdiSquares Application 1. In the Step 6 dialog box, manually edit the class names as CSquaresApp. Everywhere else, accept the AppWizard defaults. The SdiSquares Application Add the member variables m_clrGrid and m_clrCurrentColor to the document class, and add code to initialize them to OnNewDocument. AppWizard overrides OnNewDocument, so all you have to do is add the statements that initialize the data members.
24
The SdiSquares Application Add the member functions GetCurrentColor, GetSquare, and SetSquare to the document class. Be sure to make them public member functions, since they must be accessible to the view. Modify the Serialize function that AppWizard included in the document class to serialize m_clrGrid and m_clrCurrentColor. Implement the view's OnDraw function. AppWizard generates a do-nothing OnDraw function; you write the code to perform application-specific duties. The SdiSquares Application Add the WM_LBUTTONDOWN handler (OnLButtonDown) to the view. You can add the message handler by hand or use ClassWizard to add it. I used ClassWizard. Open the AppWizard-generated application menu for editing, delete the Edit menu, and add the Color menu. Then write command and update handlers for the new menu items. As with message handlers, you can add command and update handlers manually or you can add them with ClassWizard's help. SDI vs MDI MDI MDI application frame windows are derived from the class CMDIFrameWnd. An MDI application has multiple child windows, each of which corresponds to an individual document. Microsoft Word is a good example of an MDI application. When you run AppWizard to create a new project, MDI is the default application type The parent-child hierarchy of a Windows MDI application. MDI application MDI windows MDI classes
An MDI application has two frame window classes and many frame objects Base Class CMDIFrameWnd CMDIChildWnd
CChildFrame The MDI frame-view window relationship Splitter Windows A splitter window is a window that can be divided into two or more panes horizontally, vertically, or both horizontally and vertically using movable splitter bars. Each pane contains one view of a document's data. The views are children of the splitter window, and the splitter window itself is normally a child of a frame window. Splitter windows Using splitter windows provided by MFC, a single document interface (SDI) application can present two or more views of the same document in resizeable "panes" that subdivide the frame window's client area In an SDI application, the splitter window is a child of the top-level frame window. In an MDI application, the splitter window is a child of an MDI document frame. Types of splitter windows static splitter window:
The numbers of rows and columns in a static splitter window are set when the splitter is created and can't be changed by the user. The user is, however, free to resize individual rows and columns. A static splitter window can contain a maximum of 16 rows and 16 columns. For an example of an application that uses a static splitter, look no further than the Windows Explorer. Explorer's main window is divided in half vertically by a static splitter window.
Static splitter window Dynamic Splitter window A dynamic splitter window is limited to at most two rows and two columns, but it can be split and unsplit interactively. The views displayed in a dynamic splitter window's panes aren't entirely independent of each other: when a dynamic splitter window is split horizontally, the two rows have independent vertical scroll bars but share a horizontal scroll bar. Similarly, the two columns of a dynamic splitter window split vertically contain horizontal scroll bars of their own but share a vertical scroll bar. The maximum number of rows and columns a dynamic splitter window can be divided into are specified when the splitter is created. Thus, it's a simple matt Dynamic Splitter window Procedure for Creating and initializing a dynamic splitter window
26
2. Override the frame window's virtual OnCreateClient function, and call CSplitterWnd::Create to create a dynamic splitter window in the frame window's client area. Creating splitter window Assuming m_wndSplitter is a CSplitterWnd object that's a member of the frame window class CMainFrame, the following OnCreateClient override creates a dynamic splitter window inside the frame window:
BOOL CMainFrame::OnCreateClient (LPCREATESTRUCT lpcs, CCreateContext* pContext) { return m_wndSplitter.Create (this, 2, 1, CSize (1, 1), pContext); } Creating splitter window The first parameter to CSplitterWnd::Create identifies the splitter window's parent, which is the frame window. The second and third parameters specify the maximum number of rows and columns that the window can be split into. Because a dynamic splitter window supports a maximum of two rows and two columns, these parameter values will always be 1 or 2. The fourth parameter specifies each pane's minimum width and height in pixels. The framework uses these values to determine when panes should be created and destroyed as splitter bars are moved. Creating splitter window CSize values equal to (1,1) specify that panes can be as little as 1 pixel wide and 1 pixel tall. The fifth parameter is a pointer to a CCreateContext structure provided by the framework. The structure's m_pNewViewClass member identifies the view class used to create views in the splitter's panes. The framework creates the initial view for you and puts it into the first pane. Other views of the same class are created automatically as additional panes are created.
27
OnDragEnterAdjusts the focus rectangle and then calls OnDragOver OnDragOverMoves (determines cursor shape) the dotted focus rectangle and sets the drop effect
OnDragLeave Cancels the transfer operation; returns the rectangle to its original position and size OnDrop Adjusts the focus rectangle and then calls the helper function to get formats from the data object The Drag-and-Drop Sequence DoPaste
1. User presses the left mouse button in the source view window. 2. Mouse button handler calls CRectTracker::HitTest and finds out that the cursor was inside the tracker rectangle. 3. Handler stores formats in a COleDataSource object. 4. Handler calls COleDataSource::DoDragDrop for the data source. 5. User moves the cursor to the view window of the target application. 6. OLE calls IDropTarget::OnDragEnter and OnDragOver for the COleDropTarget object, which calls the corresponding virtual functions in the target's view. The OnDragOver function is passed a COleDataObject pointer for the source object, which the target tests for a format it can understand. 7. OnDragOver returns a drop effect code, which OLE uses to set the cursor. 8. OLE calls IDataSource::QueryContinueDrag on the source side to find out whether the drag operation is still in progress. The MFC COleDataSource class responds appropriately. 9. User releases the mouse button to drop the object in the target view window. 10. OLE calls IDropTarget::OnDrop, which calls OnDrop for the target's view. Because OnDrop is passed a COleDataObject pointer, it can retrieve the desired format from that object. 11. When OnDrop returns in the target program, DoDragDrop can return in the source program. OLE Embedded Components and Containers A component that supports in-place activation also supports embedding Both in-place activation and embedding store their data in a container's document The container can activate both. An in-place-capable component can run inside the container application's main window, taking over the container's menu and toolbar,
2
An embedded component can run only in its own window, and that window has a special menu that does not include file commands. Embedding relies on two key interfaces, IOleObject and IOleClientSite, which are used for in-place activation as well.
MFC base classes COleIPFrameWnd, COleServerDoc, and COleServerItem. COleIPFrameWnd class is rather like CFrameWnd. It's your
application's main frame window, which contains the view. It has a menu associated with it, IDR_SRVR_INPLACE, which
will be merged into the container program's menu. The embedded menu is IDR_SRVR_EMBEDDED, and the
It contains added features that support OLE connections to the container. The COleServerItem class works with the COleServerDoc class.
3
The EX28A ExampleAn MFC In-Place-Activated Mini-Server Here are the steps for creating the program from scratch: 1. Run AppWizard to create the EX28A project in the \vcpp32\ex28a directory. Select Single Document interface. Click the Mini-Server option in the AppWizard Step 3 dialog shown here.
2. Examine the generated files. You've got the familiar application, document, main frame, and view file
s, but you've got two new files too. 3. Add a text member to the document class. Add the following public data member in the class declaration in ex28aDoc.h: CString m_strText; Set the string's initial value to Initial default text in the document's OnNewDocument member function.
4
4. Add a dialog to modify the text. Insert a new dialog template with an edit control, and then use ClassWizard to generate a CTextDialog class derived from CDialog. Don't forget to include the dialog class header in ex28aDoc.cpp. Also, use ClassWizard to add a CString member variable named m_strText for the edit control. 5. Add a new menu command in both the embedded and in-place menus. Add a Modify menu command in both the IDR_SRVR_EMBEDDED and IDR_SRVR_INPLACE menus. To insert this menu command on the IDR_SRVR_EMBEDDED menu, use the resource editor to add an EX28A-EMBED menu item on the top level, and then add a Modify option on the submenu for this item. Next add an EX28A-INPLACE menu item on the top level of the IDR_SRVR_INPLACE menu and add a Modify option on the EX28A-INPLACE submenu. To associate both Modify options with one OnModify function, use ID_MODIFY as the ID for the Modify option of both the IDR_SRVR_EMBEDDED and IDR_SRVR_INPLACE menus. Then use ClassWizard to map both Modify options to the OnModify function in the document class. Code the Modify command handler as shown here: void CEx28aDoc::OnModify() { CTextDialog dlg; dlg.m_strText = m_strText; if (dlg.DoModal() == IDOK) { m_strText = dlg.m_strText; UpdateAllViews(NULL); // Trigger CEx28aView::OnDraw UpdateAllItems(NULL); // Trigger //CEx28aSrvrItem::OnDraw SetModifiedFlag(); } 6. Override the view's OnPrepareDC function. Use ClassWizard to generate the function, and then replace any existing code with the following line: pDC->SetMapMode(MM_HIMETRIC); 7. Edit the view's OnDraw function. The following code in ex28aView.cpp draws a 2-cm circle centered in the client rectangle, with the text wordwrapped in the window: void CEx28aView::OnDraw(CDC* pDC) { CEx28aDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); FALSE, 0,
DEFAULT_QUALITY,
DEFAULT_PITCH
CFont* pFont = pDC->SelectObject(&font); CRect rectClient; GetClientRect(rectClient); CSize sizeClient = rectClient.Size(); pDC->DPtoHIMETRIC(&sizeClient); CRect rectEllipse(sizeClient.cx / 2 - 1000, -sizeClient.cy / 2 + 1000, sizeClient.cx / 2 + 1000, -sizeClient.cy / 2 - 1000); pDC->Ellipse(rectEllipse); pDC->TextOut(0, 0, pDoc->m_strText); pDC->SelectObject(pFont); } 8. Edit the server item's OnDraw function. The following code in the SrvrItem.cpp file tries to draw the same circle drawn in the view's OnDraw function. BOOL CEx28aSrvrItem::OnDraw(CDC* pDC, CSize& rSize) { // Remove this if you use rSize UNREFERENCED_PARAMETER(rSize); CEx28aDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: set mapping mode and extent // (The extent is usually the same as the size returned from // OnGetExtent) pDC->SetMapMode(MM_ANISOTROPIC); pDC->SetWindowOrg(0,0); pDC->SetWindowExt(3000, -3000); CFont font; font.CreateFont(-500, 0, 0, 0, 400, FALSE, FALSE, 0, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_SWISS, "Arial"); CFont* pFont = pDC->SelectObject(&font); CRect rectEllipse(CRect(500, -500, 2500, -2500)); pDC->Ellipse(rectEllipse); pDC->TextOut(0, 0, pDoc->m_strText); pDC->SelectObject(pFont); return TRUE; }
9. Edit the document's Serialize function. The framework takes care of loading and saving the document's data from and to an OLE stream named Contents that is attached to the object's main storage. You simply write normal serialization code, as shown here: void CEx28aDoc::Serialize(CArchive& ar) { if (ar.IsStoring()) { ar << m_strText; } else { ar >> m_strText; } } There is also a CEx28aSrvrItem::Serialize function that delegates to the document Serialize function. 10. Build and register the EX28A application. You must run the application directly once to update the Registry. 11. Test the EX28A application. You need a container program that supports in-place activation. Use Microsoft Excel 97 or a later version if you have it, or build the project in the MFC DRAWCLI sample. Choose the container's Insert Object menu item. If this option does not appear on the Insert menu, it might appear on the Edit menu instead. Then select Ex28a Document from the list. When you first insert the EX28A object, you'll see a hatched border, which indicates that the object is in-place active. The bounding rectangle is 3-by-3-cm square, with a 2-cm circle in the center, as illustrated here.
CHAPTER 2
ActiveX Controls Introduction Microsoft Visual Basic (VB) was introduced in 1991 and has proven to be a wildly popular and successful application development system for Microsoft Windows The 16-bit versions of VB supported Visual Basic controls (VBXs), ready-to-run software components that VB developers could buy or write themselves. VBXs became the center of a whole industry, and pretty soon there were hundreds of them. The VBX standard, which was highly dependent on the 16-bit segment architecture, did not make it to the 32-bit world. ActiveX Controls (formerly known as OLE controls, or OCXs) are the industrial-strength replacement for VBXs based on Microsoft COM technology. ActiveX controls can be used by application developers in both VB and Visual C++ 6.0. ActiveX controls can be written in C++ with the help of the MFC library or with the help of the ActiveX Template Library (ATL).
Ordinary ControlsA Frame of Reference These controls are all child windows that you use most often in dialogs, and they are represented by MFC classes such as CEdit and CTreeCtrl. The client program is always responsible for the creation of the control's child window. Ordinary controls send notification command messages (standard Windows messages), such as BN_CLICKED, to the dialog. If you want to perform an action on the control, you call a C++ control class member function, which sends a Windows message to the control. The controls are all windows in their own right. All the MFC control classes are derived from CWnd, so if you want to get the text from an edit control, you call CWnd::GetWindowText. But even that function works by sending a message to the control. Windows controls are an integral part of Windows, even though the Windows common controls are in a separate DLL. How ActiveX Controls Are Similar to Ordinary Controls You can consider an ActiveX control to be a child window, just as an ordinary control is. If you want to include an ActiveX control in a dialog, you use the dialog editor to place it there, and the identifier for the control turns up in the resource template. If you're creating an ActiveX control on the fly, you call a Create member function for a class that represents the control, usually in the WM_CREATE handler for the parent window. When you want to manipulate an ActiveX control, you call a C++ member function, just as you do for a Windows control. The window that contains a control is called a container. ActiveX Controls vs. Ordinary Windows Controls How ActiveX Controls Are Different from Ordinary ControlsProperties and Methods The most prominent ActiveX Controls features are properties and methods. Properties have symbolic names that are matched to integer indexes. For each property, the control designer assigns a property name, such as BackColor or GridCellEffect, and a property type, such as string, integer, or double. The client program can set an individual ActiveX control property by specifying the property's integer index and its value. The client can get a property by specifying the index and accepting the appropriate return value. In certain cases, ClassWizard lets you define data members in your client window class that are associated with the properties of the controls the client class contains. The generated Dialog Data Exchange (DDX) code exchanges data between the control properties and the client class data members. ActiveX Controls vs. Ordinary Windows Controls How ActiveX Controls Are Different from Ordinary ControlsProperties and Methods ActiveX Controls methods are like functions. A method has a symbolic name, a set of parameters, and a return value. You call a method by calling a C++ member function of
9
the class that represents the control. A control designer can define any needed methods, such as PreviousYear, LowerControlRods, and so forth. An ActiveX control doesn't send WM_ notification messages to its container the way ordinary controls do; instead, it "fires events." An event has a symbolic name and can have an arbitrary sequence of parametersit's really a container function that the control calls. Like ordinary control notification messages, events don't return a value to the ActiveX control. Examples of events are Click, KeyDown, and NewMonth. Events are mapped in your client class just as control notification messages are. ActiveX Controls vs. Ordinary Windows Controls How ActiveX Controls Are Different from Ordinary ControlsProperties and Methods In the MFC world, ActiveX controls act just like child windows, but there's a significant layer of code between the container window and the control window. In fact, the control might not even have a window. When you call Create, the control's window isn't created directly; instead, the control code is loaded and given the command for "in-place activation." The ActiveX control then creates its own window, which MFC lets you access through a CWnd pointer. It's not a good idea for the client to use the control's hWnd directly, however. A DLL is used to store one or more ActiveX controls, but the DLL often has an OCX filename extension instead of a DLL extension. Your container program loads the DLLs when it needs them, using sophisticated COM techniques that rely on the Windows Registry. Once you specify an ActiveX control at design time, it will be loaded for you at runtime. Obviously, when you ship a program that requires special ActiveX controls, you'll have to include the OCX files and an appropriate setup program. Installing ActiveX Controls Your first step is to copy the ActiveX control's DLL to \Windows\System for Microsoft Windows 95 or \Winnt\System32 for Microsoft Windows NT. Copy associated files such as help (HLP) or license (LIC) files to the same directory. Your next step is to register the control in the Windows Registry. Actually, the ActiveX control registers itself when a client program calls a special exported function. The Windows utility Regsvr32 is a client that accepts the control name on the command line. After you register your ActiveX control, you must install it in each project that uses it. That doesn't mean that the OCX file gets copied. It means that ClassWizard generates a copy of a C++ class that's specific to the control, and it means that the control shows up in the dialog editor control palette for that project. Installing ActiveX Controls Choose Add To Project from the Project menu and then choose Components And Controls.
10
This gets you the list of all the ActiveX controls currently registered on your system.
11
Properties BackColor Day DayFont DayFontColor DayLength FirstDay GridCellEffect GridFont GridFontColor GridLinesColor Month MonthLength ShowDateSelectors
Methods AboutBox NextDay NextMonth NextWeek NextYear PreviousDay PreviousMonth PreviousWeek PreviousYear Refresh Today
Events AfterUpdate BeforeUpdate Click DblClick KeyDown KeyPress KeyUp NewMonth NewYear
Each of the properties, methods, and events has a corresponding integer identifier.
12
Information about the names, types, parameter sequences, and integer IDs is stored inside the control and is accessible to ClassWizard at container design time.
ActiveX Control Container Programming MFC and ClassWizard support ActiveX controls both in dialogs and as "child windows." To use ActiveX controls, you must understand how a control grants access to properties, and you must understand the interactions between your Dialog Data Exchange (DDX) code and those property values. Property Access The ActiveX control developer designates certain properties for access at design time. Those properties are specified in the property pages that the control displays in the dialog editor when you right-click on a control and choose Properties.
Property Access When you click on the All tab, you will see a list of all the design- time-accessible properties, which might include a few properties not found on the Control tab All the control's properties, including the design-time properties, are accessible at runtime. Some properties, however, might be designated as read-only.
13
ClassWizard's C++ Wrapper Classes for ActiveX Controls When you insert an ActiveX control into a project, ClassWizard generates a C++ wrapper class, derived from CWnd, that is tailored to your control's methods and properties. The class has member functions for all properties and methods, and it has constructors that you can use to dynamically create an instance of the control. ClassWizard's C++ Wrapper Classes for ActiveX Controls unsigned long CCalendar::GetBackColor() { unsigned long result; InvokeHelper(DISPID_BACKCOLOR, DISPATCH_PROPERTYGET, VT_I4, (void*)&result, NULL); return result; } void CCalendar::SetBackColor(unsigned long newValue) { static BYTE parms[] = VTS_I4;
InvokeHelper(DISPID_BACKCOLOR, DISPATCH_PROPERTYPUT, VT_EMPTY, NULL, parms, newValue); } ClassWizard's C++ Wrapper Classes for ActiveX Controls
14
short CCalendar::GetDay() { short result; InvokeHelper(0x11, DISPATCH_PROPERTYGET, VT_I2, (void*)&result, NULL); return result; } void CCalendar::SetDay(short nNewValue) { static BYTE parms[] = VTS_I2;
InvokeHelper(0x11, DISPATCH_PROPERTYPUT, VT_EMPTY,NULL, parms, nNewValue); } ClassWizard's C++ Wrapper Classes for ActiveX Controls COleFont CCalendar::GetDayFont() { LPDISPATCH pDispatch; InvokeHelper(0x1, (void*)&pDispatch, NULL); return COleFont(pDispatch); } void CCalendar::SetDayFont(LPDISPATCH newValue) { static BYTE parms[] = VTS_DISPATCH; NULL, DISPATCH_PROPERTYGET, VT_DISPATCH,
InvokeHelper(0x1, DISPATCH_PROPERTYPUT, VT_EMPTY, parms, newValue); } ClassWizard's C++ Wrapper Classes for ActiveX Controls VARIANT CCalendar::GetValue() { VARIANT result; InvokeHelper(0xc, (void*)&result, NULL); DISPATCH_PROPERTYGET,
15
VT_VARIANT,
return result; } void CCalendar::SetValue(const VARIANT& newValue) { static BYTE parms[] = VTS_VARIANT; NULL,
InvokeHelper(0xc, DISPATCH_PROPERTYPUT, VT_EMPTY, parms, &newValue); } ClassWizard's C++ Wrapper Classes for ActiveX Controls void CCalendar::NextDay() { InvokeHelper(0x16, DISPATCH_METHOD, VT_EMPTY, NULL, NULL); } void CCalendar::NextMonth() { InvokeHelper(0x17, DISPATCH_METHOD, VT_EMPTY, NULL, NULL); }
ClassWizard's C++ Wrapper Classes for ActiveX Controls The first parameter of each InvokeHelper function match with the dispatch ID for the corresponding property or method in the Calendar control property list. Properties always have separate Set and Get functions. To call a method, simply call the corresponding function. To call the NextDay method from a dialog class function, you write code such as this: m_calendar.NextDay(); m_calendar is an object of class CCalendar, the wrapper class for the Calendar control.
AppWizard Support for ActiveX Controls When the AppWizard ActiveX Controls option is checked (the default), AppWizard inserts the following line in your application class InitInstance member function: AfxEnableControlContainer(); It also inserts the following line in the project's StdAfx.h file:
16
#include <afxdisp.h> If you decide to add ActiveX controls to an existing project that doesn't include the two lines above, you can simply add the lines. ClassWizard and the Container Dialog If your template contains one or more ActiveX controls, you can use ClassWizard to add data members and event handler functions. Dialog Class Data Members vs. Wrapper Class Usage What kind of data members can you add to the dialog for an ActiveX control? If you want to set a control property before you call DoModal for the dialog, you can add a dialog data member for that property. If you want to change properties inside the dialog member functions, you must take another approach: you add a data member that is an object of the wrapper class for the ActiveX control. Dialog Class Data Members vs. Wrapper Class Usage MFC DDX logic. The CDialog::OnInitDialog function calls CWnd::UpdateData(FALSE) to read the dialog class data members, and the CDialog::OnOK function calls UpdateData(TRUE) to write the members. Suppose you added a data member for each ActiveX control property and you needed to get the Value property value in a button handler. If you called UpdateData(FALSE) in the button handler, it would read all the property values from all the dialog's controlsclearly a waste of time. It's more effective to avoid using a data member and to call the wrapper class Get function instead. To call that function, you must first tell ClassWizard to add a wrapper class object data member. Dialog Class Data Members vs. Wrapper Class Usage Suppose you have a Calendar wrapper class CCalendar and you have an m_calendar data member in your dialog class. If you want to get the Value property, you do it like this: COleVariant var = m_calendar.GetValue(); you want to set the day to the 5th of the month before the control is displayed. To do this by hand, add a dialog class data member m_sCalDay that corresponds to the control's short integer Day property. Then add the following line to the DoDataExchange function: DDX_OCShort(pDX, ID_CALENDAR1, 0x11, m_sCalDay); Following codes construct and display dialog CMyDialog dlg; dlg.m_sCalDay = 5;
17
dlg.DoModal(); Dialog Class Data Members vs. Wrapper Class Usage The DDX code takes care of setting the property value from the data member before the control is displayed. No other programming is needed. Mapping ActiveX Control Events ClassWizard lets you map ActiveX control events the same way you map Windows messages and command messages from controls. If a dialog class contains one or more ActiveX controls, ClassWizard adds and maintains an event sink map that connects mapped events to their handler functions. It works something like a message map. Locking ActiveX Controls in Memory Normally, an ActiveX control remains mapped in your process as long as its parent dialog is active. That means it must be reloaded each time the user opens a modal dialog. The reloads are usually quicker than the initial load because of disk caching, but you can lock the control into memory for better performance. To do so, add the following line in the overridden OnInitDialog function after the base class call: AfxOleLockControl(m_calendar.GetClsid()); The ActiveX control remains mapped until your program exits or until you call the AfxOleUnlockControl function. ExampleAn ActiveX Control Dialog Container Step 1: Verify that the Calendar control is registered. If the control does not appear in the Visual C++ Gallery's Registered ActiveX Controls page, copy the files MSCal.ocx, MSCal.hlp, and MSCal.cnt to your system directory and register the control by running the REGCOMP program. Step 2: Run AppWizard to produce \vcpp32\ex08a\ex08a. Accept all of the default settings but two: select Single Document and deselect Printing And Print Preview.
18
In the AppWizard Step 3 dialog, make sure the ActiveX Controls option is selected, as shown below. Step 3: Install the Calendar control in the EX08A project. Choose Add To Project from Visual C++'s Project menu, and then choose Components And Controls. Choose Registered ActiveX Controls, and then choose Calendar Control 8.0.
19
Step 4: Edit the Calendar control class to handle help messages. Add Calendar.cpp to the following message map code: BEGIN_MESSAGE_MAP(CCalendar, CWnd) ON_WM_HELPINFO() END_MESSAGE_MAP() In the same file, add the OnHelpInfo function: BOOL CCalendar::OnHelpInfo(HELPINFO* pHelpInfo) { // Edit the following string for your system ::WinHelp(GetSafeHwnd(), "c:\\winnt\\system32\\mscal.hlp", HELP_FINDER, 0); return FALSE; } In Calendar.h, add the function prototype and declare the message map: protected:
20
afx_msg BOOL OnHelpInfo(HELPINFO* pHelpInfo); DECLARE_MESSAGE_MAP() The OnHelpInfo function is called if the user presses the F1 key when the Calendar control has the input focus. We have to add the message map code by hand because ClassWizard doesn't modify generated ActiveX classes.
Step 5:Use the dialog editor to create a new dialog resource. Choose Resource from Visual C++'s Insert menu, and then choose Dialog. The dialog editor assigns the ID IDD_DIALOG1 to the new dialog. Next change the ID to IDD_ACTIVEXDIALOG, change the dialog caption to ActiveX Dialog, and set the dialog's Context Help property (on the More Styles page). Accept the default OK and Cancel buttons with the IDs IDOK and IDCANCEL, and then add the other controls . Step 5: Use the dialog editor to create a new dialog resource.(2) Make the Select Date button the default button. Drag the Calendar control from the control palette. Then set an appropriate tab order. Assign control IDs as shown in the following table. ID IDC_CALENDAR1 IDC_SELECTDATE IDC_DAY IDC_MONTH IDC_YEAR IDC_NEXTWEEK Next Week button
Control Calendar control Select Date button Edit control Edit control Edit control
Step 6: Use ClassWizard to create the CActiveXDialog class If you run ClassWizard directly from the dialog editor window, it will know that you want to create a CDialog-derived class based on the IDD_ACTIVEXDIALOG template. Simply accept the default options, and name the class CActiveXDialog.
21
Click on the ClassWizard Message Maps tab, and then add the message handler functions . To add a message handler function, click on an object ID, click on a message, and click the Add Function button. If the Add Member Function dialog box appears, type the function name and click the OK button.
Step 6: Use ClassWizard to create the CActiveXDialog class (2) Message Handler Function Object ID CActiveXDialog IDC_CALENDAR1 IDC_SELECTDATE IDC_NEXTWEEK IDOK Message WM_INITDIALOG NewMonth (event) BN_CLICKED BN_CLICKED BN_CLICKED Member Function OnInitDialog (virtual function) OnNewMonthCalendar1 OnSelectDate OnNextWeek OnOK (virtual function)
Step 7:Use ClassWizard to add data members to the CActiveXDialog class. Click on the Member Variables tab, and then add the data members
22
Step 8: Edit the CActiveXDialog class. Add the m_varValue and m_BackColor data members, and then edit the code for the five handler functions OnInitDialog, OnNewMonthCalendar1, OnSelectDate, OnNextWeek, and OnOK. Step 8.1 : Edit the CActiveXDialog class. void CActiveXDialog::OnNewMonthCalendar1() { AfxMessageBox("EVENT: CActiveXDialog::OnNewMonthCalendar1"); } void CActiveXDialog::OnSelectDate() { CDataExchange dx(this, TRUE); DDX_Text(&dx, IDC_DAY, m_sDay); DDX_Text(&dx, IDC_MONTH, m_sMonth); DDX_Text(&dx, IDC_YEAR, m_sYear); m_calendar.SetDay(m_sDay); m_calendar.SetMonth(m_sMonth); m_calendar.SetYear(m_sYear);
23
} void CActiveXDialog::OnNextWeek() { m_calendar.NextWeek(); } void CActiveXDialog::OnOK() { CDialog::OnOK(); m_varValue = m_calendar.GetValue(); // no DDX for VARIANTs } Step 8.2 : Edit the CActiveXDialog class. (3) The OnSelectDate function is called when the user clicks the Select Date button. The function gets the day, month, and year values from the three edit controls and transfers them to the control's properties. ClassWizard can't add DDX code for the BackColor property, so you must add it by hand. In addition, there's no DDX code for VARIANT types, so you must add code to the OnInitDialog and OnOK functions to set and retrieve the date with the control's Value property.
Step 9 : Connect the dialog to the view. Use ClassWizard to map the WM_LBUTTONDOWN message, and then edit the handler function as follows: void CEx08aView::OnLButtonDown(UINT nFlags, CPoint point) { CActiveXDialog dlg; dlg.m_BackColor = RGB(255, 251, 240); // light yellow COleDateTime today = COleDateTime::GetCurrentTime(); dlg.m_varValue= COleDateTime(today.GetYear(), today.GetMonth(), today.GetDay(), 0, 0, 0); if (dlg.DoModal() == IDOK) { COleDateTime date(dlg.m_varValue); AfxMessageBox(date.Format("%B %d, %Y")); }
24
} The code sets the background color to light yellow and the date to today's date, displays the modal dialog, and reports the date returned by the Calendar control. You'll need to include ActiveXDialog.h in ex08aView.cpp.
Step 10: Edit the virtual OnDraw function in the file ex08aView.cpp. To prompt the user to press the left mouse button, replace the code in the view class OnDraw function with this single line: pDC->TextOut(0, 0, "Press the left mouse button here.");
Step 11: Build and test the EX08A application. Open the dialog, enter a date in the three edit controls, and then click the Select Date button. Click the Next Week button. Try moving the selected date directly to a new month, and observe the message box that is triggered by the NewMonth event. Watch for the final date in another message box when you click OK. Press the F1 key for help on the Calendar control.
Creating ActiveX Controls at Runtime Insert the component into your project. ClassWizard will create the files for a wrapper class. Add an embedded ActiveX control wrapper class data member to your dialog class or other C++ window class. An embedded C++ object is then constructed and destroyed along with the window object. Choose Resource Symbols from Visual C++'s View menu. Add an ID constant for the new control. If the parent window is a dialog, use ClassWizard to map the dialog's WM_INITDIALOG message, thus overriding CDialog-::OnInitDialog. For other windows, use ClassWizard to map the WM_CREATE message. The new function should call the embedded control class's Create member function. This call indirectly displays the new control in the dialog. The control will be properly destroyed when the parent window is destroyed. In the parent window class, manually add the necessary event message handlers and prototypes for your new control. Don't forget to add the event sink map macros. ClassWizard doesn't help you with event sink maps when you add a dynamic ActiveX control to a project. Consider inserting the target control in a dialog in another temporary project. After you're finished mapping events, simply copy the event sink map code to the parent window class in your main project.
25
CHAPTER 4
Component Object Model
It allows developers to write software that runs regardless of issues such as thread awareness and language choice Benefits of COM Distributed Components
Component Object Model How should one chunk of software access the services provided by another chunk of software? COM: A standard approach to access all kinds of software services, regardless of how they are provided COM is not a programming language COM is not DLL COM is not only a set of API or functions with the benefits of object orientation language independent COM defines a binary interface that objects must support
26
27
Identifying an Interface Human-readable name Globally Unique Identifier (GUID) Interface Identifier (IID)
Immutability of the Interfaces Once an interface has been implemented in released software, it cannot be changed To add new functionality or to modify existing functionality requires defining an entirely new interface, with a new and different IID The creator of the software is free to stop supporting the original interface but is absolutely prohibited from changing it
Changing Features to an interface The objects creator must define a new interface, say multiply that includes the new or changed methods and the COM object continues to support add as before, but it now also support multiply. Clients that are unaware of the upgrade never ask for a pointer to mutiply, they continue to use add as before
28
COM Classes Class identifier (CLSID) An object of a specific class supports a certain set of interfaces An objects class identifies a particular implementation of a group of interfaces
COM Library The COM library implements a group of functions that supply basic services to objects and their clients The COM librarys services are accessed through ordinary function calls
System Registry The classes of all objects that the COM library will be asked to create on this machine must be registered Registry mapping includes CLSID Kinds of servers Pathname for the file containing the servers DLL or executable, or for where to find remote servers executable
29
Reusing COM Objects One COM object cant reuse anothers code through inheritance Containment (delegation)
Aggregation
Marshaling and Type Information Marshaling makes that the client can invoke the methods in the same way, regardless of where the object is implemented
30
31
STEP 2
32
STEP 3
33
STEP 4
34
STEP 5
STEP 6
35
STEP 7
36
STEP 8
STEP 9
37
SAVE COMPILE by F7
38