Enter the key in the Custom dialog box in the VC program

Source: Internet
Author: User
In a dialog box-based program, the program exits every time you press the Enter key. The effect is the same as that of pressing the default "OK" button in the dialog box, it is useless even if the "OK" button's bs_defpushbutton attribute is removed. How can we customize the carriage return key? This problem has been around for a long time in Windows development. It is an annoying problem for beginners. Fortunately, many solutions have been found to solve this problem. This example will show you how to customize the Enter key behavior.

  I. Implementation Method

If you want to invalidate the Enter key, the simplest method is to overload the onok () function. This is not a bad idea, but if you reload the onok () function, let it do nothing, so what if you want to do something by pressing the "OK" button with the mouse? You can change the ID of the Enter key, for example, id_my_ OK, and write a processor that calls enddialog (). This method can also work, but it seems a little unprofessional.

Another method is the "default" attribute of the "Disable" Enter key. This is also the method proposed at the beginning of this article. The reason why it fails is that it is not enough to set the bs_defpushbutton attribute of the "OK" button, you can use the spy ++ tool in Visual C ++ to observe it carefully, so that you can find that the Enter key still sends an Exit message. What is the problem? You must differentiate the OK button and the Enter key. You can write an onok processor to call the getcurrentmessage () function to obtain the final message sent, which should be wm_command, check the low-order word of wparam to see where the command comes from.

To solve the problem, you must understand what is happening behind the scenes. In spy ++, you can see that when you press the Enter key, windows sends a special wm_getdefid message to obtain the default command ID. Windows then sends it as wm_command. Therefore, we need to reload the wm_getdefid message. In the relevant Windows documents, wm_getdefid returned value is described as follows: "If there is a default button, the High-level words returned include dc_hasdefid, and the low-level words include the controlled identifier. Otherwise, the returned value is zero ". Based on the description in this section, if no default button exists, the return value should be zero. If you want to disable the default ID, you must return dc_hasdefid in the high-level text. To do this, define and implement the message ing function as follows:

Begin_message_map (cmydlg, cdialog)
On_message (dm_getdefid, ongetdefid)
...
End_message_map ()
Lresult cmydlg: ongetdefid (wparam WP, lparam LP)
{
Return makelong (0, dc_hasdefid );
}

Because MFC does not have a macro corresponding to dm_getdefid, you must use a general on_massage macro. In this way, you can press the Enter key at will, but nothing will happen.

The above method solves the problem of exiting by pressing the Enter key program, but another problem arises: What if you want to do something about the return key? Some people have asked you how to map the Enter key to the tab key. You can press the Enter key just as you press the tab key. That is to say, the input focus is moved to the control of the next dialog box. This requires some work, but the simplest way is to use the acceleration key. Many programmers try to use onchar () to respond to a function, but it is a low-level interesting thing. Try to avoid using it. Even worse, messages such as wm_keydown and wm_keyup should be avoided. Who can process these messages? Onchar () can be used to restrict characters that can be entered in the edit box, such as numbers and letters. To map a key to a command, the acceleration key is the best method.

In this example, an acceleration key is created for vk_return, Which is mapped to the command id_my_enter, and a command processor is written to implement anything you want.

If you are careful, you will find another problem that has not been solved, that is, the acceleration key is not automatically processed in the MFC dialog box, and you must write your own code to do this. To understand why, let's look back at the windows development process. In the age of C and the original Windows API, every Windows program had a central loop called message pump:

While (getmessage (...)){
Translatemessage (...);
Dispatchmessage (...);
}

Here, the details are not the most important. The most important thing is that the message does not reach the program process. You must request the message. This is a man-made, non-preemptive multi-task method. This method uses the collaboration of each task to simulate a multi-task environment. As more and more functions are added, someone has come up with the idea of accelerating the key table, this table is used to map keys and command IDs. To achieve this, Microsoft invented a function called translateaccelerator. Now the message pump looks like this:

While (getmessage (...)){
If (translateaccelerator (haccel ...)){
// Handled, continue looping
} Else {
Translatemessage (...);
Dispatchmessage (...);
}
}

Haccel is a key table handle. The details here are also not important. What is important is how to use the key table, that is, a special function is required to interpret the key message as a wm_command message. The translateaccelerator () function searches for characters in the wm_keydown, wm_char, and wm_keyup sequences that match the table's key values. If it is found, it inserts a wm_command into the message queue. The command ID in the Message Queue can be any entry defined in the acceleration key table. In this way, you only need to set the acceleration key table (in the resource) and remember to call the corresponding function translateaccelerator (), so you don't have to worry about anything.

With the increasing maturity of Visual C ++ and MFC, almost all of the message loops (but not all) are hidden in MFC, to give any window a chance to get a message pump, MFC provides a dedicated virtual function pretranslatemessage (), if you have enough courage to explore the Message Processing Mechanism in cwinthread, you will encounter code similar to the following:

// Simplified cwinthread
While (getmessage (...)){
If (pretranslatemessage (...)){
// Continue looping
} Else {
Translatemessage (...);
Dispatchmessage (...);
}
}

Cwinthread: pretranslatemessage () is a virtual function. In an application, its default implementation calls another virtual function cwnd: pretranslatemessage () with the same name (). Therefore, if you need to do something in a message loop, such as interpreting the acceleration key, you just need to reload the pretranslatemessage () function. In fact, this is the way the program framework cframewnd class processes the acceleration key.

Bool cframewnd: pretranslatemessage (MSG * PMSG)
{
......
If (PMSG-> message >=wm_keyfirst & PMSG-> message <= wm_keylast)
{
: Translateaccelerator (m_hacceltable ,...);
}
}

Which of the following is the cframewnd class? Emei uranium Wu kangben glaze? framewnd: loadframe () function uses the same ID (such as idr_mainframe) as the document template) search for the acceleration key table and load it to the m_hacceltable variable. All the processing details are automated and concealed in MFC, so readers don't have to worry about it. However, the above content is only for the main framework. If it is a dialog box, it is another situation, because cdialog is not derived from cframewnd, so it does not inherit any content related to the acceleration key. Don't worry about this problem. We can imitate the work of cframewnd and easily add the acceleration key function for the dialog box. The first step is to load the acceleration key. The best place to load the acceleration key is in the oninitdialog function of the dialog box:

Bool cmydlg: oninitdialog ()
{
Cdialog: oninitdialog ();
......
// Load Accelerators
M_haccel =: loadaccelerators (afxgetresourcehandle (), m_lpsztemplatename );
Assert (m_haccel );
Return true;
}

Any ID can be used in the acceleration key table. For example, the code above uses the ID of the dialog box itself (m_lpsztemplatename can be either a string name or an integer ID used by makeintresource ).

// The acceleration key (in dlgkeys. Rc) in this example)
Idd_mydialog accelerators discardable
Begin
Vk_return, id_my_enter, primary key, noinvert
End

Once the acceleration key has been loaded, the remaining thing is to reload the pretranslatemessage function to map messages:

Bool cmydlg: pretranslatemessage (MSG * PMSG)
{
If (wm_keyfirst <= PMSG-> message & PMSG-> message <= wm_keylast)
{
Haccel = m_haccel;
If (haccel &: translateaccelerator (m_hwnd, haccel, PMSG ))
Return true;
}
Return cdialog: pretranslatemessage (PMSG );
}

The reason why you want to check the key-type messages (from WM _ keyfirst to wm_keylast) is to increase the speed. If you know that it is not a key message, you don't have to waste time calling translateaccelerator (). Besides, translateaccelerator () is a virtual function without adding a message ing entry. Just write this function.

  Ii. programming steps

1. Start visual c ++ 6.0, generate a Win32 application, and name the program "dlgkeys ";

2. Use classwizard to add cdlgwithaccelerators and cmydlg classes to the application;

3. Add the acceleration key resource to the program resources. The content is as follows: id_my_enter, vk_returnvirtkey, and foreign key;

4. Add code and compile and run the program.

[Nextpage]

3. program code

/////////////////////////////////////////
# Include "stdafx. H"
// Generic dialog-that-uses-accelerators.
Class cdlgwithaccelerators: Public cdialog {
Public:
Cdlgwithaccelerators (uint nidtemplate, cwnd * pparentwnd = NULL );
Cdlgwithaccelerators (lpctstr lpsztemplatename, cwnd * pparentwnd = NULL );
~ Cdlgwithaccelerators ();
Protected:
Haccel m_haccel; // accelerator table
// MFC overrides
Virtual bool oninitdialog ();
Virtual bool pretranslatemessage (MSG * PMSG );
Declare_message_map ()
};

//////////////////////////////////////// ///
// Cdlgwithaccelerators is a general-purpose class that adds accelerators to cdialog.
# Include "stdafx. H"
# Include "dlgaccel. H"
# Ifdef _ debug
# Define new debug_new
# UNDEF this_file
Static char this_file [] = _ file __;
# Endif
Begin_message_map (cdlgwithaccelerators, cdialog)
End_message_map ()
Cdlgwithaccelerators: cdlgwithaccelerators (lpctstr lpsztemplatename,
Cwnd * pparentwnd): cdialog (lpsztemplatename, pparentwnd)
{}

Cdlgwithaccelerators: cdlgwithaccelerators (uint nidtemplate,
Cwnd * pparentwnd): cdialog (nidtemplate, pparentwnd)
{}

Cdlgwithaccelerators ::~ Cdlgwithaccelerators ()
{}

///////////////////// Pre-translate message: translate keystrokes using acclerator table.
Bool cdlgwithaccelerators: pretranslatemessage (MSG * PMSG)
{
If (wm_keyfirst <= PMSG-> message & PMSG-> message <= wm_keylast ){
Haccel = m_haccel;
If (haccel &: translateaccelerator (m_hwnd, haccel, PMSG ))
Return true;
}
Return cdialog: pretranslatemessage (PMSG );
}

//// // Initialize dialog: Load Accelerators
Bool cdlgwithaccelerators: oninitdialog ()
{
Bool Bret = cdialog: oninitdialog ();
// Load dialog's Accelerators
M_haccel =: loadaccelerators (afxgetresourcehandle (),
M_lpsztemplatename); // Use same resource name as Dialog
Return Bret;
}

//////////////////////////////////////// /////////////////////////////////
# Include "stdafx. H"
# Include "resource. H"
# Include "dlgaccel. H"
# Include "tracewin. H"
# Ifdef _ debug
# Define new debug_new
# UNDEF this_file
Static char this_file [] = _ file __;
# Endif

/// // MFC App
Class cmyapp: Public cwinapp {
Public:
Cmyapp ();
~ Cmyapp ();
Virtual bool initinstance ();
Declare_message_map ()
};

Cmyapp theapp; // The one-and-only app

//// // Frame window
Class cmainframe: Public cframewnd {
Protected:
Virtual bool precreatewindow (createstruct & CS );
Public:
Cmainframe ();
~ Cmainframe ();
};

/// // Typical Dialog
Class cmydlg: Public cdlgwithaccelerators {
Public:
Cmydlg (cwnd * pparent = NULL); // standard Constructor
Protected:
Hicon m_hicon;
Void nextintaborder ();
// MFC overrides
Virtual bool oninitdialog ();
Afx_msg void onmyenter ();
Afx_msg lresult ongetdefid (wparam WP, lparam LP );
Declare_message_map ()
};

Begin_message_map (cmyapp, cwinapp)
End_message_map ()

Cmyapp: cmyapp ()
{
// Nothing to do
}

Cmyapp ::~ Cmyapp ()
{
// Nothing to do
}

/// // Initinstance: Create dialog as child
Bool cmyapp: initinstance ()
{
// Create frame window and load it
Cmainframe * pframe = new cmainframe;
M_pmainwnd = pframe;
Pframe-> loadframe (idr_mainframe, ws_overlapped, null, null );
Cmydlg DLG (pframe); // create dialog and run it
Int nresponse = DLG. domodal ();
If (nresponse = idok)
{}
Else if (nresponse = idcancel)
{}
Return false; // quit
}

Cmainframe: cmainframe ()
{
// Nothing to do
}

Cmainframe ::~ Cmainframe ()
{
// Nothing to do
}

//// // Pre-create window: Set ws_ex_toolwindow style to hide dialog from Task Bar
Bool cmainframe: precreatewindow (createstruct & CS)
{
If (cframewnd: precreatewindow (CS )){
CS. dwexstyle | = ws_ex_toolwindow;
Return true;
}
Return false;
}

Begin_message_map (cmydlg, cdlgwithaccelerators)
On_command (id_my_enter, onmyenter)

// The following is not needed since I am using accelerators to map
// Enter to id_my_enter. But if all you want to do is ignore the Enter key,
// You can handle dm_getdefid as below.
// On_message (dm_getdefid, ongetdefid) // not used
End_message_map ()

Cmydlg: cmydlg (cwnd * pparent): cdlgwithaccelerators (idd_mydialog, pparent)
{}

//// // Initialize dialog:
Bool cmydlg: oninitdialog ()
{
Cdlgwithaccelerators: oninitdialog ();
// Set the icon for this dialog. The framework does this automatically
// When the application's main window is not a dialog
M_hicon = afxgetapp ()-> loadicon (idr_mainframe );
Assert (m_hicon );
Seticon (m_hicon, true); // set big icon
Seticon (m_hicon, false); // set small icon
// Use same resource name as dialog to load dialog's Accelerators
M_haccel =: loadaccelerators (afxgetresourcehandle (), m_lpsztemplatename );
Assert (m_haccel );
Return true; // return true unless you set the focus to a control
}

/////// // This is called to handle id_my_enter -- IE, enter key.
Void cmydlg: onmyenter ()
{
Trace (_ T ("cmydlg: onmyenter/N "));
Nextintaborder (); // move to next Control
}

//// // Helper function to move focus to the next control.
Void cmydlg: nextintaborder ()
{
Cwnd * pwndnext = getnextdlgtabitem (getfocus ());
If (pwndnext ){
Pwndnext-> setfocus ();
}
}

//////////////////
// This function is not used, since its message map entry is commented out.
// If all you want to do is ignore the Enter key (not map it to a command ),
// Then all you have to do is return zero here. Note that you must return
// The special code dc_hasdefid in the high-order word !!
Lresult cmydlg: ongetdefid (wparam WP, lparam LP)
{
Trace (_ T ("cmydlg: ongetdefid/N "));
Return makelong (0, dc_hasdefid );
}

  Iv. Summary

To sum up, in MFC, the acceleration key function is added to the dialog box by loading the acceleration key and reloading the pretranslatemessage () function. That is to say, if developers decide to use the acceleration key in the dialog box, they do not need to worry about the processing of the ongetdefid () function, but should focus on implementing the wm_command message response processing function corresponding to the acceleration key.
 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.