MFC - Creating a child view of CSplitterWnd fails - c++

Good evening,
I am stuck on a really strange problem.
I have a CSplitterWnd object and I want to add two child pane to it.
The two pane are from two subclass of the CFormView class.
I add them with the following code :
(Please note that this code is called in the OnCreateClient function of the CMainFrame class)
BOOL result = SplitWindow.CreateStatic(this, 1, 2);
if (!result)
return FALSE;
CRect cRect;
GetClientRect(&cRect);
int iHeight = theApp.GetProfile().ImageHeight ? theApp.GetProfile().ImageWidth : cRect.Height() / 2;
CSize szSize1(cRect.Width() / 2, iHeight);
if (!SplitWindow.CreateView(0, 0, RUNTIME_CLASS(CView1), szSize1, pContext))
{
SplitWindow.DestroyWindow();
return FALSE;
}
CSize szSize2(cRect.Width() / 2, iHeight);
if (!SplitWindow.CreateView(0, 1, RUNTIME_CLASS(CView2), szSize2, pContext))
{
SplitWindow.DestroyWindow();
return FALSE;
}
The CView1 and CView2 class are two class derived from a class CBaseView class which itself is a child of CFormView (I use only one dialog definition in CBaseView).
The problem I am facing is that the call to "CreateView" always returns FALSE.
I went inside the MFC code and found out a strange thing that is shown in the following image :
In the CreateView function of CSplitterWnd class, I see that a check to the pWnd pointer address is failing despite the pointer not being NULL.
I am using debug compilation with all optimizations disabled so what I see in the debugger should be true, right ?
Is there someone that would know about this ?
My main purpose is to be able to create those sub-views.
I have to add that at first it was working, then I added the class CBaseView and derivated the CView1 and CView2 from CBaseView.
When it was working CView1 and CView2 were at that time children of CFormView.
I tried to go back to this configuration, but I saw exactly the same problem as I have now.
Hope someone could help.
Thanks in advance.

I found the solution.
It was due to the resource dialog linked to the CBaseView.
It contains MFCRangeSlider resource that was not yet handled. Deleting this resource or handling it properly is enough to succeed in creating the child views.

Related

How to get CDocument for derived CMFCShellTreeCtl on an Outlook Style MFC program?

I am new to MFC and have built an "outlook" style MFC app using the wizard. I've extended the CMFCShellTreetCtrl using CMyShellTreeCtrl and had data member variables and all was working fine. Now I want to move the data over to the CDocument class. Since there is several accesses to the data as each item is clicked or enumerated, I thought I would create a member variable m_pDoc to access the public variables in the CDocument. The problem I'm having, I can't find where to get the CDocument as it appears it's not setup when the trees OnCreate is called. That is in OnCreate
CWnd* pWndMain = AfxGetMainWnd();
ASSERT(pWndMain);
ASSERT(pWndMain->IsKindOf(RUNTIME_CLASS(CFrameWnd)) && !pWndMain->IsKindOf(RUNTIME_CLASS(CMDIFrameWnd))); // Not an SDI app.
m_pDoc = (CMyDoc*) ((CFrameWnd*)pWndMain)->GetActiveDocument();
returns NULL in m_pDoc and if I tried in an AfterCreate() (which is called after CreateOutlookBar) it's too late as m_pDoc is already being used and get a crash.
// Create and setup "Outlook" navigation bar:
if (!CreateOutlookBar(m_wndNavigationBar, ID_VIEW_NAVIGATION, m_wndTree, m_wndCalendar, 250))
{
TRACE0("Failed to create navigation pane\n");
return -1; // fail to create
}
m_wndTree.AfterCreate();
Any Ideas?
TIA!!
I came up with the following but I'm not sure if this is the proper way. Again, I'm new to MFC (I always have used Win32 directly). This works so it's AN answer, but is it THE correct answer?
POSITION docpos = AfxGetApp()->GetFirstDocTemplatePosition();
CDocTemplate *doctemplate = AfxGetApp()->GetNextDocTemplate(docpos);
docpos=doctemplate->GetFirstDocPosition();
m_pDoc = (CMyDoc*)doctemplate->GetNextDoc(docpos);
ASSERT(m_pDoc);
ASSERT(m_pDoc->IsKindOf(RUNTIME_CLASS(CMyDoc)));
TIA!!

OnInitialUpdate called twice

One of our MFC legacy applications shows a behavior I do not really understand right now. I am not an MFC expert, however. The symptom that I observe and which is causing problems is that OnInitialUpdate of the views is called twice by the framework during app start-up. As I understand the documentation it should be called only once.
My investigation revealed that the app calls the following code in its InitInstance:
CMainFrame *const pMainFrame = new CMainFrame;
if (!pMainFrame->LoadFrame(IDR_MAINFRAME)) { return FALSE; }
m_pMainWnd = pMainFrame;
This causes CFrameWnd::LoadFrame being called inside the framework. From here both calls to OnInitialUpdate originate. The first when Create is called and the second when SendMessageToDescendants is called to send WM_INITIALUPDATE.
Our app overloads CMainFrame::OnCreateClient to create a splitter window:
Splitter.CreateStatic(this, 2, 2);
Splitter.CreateView(0, 0, RUNTIME_CLASS(MyView),CSize(0,0), pContext);
// ...
The call to Splitter.CreateView is what triggers OnInitialUpdate to be called the first time. But as I could see from the documentation this implementation is as intended. So unfortunately this is where I get stuck. I don't understand if the OnInitialUpdate call from SendMessageToDescendants(WM_INITIALUPDATE, 0, 0, TRUE, TRUE) or from CSplitterWnd::CreateView is wrong.
What else could I take a look at to figure out the root cause of this problem?
Is it possible for OnInitialUpdate to be called twice and we need to make sure that our app can cope with this?
If the answer to question 2 is yes: What is the best way to archive this?
EDIT
Further investigation has shown that CSplitterWnd::CreateView only sends WM_INITIALUPDATE if the passed CCreateContext is a nullptr. So I checked CMainFrame::OnCreateClient and it is indeed called by the framework with pContext being nullptr.
EDIT2
As requested in the comments here is the code adding the DocTemplate:
CSingleDocTemplate *const pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CMenuDoc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CMenuView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
There is also a call
Splitter.CreateView(1, 1, RUNTIME_CLASS(CMenuView),CSize(0,0), pContext);
in the CMainFrame::OnCreateClient. If I understand the comments correctly this is wrong?
The two calls of OnInitialUpdate are for the same instances of the views. If I followed the stack trace correctly the reason for the CCreateContext being a nullptr is that no context is passed to LoadFrame. The context passed to CFrameWnd::LoadFrame is just forwarded to CMainFrame::OnCreateClient. But what context should I pass?

CWindowImpl - Create() returns error code 1406

I just tried to setup a small win32 project, and was just about to create a window.
I created a dialog in the resource designer and set up this class for my window:
#pragma once
#include "stdafx.h"
class TTTMainDialog : public CWindowImpl<TTTMainDialog>
{
public:
DECLARE_WND_CLASS(_T("TTTDlg"))
enum { IDD = IDD_TTT_DIALOG };
BEGIN_MSG_MAP_EX(MusicPlayerDialog)
MSG_WM_INITDIALOG(OnInitDialog);
MSG_WM_CLOSE(OnClose);
MSG_WM_DESTROY(OnDestroy);
END_MSG_MAP()
TTTMainDialog();
~TTTMainDialog();
private:
const BOOL OnInitDialog(const CWindow wndFocus, const LPARAM lInitParam);
void OnClose();
void OnDestroy();
};
As you can see, I added the window class declaration, I inherited CWindowImpl, I registered the dialog. I don't think I forgot something here.
In the class which is supposed to create the dialog, I tried to create it like this:
TTTMainDialog myDialog;
HWND handle = myDialog.Create(NULL);
myDialog.ShowWindow(nCmdShow);
However, the Create method does return NULL all the time. I checked the error code with GetLastError(), and it turns out i am getting error code 1406, or "ERROR_TLW_WITH_WSCHILD".
The msdn documentation says the following about this error:
"Cannot create a top-level child window."
I tried to google up on this, but there is not much to find.
If I had to take a guess I would say the problem is caused by some window class name details, but i'm really not sure.
Any advice?
You are trying to build a window class from wrong pieces.
The error is pretty descriptive: you are trying to create a parentless window with a WS_CHILD style and this does not work out.
You get the child style from default template parameter: CWindowImpl -> CWindowImplBaseT -> TWinTraits -> CControlWinTraits. CControlWinTraits is supposed for use with child control windows.
If you are going to use a dialog template (IDD_TTT_DIALOG) then the proper base class is CDialogImpl, which is already prepared to use proper window styles. Also, it has what it takes to create both modal and modeless dialogs. The latter act more like windows and are non-blocking but in the same time take dialog template resource with predefined controls.
Small example for CDialogImpl use
Or, another one

Weird bug in Qt application

In my application, I have my re-implemented QGraphicsView checking for a mouseReleaseEvent(), and then telling the item at the position the mouse is at to handle the event.
The QGraphicsItem for my view is made up of two other QGraphicsItems, and I check which one of the two is being clicked on (or rather having the button released on), and handle the respective events.
In my Widget's constructor, I set one of the items as selected by default, using the same methods I used when the items detect a release.
When I debugged, I found that for the LabelItem, select is called without a problem from the constructor (and the result is clear when I first start the application). But, when I click on the items, the application terminates. I saw that I was getting into the select function, but not leaving it. So the problem is here.
Which is very weird, because the select function is just a single line setter.
void LabelItem::select()
{
selected = true;
}
This is the mouseReleaseEvent;
void LayerView::mouseReleaseEvent(QMouseEvent *event)
{
LayerItem *l;
if(event->button() == Qt::LeftButton)
{
l = (LayerItem *) itemAt(event->pos());
if(l->inLabel(event->pos()))
{ //No problem upto this point, if label is clicked on
l->setSelection(true); //in setSelection, I call select() or unselect() of LabelItem,
//which is a child of LayerItem, and the problem is there.
//In the constructor for my main widget, I use setSelection
//for the bottom most LayerItem, and have no issues.
emit selected(l->getId());
}
else if(l->inCheckBox(event->pos()))
{
bool t = l->toggleCheckState();
emit toggled(l->getId(), t);
}
}
}
When I commented the line out in the function, I had no errors. I have not debugged for the other QGraphicsItem, CheckBoxItem, but the application terminates for its events as well. I think the problem might be related, so I'm concentrating on select, for now.
I have absolutely no clue as to what could have caused this and why this is happening. From my past experience, I'm pretty sure it's something simple which I'm stupidly not thinking of, but I can't figure out what.
Help would really be appreciated.
If the LabelItem is on top of the LayerItem, itemAt will most likely return the LabelItem because it is the topmost item under the mouse. Unless the LabelItem is set to not accept any mouse button with l->setAcceptedMouseButtons(0).
Try to use qgraphicsitem_cast to test the type of the item. Each derived class must redefine QGraphicsItem::type() to return a distinct value for the cast function to be able to identify the type.
You also could handle the clicks in the items themselves by redefining their QGraphicsItem::mouseReleaseEvent() method, it would remove the need for the evil cast, but you have to remove the function LayerView::mouseReleaseEvent() or at least recall the base class implementation, QGraphicsView::mouseReleaseEvent(), to allow the item(s) to receive the event.
I have seen these odd behaviours: It was mostly binary incompatibility - the c++ side looks correct, and the crash just does not make sense. As you stated: In your code the "selected" variable cannot be the cause. Do you might have changed the declaration and forgot the recompile all linked objects. Just clean and recompile all object files. Worked for me in 99% of the cases.

MFC: Showing / Hiding Splitter Panes

In my application I have a number of panes from m_wndspliter classes. What I want to do is at run time show and hide one of these panes. Whilst with the following code I can show and hide the view associated with the pane, I can't temporarily remove the pane itself.
CWnd * pCurView = m_wndSplitter2.GetPane(2, 0);
if( !pCurView == NULL )
{
if( fShow )
{
pCurView->ShowWindow(SW_SHOW);
RecalcLayout();
}
else
{
pCurView->ShowWindow(SW_HIDE);
RecalcLayout();
}
}
Any examples / ideas ?
You need to call CSplitterWnd::DeleteView to do this, which basically means that you have to save your CView elsewhere if you intend to restore it. Usually this is not a problem as all data should be stored in the CDocument rather than CView, but in practice this may not be the case.
The way I have handled this in the past is to have a copy constructor for my CView classes so I could easily store them in temporary variables.
Does this help?
http://www.codeguru.com/cpp/w-d/splitter/article.php/c1543
I have used something very similar myself,
Only the CExtSplitter class from the CodeProject article https://www.codeproject.com/Articles/2707/A-Static-Splitter-with-the-Ability-to-Hide-Show-Mu worked for me.
This is still VC6 code but it worked with minor adaptions.