views:

337

answers:

4

Hi, I was previously using a CSplitterWnd in a MFC application, using it's CreateView function. Everything was working fine but now I would like to pass a parameter to the constructor of my views, so I cannot use MFC dynamic object creation (DECLARE_DYNCREATE and IMPLEMENT_DYNCREATE) because they require an empty constructor.

After searching a little on the internet I found an exemple that looks like this:

m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CMyView), CSize(0,0), pContext);
m_wndSplitter.CreateView(0,1,RUNTIME_CLASS(CMyView),  CSize(0,0), pContext);
m_pView0=(CMyView *)m_wndSplitter.GetPane(0,0);
m_pView1=(CMyView *)m_wndSplitter.GetPane(0,1);

This could be a workaround (i.e.: create a new function in CMyView letting me specify what I want) but this would be ugly and error prone. Anyone know if there is another way I could do this?

Edit: Adding more details after ee's answer:

Your right that the initialize method would work but this force me to remember to call that initialize method, but like you pointed out I will probably not create these views many times so that should be ok. Another thing I would maybe like is to manage the lifetime of the view myself so again this is not possible using CreateView.

Thanks

A: 

I don't think there's any way to just hand a view pointer to the splitter window. Instead you will need to derive a class from CWplitterWnd and override the CreateView virtual method.

The default method does something like pClass->CreateObject(), but your version can create the object however you want. You will need to take care of any other details handled by that method, however, since you will not be able to call the default implementation.

Tim Sylvester
+1  A: 

When you say it would be ugly and error prone, do you mean that the creation of your view will happen many times in many places? If so, then I would partially agree with you.

However, if you just have two cases wherein you create view on app startup, then "ugly" and "error prone" boils down to two additional lines:

m_wndSplitter.CreateView(0,0,RUNTIME_CLASS(CMyView), CSize(0,0), pContext);
m_wndSplitter.CreateView(0,1,RUNTIME_CLASS(CMyView),  CSize(0,0), pContext);
m_pView0=(CMyView *)m_wndSplitter.GetPane(0,0);
m_pView1=(CMyView *)m_wndSplitter.GetPane(0,1);
//additional stuff
m_pView0->Initialize(v1, v2, v3);
m_pView1->Initialize(v4, v5, v6);

That doesn't seem so bad to me. Perhaps there is a specific situation you are trying to avoid?

ee
Added some more info but you have a good point I don't think I will create these views many time. Thanks.
n1ck
Voting you up since, although it did not exactly solve my needs, in other case it might be the best answers. Good point of creating the views only once. I hadn't though about it.
n1ck
Glad to be of assistance, and thanks for the upvote.
ee
+1  A: 

I haven't tried myself but I think something like this should work:

CMyView *pView = new CMyView( PARAM );

splitter.CreateView(    0, 
          0, 
          pView->GetRuntimeClass(),
          size,  
          0);

Obviously you still need to use DECLARE_BYNCREATE in your view (CMyView) and you will need to provide a default constructor but you will be able to use a parameterized constructor.

Javier De Pedro
I did not try it yet but I don't think it will work. The CreateView function call pClass->CreateObject() which will call m_pfnCreateObject from the RuntimeClass struct, creating a new object. Though I could probably create a new function that return the newly create CMyView* instead of creating a new one and override the runtime class m_pfnCreateObject parameter. I think this should work. I'll try it and post the answer if it work. Thanks.
n1ck
Voting you up since, although your answer is not correct, you got me on the right path to solve my problem, Thanks.
n1ck
A: 

After checking Javier De Pedro's answer I though I could override the creation function so I did (semi-pseudo-code):

class ObjGetter
{
    static CObject* obj;
public:
    ObjGetter(CObject* obj_){obj = obj_;}
    static CObject* __stdcall getObj() { return obj; }
};

CObject* ObjGetter::obj = NULL;

BOOL CMyFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext) 
{
//...
  myView = new CMyView(NULL);
  CRuntimeClass rt(*myView->GetRuntimeClass());
  ObjGetter objGetter(myView);
  rt.m_pfnCreateObject = &ObjGetter::getObj;

  m_wndSplitter.CreateView(0,0, &rt, CSize(0,0), pContext);
}

Now this work but there is the problem that it will destroy my class when closing and I said I would maybe want to track memory myself so I overloaded PostNcDestroy in CMyView to do nothing instead of calling delete this:

CMyView::PostNcDestroy(){}

Now it should prevent it from getting deleted but now it crash when exiting so I overriden CMyFrame::OnClose like this:

void CMyFrame::OnClose()
{
   m_wndSplitter.DeleteView(0, 0);
   delete myView; myView = NULL; //seems to be needed to be deleted before 
                                 //CFrameWnd::OnClose or it crash
   CFrameWnd::OnClose();
}

Now theorically I should be able to keep the myView pointer elsewhere as long as I delete it before the document exit.

Thanks for your help guys.

n1ck