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.
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;
}
Has anyone found a good way to save dialog data to a database in CMyDialog::OnOK?
void CMyDialog::OnOK()
{
// If I save my data here, I don't know if DoDataExchange()
// found validation errors.
CDialog::OnOK();
// If I save my data here, EndDialog() has already been called
}
Looking for ideas on how best to structure this. I know the norm is to have the caller save the data as needed but I don't want the dialog to close if I encounter an error saving the data to the database.
It seems like a good solution would be if CDialog::UpdateData() were virtual, but it is not.
Why not just use UpdateData?
The return value:
Nonzero if the operation is successful; otherwise 0. If bSaveAndValidate is TRUE, then a return value of nonzero means that the data is successfully validated.
So:
void CMyDialog::OnOK()
{
if(!UpdateData(TRUE))
{
// There was some error with the validation procedure so don't end the dialog.
return; // Suppress closing dialog
}
// OK to save data
if(!SaveDataToDatabase())
{
// Some error
return;
}
// Data validated Ok and was saved to DB OK, so close
EndDialog(IDOK);
}
Unless I miss understand your question.
So, it seems clear MFC wasn't designed to work this way.
But the simplest solution I found was to modify DoDataExchange() as follows:
void CMyDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
// DDX and DDV calls go here
if (pDX->m_bSaveAndValidate)
{
if (!SaveData())
pDX->Fail();
}
}
The code above relies on SaveData() displaying an error message and returning false if it encounters any errors.
The result is that the regular validation is performed before I attempt to save my data. And, if the code that saves the data fails, I can still prevent the dialog box from closing via the same technique that the MFC validation methods use. (Namely, by calling pDX->Fail()).
In your comment you say this:
// If I save my data here, EndDialog() has already been called
All that means is that the HWND named m_hWnd has been closed and all the child controls with it. The window is dead, but not your instance of your CVehicleDlg. The member variables associated with the data exchange will have the values from the controls transferred to them. You should be good to go for saving.
Another approach would be to catch OnDestroy in your class instead of OnOK. That way, you get the default processing of OnOk to do the data validation. The window wouldn't be destroyed unless the data validation failed. You'll have to special case OnCancel to set some flag to "not save" when your dialog is destroyed.
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.
I have a solution and it has two projects in it. When I got the code they told one project handles the visual part and the other has the logic part. Now I added one button to the window. To do that i edited the project which handles the visual part. I am very new to this but creating and adding buttons is fairly straightforward in visual studio 2010. Now the problem is I want to detect if the button is pressed from the other project. I am sure that the projects are sharing some data, but I am not being able to capture it. For now I am changing a value in a file and reading the same data from the other project to check if the button is pressed. But I think there is a better way to do it. Can anyone help?
I don't think the two projects are sharing automatically. You will have to define the interface that the two projects communicates. For instance, in your solution above the "a value in a file" is the "interface" you have defined. What sounds like you are trying to achieve is to separate the controller (logic part) and view (visual part) separately, which seems to indicate that your project is using MVC model.
I would suggest defining an abstract class (interface) that defines the interaction you want between the two projects. All they have to share is a single header file.
For example:
// Solution A (Controller - logic part)
// MyUIHandler.h
class IMyUIHandler //You can also use microsoft's interface keyword for something similar.
{
public:
HRESULT onButtonPressed() = 0; // Note that you can also add parameters to onButtonPressed.
};
HRESULT getMyUIHandler(IMyUIHandler **ppHandler);
Then implement this interface:
// Solustion A (Controller - logic part)
// MyUIHandler.cpp
#include "MyUIHandler.h"
class CMyUIHandler : public IMyUIHandler
{
private:
// Add your private parameter here for anything you need
public:
HRESULT onButtonPressed();
}
HRESULT getMyUIHandler(IMyUIHandler **ppHandler)
{
// There are many ways to handle it here:
// 1. Define a singleton object CMyUIHandler in your project A. Just return a pointer
// to that object here. This way the client never releases the memory for this
// object.
// 2. Create an instance of this object and have the client release it. The client
// would be responsible for releasing the memory when it's done with the object.
// 3. Create an instance of this object and have a class/method in Solution A release
// the memory.
// 4. Reference count the object, for example, make IUnknown the parent class of
// IMyUIHandler, and implement the IUnknown interace (which is boiler plate code).
// Since I don't know your project it's hard for me to pick the most suitable one.
...
*ppHandler = myNewHandler;
...
return S_OK;
}
CMyUIHandler can simply be your existing class that already handles some of the logic.
In solution B you should will call getMyUIHandler in some initialize function, for example the controller of the UI class, save that as your member. Then "Button clicked" event handler that VS creates for you.
// Solution B (View - visual part)
// MyUIView.h
class MyUIView
{
protected:
IMyUIHandler *m_pHandler;
}
// MyUIView.cpp
CMyUIView::CMyUIView(...)
{
...
hr = getMyUIHandler(&m_pHandler);
// error handler, etc...
...
}
// In the function that is created for you when button is clicked (I'm not sure if I get the signature right below.
void OnClick(EventArgs^ e)
{
...
hr = m_pHandler->onButtonPressed();
...
}
Then you can pass any parameter you define for the function onButtonPressed as soon as the button is clicked.
So I have this MFC dialog program I am working with. The dialogs are written but now I am having difficulty passing data around from dialog to dialog. I have the following structure _dlgDataHandler set up in a class derived from CWinApp and have have created a "new" statement for a pointer to this type.
//.......SRK.h file
class CSRK_App : public CWinApp
{
public:
CFSB_App();
// added the following data structure for data passing withing the program
typedef struct _dlgDataHandler {
char RepetitionRadio[24];
// another member
// yet another member and so on as necessary
} *dlgDataHandlerPtr;
// extern dlgDataHandlerPtr dlgDataHandler;
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CSRK_App)
public:
virtual BOOL InitInstance();
//}}AFX_VIRTUAL
// Implementation
//{{AFX_MSG(CSRK_App)
// NOTE - the ClassWizard will add and remove member functions here.
// DO NOT EDIT what you see in these blocks of generated code !
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
//....... SRK.cpp A pointer to a new dataHandler created in this block about 2/3 the way down
// CSRK_App initialization
BOOL CSRK_App::InitInstance()
{
AfxEnableControlContainer();
// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need.
//SetRegistryKey(_T("Local AppWizard-Generated Aplications"));
#ifdef _AFXDLL
Enable3dControls(); // Call this when using MFC in a shared DLL
#else
Enable3dControlsStatic(); // Call this when linking to MFC statically
#endif
//CSRK_Dlg dlg;
CDialogMain dlg("SRK - Beta"); // added 12/27 **
m_pMainWnd = &dlg;
//const char* m_pszHelpFilePath = NULL;
//free((void*)m_pszHelpFilePath);
//m_pszHelpFilePath=_tcsdup(_T("c:\SRKHelp.rtf"));
// the following line added to allocate memory for the structure
dlgDataHandlerPtr dlgDataHandler = new _dlgDataHandler;
dlg.SetWizardMode(); // added 12/27 **
int nResponse = dlg.DoModal();
if (nResponse == IDOK)
{
// TODO: Place code here to handle when the dialog is
// dismissed with OK
}
else if (nResponse == IDCANCEL)
{
// TODO: Place code here to handle when the dialog is
// dismissed with Cancel
}
// Since the dialog has been closed, return FALSE so that we exit the
// application, rather than start the application's message pump.
return FALSE;
}
In the dialog .cpp files, there are five, I need to be able to get data from the AFX variables "m_" and load them into this dataHandler Structure (or another one like it) so that they can be used in other dialogs and parts of the program, specifically my actual code when all the dialog data collection is done. Someone said to use AfxGetApp() so that I could have a handle on the current instance but I do not understand what they are talking about. And yes I have read about it in many forums, I just don't get it. I further realize this is probably not the best way to do it. I am trying to learn MFC/OOP with what time I have available, but for now, I am just trying to get a handle on the basic process as I can tune it later once I understand how to collect and pass simple data around.
I further don't understand how calling AfxGetApp() will help me get a handle on the members of CSRK_App. It inherited CWinApps public members but AfxGetapp() can't see what CSRK_App has... can it?
First, to explain the AfxGetApp advice you have received. There is some extra hand-waving using 'new' and a pointer, but this is basically using a global variable for the structure that holds your data. This is not the best way to do what you are trying to do. There are many pitfalls.
AfxGetApp() is an MFC call that returns a pointer to your main App class derived from CWinApp.
If you want to use that returned pointer, you need to cast it as a CSRK_App* pointer with:
CSRK_App* pApp = static_cast <CSRK_App*> ( AfxGetApp());
Then you can use pApp->dlgDataHandlerPtr->... to access the variables you need.
Now, for the pitfalls. Someone else may chime in with a reason why the 'new' and the pointer are helpful, but I do not see any advantage to this approach as compared to just having a local variable dlgDataHandler inside your CSRK_App class. That would simplify the code.
The next issue is that all your data is public in a struct. Any dialog class that can call AfxGetApp can read or write any data in that struct. You have no way to control access.
Also, all of your dialog classes must now include SRK_App.h so they know the structure, and have access to all other variables in that App class.
A cleaner, object-oriented approach would be to declare the struct (class) for the data in a separate .h file that could be included in the dialog classes. Then, you would pass a pointer/reference to this data into the constructor of the dialog classes. The dialog class would have no need to know anything about the App class.
For an even higher level of segregation, the dialog classes can be written so they only get a copy of the dlgDataHandler class passed in before calling .DoModal(), and then after the DoModal call returns with IDOK, the App class can have control over which data from the dialog gets updated into the dlgDataHandler class. The advantage of this approach is that it insures that no matter how the dialog class is programed, the user can always "Cancel" the dialog without modifying any data.