Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations strongm on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Calling a new document

Status
Not open for further replies.

Huskey

Vendor
Aug 23, 2002
53
GB
Hi,

Any idea how to call a document from a CDocument? For example, my application is a MDI. I have three main application class:
CtestApp
CtestDoc
CtestView

My program is using Tree Ctrl. So, in my Ctestview, I need to click on the item on the Tree to invoke a new document. How to do that???

Please help! Many thanks!
 
I'm not sure exactly what you mean but I'm assuming you need to use windows messaging functions.

Perhaps, you could send a message to the main frame which then relays the message and creates a new document.

You would use the function PostMessage() to achieve this. You past in a pointer to the window you want to send the message to along with the actual message and wParam and lParam fields.

If I was to send a message to the main frame I would first get a pointer to the main frame window by calling AfxGetMainWnd(). I would pass this pointer to PostMessage() along with a message (eg. WM_USER). The wParam and lParam fields can be anything you want to help process the message.

I would catch the message in the MainFrame code (PreTranslateMessage() or whatever) and then do things from there.
 
Hi can you please explain me in more detail perhaps with a sample code...

In my code, double click on the item of the tree will run this void function: OnDblClickTree1 and then invoke the new document class. The sample of my code:

void CTestView::OnDblClickTree1(NMHDR* pNMHDR, LRESULT* pResult)
{
HTREEITEM htiTmp;
CMenu menu;
CMenu *pop_up_menu;
CPoint point;
UINT nFlagsHit;
TV_HITTESTINFO tvhittest;

GetCursorPos(&point);
ScreenToClient(&point);
HTREEITEM hItem = m_secTree.HitTest(point, &nFlagsHit);
int iIndex;
CString hTextImport;
iIndex = m_secTree.ItemIndex(hItem);
hTextImport = m_secTree.GetItemText(hItem);
HTREEITEM hParentt = m_secTree.GetParentItem(hItem);

if (hParentt == htiSpcs1)
{
This is the part I would like to invoke the New Document Class. If I used:

CGRIDDOC* pDoc = GetDocument();

It will crash with my existing Document class which is CTestDoc.
}

Please help... Deeply appreciate that!
}

 
OK... let's assume you have processed and caught the selected item in the tree. First get a pointer to the main frame window:

CWnd* pMainFrameWnd = ::AfxGetMainWnd();

Then, pass that pointer into the ::postMessage() function to pass a message onto your main frame code:

::postMessage(pMainFrameWnd,WM_USER,NULL,NULL);

Now, use class wizard to add a PreTranslateMessage() function handler for your CMainFrame code:

BOOL CMainFrame::preTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class

// ok, let's catch the message we sent

if (pMsg->message == WM_USER)
{
// do something here
}

return CMDIFrameWnd::preTranslateMessage(pMsg);
}


You can further expand on this messaging by using the wParam and lParam parts of the message. For example, you could give the wParam/lParam parts specific numeric codes which define different actions to take when the message is intercepted in your main frame code.

#define kDoThis 1234
#define kDoThat 1235

::postMessage(pMainFrameWnd,WM_USER,kDoThis,NULL);

Then, in your main frame code, you can do this or that depending on the value of the wParam part of the message:

BOOL CMainFrame::preTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class

// ok, let's catch the message we sent

if (pMsg->message == WM_USER && pMsg->wParam == kDoThis)
{
// do this
}
else if (pMsg->message == WM_USER && pMsg->wParam == kDoThat)
{
// do that
}

return CMDIFrameWnd::preTranslateMessage(pMsg);
}



Hope this helps to explain things a bit better.

You will find it easier to do what you want to do if you're passing the message to the main frame code first because the main frame contains all the documents you have open. So, if you want an open document to open another new document, post a message to main frame with a specific code:

#define kOpenNewDoc 12345

::postMessage(pMainFrameWnd,WM_USER,kOpenNewDoc,NULL);

In your main frame code, catch the message and then open a new document:

BOOL CMainFrame::preTranslateMessage(MSG* pMsg)
{
// TODO: Add your specialized code here and/or call the base class

// ok, let's catch the message we sent

if (pMsg->message == WM_USER && pMsg->wParam == kOpenNewDoc)
{
// open a new document
}

return CMDIFrameWnd::preTranslateMessage(pMsg);
}


:)
 
Hi,

Thank you very much for your explanation. I am a bit clear now. I have tried exactly what you have told me... I have a compilation error:
.
.
.
if (hParentt == htiSpcs1)
{
#define kDoThis 1234
CWnd* pMainFrameWnd = ::AfxGetMainWnd();
::postMessage(pMainFrameWnd,WM_USER,NULL,NULL);
}

Compilation Error:
==================
C:\Test\TestView.cpp(1793) : error C2664: 'PostMessageA' : cannot convert parameter 1 from 'class CWnd *' to 'struct HWND__ *'
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
Error executing cl.exe.
Creating browse info file

I am not sure if I should put the ::postMessage under CTestView. Please advise as I am just a fresh programmer.

Thank you.
 
Sorry about that... there's actually two versions of PostMessage you can use. I was working entirely from memory which is why I neglected to use the HWND instead.

Firstly, you can get the HWND of any CWnd* by using:

CWnd* pMF = ::AfxGetMainWnd();
HWND pMFHWND = pMF->m_hWnd;

then post the message:

::postMessage(pMFHWND,WM_USER,NULL,NULL);

However, recall that your view is actually a window (CWnd) object. In that case, you could actually use the CWnd::postMessage() function directly:

PostMessage(WM_USER,NULL,NULL);

Notice the lack of the first parameter. This is because you are actually using the CWnd member function rather than the old API function so it uses the HWND of the calling class - in your case, your calling class is your CTestView.

To cut a long story short, if you want to post the message from your view class, use the CWnd member function. If you want to call it from your document class, use the API function.

Hope this clears it up.
 
Oh, by the way.... your #define statements belong at the top of your header file - you don't normally stick them in your code! :)
 
I screwed up a little on the description of the CWnd::postMessage() function!!

When calling the function from your view, you still need the pointer to the main frame window:

CWnd* pMF = ::AfxGetMainWnd();

you then use that pointer to send the message to the main frame:

pMF->PostMessage(message,wParam,lParam);

Otherwise, if you had excluded the pMF-> part, your view would have simply posted the message to itself!!

:-(
 
Hi again Huskey, in case you're still a bit misty on the subject I've written an FAQ on the subject - you can find it under the heading 'Programming' if you click on the FAQs tab above.

I've also gone into the subject of being able to pass pointers to classes and other objects from window to window by using the PostMessage() function which you may find interesting once you get more into messages.
 
Hi, thank you so much for your replies. I have solved part of my problem with the help of your code. Thank you!

This is the code I am using. Whenever I double click the item on the tree, a message "I am here" appears. I assume that the pointer has passed the required message to CMainFrame. However, I got the problem of opening the new document. This time I want to open the new document class which is base on CXGRIDDOC Class. Could you tell me how to do that? I have been struggling for some time trying to do this. Someone suggested me to use GetDocument(). The compilation is Ok but the new document is not created. Please help if you can.

Many thanks

Best regards,


BOOL CMainFrame::preTranslateMessage(MSG* pMsg)
{
if (pMsg->message==WM_USER)
{
MessageBox("I am here"," ",MB_ICONINFORMATION);
CXGRIDDOC* CXGRIDVIEW::GetDocument();
}
return CMDIFrameWnd::preTranslateMessage(pMsg);
}

 
OK, I'm not entirely sure what you mean but I'm assuming that you want to create a new document in your main frame window FROM the main frame code just as if someone had actually chosen 'New' from the 'File' menu????

If that is the case then I'm quite sure you could accomplish this using the CCommandLineInfo class. Now let me tell you this: I've never actually used this class to make a new document but i have used it to open an exisiting document. I had a dialog (owned by the main frame) which displays a list of files saved by the user. I set it up so that when the user double-clicked an item in the list it opened that document for editing. This is how I did it:

// I have the document's file name and path in
// the iText string

CString iText = "c:\\someDirectory\\someFileName";

// Use a CCommandLineInfo object to open the
// chosen file

CCommandLineInfo cmdInfo;
cmdInfo.m_nShellCommand = CCommandLineInfo::FileOpen;
cmdInfo.m_strFileName = iText;
CWinApp* thisApp = ::AfxGetApp();
if (!thisApp->ProcessShellCommand(cmdInfo))
{
::AfxMessageBox("An error occurred while trying to open the selected file.",
MB_ICONEXCLAMATION);
}

Now I actually think (I may be wrong without trying this out first) that you can do this to create and display a new document:

CCommandLineInfo cmdInfo;
cmdInfo.m_nShellCommand = CCommandLineInfo::FileNew;
CWinApp* thisApp = ::AfxGetApp();
thisApp->ProcessShellCommand(cmdInfo);

Obviously no file name or path needs to be provided and notice the
Code:
::FileNew
instead of the
Code:
::FileOpen

Why don't you give it a try and see if it's what you're looking for.
 
Hi qednick,

Thanks very much. I can open a document after double clicking on the tree item with the code you have suggested. However, the document appears is based on the default CTestDoc. Is there any way to make the document that appears based on the new document class I defined as CXGRIDDOC and CXGRIDVIEW?

My main application:
CTestApp
CTestDoc
CTestView

My new document class:
CXGridDoc
CXGridView

The reason why I am doing this is because I will add the GRID class onto this new document class (CXGRIDDOC and CXGRIDVIEW) so that I can assign values on a per cell basis from the data of the tree item I double clicked.

That's basically what I am trying to do. Perhaps I should brief you more in details about my work. My program first loads the database (e.g. Excel and Access) using ODBC. Then, after selecting to import the specified TABLE appears in the database, the TABLE is loaded and stored in another different VirtualTable Class. Then, I will fill the display grid cell per cell so that it reflects the contents of the VirtualTable object.

If it is not too much trouble, please help me as I am a fresh Visual C++ programmer and this is a bit too high standard for me to accomplish. I deeply appreciate your kindness and advices.

Many thanks

Best regards,
 
Hmmmmm, I'm not sure how you can make the CCommandLineInfo class create a new none-default document type. Perhaps you could store a CXGridDoc as a blank template and then use CCommandLineInfo to open that template instead of creating a new document. For all intents and purposes it will appear to the user that a new and empty CXGridDoc document has appeared for them.

This may not be the ideal solution or the most elegant. But it is certainly an option to consider.
 
Hi GedNick,

It still does not work but I will try what you have suggested me. Thank you for your time and help. I deeply appreciate that.

best regards,

 
If you use CCommandLineInfo to open an empty CXGridDoc document, you can then reset the doc's 'modified' flag to FALSE and set the doc's path/file name to "". This will make a new empty CXGridDoc appear for the user.
There's no reason why this shouldn't work unless you've got a typo or something in your calling code.
:)
 
Hi qednick,

thank you for your reply. What i mean is that I still cant work it out how to store my CXGridDoc as a blank template and then use CCommandLineInfo to open that template instead of creating a new document. Sorry for my knowledge in Visual C++. I am a new programmer who has no idea about Visual C++.. Could you provide me some hints or anything? Sorry if it troubles you. thank you for your time and help.

best regards,
 
No worries Huskey... I'm assuming you can actually create and open a CXGridDoc document - if not, then you need to alter your InitInstance function of the Application code:

// Register the application's document templates. Document templates
// serve as the connection between documents, frame windows and views.

CMultiDocTemplate* pDocTemplate;
pDocTemplate = new CMultiDocTemplate(
IDR_YOURTYPE,
RUNTIME_CLASS(COriginalDoc),
RUNTIME_CLASS(CChildFrame), // custom MDI child frame
RUNTIME_CLASS(COriginalView));
AddDocTemplate(pDocTemplate);

You should see some code like the above. You need to temporarily change the COriginalDoc and COriginalView bits to the CXGridDoc and CXGridView type.

Create a new empty CXGridView doc and save it to disk somewhere and remember the name and path.

Now, you can use this document as a template for your CXGridDoc documents. Using the CCommanLineInfo class, you can open one of these at will by simply opening the CXGridDoc that you saved to disk:

// where 'nameAndPath' is the doc name and path
CCommandLineInfo cmdInfo;
cmdInfo.m_nShellCommand = CCommandLineInfo::FileOpen;
cmdInfo.m_strFileName = nameAndPath;
CWinApp* thisApp = ::AfxGetApp();
thisApp->ProcessShellCommand(cmdInfo);

(Don't forget: when you are specifying the path and file name, to preceed any backslahes with another backslash! eg: CString nameAndPath = "c:\\someDir\\theFile")

Then, in your CXGridDoc.cpp file, you need to use classwizard to create a function which handles the OnOpenDocument message.

You should end up with an OnOpenDocument() function. You need to edit this function by adding some code:

BOOL CXGridDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
if (!CDocument::OnOpenDocument(lpszPathName))
return FALSE;

// TODO: Add your specialized creation code here

m_strPathName = "";
SetModifiedFlag(FALSE);

return TRUE;
}

The additions are shown in bold type above. The first bit of the added code simply resets the document's filename and path to nothing. Thereby, effectively creating a new emtpy document based on the one you saved as a template earlier. The SetMidifiedFlag(FALSE) part is there to ensure that the document's modified flag does not indicate that this document has been modified.

There's probably a better way to go around all this but this is simply a quick solution to your problem. Give it a try and see what happens. :)
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top