Programming windows 5th edition Chapter 4 output text
1. this chapter further describes the notes for displaying text, including how to calculate the output coordinates based on the font size, how to obtain the device handle and start drawing under different circumstances, how to add a scroll bar to a program.
2. First of all, it is very important to introduce, that is, under what circumstances windows will send the wm_paint message to our program, as shown below:
(1) If one of the following conditions occurs, the wm_paint message will be sent:
When a user moves a window or displays a window, the area hidden before the window is visible again.
The user changes the window size (if the window type style has cs_hredraw and cs_vredraw flag settings ).
The program uses the scrollwindow or scrolldc function to scroll part of the display area.
The program uses the invalidaterect or invalidatergn function to deliberately generate the wm_paint message.
(2) In the following cases, Windows tries to save a display area for us, so that we do not need to allow our program to re-paint, but Windows does not necessarily save the result, that is, in the following cases, windows may send the wm_paint message:
Windows erasure overwrites the dialog box or message box of some windows.
Drop-down menu and release it.
Displays a tooltip message.
(3) In the following cases, Windows will always save the covered display area and restore it. That is to say, the wm_paint message will not be sent:
Move the cursor over the display area.
The icon is dragged across the display area.
3. Valid and invalid rectangles. When Windows sends a wm_paint message, it will save the display area to be repainted to a paintsturcture. From here, our program will know which part of the content needs to be repainted. Windows does not save multiple wm_paint messages in the message queue, because when you add a wm_paint message, if a wm_paint message exists in the queue, in this case, Windows will remove the invalid rectangle in the previous wm_paint message and combine it with the current rectangle to form a new moment, and then let our message processing function process it, windows does not save multiple wm_paint messages in the message queue. In the previous chapters, we can see that when you call ininpaint, You need to input a paintstruct structure. Windows will fill in the invalid rectangle in this data structure.
The concept of invalid rectangle is very important. When processing a message that involves re-painting, the message processing function must declare the invalid rectangle as a valid rectangle. Otherwise, Windows will think that our re-painting is not complete, the result is -- Windows always sends the wm_paint message to our program!
You can call the beginpaint function to make the entire display area valid, or call the validaterect function to make any rectangular area in the display area valid. Once the rectangle becomes valid, any wm_paint message that involves the rectangle area will be deleted. Therefore, in our code, even if we do not do anything in wm_paint message processing, we must call beginpaint and endpaint to make the invalid rectangle valid, otherwise, wm_paint will be sent all the time.
4. device content (DC ). As we can see above, after beginpaint is called, an HDC will be returned, which is actually a DC handle. DC is actually the data structure stored inside Windows GDI, with HDC, we have the ability to output things to specific display devices. In DC, most of the data of some graphic attributes is stored. For example, in textout, the DC stores the color of the text, the background color of the text, and the XY coordinate ing to the display area of the window, the font used. Therefore, to modify the text output format, color, etc., we need to operate on the DC, and then use the modified DC to draw, in order to display the expected results.
5. OK. Since the first step of drawing and outputting text is to obtain HDC, I will explain the two methods to obtain HDC. Note: Apart from calling the DC created by createdc, the program cannot save other DC handles (HDC) between two messages ).
(1) method 1. This method is used to process the wm_paint message. Is to call the beginpaint, endpaint function pair. As mentioned above, to process the wm_paint message, do nothing and call these two functions. Otherwise, the wm_paint message will be sent continuously (as described above ). When calling beginpaint, we also need to fill in a paintstruct with the following structure:
-
Code: select all
-
typedef struct tagPAINTSTRUCT
{
HDC hdc ;
BOOL fErase ;
RECT rcPaint ;
BOOL fRestore ;
BOOL fIncUpdate ;
BYTE rgbReserved[32] ;
} PAINTSTRUCT ;
Beginpaint will fill in data for us in this structure. Here, we only need to pay attention to the first three fields, followed by windows. HDC is not mentioned; If ferase is marked as false by beginpaint, it indicates that Windows has used the background to erase invalid rectangles (when the background is defined in the registration window category, as mentioned earlier, when beginpaint is called, windows will erase invalid rectangle content for us). If we don't want windows to erase invalid rectangle for us, for example, if many require rapid display, we can process wm_erasebkgnd messages, add our code to it. However, if our program calls invalidaterect to trigger the wm_paint message, the last parameter of the invalidaterect function can be used to specify whether to erase the invalid region. If this parameter is false, in Windows, the invalid region will not be erased. In this case, the value of the ferase field is true. In the end, the third field is rcpaint, which is the definition of the invalid rectangle, we can use this to obtain the part that needs to be re-painted, and, in fact, Windows will automatically cut down our drawing operations, drawing operations not in this rectangle area will be ignore! If we really need to draw a rectangle outside this rectangle, we can call invalidaterect (hwnd, null, true) before calling beginpaint, so that the entire display area will become invalid rectangle. In this case, we generally re-paint all the display areas regardless of the number of three or three.
Getupdaterect. This method can also be used to obtain the current invalid rectangular area. In fact, beginpaint is enough, so I think this method is of little use. It should be noted that if you call getupdaterect after beginpaint is called, the resulting rectangle will be empty rect, as we have mentioned earlier, beginpaint declares the entire display area as a valid area.
(2) method 2. Method 1 is applicable to the wm_paint message processing function. In fact, we also need to plot other messages during processing. How can we obtain HDC at this time? It's easy to call the getdc method. The getdc function is simpler. You only need to input a window handle hwnd and an HDC will be returned. Like beginpaint and endpaint, getdc must be paired with releasedc. The HDC returned by getdc contains an invalid rectangle, which is the entire display area (because this is not in the wm_paint message ). The getdc method is generally used when the corresponding keyboard and mouse messages are sent (for example, a program drawing with the mouse). At this time, we can update the display area immediately based on the mouse input, you do not have to wait until the wm_paint message is processed.
The getdc and releasedc methods do not declare an area as a valid rectangular area. We can achieve this by calling the validaterect function.
Functions similar to getdc include getwindowdc. Getdc returns the device content handle used for writing to the display area of the window, while getwindowdc returns the device content handle written to the entire window. For example, your program can use the device content handle returned from getwindowdc to write text in the title column of the window. In this case, the program should also process the message wm_ncpaint (non-display area painting.
6. textout function. The function is prototype as follows:
Textout (HDC, X, Y, pstext, ilength );
Notes:
(1) do not include carriage return, line feed, deletion, and other control characters in the pstext string. These characters will be displayed as square characters.
(2) ilength is the number of characters, not the number of bytes.
(3) the X and Y coordinates are called logical coordinates. It is related to the coordinate ing mode defined in HDC. The default value is mm_text. At this time, the logical coordinates are the same as the actual coordinates, that is, the upper left corner is 0, 0, x, to the right, and Y. Different coordinate modes can be defined as needed. The next chapter will explain.
7. System font. The font must be explained, because we must know the width, height, and other information of the font when drawing the text to the correct place we want. Here, we will only talk about the standard system font that comes with windows. We will study other fonts on our own. In earlier versions of Windows, the system font is an equal-width font, that is, all letters have a single width. Now no, for example, W and I occupy different widths, because non-width fonts are more conducive to reading. A system font is a dot matrix font. That is to say, a character image is defined as a pixel block. Chapter 1 describes TrueType (a word body defined by a contour ).
OK. Like the getsystemmetrics function, we can use gettextmetrics to obtain the font information. Gettextmetrics fills the textmetric structure. This structure has 20 fields. We only care about the first seven fields:
-
Code: select all
-
typedef struct tagTEXTMETRIC
{
LONG tmHeight ;
LONG tmAscent ;
LONG tmDescent ;
LONG tmInternalLeading ;
LONG tmExternalLeading ;
LONG tmAveCharWidth ;
LONG tmMaxCharWidth ;
[other structure fields]
}
TEXTMETRIC, * PTEXTMETRIC ;
// Get the text metrics
HDC = getdc (hwnd );
Gettextmetrics (HDC, & TM );
Releasedc (hwnd, HDC );
For the meanings of these fields, see Appendix 1.
In our daily programming process, we only need to pay attention to these key points (calculate the width and height of the characters to facilitate us to determine the coordinates during textout ):
(1) lower case characters generally use tmavecharwidth to determine the width.
(2) width of uppercase characters: TM. tmpitchandfamily & 1? 3: 2) * tmavecharwidth/2, that is, first check whether tmpitchandfamily is 0 or 1. If it is 0, the width of uppercase characters is the same as that of lowercase characters, set the width of uppercase characters to 1.5 times the average width of lowercase characters.
(3) character height: TM. tmheight + TM. tmexternalleading
8. we can put the code for determining the character width and height in the wm_create message for initialization. When using textout, we can consider using the wsprintf function to format the character string, the wsprintf function returns the number of characters in the formatted string. This value is exactly used by the ilength parameter of textout, as shown below:
Textout (HDC, X, Y, szbuffer, wsprintf (szbuffer, text ("the sum of % I and % I is % I"), IA, IB, IA + IB ));
9. An example is provided in the book. You can read the exercise code. The example is well understood. The settextalign (HDC, ta_right | ta_top) in it specifies the X coordinate of textout, And the Y coordinate is from the coordinate in the upper right corner of the character, rather than the default upper left corner, the right alignment of the string is achieved.
10. In the above code example, sysmets1 has a very obvious drawback: The display space is not enough to show our drawing, so we naturally introduced the concept of a scroll bar. This chapter describes the two scroll bars. The second is a common practice and a scientific practice. The first practice is flawed and the size of the scroll bars is fixed. Let's talk about it one by one, which is of great value.
11. Regardless of the scroll bar method, we need to set the scroll range and output text when the window size changes. Therefore, the following code processes the wm_size message. We can get the size of the current window in the wm_size message:
-
Code: select all
-
case WM_SIZE:
cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
return 0 ;
In the wm_size message, the low 16 bits of lparam indicate the width of the current display area (not the entire window), and the high 16 bits indicate the height of the current display area. The loword and hiword macros provided by windows can be used to retrieve these two values.
12. it is very easy to add a scroll bar to the window. Add ws_vscroll and ws_hscroll to the window when defining the window style in the third parameter of createwindow. First, let's look at the first method to implement the scroll bar, which is achieved through several key functions:
// Set Ibar to sb_vert or sb_horz, indicating the vertical and horizontal scroll bars.
// Imin, IMAX indicates the value range of the scroll bar
Setscrollrange (hwnd, Ibar, Imin, IMAX, bredraw );
// Set the current position of the scroll bar
Setscrollpos (hwnd, Ibar, IPOs, bredraw );
After you click the scroll bar, Windows sends us the wm_vscroll or wm_hscroll message. The two messages naturally have two message parameters: wparam and lparam. The lparam parameter can be ignored for the scroll bar created as a part of the window, because lparam makes sense only when the scroll bar is in the subwindow (usually in a dialog box ).
OK. Let's see wparam. The low 16 bits of wparam are a numerical value, indicating the operation of the current mouse on the scroll bar-that is, a notification code. The notification codes of the scroll bar are as follows (defined in winuser. h ):
Appendix 2
In general, we can ignore the sb_endscroll notification code, because we have set the current position of the scroll bar in the corresponding scroll bar notification code, it does not need to be processed in sb_endscroll. What is interesting here is sb_thumbtrack and sb_thumbposition. These two are actually the notification codes sent when we click the square of the scroll bar for operation. If we are dealing with sb_thumbtrack, it is obvious that we will not stop re-painting the window, because users only need to drag the square block of the scroll bar and we will receive this notification, if we deal with sb_thumbposition, we will only deal with it when the scroll bar is opened. The effect is that we cannot see any reaction when dragging the scroll bar, the display area is refreshed only when the scroll bar is opened. Generally, we only need to process one of the two notification codes.
In addition to the above notification code, the sb_top, sb_bottom, sb_left, and sb_right notification codes are also defined in winuser. H, indicating that the scroll bar has been moved to its smallest or largest position. However, these notification codes will never be received by the scroll bar created as part of the application window.
13. view the last attachment and you can see the above-mentioned program sysmets2 with a scroll bar. In this program, each time you click the arrow buttons at both ends of the scroll bar, a row of information is scrolled each time, out of performance considerations, we did not respond to sb_thumbtrack. In this case, we responded to sb_thumbposition and called the invalidaterect function to generate the wm_paint message. Then, we re-painted the display area. If we responded to sb_thumbtrack, we can first call invalidaterect to generate an invalid rectangle area, and then immediately call updatewindow so that Windows will not queue wm_paint messages, and directly call the message processing function to process wm_paint messages, this enables fast re-painting of content in the display area. This program works well, but there are several problems with this method of using the scroll bar:
(1) when responding to wm_vscroll and wm_hscroll messages, we retrieve the current position of the scroll bar in wparam, but we find that no. This wparam contains only 16 bits to indicate the position of the scroll bar, this limits the maximum setting range of the scroll bar.
(2) In this way, the scroll bar is always a size. the scroll bar does not dynamically change the size according to the area we can scroll, currently, the scroll bar blocks of Windows programs can automatically change the size according to the length.
14. Now let's take a look at the better implementation of the scroll bar to solve the above problems. If we look at the setscrollrange, setscrollpos, getscrollrange, and getscrollpos functions in msdn, we will be notified that these functions are outdated functions. In fact, these functions have been available since Windows 1.0, in addition, 32-bit windows is upgraded to a 32-bit parameter, but there is indeed a better function for handling the scroll bar, that is, the "Number of scroll bar information functions"-setscrollinfo and getscrollinfo.
These two functions can complete all the functions of the above functions and solve the above two problems.
Setscrollinfo (hwnd, Ibar, & Si, bredraw );
Getscrollinfo (hwnd, Ibar, & Si );
The third parameter of the two functions becomes a struct named scrollinfo:
-
Code: select all
-
typedef struct tagSCROLLINFO
{
UINT cbSize ; // set to sizeof (SCROLLINFO)
UINT fMask ; // values to set or get
int nMin ; // minimum range value
int nMax ; // maximum range value
UINT nPage ; // page size
int nPos ; // current position
int nTrackPos ; // current tracking position
}
SCROLLINFO, * PSCROLLINFO ;
This is the key:
Cbsize -- generally set to SI. cbsize = sizeof (SI); or Si. cbsize = sizeof (scrollinfo); in the future, we will find that many struct fields in windows have such fields. This field will allow future Windows versions to expand the structure and add new functions, it is also compatible with previously written code.
Fmask -- this is the key. Fmask is a flag and is defined as a heap of constants starting with SIF. These constants can be combined with |. They include:
In the setscrollinfo function, if fmask is set to sif_range, The Nmin and Nmax fields must be set to the desired scroll bar range. When the getscrollinfo function uses the sif_range flag, Nmin and Nmax are the rolling range.
The sif_pos flag is the same. When using setscrollinfo, you must set the NPOs field of the structure to the desired position. You can use the sif_pos flag to obtain the current position through getscrollinfo.
Use the sif_page flag to obtain the page size. Use the setscrollinfo function to set the npage to the desired page size. Getscrollinfo uses the sif_page flag to obtain the current page size. If you do not want to get a proportional scroll bar, do not use this flag.
When processing a wm_vscroll or wm_hscroll message with sb_thumbtrack or sb_thumbposition notification code, you can only call the getscrollinfo method. In this case, the fmask should be set to the sif_trackpos flag. From the return of the function, the ntrackpos segment of the scrollinfo structure indicates the current 32-bit volume block position.
Another fmask is the sif_disablenoscroll flag, which can only be used for setscrollinfo, indicating that the scroll bar is disabled.
The sif_all flag is a combination of sif_range, sif_pos, sif_page, and sif_trackpos. It is convenient to set the scroll bar parameter during wm_size message processing, which is also convenient for processing the scroll bar message. Because after sif_all is set, each field in scrollinfo has a value.
Therefore, we can see from the above that, first of all, the parameters involved in the position and range of the scroll bar are 32 bits, without the above 16 bits; secondly, there is an additional page, this page defines the range that a page (PAGE) can display. In this way, page and Nmin and Nmax are combined to display different sizes according to the ratio. However, you should also note that it is a bit difficult. For example, if we set Nmin = 0, Nmax = 75, and npage = 50, then the scroll bar actually has only 25 tumble units, instead of 75, because 50 Entries can be displayed on one screen (one page) and the last line will be displayed!
15. OK. For the above sysmets3, it is the most perfect program with a scroll bar. It adds both the vertical and horizontal scroll bars. Let's take a closer look at the code in it and talk about the following points:
(1) When npage is greater than or equal to Nmax, it indicates that the current screen is sufficient to show all the data. In this case, Windows will hide the scroll bar. If you do not want to hide it, you can set sif_disablenoscroll with setscrollinfo, at this time, the scroll bar will not be used and will not be hidden.
(2) when responding to wm_size, it is natural to set the range and page of the scroll bar. For page settings, the height of the current display area is divided by the height of one line of characters, indicating how many lines of characters can be displayed on a screen.
(3) when responding to a rolling message, we first set a new scroll bar position, and then use the new position to compare it with the position before rolling. If there is a change, therefore, it is very complicated to call scorllwindow, which has been replaced by scrolljavaswex. With this function, invalidaterect is not required, because this function will also generate wm_paint, this function indicates the current scroll size. The second parameter is the horizontal scroll Change, and the third parameter is the vertical scroll size. The last two null values indicate updating the entire display area.
(4) In terms of vertical scrolling, we processed sb_thumbtrack and responded to sb_thumbposition in terms of horizontal scrolling.
(5) In wm_paint, we selectively re-paint the display area based on the size of the invalid area, which brings better performance.
(6) In the future, let's take a look at scrollappswex. What improvements have been made? Currently, in this program, the mouse wheel cannot scroll the window. Can it be changed to scrollappswex?
Followed by the last question posted above:
Why do we use scrollwindow and scroll1_wex instead of invalidaterect to generate wm_paint?
Some answers found on the Internet:
Scrollgatewex () first moves the bitmap block on the DC (hardware operation) of the screen, and then sends wm_erasebkgnd (optional) to the newly exposed area) and set a new invalid area for the window (you can get this area in wm_paint ).
That is to say, windows first helps us to move some things that still need to be displayed to the appropriate position on the screen through graphics, and then sets the newly appearing part that needs to be re-painted to an invalid rectangle area, after we get this area in wm_paint, we only need to re-paint the entire area. It is indeed much more efficient.
If invalidaterect is used, it is difficult for us to determine the rolling area and perform pixel-level operations. Therefore, we can only set the entire display area to invalid. In this way, the entire display area must be re-painted when wm_paint is used, for scroll operations that drag the scroll bar, this re-painting method has very low performance.