Dynamic control creation and event response implementation in MFC

  • 2020-04-02 02:56:00
  • OfStack

The example of this article describes the dynamic creation of MFC control and event response implementation method, to share for your reference. The specific implementation method is as follows:

A dynamic control is a control that is created by Create() when needed, as opposed to a control that is placed in a dialog box beforehand.

1. Create dynamic controls:
For comparison, let's look at the creation of static controls.
Put the static controls must first establish a container, usually dialog box, then we in the dialog editor window, drag from the tool window required controls on a dialog box, then the appropriate change control ID, set control properties, a static control is created, when the dialog is displayed, its control will be displayed.
A static control does not need to be created by calling the Create() function.
While the creation of dynamic control is very different, the following button as an example, look at the creation of dynamic control process:

1. Create control ID number:

The ID number is the ID of the control. You must set an ID number for the control before you create it.
Open the resource of "String Table", double click on the blank lines, then an ID attribute dialog box will pop up, in which the ID of the input ID in the edit box, such as: IDC_MYBUTTON, input control in the Caption title or annotations (note: Caption box can't be empty, empty will lead to the failure to create), here I enter button to display the text -- dynamic button.

2. Create control objects:

Different types of controls should create different class objects:
The button control           CButton   (including normal buttons, radio buttons, and check buttons)
Edit control           CEdit
Static text control   CStatic
Label controls           CTabCtrl
The rotation control           CSpinButtonCtrl
Slide the control           CSliderCtrl
Multi - information editing control CRichEditCtrl
Progress bar control       CProgressCtrl
Scroll bar control       CSrcollBar
Combo box control       CComboBox
List box control       CListBox
Image list control   CImageCtrl
The tree control           CTreeCtrl
The animation controls           CAnimateCtrl

In this example we create a normal button of the CButton class. Note that you cannot directly define a CButton object, such as: CButton m_MyBut; This definition can only be used to define control variables for static controls, not for dynamic controls.
The right thing to do is to generate an instance with a new call to the CButton constructor:

CButton *p_MyBut = new CButton();

Then use the Create() function of the CButton class to Create the function prototype as follows:
BOOL Create( LPCTSTR lpszCaption, DWORD dwStyle, const RECT& rect, CWnd* pParentWnd, UINT nID );

LpszCaption is the text shown on the button;
DwStyle specifies the button style, which can be a combination of the button style and the window style. The values are:

Window style:
WS_CHILD   Child window, must have
WS_VISIBLE   The window is visible, usually there is
WS_DISABLED   Disable the window when creating a button that is initially gray and unavailable
WS_TABSTOP   You can select it using the Tab key
WS_GROUP   Group, the first button in a group of radio buttons

Button style:
BS_PUSHBUTTON, also known as the normal button
BS_AUTORADIOBUTTON contains a radio button with an auto-selected state
BS_RADIOBUTTON radio button, not commonly used
BS_AUTOCHECKBOX contains a check button that automatically selects the state
BS_CHECKBOX check button, not commonly used
BS_AUTO3STATE contains a three-state check button with an auto-selected state
BS_3STATE three-state check button, not commonly used
The above style specifies the type of button to be created, which cannot be used at the same time, but must be one or the other.

The BS_BITMAP button will display the bitmap

BS_DEFPUSHBUTTON is set as the default button and is only used for push-down buttons. Only one default button can be specified in a dialog box

Rect specifies the size and position of the button;
PParentWnd indicates that the parent window with the button cannot be NULL;
NID specifies the ID number associated with the button, the ID number created in the previous step.
The Create() function is slightly different for different control classes.
Ex. :

p_MyBut->Create( " Dynamic button ", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, CRect(20,10,80,40), this, IDC_MYBUTTON );

In this way, at (20,10) in the current dialog box, we create a push-down button that is 60 by 30 and has "dynamic button" text.
To make the creation process easier to use, I defined the following functions:
CButton* CTextEditorView::NewMyButton(int nID,CRect rect,int nStyle)
{
 CString m_Caption;
 m_Caption.LoadString( nID ); //Take the button title
 CButton *p_Button = new CButton();
 ASSERT_VALID(p_Button);
 p_Button->Create( m_Caption, WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | nStyle, rect, this, nID );  //Create button
 return p_Button;
}

Where m_Caption.LoadString(nID) reads the button text from the string table, so that when creating the button ID, the text should be set and the argument nStyle should be an extra style besides the required style.
Below, I call the function to create three buttons, and specify the first button as the default button with a preset ID:
CButton *p_MyBut[3];
p_MyBut[0] = NewMyButton( ID_MYBUT1, CRect(10,20,50,35), BS_DEFPUSHBUTTON );
p_MyBut[1] = NewMyButton( ID_MYBUT2, CRect(55,20,95,35), 0 );
p_MyBut[2] = NewMyButton( ID_MYBUT3, CRect(100,20,140,35), 0 );

Ii. Response of dynamic control:

Response functions for dynamic controls cannot be added with ClassWizard, they can only be added manually. Taking the button above as an example, we made the click response function of the button.

1. Add response function in MESSAGE_MAP:

The MESSAGE_MAP table defines the message response function in the form of: message name (ID, function name). When we add the function with ClassWizard, it will be automatically added in the interval enclosed by AFX_MSG_MAP, such as:

BEGIN_MESSAGE_MAP(CTextEditorView, CFormView)
 //{{AFX_MSG_MAP(CTextEditorView)
 ON_BN_CLICKED(IDC_ICONBUT0, OnIconbut0)
 //}}AFX_MSG_MAP
END_MESSAGE_MAP()

Do not add to the AFX_MSG_MAP interval when manually adding, in case the ClassWizard does not work properly, such as:
BEGIN_MESSAGE_MAP(CTextEditorView, CFormView)
 //{{AFX_MSG_MAP(CTextEditorView)
 ON_BN_CLICKED(IDC_ICONBUT0, OnIconbut0)
 //}}AFX_MSG_MAP
 ON_BN_CLICKED(ID_MYBUT1, OnMybut1)
 ON_BN_CLICKED(ID_MYBUT2, OnMybut2)
 ON_BN_CLICKED(ID_MYBUT3, OnMybut3)
END_MESSAGE_MAP()

Where ON_BN_CLICKED is a button click message.

2. Add the function definition in the header file:

When adding functions with ClassWizard, the function definition is added in the AFX_MSG interval of the header file, such as:
Protected:

 //{{AFX_MSG(CTextEditorView)
 afx_msg void OnIconbut0();
 //}}AFX_MSG
 DECLARE_MESSAGE_MAP()

We imitate this form by adding the function definition outside the interval of AFX_MSG:

protected:
 //{{AFX_MSG(CTextEditorView)
 afx_msg void OnIconbut0();
 //}}AFX_MSG
 afx_msg void OnMybut1();
 afx_msg void OnMybut2();
 afx_msg void OnMybut3();
 DECLARE_MESSAGE_MAP()

3. Write message response function:

The above is to associate the message with the function, and the specific work that should be done after clicking the button is completed in the function:

void CTextEditorView::OnMybut1()
{
 MessageBox( " Ha! Ha! You click the dynamic button. " );
}
void CTextEditorView::OnMybut2()
{
  ...
}
void CTextEditorView::OnMybut3()
{
  ...
}

In addition to the button's response function, you can also access the button with the pointer obtained above, such as:
Change the size and position of the button: p_MyBut[0]- > MoveWindow (...) ;
Modify the button text: p_MyBut[0]- > SetWindowText (...) ;
Show/hide button: p_MyBut[0]- > ShowWindow (...) ; And so on.

Iii. Recycling resources

Since the dynamic control object is generated by new, it is not automatically released by the program, so it needs to be released manually. You can remove a control when it is no longer in use:

if( p_MyBut[0] )
 delete p_MyBut[0];

That's how button controls are dynamically generated. Next, take a look at the dynamic generation of radio buttons.

Example: dynamic generation of radio button group

Radio buttons also belong to the CButton class, but because they are always used in groups, they are somewhat different from normal buttons in terms of making and using them.
Suppose you have a group of three radio buttons, and initially, the first radio button is selected.
Let's look at the static method first: put three radio buttons in the dialog box, set the properties as follows:
Radio1 properties: Visible, Group, Tab stop, Auto
Radio2 properties: Visible, Tab stop, Auto
Radio3 properties: Visible, Tab stop, Auto
This property sets up a group of three radio buttons that can only be selected one at a time, and if there are other groups of radio buttons in the dialog, they will not interfere with each other. But at this point the first button is not selected.
We then add variables to this set of radio buttons using the ClassWizard, where we simply add variables to the first radio button. Let's call the variable m_Radio and make it int. In the constructor, the ClassWizard sets the value of m_Radio to -1, and we change it to 0 so that when we run the program, we see that the first radio button is selected.
After that, you should also add the click response function for the three radio buttons using the ClassWizard, where you can modify the value of m_Radio for the three radio buttons.
Above is the usual way to make radio button group, now we want to change to dynamic generation, mainly to solve the problem of button group and click control. The following are the production steps:

1. Define the ids of the three radio buttons:

Open the "String Table" in the resource and add three ID values:
First: ID IDC_MYRADIO1, Caption 1
Second: ID is IDC_MYRADIO2, Caption is radio 2
Third: ID is IDC_MYRADIO3, Caption is radio 3
Caption is the text to be displayed on the button and can be set as required.

2. Generate three radio buttons with the Create() function of the CButton class:

For convenience, define a function to generate radio buttons:

CButton* CTextEditorView::NewMyRadio(int nID,CRect rect,int nStyle)
{
 CString m_Caption;
 m_Caption.LoadString( nID ); //Take the button title
 CButton *p_Radio = new CButton();
 ASSERT_VALID(p_Radio);
 p_Radio->Create( m_Caption, WS_CHILD | WS_VISIBLE | nStyle | WS_TABSTOP | BS_AUTORADIOBUTTON, rect, this, nID ); //Create button
 return p_Radio;
}

The function LoadString() is used to read the button text from the "String Table". The Create() function sets the necessary properties of the radio button, including Visible, Tab stop, and Auto properties.
Parameter nID is the radio button ID number, rect is the radio button size, and nStyle is other attributes except the necessary attributes. The return value is a pointer to the new button.
With this function in place, you simply call the function in turn when you create a radio button group, where the first radio button in the radio button group must specify the WS_GROUP attribute.
CButton *p_MyRadio[3];
p_MyRadio[0] = NewMyRadio( IDC_MYRADIO1, CRect(15,90,60,105), WS_GROUP );
p_MyRadio[1] = NewMyRadio( IDC_MYRADIO2, CRect(15,108,60,123), 0 );
p_MyRadio[2] = NewMyRadio( IDC_MYRADIO3, CRect(15,126,60,141), 0 );

3. Define the control variable of the radio button group and set the first radio button as the selected state:

You cannot add variables here with ClassWizard, and do not add control variables in DoDataExchange(), because dynamic controls do not exist to begin with, and adding control variables in DoDataExchange() will cause a run error. Here we just need to define an int variable as the control variable in the header file, such as:

int m_SelRadio;

Set its initial value to 0 in the constructor: m_SelRadio = 0;
In the create button statement above, use the SetCheck() function to set the initially selected button:
CButton *p_MyRadio[3];
p_MyRadio[0] = NewMyRadio( IDC_MYRADIO1, CRect(15,90,60,105), WS_GROUP );
p_MyRadio[1] = NewMyRadio( IDC_MYRADIO2, CRect(15,108,60,123), 0 );
p_MyRadio[2] = NewMyRadio( IDC_MYRADIO3, CRect(15,126,60,141), 0 );
p_MyRadio[m_SelRadio]->SetCheck(1); // Set the first radio to the selected state

In the SetCheck() function, a parameter of 1 means set to the selected state, and a parameter of 0 means unselected state.

4. Add mouse click response function:

The state of a radio button can be changed automatically when the mouse clicks on it. Here we need to modify the value of the control variable m_SelRadio to keep track of the selected radio button.
First, link the mouse click message with the response function in MESSAGE_MAP:

BEGIN_MESSAGE_MAP(CTextEditorView, CFormView)
 //{{AFX_MSG_MAP(CTextEditorView)
 ON_BN_CLICKED(IDC_ICONBUT0, OnIconbut0) //The ClassWizard adds
here  //}}AFX_MSG_MAP
 ON_BN_CLICKED(IDC_MYRADIO1, OnMyRadio1) //Radio button 1
 ON_BN_CLICKED(IDC_MYRADIO2, OnMyRadio2) //Radio button 2
 ON_BN_CLICKED(IDC_MYRADIO3, OnMyRadio3) //Radio button 3
END_MESSAGE_MAP()

Then define the click function in the header file MESSAGE_MAP:

protected:
 //{{AFX_MSG(CTextEditorView)
 afx_msg void OnIconbut0(); //The ClassWizard adds
here  //}}AFX_MSG
 afx_msg void OnMyRadio1(); //Radio button 1
 afx_msg void OnMyRadio2(); //Radio button 2
 afx_msg void OnMyRadio3(); //Radio button 3
 DECLARE_MESSAGE_MAP()

Be careful not to add the function to the AFX_MSG interval in case it affects the use of the ClassWizard.
Define specific response functions (this is done manually, not with ClassWizard) :

//Click the radio button 1 void CTextEditorView: : OnMyRadio1 () < br / >
{
 m_SelRadio=0;
}
  //Click the radio button 2 void CTextEditorView: : OnMyRadio2 () < br / > {
 m_SelRadio=1;
}
  //Click the radio button 3 void CTextEditorView: : OnMyRadio3 () < br / > {
 m_SelRadio=2;
}

5. Recycling resources:

In the destructor, reclaim the radio button created (or immediately when the radio button is not used) :

CTextEditorView::~CTextEditorView()
{
 int i;
 for( i=0; i<3; i++)
 {
  if(p_MyRadio[i])
   delete p_MyRadio[i];
 }
}

Above is the dynamic control of the generation and response method, a variety of different control practices slightly different, but the idea and steps are similar, I hope that this article described for everyone's VC programming help.


Related articles: