I still prefer to use cases to explain the problem. The requirement case is described as follows: to implement a window class derived from cdialog, you must modify the shape of the mouse pointer when moving the cursor to the window client area, display the shape of a small hand.
For the convenience described later, we assume that the derived window class is named cmydialog, and the resource corresponding to the mouse cursor is idc_cursor_hand.
It is often seen that some colleagues will use the following method to meet the above requirements:
(1) Add the voing function void cmydialog: onmousemove () for the wm_mousemove message to cmydialog ();
(2) In onmousemove (), call setcursor () to change the cursor shape. The Code is as follows:
Void cmydialog: onmousemove (uint nflags, cpoint point)
{
// Todo: add your message handler code here and/or call default
Setcursor (: loadcursor (afxgetresourcehandle (), makeintresource (idc_cursor_hand )));
Cdialog: onmousemove (nflags, point );
}
This implementation method has two main defects: first, the blinking problem. The person with the eye should be able to see that the cursor will flash obviously while moving the mouse. Second, when you click the mouse, when you double-click the cursor, The setcursor () action fails and the cursor shape changes back to the default pointer shape.
In fact, in the MFC program, there are three more orthodox methods to modify the mouse cursor shape. The so-called Orthodox is not what I blow, they are from the msdn technical article: HOWTO: change the mouse pointer for a window in MFC (q131991).
Three methods
Here are three ways an application can change the mouse pointer in a window:
(1) override the cwnd: onsetcursor () function. Call Windows API setcursor () function to change the pointer.
(2) register your own window class with the desired mouse pointer, override the cwnd: precreatewindow () function, and use the newly-registered window class to create the window.
(3) to show the standard hourglass pointer, an application can call the c0000target: beginwaitcursor (), which displays the hourglass, and call each target: endwaitcursor () to revert back to the default pointer. this scheme works only for the duration of a single message. if the mouse is moved before a call to endwaitcursor is made, Windows sends a wm_setcursor message to the window underneath the pointer. the default handling of this message resets the pointer to the default type, the one registered with the class, so you need to override cwnd: onsetcursor () for that window, and reset the pointer back to the hourglass.
Method 1 and method 2 are mainly for those that are similar to the requirement case and want to customize the mouse and cursor shape in the program. Both methods can be used to change the system's default mouse and cursor (hereinafter referred to as the default cursor) shape to a custom cursor shape. Method 3 is mainly for the application of the hourglass-shaped mouse cursor (hereinafter referred to as the hourglass cursor). The Hourglass cursor is an important and common user experience in the Windows operating system, for operations that have a relatively long execution cycle (usually requiring users to wait for a period of time), the system usually changes the default cursor to an hourglass cursor during Operation execution, to prompt the user to wait.
In the subsequent articles, I will explain the above three methods based on the msdn technical article and my own practices and the actual code that can be run.
This article from the csdn blog, reproduced please indicate the source: http://blog.csdn.net/duxiuxing/archive/2007/05/07/1599771.aspx
Continue to the topic that has not been completed in the previous article. Next we will continue to explain the three methods mentioned above through the code.
Add the member variable hcursor m_hmycurosr in cmydialog and initialize it in the cmydialog constructor:
M_hmycurosr = NULL;
My idea is to use cmydialog: m_hmycurosr to save the mouse and cursor shape to be used. When it is null, the default mouse and cursor are used. In addition, the Public member function setmycursor () is added to cmydialog for external calls (this can be understood). The implementation is as follows:
//////////////////////////////////////// //////////////
//
// Function name: cmydialog: setmycursor
//
// Access: Public
//
// Description: set a new mouse cursor.
//
// Parameters:
// Hcursor
// New mouse cursor handle. If it is null, the default mouse cursor shape is used.
//
// Return value: return the previously used mouse cursor handle
//
//////////////////////////////////////// //////////////
Hcursor cmydialog: setmycursor (hcursor)
{
Hcursor HRET = m_hmycurosr;
M_hmycurosr = hcursor;
Return HRET;
}
Method 1: Call the API function setcursor () to modify the mouse cursor shape when processing the wm_setcursor message.
If the mouse causes the cursor to move in a window and the mouse input is not captured, the window will receive the wm_setcursor message, we can understand wm_setcursor as a message specifically used to set the mouse cursor shape. In vc6, The classwizard can automatically generate the message function onsetcursor () corresponding to wm_setcursor, which is implemented as follows:
On_wm_setcursor ()
Bool cmydialog: onsetcursor (cwnd * pwnd, uint nhittest, uint message)
{
If (htclient = nhittest)
& (M_hmycurosr! = NULL ))
{
: Setcursor (m_hmycurosr );
Return true;
}
Return cdialog: onsetcursor (pwnd, nhittest, message );
}
* It should be noted that after the mouse cursor is set, you should let the function return true to prevent the system from performing default processing.
This article from the csdn blog, reproduced please indicate the source: http://blog.csdn.net/duxiuxing/archive/2007/05/07/1599822.aspx
Method 2: Use afxregisterclass () or afxregisterwndclass () to directly register a window type that specifies the mouse and cursor with the system.
The mouse cursor specified when the window type is registered is also called the full cursor. Under the MFC framework, we usually put this step in the cwnd: precreatewindow () function.
Bool cmydialog: precreatewindow (createstruct & CS)
{
If (m_hmycurosr! = NULL)
{
CS. lpszclass = afxregisterwndclass (
Cs_dblclks | cs_hredraw | cs_vredraw, // use any window styles
M_hmycurosr,
(Hbrush) (color_window + 1); // background brush
}
Return cdialog: precreatewindow (CS)
}
There is no difference between method 1 and method 2 in terms of the final implementation effect. However, from the perspective of ease of use and flexibility, I personally prefer method 1. Its ease of use is because I don't need to remember the usage of the afxregisterwndclass () function. Its flexibility lies in its ability to precisely control the shape of the mouse pointer while the program is running, for example, I want to change the above case: to implement a window class derived from cdialog, we need to modify the shape of the mouse pointer when moving the cursor to a specific area in the window client area, display the shape of a small hand. The size of a specific region is specified by its member variable crect m_rcchangecursor.
You only need to rewrite onsetcursor () to meet the above requirements:
Bool cmydialog: onsetcursor (cwnd * pwnd, uint nhittest, uint message)
{
// Todo: add your message handler code here and/or call default
If (m_hmycurosr! = NULL)
{
Cpoint Pt (0, 0 );
: Getcursorpos (& pt );
Screentoclient (& pt );
If (m_rcchangecursor.ptinrect (PT ))
{
: Setcursor (m_hmycurosr );
Return true;
}
}
Return cdialog: onsetcursor (pwnd, nhittest, message );
}
Before the end of this article, let's look back at the processing mechanism of the wm_setcursor message:
(A) The default window process first sends the wm_setcursor message to the window or control under the current cursor;
(B) if the window or control processes the wm_setcursor message (that is, true is returned), Windows will not do anything redundant and the message will end after processing;
(C) if the window or control does not process the wm_setcursor message (for example, false is returned or the default processing of the MFC base class is called), the parent window will continue to process the wm_setcursor;
(D) if the parent window does not do any processing, use the full cursor;
(E) If there is no full cursor, use the default arrow cursor of the current system.
This article from the csdn blog, reproduced please indicate the source: http://blog.csdn.net/duxiuxing/archive/2007/05/11/1603843.aspx
Modify the shape of the mouse and cursor (4) Add to favorites
Method 3: The Hourglass cursor is supported by the Windows operating system by default. Microsoft has encapsulated the support for the hourglass cursor in the MFC framework, developers do not need to introduce cursor Resources in the program. In the MFC program, you can use the following three methods to operate the hourglass cursor:
(1) cwinapp: dowaitcursor ();
(2) cshorttarget: beginwaitcursor (), cshorttarget: endwaitcursor () and cshorttarget: restorewaitcursor ();
(3) cwaitcursor encapsulation class.
The debug tracing shows that the encapsulation of (2) and (3) is ultimately implemented by calling (1. They have the following relationships:
Description (1) (2) (3)
Open the hourglass cursor cwinapp: dowaitcursor (1) c0000target: beginwaitcursor () cwaitcursor Constructor
Close the hourglass cursor cwinapp: dowaitcursor (-1) c1_target: endwaitcursor () cwaitcursor destructor
Refresh the hourglass cursor cwinapp: dowaitcursor (0) c1_target: restorewaitcursor () cwaitcursor: Restore ()
The following describes several instance scenarios:
Scenario 1: Open the hourglass cursor before starting the chief operation, and close the hourglass cursor after the long operation. Long operations here refer to operations that take a relatively long time to complete, such as copying and pasting large files.
Void cmydialog: dosomelengthyoperation ()
{
Beginwaitcursor (); // or afxgetapp ()-> dowaitcursor (1)
//... CEO operation
Endwaitcursor (); // or afxgetapp ()-> dowaitcursor (-1)
}
If other windows are displayed in the domodal () mode during execution of a long operation, you need to perform the hourglass cursor operation after domodal () is executed. The following code is modified from msdn:
Void cmydialog: dosomelengthyoperation ()
{
Cwaitcursor wait; // display wait cursor
// Do some lengthy processing
// The dialog box will normally change the cursor
// The standard arrow cursor.
Csomedialog DLG;
DLG. domodal ();
// It is necessary to call restore here in order
// To change the cursor back to the wait cursor.
Wait. Restore ();
// Do some more lengthy processing
// Destructor automatically removes the wait cursor
}
Scenario 2: There are some operations or calls. Users cannot immediately feel that the operation has been executed from the UI. At this time, we can switch the cursor over the hourglass and keep the display for 1 to 2 seconds, to indicate that the user operation has been executed. The most typical case I have encountered is that the client calls IE to perform webpage jump operations.
Void cmydialog: jumpwebsite (lpctstr lpszurl)
{
Cwaitcursor wait;
: ShellExecute (getsafehwnd (), _ T ("open"), _ T ("iexplore.exe"), lpszurl, null, sw_shownormal );
// Hold the hourglass cursor for at least 1 second
: Sleep (1000 );
}
In scenarios 1 and 2, the opening and closing of the hourglass cursor are performed in the same continuous processing process (in the sample code, "In the same continuous processing process" is actually the meaning of "in the same function"). A long operation will cause the UI to be unresponsive in a short period of time, their implementation principle is to set the cursor to an hourglass before the UI does not respond (that is, before the long operation starts, in this way, the cursor remains in the hourglass shape during the time when the UI is unresponsive (that is, during long operation execution. In this case, the display time of the hourglass cursor depends largely on the time required to complete the long operation. This is something we should realize.
Scenario 3: To precisely control the hourglass cursor, msdn provides the following sample code:
Step 1: add the member variable bool m_bshowwaitcurosr to cmydialog and initialize it in the cmydialog constructor;
M_bshowwaitcurosr = false;
Step 2: add the showwaitcursor () function to control the opening and closing of the hourglass cursor;
//////////////////////////////////////// //////////////
//
// Function name: cmydialog: showwaitcursor
//
// Access: Public
//
// Description: controls the opening and closing of the hourglass cursor.
//
// Parameters:
// Bshow
// True: Open the hourglass cursor
// False: Close the hourglass cursor
//
//////////////////////////////////////// //////////////
Void cmydialog: showwaitcursor (bool bshow)
{
If (bshow)
{
M_bshowwaitcurosr = true;
Beginwaitcursor ();
}
Else
{
Endwaitcursor ();
M_bshowwaitcurosr = false;
}
}
Step 3: process the wm_setcursor message.
On_wm_setcursor ()
Bool cmydialog: onsetcursor (cwnd * pwnd, uint nhittest, uint message)
{
If (m_bshowwaitcurosr)
{
Restorewaitcursor ();
Return true;
}
Return cdialog: onsetcursor (pwnd, nhittest, message );
}
This article from the csdn blog, reproduced please indicate the source: http://blog.csdn.net/duxiuxing/archive/2007/05/14/1607752.aspx
Through the previous article, we already know that the methods of using MFC to operate the hourglass cursor are ultimately implemented by calling cwinapp: dowaitcursor. The following is the implementation code of the cwinapp: dowaitcursor:
Void cwinapp: dowaitcursor (INT ncode)
{
// 0 => restore, 1 => begin,-1 => end
Assert (ncode = 0 | ncode = 1 | ncode =-1 );
Assert (afxdata. hcurwait! = NULL );
Afxlockglobals (crit_waitcursor );
M_nwaitcursorcount + = ncode;
If (m_nwaitcursorcount> 0)
{
Hcursor hcurprev =: setcursor (afxdata. hcurwait );
If (ncode> 0 & m_nwaitcursorcount = 1)
M_hcurwaitcursorrestore = hcurprev;
}
Else
{
// Turn everything off
M_nwaitcursorcount = 0; // prevent Underflow
: Setcursor (m_hcurwaitcursorrestore );
}
Afxunlockglobals (crit_waitcursor );
}
Based on the above code, we can at least analyze the following knowledge points:
(1) afxdata is a global structure. cwinapp stores the handle of the hourglass cursor in its member variable afxdata. in hcurwait, cwinapp: dowaitcursor () is a virtual function. If we need to implement a custom hourglass cursor, directly modify afxdata. hcurwait values or the heavy-load cwinapp: dowaitcursor () should be the first practice to consider;
(2) The cwinapp enables, disables, and refreshes the hourglass cursor through its member variable cwinapp: m_nwaitcursorcount, the cwinapp manages the reference count of the hourglass cursor. the type of the reference count operation is determined by the ncode value of the function parameter;
(3) The Code Implementation of cwinapp: dowaitcursor () contains the call to the API function setcursor (), which is similar to modifying the shape of the mouse cursor (2) the content involved is consistent;
* If you want to view the implementation code of an MFC function (I know), you can use the following three methods to make up for your lack of expression ability and understanding ability. I will give an example to explain it a little, for example, you need to check the implementation code of cwinapp: dowaitcursor:
Method 1: locate through the search function of VC;
(1.1) Call out the search window through the main menu edit-> Find in files;
(1.2) Fill in the find what Search Keyword: cwinapp: dowaitcursor;
(1.3) Fill in folder to find the storage path of the MFC code: C:/program files/Microsoft Visual Studio/vc98/mfc;
(1.4) Fill in other search options based on the actual situation;
(1.5) Click "find" to start searching
Method 2: Install visual assist and use its code jump function to locate the problem;
Based on my many years of experience, visual assist can indeed improve the speed of code writing. As a VC programmer, if you haven't used it yet, it is recommended to install and use it. On www.vckbase.com, there is a topic called the programmer toolbox. The "Auxiliary development tools" category contains the latest version for download.
Method 3: Set the breakpoint where the function is called in the Code and locate it through debug.
Use the previous code example to add the following code to the cmydialog: oninitdialog () function:
Bool cmydialog: oninitdialog ()
{
Cdialog: oninitdialog ();
// Todo: add extra initialization here
Afxgetapp ()-> dowaitcursor (0 );
Return true; // return true unless you set the focus to a control
}
Press F9 to set a breakpoint in the row where dowaitcursor () is located. Run the program to the breakpoint in debug mode, and press F11, Shift + F11, and F11 in sequence. cwinapp: dowaitcursor () the implementation code is displayed in front of you.
This article from the csdn blog, reproduced please indicate the source: http://blog.csdn.net/duxiuxing/archive/2007/05/18/1614730.aspx