Can someone explain me how to pass a custom class object to another function in wxWidgets? I have a wxDialog class called AddUser which contains a void type OnButtonClick function that creates an object of a custom class "User". How can I pass that object to another OnButtonClick function that is located in Main class?
One important thing to know (in case you don't already) about wxDialog is that it is quite okay to create them on the stack (most wxWidgets windows should be created on the heap).
This means that your dialog instance remains available even after it has been closed by the user pressing "Ok". You can test for user responses by the following code:
... existing method ...
AddUser dialog (this);
if (dialog.ShowModal() == wxID_OK)
{
... process new user ...
}
Because the dialog is still instantiated, you can include a method in your dialog code that returns the new user as follows:
User AddUser::GetUser ()
{
return newUser;
}
However, you should of course be careful where the new user is actually created. For example, if the new user object is created locally in the dialog, then you will need to make a copy of it (as the above example will do). If it is created on the heap (which I wouldn't advise) then you can return a pointer. A third alternative would be to pass a reference to the GetUser method so the dialog method looks like this:
bool AddUser::GetUser (User& user)
{
// Make sure that all fields are valid. Simple example given, but
// should be more complete.
if (TextName->GetValue() != "" && TextSurname->GetValue() != "")
{
user.setName(TextName->GetValue());
user.setSurname(TextSurname->GetValue());
return true;
}
return false;
return newUser;
}
And the call looks like this:
void wxBiblioFrame::OnButAddUserClick(wxCommandEvent& event)
{
AddUser dialog(this);
myUserDialog dialog (this);
myUserClass newUser;
if (dialog.ShowModal() == wxID_OK)
{
if (dialog.GetUser (newUser))
{
... process and store the new user ...
}
else
{
... handle the error ...
}
}
// NOTE: no need to Destroy() the dialog.
}
By the way, unless your user class is huge, I wouldn't be too concerned making copies of the object from an efficiency point of view. Creating and closing the dialog is likely to dwarf any time taken in making a copy.
You can't call an OnClick event and pass something different than the parameters in the event signature. If you need somthing like tis then maybe you should consider reiterating your application's architecture.
Related
I have a problem for my MFC project as follows:
When I click on the destroy button, I want to show a messagebox "asking save file" before document closed.
But I can't.
The message is always shown after the document was closed.
A lot of places that I have placed code.
CloseDocument Function of Document Class
OnDestroy Function of MainFrame Class
Destructor Function of View Class
ExitInstance Function of App Class
But without success.
Can someone show what's wrong?
A WM_CLOSE message is sent to a window when the user wants close it. A custom message handler can decide whether it wants to initiate window destruction from here, or go back (or initiate window destruction after storing information).
In an MFC application this is reflected as the OnClose member function of CWnd or a CWnd-derived classes (such as CFrameWnd). Client code can provide a custom implementation and wire it up through the message map. When it does it should only call into the base class implementation if it wants the program to terminate.
Assuming that your CMainFrame class derives from CFrameWnd you will need to make the following changes:
MainFrm.h (add the following class member)
class CMainFrame : public CFrameWnd
{
// ...
protected:
void OnClose();
// ...
};
MainFrm.cpp (message map)
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
// ...
ON_WM_CLOSE()
END_MESSAGE_MAP()
MainFrm.cpp (implementation)
void CMainFrame::OnClose()
{
if (::AfxMessageBox(_T("Close application?"), MB_YESNO) == IDYES)
{
// Have the base class initiate destruction
CFrameWnd::OnClose();
} else {
// Do nothing
}
}
An attempt to close the application's main window pops up a confirmation dialog. If the user selects "Yes", the program terminates. Otherwise the program continues to execute. In that case the document will remain unchanged.
The simpler approach, a Oneliner as described in MSDN
'By calling this function consistently, you ensure that the framework prompts the user to save changes before closing a document. Typically you should use the default value of TRUE for the bModified parameter. To mark a document as clean (unmodified), call this function with a value of FALSE.'
BOOL CMyDocument::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
// TODO: add reinitialization code here
// (SDI documents will reuse this document)
SetModifiedFlag(TRUE); // Set this somewhere, when you want to safe the document before the document goes off
return TRUE;
}
I am running into the following issue:
Users presses "Ctrl+N" which goes into function MainWindow::newAction()
In MainWindow::newAction(), create a QDialog dlg(centralWidget()) and call dlg.exec()
While dlg is open, users pressed "Ctrl+N" again
The result is that dlg never gets deleted (it will only get deleted once centralWidget() gets deleted).
The call stack is something like this:
MainWindow::newAction ()
...
MainWindow::newAction()
I am wondering how to handle this case. I want all of the local dialog variables from the first call to newAction() to be deleted by the time we go into the function newAction() again.
You also can try something like this:
void MainWindow::newAction() {
const auto dialog = new MyDialog(centralWidget());
// When user will close the dialog it will clear itself from memory
connect(dialog, &QDialog::finished, [dialog]() {
dialog->deleteLater();
});
dialog->exec();
}
However, a good move would be to stop user from summoning more QDialogs than a single one, given that this one is a modal dialog(might be a good idea to keep this dialog pointer as a class member and check is it on screen already before calling exec() on it.
If i understood the question right, you want one dialog to be opened and want to delete it before a new dialog request comes in?
If that's the case you can do following:
In MainWindow.h declare QDialog *dlg = nullptr
In your MainWindow.cpp newAction() function you can do following:
void newAction()
{
if(dlg != nullptr)
{
dlg->close();
dlg->deleteLater();
//or
//dlg->destroy(); // this will immediately free memory
}
dlg = new QDialog(centralWidget());
...
//dlg->exec(); // This will automatically make QDialog modal.
dlg->show(); // This will not make a QDialog modal.
}
I hope this will help. Remember QDialogs when displayed with exec() they automatically behave as Modal window. show() will make it non-modal.
I am a newbie writing MFC code, and I have a problem adding a string to a list box. The code shown works OK when I call the AddString object directly, but not when I add it to my own Test object as shown. WHat do I have to do so that AddString still works from inside another object? Maybe some kind of inheritance problem?
Thanks for bearing with me on this newbie question!
Duncan
void CFileProcessorDlg::Test()
{
m_strFullName1.Format(_T("Starting to process files"));
m_Message1.AddString(m_strFullName1); // Add string to list box
}
void CFileProcessorDlg::OnClickedButtonStart()
{
//Duncan's Stuff
// TODO: Add your control notification handler code here
CFileProcessorDlg dlg;
UpdateData(); // Transfer data from controls to variables
//dlg.ProcessFiles(m_InputFile, m_OutputFile); // Actually process the files here
// Problem 5/22/2017 - this works here, but not if I move it into the Test object.
//m_strFullName1.Format(_T("Starting to process files"));
//m_Message1.AddString(m_strFullName1); // Add string to list box
dlg.Test();
}
Terminology first: the entity you're calling "my own Test object" is actually a method.
That out of the way, you're instantiating a(nother) CFileProcessorDlg object (named dlg) from within your CFileProcessorDlg::OnCickedButtonStart() method.
That dlg object is created, you call its Test() method, and then the object goes out of scope at the end of CFileProcessorDlg::OnCickedButtonStart(), so it would not have any useful effect on the object calling object (this).
I think you want to do something like this:
void CFileProcessorDlg::OnClickedButtonStart()
{
//Duncan's Stuff
// TODO: Add your control notification handler code here
UpdateData(); // Transfer data from controls to variables
//dlg.ProcessFiles(m_InputFile, m_OutputFile); // Actually process the files here
// Problem 5/22/2017 - this works here, but not if I move it into the Test object.
//m_strFullName1.Format(_T("Starting to process files"));
//m_Message1.AddString(m_strFullName1); // Add string to list box
Test(); // more explicitly: this->Test()
}
I left your comments for context, but the net change was to not instantiate a new CFileProcessorDlg object.
I want to update a user interface when I clicked a button. However, I'm not using a direct way inside CProjectDlg. I have a CMain class which will handle the operation.
Here is my code:
ProjectDlg.cpp
void CProjectDlg::OnBnClickedButton1()
{
CMain *ptr = new CMain();
ptr->Click();
CString test = m_edit1;
}
Main.cpp
void CMain::Click()
{
CProjecttDlg *ptr = new CProjectDlg();
ptr->m_edit1.SetString(L"This is a test.");
}
In the debug mode, I found the address of m_edit1 is not same. So the function is useless.
I need to pass the same address of m_edit1 to the Click() function. How do I do that?
Thank you.
Each time you click, you create a new dialog.
CProjecttDlg *ptr = new CProjectDlg();
What you must do is to create it just once (maybe at CMain constructor? or the first time click is accessed). To store its value, just make ptr a member of CMain(define in .h, and so on) instead of a local variable.
You have a problem there. You are calling CMain::Click fron a CProjectDlg instance, but create a new instance of CProjectDlg inside CMain::Click, and set the edit box in that new dialog, not in the original one.
I don't know exactly what you are trying to do, but one thing that could work is to pass a pointer to the dialog to the CMain constructor, and then in CMain::Click use it ot set the edit box. Something like this:
//CMain.h
class CMain
{
public:
CMain(CProjectDlg*);
Click();
protected:
CProjecDlg* m_Dlg;
}
// CMain.cpp
CMain::CMain(CProjectDlg* dlg)
{
m_Dlg = dlg;
}
CMain::Click()
{
m_Dlg->m_edit1.SetString(L"This is a test.");
}
Apart from that, I don't know if it would be necessary to create a new instance of CMain every time the user clicks the bottom.
And finally, the possible solution I've provided might work, but it might also not be "correct". Without more details as to what you are trying to do, there's not much more I can help you with, though.
For example, in an MFC program, I have my main application and a 'class'. What should I do if I want to update a control (say, a listbox) that is situated on my main application from that 'class'?
heres an example that worked for me
theApp.m_pMainWnd->GetDlgItem(IDC_BUTTON6)->SetWindowTextW(L"Run Auto Test");
Your class can be designed to trigger an event which your main application can listen for. Then, a listener/event handler/delegate can be called to handle the event and update the listbox. Typically, most event formats pass a reference of the sender, in this case your 'class', as well as an object containing event arguments. These arguments can be used to pass the list of items you want to add to your listbox.
If you have the handle to dialog object in your class, then you can use GetDlgItem(ResourceID) to get list control object.
The easiest approach is to expose the listview from your application form/window to the classes that use it. You can do this either by passing the listview object (or parent window) to the class constructor, or storing it in a static variable that is accessible by the class.
For better encapsulation, you can put a method in the application that the class can call, e.g. "AddItemToListBox()". This allows the application object to remain in control of how you access the listbox. Again you can do this as a static method, or pass the main program object's 'this' pointer into the class constructor.
i.e.
class CApplication
{
CListBox m_ListBox;
public:
static void CApplication::AddItemToListBox(CString itemText)
{
// Add the item as you wish here
}
}
class CMyClass
{
afx_msg void CMyClass::OnMouseDown(...)
{
CApplication::AddItemToListBox("This is a test");
}
}