converting a CView to a CFormView - c++

I have a CView and I've been painting stuff on it just fine. Then I realized I needed to add some controls like text boxes and combo boxes to my CView. So I am trying to convert my CView into a CFormView which does not have a default constructor. But I need a default constructor for this line IMPLEMENT_DYNCREATE(CMyView, CFormView) so I have created a default constructor like this CMyView::CMyView():CFormView( ( UINT )666 ) { ... }. That 666 is because I don't know which argument I am supposed to pass there. I am guessing that I need to pass the ID of my CMyView class. I can't find the id of my CMyView class which was originally created by the Visual Studio project wizard automatically. Where should I look for it? When I run the program I get this error: First-chance exception at 0x75AEC41F in myapp.exe: Microsoft C++ exception: CInvalidArgException at memory location 0x003CF134.
Critical error detected c0000374
myapp.exe has triggered a breakpoint.
And it stops at line 51 in free.c
So my question is: how can I fix this? Also I want to keep the stuff that I previously painted in my former CView, now CFormView. Is the CFormView able to paint like the CView? If not, should I use a split pane in my CMainFrame and have a CView and a CFormView? I might have used terms specific to Java swing and I apologize for that. I am new to MFC and C++.
Thank you in advance,
Corneliu

The CFormView constructor needs the ID of the form's dialog template to be passed in. That is the template you create in the visual editor. You can see how this works by creating a little test project with a CFormView to make your declarations look like the MFC declarations in the test project.
The CFormView can be painted like a CView (in OnDraw), but you might have undesired effects on the controls if you do any scaling or scrolling of the view.
Other alternatives for mixing controls with painted output are (1) Using CControlBar to put controls on the edge of the view or (2) Put a CStatic on the CFormView and do your painting in the CStatic.

Check out Resources.rc.
You can try adding something like this:
IDD_DIALOG1 DIALOG 0, 0, 400, 400
STYLE DS_SETFONT | WS_CHILD
FONT 8, "MS Sans Serif"
BEGIN
END

Related

MFC dialog-based app with tab control - Visual Studio bugs/restrictions make recommended path impossible?

Create a dialog-based application in Visual Studio 2019. Insert a new dialog in the resources. Place a control (or two) on that dialog that you will later (try to) hook up to a variable (e.g. an edit box to enter your name).
Prepare to add that control variable by creating a class for this second dialog. If you inherit from CDialogEx (or perhaps other classes too - not tested), you can go on to add a variable for the control you added - easy, normal.
But if you had the "tab control" context from the title above in your mind, and chose to inherit from CMFCPropertyPage instead, can you add a variable subsequently? No you cannot - the class added makes no reference to the ID of the dialog resource, and so the Add Variable process has no basis to find a class to add the variable to.
I think this is a bug in Visual Studio... but I'm not 100% sure because I have always found the documentation around property sheets and property pages somewhat confusing. Specifically:
Do the classes implementing the tabs contents on a tab control "contain" the controls "on" the tab... or is the tab control really just a way to provide visual cues to show/hide sets of controls, and all of those controls and associated variables reside in one class?
I believe it's intended to be the former, but I can imagine that one uses a tab control because there are strong similarities between tab contents, and therefore potential benefits in implementing all the control variables in one place so as to avoid duplication. I just wish it was stated explicitly somewhere what the intention was.
Wider context: I'm trying to implement a dialog-based app with a tab control dominating that dialog. MS documentation says to implement a tab control using CPropertySheet and CPropertyPage to implement the tabbed dialog and tab contents. However, there is no (direct) way to create a dialog-based application whose main dialog inherits from CPropertySheet. When you look for examples of tab controls at the application level, you readily find things that deviate from the documented path considerably - using neither CPropertySheet nor CPropertyPage and using the WM_LBUTTONDOWN event instead of TCN_SELCHANGE, both without obvious reasons.
Any tips on (a) how to repair the apparent Visual Studio bug and/or (b) how to inherit from CPropertySheet for my application dialog and/or (c) where to find a clearer and more conventional example of tab control use at the top level would be greatly appreciated.
Given that there appears to be no clear and consistent way of approaching this, if (like me) you think "how hard can it be?", you might naively set out to see if you can create a new dialog-based application based on CDialog and manually convert it to CPropertyPage. You might fare better; I quickly hit a weird problem with the app compiling but not running - it could not load the window caption from the resource ID, despite the caption literally being there in black and white. Due to the weirdness, this was a red flag, and so to my mind problem (b) seems to be not worth the effort.
Without a CPropertySheet at the top level, there is no point having any kind of CPropertyPage or CMFCPropertyPage involved, making problem (a) pointless. That said, if you manually convert from (default) CDialogEx to CPropertyPage for your tab classes, it seems to have no problem compiling and running - the tabs just don't have any functionality that integrates with the main dialog.
Instead, I can now recommend working through the details of the video example even though it does strange things at first glance. You can achieve the desired result in more or less the same way as follows:
Use plain CDialogEx throughout, and so provide all the tab switching/showing/hiding/control work yourself (which the example demonstrates).
Create your main dialog and all tabs in the resource editor, adding the CDialog-based classes from there.
If you create your tab dialog resources via Insert Dialog (generic) instead of Add Resource (uses specific templates) then you will have to manually set certain dialog properties for each tab:
Border: Thin (or I preferred None)
Style: Child
System Menu: false
Title Bar: false
You can simplify the repositioning of the tabs (see below; it barely justifies the separate SetRectangle() function)
You should (probably) drive the tab changing from the TCN_SELCHANGE event instead of WM_LBUTTONDOWN
As noted, repositioning the tabs can be simplified to something like:
CRect tabRect;
m_tabControl.GetWindowRect( &tabRect );
for ( int i = 0; i < m_totalTabs; i++ )
{
m_pTabPage[ i ]->SetWindowPos( &wndTop, tabRect.left, tabRect.top, 0, 0,
i == m_currentTab? SWP_NOSIZE|SWP_SHOWWINDOW : SWP_NOSIZE|SWP_HIDEWINDOW );
}
Additional calls to ShowWindow() for each tab shown in the example are redundant.
One time, I had made on application as such. I don't remember if I used the Visual Studio new project wizard. Most probably, I did. But I will assist you on how to do it if you can not reach how to do it via the assistant.
The essential things:
Define the main dialog in the resources and the respective class in its H and CPP files.
In the YourAppClass::InitInstance you will need to have something like:
YourMainDlgClass dlg;
m_pMainWnd = &dlg;
INT_PTR 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
}
else if (nResponse == -1)
{
TRACE(traceAppMsg, 0, "Warning: dialog creation failed, so application is terminating unexpectedly.\n");
TRACE(traceAppMsg, 0, "Warning: if you are using MFC controls on the dialog, you cannot #define _AFX_NO_MFC_CONTROLS_IN_DIALOGS.\n");
}
Insert a CMFCTabCtrl element in your dialog for tabs, instead of using a CPropertySheet.

"Debug Assertion Failed" when MFC button control EnableWindow method is used in class constructor

I am using Visual Studio 2019 and building a C++ desktop application. I have a dialog class inherited from CDialogEx. In this class I have a few buttons, i.e. CButton as members.
When I use the EnableWindow method in the class constructor, it reports:
Debug Assertion Failed! Program C:\WINDOWS\SYSTEM32\mfc140ud.dll File:
d....\winocc.cpp line 345
If I commented it out the application worked fine. Any idea why I have this error? All I wanted to do is to disable the buttons as soon as the dialog shows up - that is why I use this method in the constructor.
There is a difference between a C++ class and the actual creation of the dialog window and its controls.
The constructor is for class related items, not window related items. For example, if you want to initialize member variables, then the constructor would be the place for it. But at construction, no dialog window has been created, thus there are no child controls that exist, thus the error you're seeing.
The place where you can assume that the window is created is in the dialog's OnInitDialog member function. This is where you should be able to call EnableWindow on the controls.
If you do not have an OnInitDialog, you can add that function using the class wizard.

MFC: Emdedded child dialog is not showing up within parent dialog

I came across a tutorial showing how to embed a child dialog within a parent dialog using MFC. I am using Visual Studio 2015. My setup is as follows. Using the Visual Studio MFC Application Wizard to create a new MFC Visual C++ Project called MFCApplication3, I select a Dialog based application where MFC is used in a Shared DLL. Using boilerplate code, I have a simple Thick Frame Dialog, no maximize or minimize box.
In my resource view, I go to my Dialog editor to edit the main dialog. I add a picture control with a blank area in the center and name it IDC_STATIC. This will simply be used as a placeholder for my child dialog that I wish to embed. It looks like:
Still in the resource view, I create a new Dialog. I call it IDD_CHILD. I add some components. It looks like this:
Now back in the Solution Explorer, I add a class using the Add Class wizard, selecting to add an MFC Class. The class name is CChildDialog, with a base class of CDialog, and I use the already generated IDD_CHILD as the Dialog ID. It generates the .cpp and associated .h file. In the constructor of CChildDialog, I add a call to the Create function so the constructor becomes:
CChildDialog::CChildDialog(CWnd* pParent /*=NULL*/)
: CDialog(IDD_CHILD, pParent)
{
Create(IDD_CHILD, pParent);
}
Now I modify the dialog code generated automatically when I created the project. In CMFCApplication3Dlg.h, I add a private member of type CChildDialog* called m_childDlg, and #include the associated header file. In CMFCApplication3Dlg.cpp, I add this to the OnInitDialog function prior to the return statement:
CRect rc;
GetDlgItem(IDC_STATIC)->GetWindowRect(rc);
ScreenToClient(&rc);
m_childDlg = new CChildDialog(this);
m_childDlg->MoveWindow(rc);
Now I build the solution, run it, but it looks like it does in the first picture. A blank placeholder spot for a child dialog, but no child dialog. What could I be doing wrong?
It turns out (while composing this question) that the answer to my problem was two properties I need to set while in the resource view. When I have the child dialog open (IDD_CHILD), within the properties pane, I need to set the following properties:
Style: Child
Visible: TRUE
(I am not sure why Visible defaults to FALSE in this case). Making those two changes, voila! I get my embedded dialog:

Tab Order with CTabCtrl and child CFormViews

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().

MFC without document/view architecture

I'd like some help on using MFC without the document/view architecture.
I created a project without doc/view support, Visual C++ created a CFrameWnd and a view that inherits from CWnd. I replaced the view inheriting from CWnd with a new view that inherits from CFormView.
However, when I run my program, after I close the window I get a heap corruption error.
If inside where the frame window handles WM_CREATE, you change the code to create the instance of CFormView with the "magic" id of AFX_IDW_PANE_FIRST, you'll find it becomes the view for the frame window. This is due to the behaviour of CFrameWnd::InitialUpdateFrame(), which will be called from within MFC. The MSDN page comments on this helpful little feature:
http://msdn.microsoft.com/en-us/library/ch3t7308.aspx
Since you want to use the dialog editor and you don't want the document/view architecture, then maybe a "Dialog based" application is what you need.
The problem is MFC's lifecycle management. The view declaration (created by Visual C++ wizard) is:
CChildView m_wndView;
I replaced the above code with:
CChildFormView m_wndView;
CChildView inherits from CWnd, CChildFormView inherits from CFormView. Both views were created by the wizard, but only CChildFormView uses the DECLARE_DYNCREATE/IMPLEMENT_DYNCREATE macros.
Since m_wndView is being created in the stack, when MFC automagically calls delete I get the error.