Github repository :https://github.com/github/VisualStudio
What Will I Learn?
- Learn the basic knowledge of modeless dialog.
- Learn how to registe callback function.
- Learn to pass messages between dialogs through callback functions.
Requirements
- The basic knowledge of C++.
- Messages between dialogs.
- The basic response functions for dialog.
Difficulty
- Intermediate
Tutorial Contents
issue raised
Some time ago, I was doing Github development based on Visual Studio 2015 . The need to pick up parameters from different dialog boxes is generated when I was writing a feature recently. The idea is to meet the demand by passing information between modeless dialogs.
Model dialog and modeless dialog
Unlike modal dialogs, modeless dialogs do not monopolize user input, and users can still interact with other interfaces when they open a modeless dialog. The modeless dialog is basically similar to the modal dialog box, it includes both the design dialog template and the derived class of the design CDialog class.
There are several differences between modal and modeless dialog:
different | Model dialog | Modeless dialog |
---|---|---|
Visible | CWnd::ShowWindow(SW_SHOW) | Must set Visible property |
Create | Local variables | new CModelessDialog |
Show | CDialog::DoModal | CDialog::Create |
Destroy | CDialog::EndDialog | CWnd::DestroyWindow |
Delete | No need to delete itself | CWnd::PostNcDestroy delete this |
several points of knowledge
What is callback funtion?
A callback function is a function called by a function pointer. If you pass a function's pointer (address) as an argument to another function which is used to call the function it points to, we say this is a callback function. The callback function is not invoked directly by the implementing party of the function, but is invoked by another party when a particular event or condition occurs, and is used to respond to the event or condition.
page source:CSDN
callback function definition: function pointer
The callback is implemented by the function pointer in C language, and the callback is implemented by passing the address of the callback function to the modulated function. Therefore, to implement the callback, you must first define the function pointer, see the following example:
void Func(char *s);// The prototype of the function
void (*pFunc) (char *);//function pointer
As you can see, the definition of a function is very similar to the definition of a function pointer. In general, we need to customize the function pointer type in order to simplify the variable definition of function pointer type and improve the readability of the program.
typedef void(*pFunc)(char *);
Composition of callback functions
The implementation process of the callback function consists of the following sections:
- registe
- unregiste
- callback Function
Code design
Create a modeless dialog
Modaless dialog objects are created dynamically in the heap with the new operator, instead of being embedded in other objects in the form of member variables or built on the stack as local variables. You should typically declare a pointer member variable to a dialog class within the owner window Class of the dialog to access the dialog object. Start the dialog by calling the Cdialog::create function.
void CGithubDlg::OnBnClickedButtonView()
{
//New a modeless dialog
m_pBranchDlg = new CBranchDlg();
//Create window
m_pBranchDlg->Create(IDD_DIALOG_BRANCH);
m_pBranchDlg->ShowWindow(SW_SHOW);
}
define SelParamFunc
What we want the callback function to do is that once the user clicks on an item in the branch list in the GitHub dialog, the information in this row will be returned to the Branch dialog as shown in the following figure.
So two parameters are required in the callback function, CString & strRowData represents the selected row of data, translates into a CString for delivery, and the second CWnd * Pwnd represents the window to accept the data.
Therefore, the response function should be as follows:
void SelFunc_Parameter(CString &strSelData, CWnd * pWnd)
{
//do something to set strSelData to the editctrls of Branch dialog
}
strSelData is a combination string with a # delimiter, in which we need to parse the string into three substrings. I wrote the following function to split the string.
//strline is the string need to split. ch is is Separator. arrRes is a string array after the split
// For example: strLine is "Rose # Branch_check_in # new function check in history", ch is "#", arrRes will has three child(Rose,Branch_check_in,new function check in history).
void GetSplitStrArr(CStringArray &arrRes, const CString strLine, TCHAR ch)
{
CString strTemp;
int nStart = 0, nEnd = 0;
if (strLine.IsEmpty())
{
return;
}
while ((nEnd = strLine.Find(ch, nStart)) > 0)
{
strTemp = strLine.Mid(nStart, nEnd - nStart);
nStart = nEnd + 1;
strTemp.Trim();
arrRes.Add(strTemp);
}
strTemp = strLine.Mid(nStart);
strTemp.Trim();
arrRes.Add(strTemp);
}
The next step is to put these strings into the control of the corresponding Branch dialog box, respectively.
void SelFunc_Parameter(CString &strSelName, CWnd * pWnd)
{
CBranchDlg * pDlg = (CBranchDlg *)pWnd;
if (pDlg != NULL)
{
CStringArray arrLineInfo;
GetSplitStrArr(arrLineInfo, strSelName, _T('#'));
if (arrLineInfo.GetSize() >= 3)
{
// set the first one to IDC_EDIT_USER ctrl
CEdit * pEdtUser = (CEdit *)pDlg->GetDlgItem(IDC_EDIT_USER);
pEdtUser->SetWindowTextW(arrLineInfo[0]);
// set the second one to IDC_EDIT_BRANCH ctrl
CEdit * pEdtBranch = (CEdit *)pDlg->GetDlgItem(IDC_EDIT_BRANCH);
pEdtBranch->SetWindowTextW(arrLineInfo[1]);
// set the third one to IDC_EDIT_NOTE ctrl
CEdit * pEdtNote = (CEdit *)pDlg->GetDlgItem(IDC_EDIT_NOTE);
pEdtNote->SetWindowTextW(arrLineInfo[2]);
}
}
}
The corresponding effect is probably:
Now let's take a look at the application of typedef in the definition of callback functions.
typedef void(*SelParamFunc)(CString & strRowData, CWnd * pWnd);
The function of typedef is to define a new type. The first sentence defines a SelParamFunc type and defines the type as a pointer to a function that takes a cstring& and CWnd * as two parameters and returns a void type. The definition is completed as shown in the following illustration:
Registe and unRegiste
The callback function needs to be registered before responding so that the function entry can be found at the appropriate time.
Similarly, you need to unRegiste at the end so that the next response will not be affected by the original function pointer. Add the following code to the Cgithubdlg class:
class CGithubDlg : public CDialogEx
{
////////////////////////////////
public:
SelParamFunc mpSelFunc;
CWnd * m_pWnd;
void RegisteSelFunc(SelParamFunc pFunc, CWnd * pWnd);
void UnRegisteSelFunc();
};
Add the following code to the CGithubDlg.cpp:
void CGithubDlg::RegisteSelFunc(SelParamFunc pFunc, CWnd * pWnd)
{
// Registe the callback funtion and set the target window
mpSelFunc = pFunc;
m_pWnd = pWnd;
}
void CGithubDlg::UnRegisteSelFunc()
{
// UnRegiste the callback funtion and set the target window to NULL
mpSelFunc = NULL;
m_pWnd = NULL;
}
So what's the right time to register this callback function? We chose to register the callback function after the modeless dialog was just bounced.
Response callback
The response of the callback function needs to be manually invoked in its own specific place.
In our example, you should respond when you select a row in the list. First SELECT Branch List CTRL, and then find Contrl event in the Properties page. Select the NM_CLICK event.
Automatically adds a function:afx_msg void OnNMClickListBranch(NMHDR *pNMHDR, LRESULT *pResult);
In this function, the following code is automatically generated:
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
Let's take a look at the structure of LPNMITEMACTIVATE:
typedef struct tagNMITEMACTIVATE
{
NMHDR hdr;
int iItem;
int iSubItem;
UINT uNewState;
UINT uOldState;
UINT uChanged;
POINT ptAction;
LPARAM lParam;
UINT uKeyFlags;
} NMITEMACTIVATE, *LPNMITEMACTIVATE;
You can get the selected row from this variable exactly. So you can get the data for that row directly here, and then feed back to branch this dialog box by using the callback function. The code for the implementation is as follows:
void CGithubDlg::OnNMClickListBranch(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMITEMACTIVATE pNMItemActivate = reinterpret_cast<LPNMITEMACTIVATE>(pNMHDR);
//Get the row of the selected item
int nRow = pNMItemActivate->iItem;
//Get data from branchs vector
sBranch hit = m_vecBranchs[nRow];
//Create the reasult string need to be send back
CString strRes;
strRes = hit.m_strUser + _T("#") + hit.m_strName + _T("#") + hit.m_strNote;
//Call the callback function
mpSelFunc(strRes, m_pBranchDlg);
*pResult = 0;
}
PostNcDestroy
Because the modeless dialog object is created with the new operator, you must delete the dialog object with the Delete operator after the dialog closes. Generally,After a window is deleted on the screen, the frame will calls CWnd::PostNcDestroy, a virtual function in which the program can complete the task of deleting a Window object, as follows:
void CBranchDlg::PostNcDestroy()
{
delete this;
CDialogEx::PostNcDestroy();
}
The results of the final implementation are as follows:
Conclusion
The application of callback functions is flexible and extensive.
The above example is just one of the small applications. Because the caller can be separated from the callee, the caller does not care who the callee is. It only needs to know that there is a called function with a specific stereotype and a restricted condition. Therefore, it is widely used in the process of software development.
I hope this tutorial will help you.
Thank you for your attention.
@hushuilan
Thank you for your contribution.
While I liked the content of your contribution, I would still like to extend few advices for your upcoming contributions:
Looking forward to your upcoming tutorials.
Need help? Write a ticket on https://support.utopian.io/.
Chat with us on Discord.
[utopian-moderator]
Dear @mcfarhat. Thank you for your valuable advice.
Hey @hushuilan
Thanks for contributing on Utopian.
We're already looking forward to your next contribution!
Contributing on Utopian
Learn how to contribute on our website or by watching this tutorial on Youtube.
Want to chat? Join us on Discord https://discord.gg/h52nFrV.
Vote for Utopian Witness!
Congratulations @hushuilan! You received a personal award!
Click here to view your Board
Do not miss the last post from @steemitboard:
Congratulations @hushuilan! You received a personal award!
You can view your badges on your Steem Board and compare to others on the Steem Ranking
Vote for @Steemitboard as a witness to get one more award and increased upvotes!