Apply button in CDialog - c++

I have a dialog in which after pressing button OK, the program uses the data in the dialog and draws a plot. I need to draw the plot without having to close the dialog as with IDOK, hence the apply button.
The code with drawing the dialog is,
INT_PTR val = dlg->DoModal();
if ( val == IDOK) {
//draw plot
}
The code of onOK and onApply
void DLg::OnOK() {
GetDataGrid();
CDialog::OnOK();
}
void DLg::OnBnClickedApply()
{
GetDataGrid();
}
How do I get DoModal() to return a value on onApply() without closing the dialog?
Any help would be appreciated.

A modal dialog can't return a value and leave the dialog open. You could either make your dialog non-modal, or post your main window a message from the OnBnClickedApply function that makes it draw the plot.

I tend to put drawing into a separate thread and would call it wherever needed. So you can either
(1) call the OnDrawPlot again in your Apply button
if ( val == IDOK) {
AfxBeginThread(...);//draw plot
}
void DLg::OnBnClickedApply()
{
AfxBeginThread(...);//draw plot
}
(2) send the return value back to the DoModal using EndDialog method
What parameters are there in EndDialog ?
An example can be found here.

Declare a variable in CDialog derived class preferably public. Then just at OnOK assign this variable to appropriate value. The caller would use it directly.
class Dlg : public CDialog
{
public:
int TheVariable;
...
};
Call site:
if(dlg.DoModal()==IDOK)
{
dlg.TheVariable; // Use the variable
}
However, if you need to draw on the dialog itself (and not to other window, which has launched the dialog), then don't call CDialog::OnOK or EndDialog in your OnOK override. In this case, you need to do painting in dialog itself.

Related

Create MFC controls in CDialog constructor [win32/MFC]

I'm working on the library, that wraps some MFC classes and methods. I want the user to be able to dynamically create a CDialogEx using a template in memory. For the modal dialogs, I call CDialog::InitModalIndirect and then CDialog::DoModal. For the modeless dialogs, I call CDialog::CreateIndirect and then CWnd::Show.
The code looks something like this:
// inside my library
class MyDialog : public CDialogEx
{
public:
MyDialog(CWnd* parent) : CDialogEx()
{
parent_ = parent;
my_template_data_ = CreateSomeGenericTemplate();
// OnInitDialog should be preferably called here
}
void ShowModal()
{
InitModalIndirect(my_template_data_, parent_);
DoModal(); // but it's called here - too late
}
void ShowModeless()
{
CreateIndirect(my_template_data_, parent_);
Show(); // but it's called here - too late
}
MyButton* GetButton(int id)
{
// returns the instance of my MyButton, which is a subclassed CButton
}
private:
BOOL MyDialog::OnInitDialog() override
{
CDialogEx::OnInitDialog();
// CWnd::Create for the UI controls can only be called here
}
};
// user's code
// user creates the dialog - in the constructor it's not clear if modal or modeless
1. MyDialog user_dialog(some_parent); // here, I need the controls to be created
2. user_dialog.GetButton(42)->SetWindowText(L"new text"); // user wants to initialize his controls
// but he can't, because MyButton::Create was not called yet
3. user_dialog.ShowModal(); // and only then display the dialog
//by default, here the MFC calls OnInitDialog - too late,
//the SetText method needed to be set on line 2.
My problem is, that the dialog's controls (buttons etc.) can only be created inside the CDialog::OnInitDialog method, which is called automatically after DoModal (for modal)/Show (for modeless) methods. I need the controls to be created and properly initialized (with the CWnd::Create method) preferably inside the constructor. I thought about calling Show/DoModal directly inside the constructor but I don't yet know if it's going to be modal or modeless dialog. It there a solution to this? Many thanks in advance.
Why don't you refactor your code and put the common code in a InitUI() method and call it from both sides?
And don't init interface on the constructor. Do it in OnInitDialog for modal; and in Create, or OnCreate (which implies a ON_WM_CREATE() map entry) for modeless dialogs.

Child Dialog - SetWindowTextA or SendMessageA crashes program - MFC

ERROR: afxwin2.inl line 165
My app is a dialog box with a few edit boxes. Once I click the button to evaluate the information entered I want to open a child dialog to display the results. I tried overloading DoModal() like this:
//in the child dialog
//.h
CResultsDlg::CResultsDlg(CParentDlg *parent);
virtual INT_PTR DoModal(float bmi);
//.cpp
CResultsDlg::CResultsDlg(CParentDlg *parent) : CDialogEx(CResultsDlg::IDD), _parent(parent)
{ //initializations }
INT_PTR CResultsDlg::DoModal(float bmi)
{
m_sBMI.Format("%f", bmi);
m_hBMI.SetWindowTextA(m_sBMI); //crashes !!!!!!!!!!
m_hBMI.SendMessageA(WM_SETTEXT, 0, (LPARAM)"15.11"); //crashes !!!!!!!!
// OnInitDialog(); //because this wasn't getting called at all
return CDialogEx::DoModal();
}
BOOL CResultsDlg::OnInitDialog()
{
CDialogEx::OnInitDialog();
// __super::OnInitDialog(); //no difference...
m_hBMI.SetWindowTextA("10.3"); //crashes !!!
return true; // return TRUE unless you set the focus to a control
}
//in the parent dialog
//.cpp
void CParentDlg::OnBnClickedCalculate()
{
CResultsDlg childResultsDlg = this;
childResultsDlg.DoModal(15.7);
}
m_hBMI is a handle to a static text control. I tested an edit box but it still crashed.
I understand that it probably has something to do with the controls not being created yet but I tried every way I know.
Using breakpoints, I confirmed that OnInitDialog does not get called at all unless I put it in the overloaded DoModal function. SetWindowText/SendMessage still crashes in OnInitDialog with the same ASSERT error.
If I remove all SetWindowText/SendMessage then the child window does come up modal like it should but the 'result' static text control is the same as the text I set it to in the dialog editor properties pane.
Thanks !!!!!
*MORE DETAILS*-----------
void CResultsDlg::DoDataExchange(CDataExchange* pDX) // DDX/DDV support
{
CDialogEx::DoDataExchange(pDX);
DDX_Text(pDX, IDC_BMI, m_fBMI);
DDV_MinMaxFloat(pDX, m_fBMI, 0, 100);
DDX_Control(pDX, IDC_BMI, m_hBMI);
}
The usual sequence when you start a dialog is:
You call CDialog::DoModal.
The dialog window gets created.
The child controls of the dialog get created.
OnInitDialog gets called.
CDialog::OnInitDialog calls DoDataExchange.
You have a DDX_Control call in your DoDataExchange method to map the child control to a member variable.
Notice that the member variables only get initialized at the end of that sequence. You're trying to use them way before, so you get a crash.
Store the value you need for initialization in a member variable and take care of it in DoDataExchange.

Is there any function that removes icon, using visual studio 6.0?

I couldn't find any function which removes a loaded icon. Please help me:))
Or is there any other function which makes an icon hidden such as setVisible(false), or delete?
if(GetDlgItem (IDC_BUTTON1)->IsWindowEnabled())
{
m_hIcon = AfxGetApp()->LoadIcon(IDI_ICON1);
dc.DrawIcon(DT_CENTER,DT_CENTER, m_hIcon);
}
if(GetDlgItem (IDC_BUTTON1)->IsWindowEnabled()==0) //disabled
{ dc.SetTextColor(RGB(192,192,192));
m_hIcon = AfxGetApp()->RemoveIcon(IDI_ICON1);//HERE! // I try another icon
//which is null, however it didn't work because it is transparent.
}
Instead of painting another icon when the button is disabled, either draw a rectangle using a null pen and a brush of the background colour or just invalidate the icon-rectangle with bErase as TRUE when you disable the button
This code is a bit strange, especially since you don't give us any context. What is dc, a device context? Is this code inside of a OnPaint message handler function? And either way, why are you drawing the icon manually instead of letting the button control do it automatically?
The simpler approach is to monitor the enabled state of the control by listening for WM_ENABLE messages via the OnEnable member function, and calling the SetIcon member function to update the button's icon. This way, the button control keeps track of the icon, so there's no need to keep it in a member variable (m_hIcon). And removing an icon is as simple as setting it to display a null icon. There's no need or all of these ugly hacks, like using a second transparent icon, or drawing over the icon with a solid color rectangle.
Sample code, where CMyButton extends CButton:
void CMyButton::OnEnable(BOOL bEnable)
{
CButton::OnEnable(bEnable); // call the base class
if (bEnable)
{
// The button was enabled, so load and set the icon.
const HICON hIcon = AfxGetApp()->LoadIcon(IDI_ICON1);
this->SetIcon(hIcon);
}
else
{
// The button was disabled.
// First, retrieve the current icon from the button.
const HICON hIcon = this->GetIcon();
// Then, remove the icon from the button by setting a null icon.
this->SetIcon(NULL);
// Finally, delete the original icon to prevent memory leaks.
::DestroyIcon(hIcon);
}
}
It is, of course, possible to do it your way inside of the OnPaint function, if you absolutely must (though this is poor design). The trick is that the icon only gets drawn if the code to draw it gets executed. Otherwise, no icon gets drawn. The base class will not draw an icon.
So all you need is:
void CMyWindow::OnPaint()
{
CPaintDC dc(this);
const CWnd* pBtn = GetDlgItem(IDC_BUTTON1);
if (pBtn->IsWindowEnabled())
{
m_hIcon = AfxGetApp()->LoadIcon(IDI_ICON1);
dc.DrawIcon(DT_CENTER, DT_CENTER, m_hIcon);
// make sure that you delete m_hIcon when the window is destroyed!
}
else
{
// Do not draw the icon here!
// But write whatever other code you want, like...
dc.SetTextColor(RGB(192,192,192));
}
}
Obviously this tactic will only work if your code is inside of the OnPaint function, but that is where you should do all of your drawing. Otherwise it will be lost whenever the window is repainted. For a demonstration, just try minimizing the window or moving it off the screen. Force a repaint by calling CWnd::Invalidate, followed by CWnd::UpdateWindow (or alternatively `CWnd::RedrawWindow).

mfc access to formview item from dialog box

In my SDI application i need to get this behawiour. After I click on a button on the FormView, a CDialog opens. When I press the OK button on the CDialog, I call a function of the FormView. I don't want to close the CDialog. I try to do it with modeless dialog, but when i call formview function from dialog, i can't access to formview's control, like it's lost hwnd; the error is can't read memory of m_hwnd, the hwnd is ???.
This is my code:
Open modeless dialog:
CCampiDlg *m_pDialog = NULL;
HWND hCampi = NULL;
// Invoking the Dialog
m_pDialog = new CCampiDlg;
if (m_pDialog != NULL)
{
BOOL ret = m_pDialog->Create(m_pDialog->IDD, this);
if (!ret) //Create failed.
{
AfxMessageBox(_T("Error creating Dialog"));
}
m_pDialog->ShowWindow(SW_SHOW);
}
when i press the ok button in the dialog i do:
CEditorTxView pView;
box2 = (CEdit*)(GetDlgItem(IDC_CAMPI_BOX2));
box2->GetWindowTextW(campo);
pView.inserisciCampo(1, campo);
In inserisciCampo function in CEditorTxView (CFormView) i have to do operation with my control txtCtrl, but it's lost hwnd. The declaration of txtCtrl is in the CEditorTxView.h
CTx1 txtCtrl;
And initialize it in DoDataExchange function:
void CEditorTxView::DoDataExchange(CDataExchange* pDX)
{
CFormView::DoDataExchange(pDX);
DDX_Control(pDX, IDC_TX1, txtCtrl);
}
Someone can help me plz?
I can give you two answers here:
How to do what you are asking (get access to a control of the CFormView from a modeless dialog)
How to solve your underlying problem (communicate changes in a modeless dialog to the owner view)
For the first one, you have to declare a pointer to the view in the dialog class and initialize it in the constructor of the view:
class CCampiDlg : public CDialog
{
public:
CCampiDlg(CEditorTxView* pView, CWnd*pParent = NULL) // Change declaration to add pointer to view
: m_pView(pView)
{
}
// ... Whatever
private:
CEditorTxView* m_pView;
}
Now in your button handler:
CEdit* box2 = (CEdit*)(GetDlgItem(IDC_CAMPI_BOX2)); // Why not use a control variable?
box2->GetWindowTextW(campo);
m_pView->inserisciCampo(1, campo);
This should do what you are asking for. However, it is the wrong way to do it.
The problem with this approach is that the dialog knows way too much about its parent. It knows it is of type CEditorTxView and that it has a member called inserisciCampo, that takes a number and some text.
It shouldn't know that much. In fact, knowing anything about it, other than it is of type CView or even CWnd, is too much.
If the dialog knows about the view, you can't reuse the dialog with other views, and anytime the view changes its representation (what now is a textbox may be a combobox in the future, for example) the dialog must change accordingly.
The solution would be to send a message to the parent, explaining what's happened. Then the parent (the view) should know haw to handle that event. For example:
class CCampiDlg : public CDialog
{
public:
CCampiDlg(CWnd*pParent = NULL) {}
protected:
OnOk()
{
CString campo;
c_CampiBox2.GetWindowText(campo);
GetParent()->SendMessage(UWM_CAMPO2_SET, 0, (LPARAM)&campo);
}
}
In the view:
// It can be ON_REGISTERED_MESSAGE:
ON_MESSAGE(UWM_CAMPO2_SET, OnCampo2Set)
//...
LRESULT CEditorTxView::OnCampo2Set(WPARAM, LPARAM lParam)
{
CString* s = (CString*) lParam;
inserisciCampo(1, *campo);
return 0;
}
Now, you have decoupled the view and the dialog. The dialog knows nothing about the view. You can change its type, change the representation, even make it a dialog, and you don't have to change anything in the dialog. And if you need that same modeless dialog somewhere else, you just drop it there, create a message handler in the parent, and voilĂ !
For further explanations and better examples, check these articles:
Dialog and control design (Your case is explained in the section "Notifications to the environment")
Message management
On Ok button click the below code is running:
CEditorTxView pView;
box2 = (CEdit*)(GetDlgItem(IDC_CAMPI_BOX2));
box2->GetWindowTextW(campo);
pView.inserisciCampo(1, campo);
Note that, you are creating the new pView in stack and it does't attach with any window. You are not actually referring the view that already created and launched your dialog acting a parent. Revisit the above code and try the get the view:
Try the below code, if it is not working (Google it)
CFrameWnd * pFrame = (CFrameWnd *)(AfxGetApp()->m_pMainWnd);
CView * pView = pFrame->GetActiveView();

MFC: Creating modeless dialog box without displaying

I'm trying to create a simple modeless dialog box which I'm creating from my CWinApp derived InitInstance() function.
BOOL CMyApp::InitInstance()
{
...
m_pMyDialog = new CMyDialog();
m_pMyDialog->Create(CMyDialog::IDD);
...
retrun TRUE;
}
I've created the dialog template in the resource editor and the WS_VISIBLE bit is unset. My intention is to avoid showing the dialog until I explicitly call ShowWindow(SW_SHOW) but for some reason the call to Create displays the dialog.
I've tried to change the return value of OnInitDialog() to FALSE but that doesn't work.
I've even tried to call ModifyStyle() in case something else is setting the WS_VISIBLE bit.
int CMyDialog::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
if (CDialog::OnCreate(lpCreateStruct) == -1)
return -1;
ModifyStyle(WS_VISIBLE, 0);
return 0;
}
That doesn't work either. In all cases, after I call Create the dialog is displayed which isn't how I've read it should work.
The problem was with AnimateWindow() which was causing the dialog to display prematurely.