Search code examples
c++mfcmdi

Guidance / best practices on MFC MDI


I have taken up the task to write my own terminal emulator. The project, should it ever come to life, will be a replica of the VanDyke SecureCRT.

It should support both Telnet & SSH protocols, and if things go well, perhaps more.

At the moment I am working on the easiest of them, the Telnet.

The application is an MFC MDI and here is where I need your advice. How would it be best to structure it?

  • Create one Document Template for each protocol? This seems a terrible waste, since most of the code will be the same.

  • Should everything be done in the Document class? And have the View only for display?

  • Or have the View do all the work of connecting, reading user input and displaying server output?

  • Should there be two Views? One for each protocol? As far as I can understand, this is not something done easily and the best way to do it, brings me back to the two Document Templates scenario.

I have written a working command line telnet client and at the moment I've reached the point where I need to decide how to proceed with the windows one.


Solution

  • In MFC you don't usually work much (I would say at all) with the document-template class. The document-template is an association of a resource ID with a document, frame-window and view class. The framework handles all its operations, and I can't see why one may want to modify its behavior. Instead you create instance(s) of the CSingleDocTemplate (SDI) or CMultiDocTemplate (MDI) class - one for each desired combination - in the InitInstance() member of your application class.

    One solution could be:

    • Define two document types(1) (for Telnet and SSH) and two doc-template instances in CWinApp::InitInstance(), and I would say, store the two instance pointers.
    • As suggested in the comment above, operations like connecting to the server, sending or receiving data etc, should be done in the document class.
    • The view class should do things like reading user input and presenting the data acquired by the document class (2)(3).
    • The view is considered a GUI object. It can modify data in its associated document though (GetDocument()), directly or preferably through the document's member functions.

    (1) If you find that the data and/or operations of the two protocols are quite similar or nearly identical, so such an implementation would be a "terrible waste", you can define a common ancestor or "base" document class (with some virtual or pure virtual members) and modify the two document classes so that they inherit from this one, instead of CDocument.
    (2) Implementation Tip: Check the UpdateData(), OnUpdate() and UpdateAllViews() functions.
    (3) You may want to employ separate threads for the connect/send/read operations, esp if you connect to multiple servers and/or your api is synchronous/blocking only. These operations can "freeze" your application until the server responds.

    To write "purist" MFC, try to structure your program so that the document class(es) do not #include their view classes, ie the document class should not "know" or make assumptions about its views. To perform special updates you can encode the information required in the lHint and pHint parameters of the UpdateAllViews() function; an UpdateAllViews(NULL, 0, NULL) call means full update for all document's views.

    It's not particularly hard to define two or more view classes, at least I don't see why. You can even find examples of using two or three view classes for the same document, for example a "graphic" and a "form" one, under the same MDI child frame, or in separate child frames (in this case the framework names them, eg Document1[1], Document1[2] etc). And if the view operations are essentially the same, you can simply define one view type and call the virtual member functions of the base document class (this will actually call the overridden ones).