In short, I need to differentiate between framework call to
OnFileNew() and User Click on New.
Is this a correct approach/design/choice in first place?
TIA,
Jd
Does that even make sense? I think you cannot have an SDI without a
document.
But you can have a MDI with a 1 document limit though, and you can
prevent a new document from opening at startup with MDI (just as
explained in the link you provide)
Specifically, we can see:
BOOL CWinApp::ProcessShellCommand(CCommandLineInfo& rCmdInfo)
{
BOOL bResult = TRUE;
switch (rCmdInfo.m_nShellCommand)
{
case CCommandLineInfo::FileNew:
if (!AfxGetApp()->OnCmdMsg(ID_FILE_NEW, 0, NULL, NULL))
OnFileNew();
if (m_pMainWnd == NULL)
bResult = FALSE;
break;
[...]
}
So it seems that ProcessShellCommand sends directly a ID_FILE_NEW
command to the App, thus invalidating Paul's workaround.
A possible solution would be to make that particular call to OnCmdMsg
return FALSE.
To do so, you can declare a member variable in your app class, say
m_bInit, which would be FALSE until the end of InitInstance, where you
set it to TRUE, and override OnCmdMsg. If m_bInit is FALSE, (and the
command ID is ID_FILE_NEW) you return FALSE, otherwise, you call your
base class' OnCmdMsg.
I've tried it and in seems to work. OnMyNewFile does not get called
until the user asks for a new file.
I'm not sure it's the best solution, though. If I had to do it, I would
probably take the MDI route, setting a 1 document limit and doing the
"cmdInfo.m_nShellCommand = FileNothing;" thing.
Try this, BUT! At your own peril. In your initInstance, locate
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(...
and replace it with the following:
class CDocTemplateXXX : public CSingleDocTemplate
{
public:
CDocTemplateXXX(
UINT nIDResource,
CRuntimeClass* pDocClass,
CRuntimeClass* pFrameClass,
CRuntimeClass* pViewClass)
: CSingleDocTemplate(nIDResource, pDocClass, pFrameClass,
pViewClass)
, m_bStartup(true)
{}
private:
virtual CDocument* OpenDocumentFile(
LPCTSTR lpszPathName,
BOOL bMakeVisible = TRUE)
{
CFrameWnd* pFrame = STATIC_DOWNCAST(CFrameWnd, AfxGetMainWnd());
if (!lpszPathName && m_bStartup)
{ // User started the app and we have no file to open -
// do our magic.
m_bStartup = false;
// We're about to create first ever frame here, so..
ASSERT(!AfxGetThread()->m_pMainWnd);
// Create frame.
CFrameWnd* pFrame = CreateNewFrame(NULL, NULL);
if (pFrame == NULL)
{ // Possible, but un-possible :-)
ASSERT(FALSE);
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
return NULL;
}
// Set main frame (MFC will look at this later to see if it's
// worth it to continue.
AfxGetThread()->m_pMainWnd = pFrame;
// Update main frame.
InitialUpdateFrame(pFrame, NULL, bMakeVisible);
return NULL;
}
m_bStartup = false;
// No magic => do what was in base class call, but take care of
the frame
CDocument* pDocument = NULL;
BOOL bCreated = FALSE; // => doc and frame created
BOOL bWasModified = FALSE;
if (m_pOnlyDoc != NULL)
{
// already have a document - reinit it
pDocument = m_pOnlyDoc;
if (!pDocument->SaveModified())
return NULL; // leave the original one
pFrame = (CFrameWnd*)AfxGetMainWnd();
ASSERT(pFrame != NULL);
ASSERT_KINDOF(CFrameWnd, pFrame);
ASSERT_VALID(pFrame);
}
else
{
// create a new document
pDocument = CreateNewDocument();
bCreated = TRUE;
}
if (pDocument == NULL)
{
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
return NULL;
}
ASSERT(pDocument == m_pOnlyDoc);
if (pFrame == NULL)
{
ASSERT(bCreated);
// create frame - set as main document frame
BOOL bAutoDelete = pDocument->m_bAutoDelete;
pDocument->m_bAutoDelete = FALSE;
// don't destroy if something goes wrong
pFrame = CreateNewFrame(pDocument, NULL);
pDocument->m_bAutoDelete = bAutoDelete;
if (pFrame == NULL)
{
AfxMessageBox(AFX_IDP_FAILED_TO_CREATE_DOC);
delete pDocument; // explicit delete on error
return NULL;
}
}
if (lpszPathName == NULL)
{
// create a new document
SetDefaultTitle(pDocument);
// avoid creating temporary compound file when starting up
invisible
if (!bMakeVisible)
pDocument->m_bEmbedded = TRUE;
if (!pDocument->OnNewDocument())
{
// user has been alerted to what failed in OnNewDocument
TRACE(traceAppMsg, 0, "CDocument::OnNewDocument returned FALSE.
\n");
if (bCreated)
pFrame->DestroyWindow(); // will destroy document
return NULL;
}
}
else
{
CWaitCursor wait;
// open an existing document
bWasModified = pDocument->IsModified();
pDocument->SetModifiedFlag(FALSE); // not dirty for open
if (!pDocument->OnOpenDocument(lpszPathName))
{
// user has been alerted to what failed in OnOpenDocument
TRACE(traceAppMsg, 0, "CDocument::OnOpenDocument returned FALSE.
\n");
if (bCreated)
{
pFrame->DestroyWindow(); // will destroy document
}
else if (!pDocument->IsModified())
{
// original document is untouched
pDocument->SetModifiedFlag(bWasModified);
}
else
{
// we corrupted the original document
SetDefaultTitle(pDocument);
if (!pDocument->OnNewDocument())
{
TRACE(traceAppMsg, 0, "Error: OnNewDocument failed after
trying "
"to open a document - trying to continue.\n");
// assume we can continue
}
}
return NULL; // open failed
}
pDocument->SetPathName(lpszPathName);
}
CWinThread* pThread = AfxGetThread();
ASSERT(pThread);
if (bCreated && pThread->m_pMainWnd == NULL)
{
// set as main frame (InitialUpdateFrame will show the window)
pThread->m_pMainWnd = pFrame;
}
InitialUpdateFrame(pFrame, pDocument, bMakeVisible);
return pDocument;
}
// True at startup.
// False after first call to OpenDocumentFile.
bool m_bStartup;
};
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CDocTemplateXXX(
IDR_MAINFRAME,
RUNTIME_CLASS(CFileNewDoc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(CFileNewView));
if (!lpszPathName && m_bStartup) is the key: there, code creates a
frame with no document. The rest is copied from the base class. You
will need to change various ASSERTS that check the document for
validity. These are also inserted by wizard-generated code.
> Is this a correct approach/design/choice in first place?
I would think not.
As usual with these situations, question is why do you think you want
any of this. Empty document is just as good as no document. And in one
respect, it's better: when your user starts your program, he doesn't
have to through an additional hoop of actually requesting a new one.
Why are you asking your users to work more? What do you think you are
buying by starting up with no document?
And finally, did you consider opening last-open document at startup?
That's a nice touch for certain situations. Visual Studio has that as
an option. That could mostly avoid whatever you are trying to avoid.
HTH,
Goran.
Whoops! Copy-pasted MFC code (the "no magic" part) went out horribly.
Sorry 'bout that.
Goran.
Hello Mike,
Thanks for the workaround. Its working :-)
I guess when that MS Journal article was published, the framework used
to call the virtual function. But now it sends a message instead.
And your tip beautifully workarounds the problem.
Once again thanks a ton!!
Jd
Dear Goran,
Thanks for the solution :) I'll try it out.
Why I chose this "No document on startup" idea, is that my app has
some more features, which doesn't depend on the document.
User can use the same application for creating a configuration file or
can use it for status monitoring.
I am not sure if my idea is correct though.
Jd..
Ok, but what's wrong with creating your file while you have e.g. an
empty new document open? I don't see the relation between the two. I
don't quite understand what you mean by "status monitoring", but
again, isn't this also orthogonal to the doc? I have a similar app
(speaks to embedded devices over a proprietary protocol over a serial/
TAPI/TCP connection), and that indeed is orthogonal to the document in
our case.
Goran.
Joseph M. Newcomer [MVP]
email: newc...@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm
Well, it doesn't seem so. Ï understood it as needing to modify a
config file for the whole app (some kind of "options" command an such)
- happens all the time. It's an overkill to introduce another doc type
for that.
Goran.
Well, for an SDI application, it's either one doc type open, or the
other. I still think it's an overkill.
One can just "edit" the "config" file from "options" dialog or some
such, without reaching for a second DocTemplate.
Goran.
Here I sincerely agree the way SDI was implemented out of the box.
But that is just what I have done for our product. And it is a real
success. I certainly did not use Microsoft's model as a bases. But if
you create your own system it can be as good, and I think better than MDI.
<http://reserveanalyst.com/images/content/First.jpg>
I did not show the task bar, but like sea monkey, or the likes, you have
multiple accesses to running 'children'. For instance, we can run the
'Stock database', looks like an independent app, but is a delayed loaded
dll. And looks like it living so on the task bar. The client can alt tab
through several of our 'lives' and use them as they need.
I guess it depends on the needs of the app. Multi sdi does not work as
shipped in 7.1. But because I evolved it on my own I can't say what 9.0
really looks like.
SDI can work like MDI, but IMHO, even better because of the multi app
feel. Just because Microsoft doesn't deliver does not mean you can't do it.
Best, Dan.
1. We need to work on more than one thing at a time
2. We need to see more than one view at a time on a thing
3. We need to work on zero or more things at once
Note that 3 is a generalization of 1: it allows zero things to be active. So it isn't
just working on 1 or 2 or 3 or 4 things, but the possibility that all things will be
closed and no thing will be currently active. And a 'thing' is usually represented as a
'document' (there is a horrible failure to communicate what a 'document' means; I've seen
cases where they "get rid of all that useless document code" because "we aren't writing to
a file", and they've expended WEEKS of effort to no value whatsoever; I point out a
'document' is an interface to some persistent state, and in their case, the 'persistent
state' is in the embedded controller at the end of a wire, so the document is a 'virtual
document' and provides the interface to the device, at which point they say "You mean ALL
THAT WORK we expended to eliminate the unnecessary document code was wasted?" usually
followed by "and you mean ALL THAT WORK we expended so one view could talk to another so
the view could control the device was wasted?" and the answer is always "Yes, you wasted a
lot of effort and gained nothing". Sadly, they thought it was meaningful to eliminate the
"wasted code", and I asked them to compare the code-with-derived-CDocument to the
code-without-derived-CDocument and the difference is always minute: for example, 4K out of
280K (in one memoarable case) "So you thought was worth six weeks of your programmer's
time to save about 1% of the code space, or 4K out of 2GB virtual space, or you have saved
0.0002% of your virtual address space. Was this REALLY cost-effective?" and they say
"Well, we didn't want code bloat" which shows how truly incompetent most companies are in
evaluating what they are doing internally. I wonder sometimes how anyone could be this
stupid. But back in school, some programmer who learned how to program on a 64K PDP-11
told them that You Must Make Your Code As Small As Possible, and they never outgrew that
bad advice.
joe
> My bias is that I do not ever use SDI (I find it largely useless, and every time I did an
> SDI app, the customer ended up with an MDI need and I had to convert it, so I never even
> *think* of SDI as a viable model any longe).
> joe
I think you all have good points.
We have several applets that are SDI but only that it allowed it to
get a quick mainframe and view window with all the menu, command line,
toolbar, etc, especially getting all the overriding classes necessary
already provided to you.
Sure, all could of been done starting as simple dialog, and many new
applets begin this way too and add controls as required, but the
initial SDI layout was the design intent. We needed more than a DIALOG
and less than a MDI.
But I also see Joe's point for one of applets. One applet is a
RADIUS server. It is a SDI with a CVIEW that has a few splitters. This
is a product for thousands of customers. If they have multiple RADIUS
servers, they start multiple EXEs. We had a few customers that
wanted or rather "wished for" to have one EXE for all of them, for
machine monitoring. If it was MDI ready, then it would be a piece of
cake. It was never done and they accepted as it was. If we had maybe
a louder cry for it, then the effort would of done to change it to MDI.
I guess Joe's point if it was done as a MDI to begin with, then it
would of been a moot point.
Very true, but it doesn't take into account the performance issues
with having a single EXE with multiple threaded RADIUS servers, one
with their own view. That is why them wish list item remained a wish
list item, also, in our case, a majority of the time, wish items like
this are a spur of the moment thing. The operator is currently
monitor and says "Wouldn't it be nice if I can just view everything
with one exe." Sometimes we come back and say, "Ok, that would be
nice, but its only useful when you are setting on the computer most of
the time watching this stuff. Wouldn't it be better to tell us what
signals or events you are looking for to display them? That way you
don't have to sit there and watch for it?" and the discussing normally
subsides :) But thats our application. Others want multiple views
for good reasons.
--
HLS
Yes, and you can certainly do this with multi SDI. Our 'default' page is
an instance of no project view is open. It is an HTML view initially
with a default hello there page, our clients never see a blank app. But
the client can access our website for whatever at the least. The can
surf the web, but not our intention, it just happens to be available.
As for knocking out the doc, not at all. I use the doc object at all
times, even if it appears almost useless.
Our paradigm:
The master mainframe. Though the view menu you can access the major
views of the app. Each view has its own doc, of course! We do this
because it makes no sense to have any of these views available at the
same time.
Other SDI instances are available according to the state of the master
frame. For instance, you can have a project notes frame active no matter
what state the master is in. It is always there if you bring it up. If
the master is in the report view, you can open as many report
doc/view/frames as you would like but if you leave this view, they will
close and ask if you want to save, if needed, on the way.
I have a doc/view/frame modal wrapper. I only have to derive from the
doc to set menus or any other features of this set. The frame/view can
be generic snapins, like:
void ReportOptions::OnBnClickedRptEditfooter( )
{
CFrameDialog dlg(
IDR_EDIT_FOOTER,
RUNTIME_CLASS( RAFooterDoc ),
RUNTIME_CLASS( HERichFrm ),
RUNTIME_CLASS( HERichView ),
this );
...
There is a lot of potential in what you can do with MFC. SDI does not
have to mean you only have one document. It only means you don't have an
MDI mainframe.
But like I said, you have to implament it. Like I have my own derived
doc template and manager for the ifrastructure. All the app frames
register with a frame manager. If you alt tab from the app and back to
the app, you want all your frames to come to the top in an appropriate
order as if the app were a unit, just like MDI.
Best, Dan.
I did one app that I thought "should" have been a dialog-based app, but I did it as MDI
mostly because we wanted multiple views/ Turns out we needed to handle multiple devices,
so when the client called and asked for it, all I did was disable the code that allowed
only one document at a time to open. Took maybe 2 minutes. I emailed the solution 20
minutes later (I did a little testing)
I also suggest that for learning MFC, dialog-based apps are often a better choice for a
beginner.
joe
Joseph M. Newcomer wrote:
> Probably 80% of the apps I write are dialog-based apps. There are small and subtle
> differences between an dialog-based app and an SDI app with a CFormView; for example, the
> SDI app has a status bar while the dialog-based app normally doesn't (but it can be
> added). But overall most of the people don't seem to care about the distinctions.
>
> I did one app that I thought "should" have been a dialog-based app, but I did it as MDI
> mostly because we wanted multiple views/ Turns out we needed to handle multiple devices,
> so when the client called and asked for it, all I did was disable the code that allowed
> only one document at a time to open. Took maybe 2 minutes. I emailed the solution 20
> minutes later (I did a little testing)
>
> I also suggest that for learning MFC, dialog-based apps are often a better choice for a
> beginner.
>
> joe
But you "can't" always do this for commercial software or for software
served more than one person. In my experience, a good bit of the time
it is spur of the moment thing where one customer is doing something
at a particular and RARE moment and says "Geez, wouldn't it be
nice..." and makes the suggestion, but then it isn't needed 99% of the
time or even ever again. Many times no one follows up. It would be a
major mistake to believe silence means agreement. In fact, in many
circles silence could mean no interest in the suggestion.
--
HLS
Dear Joe,
Sorry , I was sick and couldn't follow up the thread.
My app is almost same as the one you mentioned.
A config file to specify the properties of a remote embedded system.
( this part is used rarely, as it is a complex system and
configuration changes very rarely) and transfer the config file to the
system.
And the other part is monitoring the system and the devices attached
to the system.
Jd
email: newc...@flounder.com