VC ++ (5): MFC dialog box (1)

Source: Internet
Author: User
Tags button attributes

The dialog box can be divided into two categories: Modal Dialog Box and non-modal dialog box. The modal dialog box means that when it is displayed, the program will pause the execution until the dialog box is closed before other tasks in the program can continue to be executed. The non-modal dialog box means that when the dialog box is displayed, other tasks in the program can be executed instead of the dialog box.
A dialog box is also a resource. You can create a new dialog box in the resource view. The new dialog box contains two buttons: OK and cancel.
In MFC, operations on resources are performed by a resource-related class. The cdialog class is related to the dialog box. When we use the Class Wizard, the compiler automatically detects that we have added a resource, prompting us whether we need to create a class for it, and then the compiler will automatically, and select the base class. You only need to enter the class name. We will name it ctestdlg. The compiler will automatically help us fill in the corresponding source file and header file name. If our class name is long, the file name will be long and can be changed.
At this point, the compiler has written two functions for this class: 1. constructor 2. dodataexchange to complete data exchange and validation.
Next we want to display this dialog box in the program. The simplest practice is to add a menu item and click the menu display dialog box.

We add a menu item in the menu, and then add a message response function for it in the View class: Create a ctestdlg object and call the domodal function to implement the modal dialog box:

void CCH_7_DialogView::OnDialog() {// TODO: Add your command handler code hereCTestDlg dlg;dlg.DoModal();}

Note that we have to include # include "testdlg. H", because this Code Class Wizard does not help us add.
The non-modal dialog box is created using CREATE. Note that the showwindow function must be called to display the dialog box after creation.

CTestDlg dlg;dlg.Create(IDD_DIALOG1,this);dlg.ShowWindow(SW_SHOW

However, the program is still not displayed after compilation. Why? Because the DLG here is a local variable, after the program is executed, it will be analyzed. Why is the previous Modal Dialog Box okay? After the modal dialog box is created, the program stops here, so its lifecycle is not over. How can this problem be solved?
1. Define the variables in this dialog box as local variables.
2. Define a pointer to the data on the stack:

CTestDlg *pDlg = new CTestDlg;pDlg->Create(IDD_DIALOG1,this);pDlg->ShowWindow(SW_SHOW);

Although this can achieve the function, it is not good because pdlg will be released at the end of the program, so we will not be able to get this pointer in the future, and naturally we will not be able to release it, memory leakage may occur. This problem can be solved by defining the pointer as a member variable of the class.
There is another difference: for the modal dialog box, click "OK" and the dialog box is "destroyed"; for the non-modal dialog box, it is "hidden ", you need to override the virtual function onok and call destroywindow. The onok of the base class only calls enddialog to hide the dialog box.

The following function is implemented: When you click the button in the dialog box, a new button is dynamically created in the dialog box.
The modal dialog box is used for convenience. Go back to the resource view, open the original dialog box, and place a button from the toolbox to the dialog box. change its ID to idc_btn_add and its name is add. Right-click it, select the Class Wizard, and add the onbtnadd message response function to it:

void CTestDlg::OnBtnAdd() {// TODO: Add your control notification handler code herem_btn.Create("TEST",BS_DEFPUSHBUTTON | WS_VISIBLE|WS_CHILD,CRect(0,0,100,100),this,123);}

We found that when we click the Add button while the program is running, a new button is displayed, but when we click it again, the program will be wrong. This is because, when we click, onbtnadd will be called. This function calls the create function to create a button. However, if you click it again, the create function will be called to associate the button with m_btn, however, m_btn is already associated with the button created for the first time, so an error occurs. To this end, we can add a member variable m_biscreate to record whether the window has been created. If it is created, the window will be destroyed and can be created at the next click:

void CTestDlg::OnBtnAdd() {// TODO: Add your control notification handler code hereif(m_bIsCreate == FALSE){m_btn.Create("TEST",BS_DEFPUSHBUTTON | WS_VISIBLE|WS_CHILD,CRect(0,0,100,100),this,123);m_bIsCreate = TRUE;}else{m_btn.DestroyWindow();m_bIsCreate = FALSE;}}

In fact, there is a simple way to solve this problem without using member variables, but static local variables.
In fact, there is a more direct way to solve this problem: cwnd has a member variable m_hwnd pointing to the window handle. We can determine whether the handle is empty:

if(!m_btn.m_hWnd){m_btn.Create("TEST",BS_DEFPUSHBUTTON | WS_VISIBLE|WS_CHILD,CRect(0,0,100,100),this,123);}else{m_btn.DestroyWindow();}

Let's take a look at the access to the control:
We added three static text boxes to the dialog box without renaming them as number1, number2, and Number3, and then added three edit boxes. You can use the functions provided by the layout menu to compare them.
First, we implement a simple function: After clicking number1, we will change its display to "Number 1 ":
The big idea must be to respond to the bn_clicked message to this control. However, we found that the ID of this control is idc_static, but this ID is not found in the Class Wizard, because all the static text boxes are this ID. In fact, this control is generally used as a tag, not to respond to messages. However, if we want it to respond to a message, it is okay, but we need to change its label to idc_number1, and then add a message response function for it:
First, the button is a window. First we need to get the static text box and then get the text above it. Then reset the text:

Void ctestdlg: onnumber1 () {// todo: add your control notification handler code herecstring STR; If (getdlgitem (idc_number1)-> getwindowtext (STR ), STR = "number1") {getdlgitem (idc_number1)-> setwindowtext ("value 1");} else {getdlgitem (idc_number1)-> setwindowtext ("number1 ");}}

However, after the program runs, it does not respond because the static text control does not receive notification messages by default: We have to select the notification in the property-> style of the control, that's all. It can be seen that two special steps are required for a static text box to respond to a message: 1. Change its ID. 2. In its style, select the notification message.

The following provides a slightly more complex function: enter a number in the two edit boxes, click the Add button, and display the result in the third edit box.
There are many implementation methods, one of which is described as follows:
1st types:

void CTestDlg::OnBtnAdd() {// TODO: Add your control notification handler code hereint num1,num2,num3;char ch1[10],ch2[10],ch3[10];GetDlgItem(IDC_EDIT1)->GetWindowText(ch1,10);GetDlgItem(IDC_EDIT2)->GetWindowText(ch2,10);num1 = atoi(ch1);num2 = atoi(ch2);num3 = num1 + num2;itoa(num3,ch3,10);GetDlgItem(IDC_EDIT3)->SetWindowText(ch3);}

The idea of this method is clear: the three strings and three intors are defined. After obtaining the string in the edit box, convert it to int, add it, convert it back, and display it in the edit box.

Second:

int num1,num2,num3;char ch1[10],ch2[10],ch3[10];GetDlgItemText(IDC_EDIT1,ch1,10);GetDlgItemText(IDC_EDIT2,ch2,10);num1 = atoi(ch1);num2 = atoi(ch2);num3 = num1 + num2;itoa(num3,ch3,10);SetDlgItemText(IDC_EDIT3,ch3);

In fact, it is similar to the first one, but it takes two steps to complete the first one through getdlgitemtext and setdlgitemtext.

Third:
Using the getdlgitemint function, this function obtains the space text directly through the control ID, converts it to the int type, and then uses the setdlgitemint function:

int num1,num2,num3;num1 = GetDlgItemInt(IDC_EDIT1);num2 = GetDlgItemInt(IDC_EDIT2);num3 = num1 + num2;SetDlgItemInt(IDC_EDIT3,num3);

Fourth:
Associate the three member variables in the edit box dialog box class, and then retrieve and set the text in the edit box using the member variables.
First, associate these three edit boxes with three member variables: In the create wizard class under the Class View, select the ctestdlg class, and select
Idc_edit1, idc_edit2, and idc_edit3 are added with int variables m_num1, m_num2, and m_num3. We found that classwizzard added three codes for us:
In the header file, comment between macros:

// Dialog Data//{{AFX_DATA(CTestDlg)enum { IDD = IDD_DIALOG1 };intm_num1;intm_num2;intm_num3;//}}AFX_DATA

In the source file constructor:

CTestDlg::CTestDlg(CWnd* pParent /*=NULL*/): CDialog(CTestDlg::IDD, pParent){//{{AFX_DATA_INIT(CTestDlg)m_num1 = 0;m_num2 = 0;m_num3 = 0;//}}AFX_DATA_INIT}

In the source file dodataexchange:

void CTestDlg::DoDataExchange(CDataExchange* pDX){CDialog::DoDataExchange(pDX);//{{AFX_DATA_MAP(CTestDlg)DDX_Text(pDX, IDC_EDIT1, m_num1);DDX_Text(pDX, IDC_EDIT2, m_num2);DDX_Text(pDX, IDC_EDIT3, m_num3);//}}AFX_DATA_MAP}

The ddx_text function converts data.
We found that when the program is running, the value in the edit box is automatically set to 0. This ing is implemented through the dodataexchange function. If this is convenient, we can add a code in the onbtnadd function:

void CTestDlg::OnBtnAdd() {m_num3 = m_num1 + m_num2;}

However, after compilation, the correct results cannot be displayed during the runtime. Why? After one-step debugging, we can find that the values of m_num1 and m_num2 are always 0 no matter how much we input! It seems necessary to take a closer look at the dodataexchange function. Through msdn, we can see that this function is not called directly, but called through the updatedata function. The updatedata function has a parameter: when it is set to true (default), it obtains data from the dialog box. When it is set to false, it writes data to the dialog box. So we just need to change it to the following code:

UpdateData();m_num3 = m_num1 + m_num2;UpdateData(FALSE);

If we enter a character into the frame, When you click Add, it will prompt you to enter an integer. Even, you can set the Integer Range in the Class Wizard. An error is returned if the request is out of the valid range.

Method 5:
Associate the edit box with the control variable. Here we select the cedit type. With these member variables, we do not need the getdlgitem (idc_edit1) Step in the first method. Just use these variables to call getwindowtext or setwindowtext.

int num1,num2,num3;char ch1[10],ch2[10],ch3[10];m_edit1.GetWindowText(ch1,10);m_edit2.GetWindowText(ch2,10);num1 = atoi(ch1);num2 = atoi(ch2);num3 = num1 + num2;itoa(num3,ch3,10);m_edit3.SetWindowText(ch3);

Method 6:
Message. By sending a specified message to the edit box (getting the text wm_gettext and setting the text wm_settext), you can:

int num1,num2,num3;char ch1[10],ch2[10],ch3[10];::SendMessage(m_edit1.m_hWnd,WM_GETTEXT,10,(LPARAM)ch1);::SendMessage(m_edit2.m_hWnd,WM_GETTEXT,10,(LPARAM)ch2);num1 = atoi(ch1);num2 = atoi(ch2);num3 = num1 + num2;itoa(num3,ch3,10);m_edit3.SendMessage(WM_SETTEXT,0,(LPARAM)ch3);

Note:
1. sendmessage is a function in the SDK. Therefore, you must add ::.
2. When sending the wm_gettext message, wparam indicates the maximum number of bytes. lparam indicates the string address, but the type conversion is required.
3. When sending the wm_settext message, wparam is not used. Enter 0, and lparam is the string address, but the type conversion is required.
Method 7:
Send a message directly to the Child control of the frame:

int num1,num2,num3;char ch1[10],ch2[10],ch3[10];SendDlgItemMessage(IDC_EDIT1,WM_GETTEXT,10,(LPARAM)ch1);SendDlgItemMessage(IDC_EDIT2,WM_GETTEXT,10,(LPARAM)ch2);num1 = atoi(ch1);num2 = atoi(ch2);num3 = num1 + num2;itoa(num3,ch3,10);SendDlgItemMessage(IDC_EDIT3,WM_SETTEXT,0,(LPARAM)ch3);

By using the member function, you do not need to obtain the edit box.

To obtain part of the text in the editing box, you can send an em_setsel message, where wparam indicates the start position and lparam indicates the end position. However, if you want to obtain a part of the text, your focus must be on this text box:

SendDlgItemMessage(IDC_EDIT3,EM_SETSEL,1,2);m_edit3.SetFocus();

To sum up, seven Chinese methods are commonly used: 1, 4, and 5.

Next we will look at another example: Implementation of the dialog box scaling function.
This function is also very common. For example, you can edit the color in the color menu of the drawing in WindowsXP. When you click a custom color, this dialog box is expanded. For features that are not needed by users, you can place them in a hidden area, which allows users to consider the main issues.
We should consider how to design this question: first, there must be a button (named "contract"). After clicking it, the part of the dialog box will be shrunk, and the button name will be changed to "Expand ". Let's solve the problem of name conversion after clicking:

Void ctestdlg: onbutton1 () {// todo: add your control notification handler code herecstring STR; If (getdlgitemtext (idc_button1, STR), STR = "contract ") {setdlgitemtext (idc_button1, "");} else {setdlgitemtext (idc_button1, "");}}

The next step is the main scene. The secret of this dialog box division is that an invisible and fine image control (usually a rectangle) is added to the original dialog box ), use this rectangle to determine the position of the dialog box after clicking to contract or expand.
We will first change the image control to idc_seperator, and then change its style to fall.

Static crect rectlarge; static crect rectsmall; If (rectlarge. isrectnull () {crect rectseparator; getwindowrect (& rectlarge); getdlgitem (idc_separator)-> getwindowrect (& rectseparator); rectsmall. left = rectlarge. left; rectsmall. top = rectlarge. top; rectsmall. right = rectlarge. right; rectsmall. bottom = rectseparator. bottom;} If (STR = "contract") {setwindowpos (null, rectsmall. width (), rectsmall. height (), swp_nomove | swp_nozorder);} else {setwindowpos (null, 0, 0, rectlarge. width (), rectlarge. height (), swp_nomove | swp_nozorder );}

In the processing function, we define three rectangles. rectlarge is the original size of the dialog box, rectsmall is the size of the dialog box after clicking scale down, And rectseparator is the size of the added line. The left, top, and right values of rectsmall are equal to rectlarge. You only need to change its button to the rectseparator button.
After obtaining the window size, we only need to use setwindowpos to reset the window position.

Our program has a slight flaw. after entering a number, press enter to return the result program and the window is closed. We hope to press enter to go to the second edit box and continue the input. What should I do?
First, solve the first problem. Why should I press enter to close it. Let's take a look at the properties of the confirm button. In the style, it is selected by default. Therefore, if we press enter, it will be processed by the message response function of the OK key. Although we did not write it, The onok function in the base class is written to close the dialog box.
Therefore, if you want to implement your own functions, you must re-write the OK message Response Function in the derived class. When we add a response function for it, the Class Wizard will add the function by default and call the components of the base class:

void CTestDlg::OnOK() {// TODO: Add extra validation hereCDialog::OnOK();}

If we comment it out, clicking OK will not close the window.
So how can I transfer the input focus to the second edit box after the carriage return function?
There are two major ideas: 1. let the edit box control generate an associated class and use this class to send keyboard messages. 2. modify the window procedure function of the edit box control. In this function, if you press enter, move it to the next edit box.
We adopt the second approach. First, where should I place the code for changing the message processing function of the window? It cannot be placed under create, because the dialog box has not been created yet. In fact, when the dialog box and its sub-control are created, a message wm_initdialog is sent before the display. we add the response function for the message for ctestdlg.

Wndproc prevproc; // declare the previous message response function // The New message processing function lresult callback neweditproc (hwnd, // handle to window uint umsg, // message identifier wparam, // first message parameter lparam // second message parameter) {If (umsg = wm_char & wparam = 0x0d) {: setfocus (getnextwindow (hwnd, gw_hwndnext )); return 1 ;}else {return prevproc (hwnd, umsg, wparam, lparam );}}

In fact, this function is not complex. if the message is a carriage return, set the focus to the next window. If not, call the original message processing function for processing. Where did the original processing function come from?

BOOL CTestDlg::OnInitDialog() {CDialog::OnInitDialog();// TODO: Add extra initialization hereprevProc = (WNDPROC)SetWindowLong(GetDlgItem(IDC_EDIT1)->m_hWnd,GWL_WNDPROC,(LONG)NewEditProc);return TRUE;  // return TRUE unless you set the focus to a control              // EXCEPTION: OCX Property Pages should return FALSE}

The Return Value of the setwindowlong function is!
The only thing you need to note is to select multiple lines in the edit box. Without this option, the edit box can only accept one message, so it cannot accept the Enter key.
We also noticed that we only wrote code for the first edit box, so pressing enter in the second edit box does not work. The following introduces another method to get the window handle: getwindow, which returns the window related to the specified window, so you can also write it like this:

::SetFocus(::GetWindow(hwnd,GW_HWNDNEXT));

Finally, we will introduce a method to obtain the window handle: getnextdlgtabitem. This function returns a control with the ws_tabstop style after the front and back of the specified control. So what is the ws_tabstop style? For button attributes, the tabulation site is available. If selected, the style is available. If not selected, no.

::SetFocus(::GetNextDlgTabItem(::GetParent(hwnd),hwnd,FALSE));

Having said so much, these methods are very troublesome: Do you have to write a code push for every control? Is there a simple way?
We can simply change the default function for responding to the Enter key:

void CTestDlg::OnOK() {// TODO: Add extra validation hereGetFocus()->GetNextWindow()->SetFocus();//CDialog::OnOK();}

Obtain the current focus. You can set the focus in the next window to get the focus.
But there is a problem with this program: When you press enter several times, it will crash, because when the last window calls getnextwindow (), it will get a null pointer, if you perform setfocus ();, an error occurs. To solve this problem, you can use getnextdlgtabitem instead. This function is used to search controls with the tabstop attribute in sequence. In the taborder of the layout menu, we can see their order.

void CTestDlg::OnOK() {// TODO: Add extra validation hereGetNextDlgTabItem(GetFocus())->GetNextWindow()->SetFocus();//CDialog::OnOK();}

Finally, let's look at the default button: For this dialog box, the default button is determined, but other buttons can be responded by modifying the attribute.

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.