I have a dialog that is derived from our specific base class that is inturn derived from CDialog class
This dialog contains a combobox with combobox items in it (string)
There is a group box, labels, checkbox and a text box as well (I will call them grouped controls for this discussion)
If I select an item in index 0 from combo box, the Grouped controls are shown and if I select an item in index 1 from combo box, the group controls should be hidden
I have used the code to show or hide the controls as
void MyDerivedClass::HideMyControls(bool hide)
{
int param = (false == hide)? SW_SHOW : SW_HIDE;
GetDlgItem(IDC_ENABLE_TP_DIRECTORY)->ShowWindow(param);//This is Checkbox control
GetDlgItem(IDC_STATIC_NOTE)->ShowWindow(param);//This is label control
GetDlgItem(IDC_STATIC_DIR_NAME)->ShowWindow(param);//This is label control
GetDlgItem(IDC_EDIT_TP_DIRECTORY)->ShowWindow(param);//This is text control
GetDlgItem(IDC_STATIC_TP_GRPBOX)->ShowWindow(param);//This is label control
InvalidateRect(NULL); //NULL for testing
UpdateWindow();
}
The issue is the controls are still seen on the screen even after the above method is called with true parameter. If I move some other window on top of the (supposed to be) hidden controls, then it is actually disappears. So this seems to be a refresh issue. My assumption is that the InvalidateRect and UpdateWindow should have taken care of repainitng this. but is not happening.
Tried with InvalidateRect(NULL) to paint the whole screen, but still no use.
Could you please suggest how I can hide the controls? any clean way of doing this?
Note: when the controls are (supposed to be) hidden but are still seen on the screen, I will not be able to perform any action on those controls (like setting checkbox or typing in the text box).
So I believe that the controls are marked as hidden but not painted.
Related
Using Visual Studio 2013, I created a dialog resource using the resource editor. It is a child control with no border and is just a collection of radio buttons, push buttons, and static text. I want to turn this into a custom control in order to place this in several different locations. Let's call this a "Panel".
I then created a regular dialog and using the Toolbox "Custom Control", defined an area for the Panel. The Panel registers itself and has a valid window handle.
I used the following example:
https://www.codeproject.com/Articles/521/Creating-Custom-Controls
The parent's DDX gets hit and the _panel is properly instantiated:
MyDialog::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX)
DDX_Control(pDX, IDC_CUSTOM_PANEL, _panel)
}
I read that I need to override the OnPaint() and OnEraseBkgnd(CDC* pDC) methods so the Panel class has these but they are empty. I do not have any custom painting to do as the Panel contains nothing but regular buttons.
What do I have to include in OnPaint()?
I also noticed that none of the member buttons are instantiated in the Panel like would normally happen in a dialog's DoDataExchange method. Instead, I've had to resort to dynamically creating each of the control's inside the Panel's PreSubclassWindow() method:
void MyPanel:PreSubclassWindow()
{
_groupBox.Create(_T("Options"), WS_CHILD|WS_VISIBLE|BS_GROUPBOX, CRect(11, 11, 112, 231), this, IDC_STATIC_GROUPBOX);
//... do this for every dialog element??? seems like overkill...
CWnd::PreSubclassWindow()
}
Why do I need to do this when I've already defined/designed the Panel and each of its controls in the resource editor?
If I do not do this in the PreSubclassWindow method, nothing will appear on the dialog.
Any help is appreciated. Thanks.
The article says override OnPaint and OnEraseBkgnd if you want to change the functionality. It doesn't say you have to override always.
Just remove ON_WM_PAINT and ON_WM_ERASEBKGND, remove OnPaint and OnEraseBkgnd if you don't need them. Or call the base class implementations if you are not making any changes:
void MyPanel::OnPaint() { CWnd::OnPaint(); }
BOOL MyPanel::OnEraseBkgnd(CDC* pDC) { return CWnd::OnEraseBkgnd(pDC); }
This will show a blank control with nothing in it, unless you add a child window to _panel as you have done in MyPanel:PreSubclassWindow
You are adding _groupBox to _panel. And you are adding _panel to the MyDialog.
MyDialog::DoDataExchange(...){DDX_Control(pDX, IDC_CUSTOM_PANEL, _panel)} is needed to invoke SubclassWindow for _panel. That in turn calls _groupBox.Create.
If MyPanel::OnPaint and MyPanel::PreSubclassWindow are not doing anything MyPanel appears as a blank control.
... do this for every dialog element??? seems like overkill...
You can directly add _groupBox to the main dialog. But if you want to add specific controls within MyPanel then you have to do it manually.
You can also create a child dialog within a main dialog. For example that's how a tab control works.
In my application I have a CFormView with a CTabCtrl, I also have 4 CFormViews that are children of the main CFormView and that are shown/hidden when the user changes the selected tab.
However, I can't find a way to make the Tab Order to work properly. If the CTabCtrl has the focus, pressing the Tab key has no effect and if one of the child CFormView has the focus the Tab key will move the focus only around the controls inside the CFormView.
I tried changing the z-order of the visible child CFormView to be right after the CTabCtrl with SetWindowPos, changed the child CFormViews styles to WS_EX_CONTROLPARENT but nothing seems to work.
You've started out from the wrong implementation: you shouldn't make a CFormView with a CTabCtrl and then stuff more CFormViews into it. This isn't going to work right. Instead, you should work with CPropertySheet and CPropertyPage, where focus handling has already been taken care of. You will still be able to access the CTabCtrl owned by the CPropertySheet by calling GetTabControl(), but MFC will take care of the problems you've encountered.
Briefly: derive classes from CPropertySheet for each of the dialog windows you want to show (e.g., CConfigPage1, CConfigPage2). Create a Dialog resource in the Resource Editor for each of them, and do all of the other standard CDialog setup.
Next, derive a class from CPropertySheet (e.g., CProps), and (optionally) handle WM_SIZE and TCN_SELCHANGE.
Finally, derive a class from a CView descendent, like CScrollView (e.g., CViewMyAwesomeStuff). Then add member variables for the CPropertySheet and CPropertyPages, and handle WM_CREATE where you Add() each page to the property sheet and then Create(this,WS_CHILD|WS_VISIBLE) the property sheet.
Bonus: You can forward the CView::OnUpdate to each child CPropertyPage by calling GetPage() in a loop and calling a function on each of them, or you can send a message to each of them (use a user-defined message, like WM_APP+1). They can discover their parent's CDocument by calling GetParent()->GetParent()->GetDocument().
I have a CMFCPropertyGridCtrl that I'm using in an options dialog box. I have a method in my options dialog class called InitPropertyGrid(). This method clears any properties and populates the CMFCPropertyGrid objects (using a custom Settings object for the property values) and appends them to the grid.
When I open my dialog box the first time all the properties show correctly. However, if I then close my dialog box and reopen it, the very last property is not drawn on the screen. All other properties are drawn normally:
First time:
All subsequent times:
As you can see, the plus/minus icon is showing minus in both cases to indicated the section is expanded. When the last item is not showing, clicking on the +/- icon once to contract and once to expand causes the last item to be correctly shown.
Note when I close the dialog box, I do not destroy it but just reshow it. However immediately before calling ShowWindow on the dialog I call the InitPropertyGrid() (called by UpdateToCurrentSettings) method.
if(optionsDialog_ == NULL)
{
optionsDialog_ = new OptionsDialog(settings_, this);
optionsDialog_->Create(OptionsDialog::IDD, this);
}
optionsDialog_->UpdateToCurrentSettings();
optionsDialog_->ShowWindow(SW_SHOW);
I found I can eliminate this problem simply by calling myPropertyGrid.ExpandAll(TRUE) at the end of the code where I initialize the property grid (InitPropertyGrid() for me). This seems to force all the properties to expand.
Okay, so I will admit I have no knowledge of windows API or even MFC.
I've got an error window popping up when things go hairy (illegal character in a filename string) and I want the error box to be modal.
For the life of me I can't figure out why it crashes when it hits doModal.
Here is the code where I think this can be fixed. This code is in the event handler of a button in the main window.
CDialog *BadFileD = new CDialog();
BadFileD->Create(IDD_STATUS, this);
BadFileD->DoModal();
Am I just being borderline retarded?
MFC dialog divides two pattern, modal dialog and modeless dialog.
(1) Modal dialog usage:
CDialog dlg;
dlg.DoModal();
(2) Modeless dialog usage:
CMyDialog *pDlg = new CMyDialog();
pDlg->Create(ID_DLG, this);
pDlg->ShowWindows(SW_SHOW);
As you can see, we need a new pointer, but do not delete it. So, you need to do the following in our CMyDialog class:
Add DestroyWindow() method in OnOk() and OnCancel().
Add "delete this;" in PostNcDestroy() method.
If you do not, your code may cause a memory leak. BadFileD is a class member, and you delete it in destructor. I suggest use Modeless dialog.
For display modal dialog you should use DoModal method only
CDialog *BadFileD = new CDialog(IDD_STATUS, this);
BadFileD->DoModal();
You can read remarks from article
If you desire to just display an error message, it may be that rather than creating your own dialog you can just use AfxMessageBox(). See Microsoft Developer Network - AfxMessageBox.
If you want to do your own dialog box typically with an MFC project you would normally:
create a dialog box template using the resource editor
create a class encapsulating the dialog box with the class wizard implementing the behavior desired
insert the code to create and display the dialog box into the appropriate place
However with a simple dialog box that requires no supporting class for complex behavior you can skip the step of creating the encapsulating class with the class wizard and just use CDialog directly.
One question that needs to be answered is the lifetime of the dialog as well as whether it is to be modal or modeless. A modal dialog box requires the user to do something for the application to continue past the modal dialog box. A modeless dialog box does not block the application the way a modal dialog box does. There is also a system modal dialog box style.
Since you say it will be a modal dialog then the lifetime will be short so the entire construction, display, and destruction will probably be in a series of lines of code. For instance in a CView class with a command handler displaying a modal dialog box you might have:
void CViewThing::OnCommandMenuItem ()
{
CDialog BadFileD(IDD_STATUS);
int iRetStatus = BadFileD.DoModal();
// check for status such as IDOK, etc.
// do whatever is necessary.
}
What the above does is create a dialog box using the dialog resource template IDD_STATUS and displays it as a modal dialog box. Since it is local object, when the variable BadFileD goes out of scope, the dialog box destructor will be triggered and resources cleaned up for you.
You can also have a modeless dialog box. In the case of a modeless dialog box you need to consider the variable lifetime because as soon as the variable goes out of scope, the destructor will trigger and the dialog box will disappear.
So for a modeless dialog box being used with some view class, perhaps providing a tool box of some kind, the CDialog variable will be a member of the CView class which is using it. After the modeless dialog box is created, it is displayed or not by using the ShowWindow() member function of the CDialog class (actually a member of the CWnd class from which CDialog is derived).
void CViewThing::OnCommandMenuItem ()
{
BadFileD.Create(IDD_STATUS, this);
BadFileD.ShowWindow(SW_SHOW); // display the dialog
}
and in the CViewThing class you would have a member variable CDialog BadFileD;.
Additional considerations
In all of the above examples we are not using pointers so that when the CDialog variable goes out of scope, either from exiting a member function or when the object using the dialog box is destroyed then the dialog box is as well. This object management is done for us.
One thing that you must take into consideration with a modeless dialog box is how to destroy it when you no longer need it.
Since a modal dialog box is usually a short term object, often created as a local variable on the stack, you normally just let it go out of scope to take care of everything dealing with destruction.
However the lifetime of a modeless dialog box requires that the DestroyWindow() method be used to destroy the dialog box when it is no longer needed. See Microsoft Developer Network - Destroying the Dialog Box.
A third usage scenario - embedding a dialog box
There is a third usage of a dialog box that sometimes comes in handy, embedding the dialog box into another window as a control.
In the above examples, the dialog box template specifies the WS_POPUP style for the dialog which is the standard style for a dialog box since the normal way that a dialog box is used is to display as a separate window.
However if you change the WS_POPUP style to WS_CHILD you can then embed the dialog box into another window as a control. You can remove the other style settings such as WS_SYSMENU, DS_MODALFRAME, and WS_CAPTION and remove the CAPTION line from the dialog template to further change the dialog box. So you will end up with something like:
IDD_STATUS DIALOGEX 0, 0, 435, 266
STYLE DS_SETFONT | WS_CHILD
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
LTEXT "this is some static text to display on this dialog.",IDC_STATIC,81,63,200,32
END
Then just use the resulting dialog box similar to how you would a modeless dialog box with ShowWindow().
If you need to reposition the embedded dialog box within its container window, you can use the SetWindowPos() method to do so. For instance the following would move the dialog box window within its containing window to be 20 pixels from the left and 10 pixels from the top of the containing window.
BadFileD.SetWindowPos(NULL, 20, 10, 0, 0, SWP_NOOWNERZORDER | SWP_NOSIZE | SWP_NOZORDER)
I have a dialog box (CDialog) with owner-drawn CTabCtrl in it. Tabs content are child dialogs (one for each tab). There is an CEdit in each tab. When the user clicks a tab, I'm hiding all child dialogs using ShowWindow(SW_HIDE) and showing a selected one.
The problem is that when I have, for example, two tabs, click inside an edit box in the first tab and then switch to second, input focus stays on that (invisible) edit box in the first tab no matter what I do in my code (tried calling all methods that potentially can set focus, nothing changed).
Try this:
GetDlgItem(IDC_YOURCONTROL)->SetFocus();
Or the related variable linked with the control:
m_YOURCONTROLControl.SetFocus();