manatttta
manatttta

Reputation: 3124

MFC Open big document

I have an Single-document Interface(SDI) Microsoft Foundation Class(MFC) app that has to load a big document file (takes about 2 minutes). For that reason, my app stays unresponsive while I am opening the document.

However, I would like my app to be responsive while opening the document. The problem is that, if I try to load my document on a thread, my OnopenDocument function (in my doc) will return before I actually open the document:

BOOL CmodguiDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
  if (!CDocument::OnOpenDocument(lpszPathName))
    return FALSE;
  start_tread(_open_doc); // just an example
  return TRUE; // Will return before the document will actually be open
}

How can I do this to be non-locking but only returning after the document is actually open? Or how can I at least make my app responsive while I am loading the document?

Thanks

Upvotes: 2

Views: 674

Answers (2)

Ulrich Eckhardt
Ulrich Eckhardt

Reputation: 17424

When it takes two minutes to just load something, then there are two reasons for that:

  1. You are doing something stupid while loading, like e.g. running expensive algorithms on the data like indexing or graphical rendering. If you store the data in containers, the overhead of allocations could also become significant. If that is the case, only perform them on demand or perform these operations asynchronously in the background. You can notify the main thread (the one running the UI) when done in order to display the rendered graphics, for example.
  2. The data is incredibly large. In that case, you will have to rethink how to access this data anyway, because any operation might be expensive. Your best bet will be to use an asynchronous model, like sending a request to the model to perform some operation. The model then sends a response to the UI to display the data. In that case, you need to think about how to make this transparent to the user though. One approach is to darken the display area after a change request and only brighten it up after the response or a progress bar.

Upvotes: 1

Marius Bancila
Marius Bancila

Reputation: 16338

Returning after the thread has been created is OK. You should define a state for the document such as loading and loaded. When you start the thread the state should be loading. The view(s) should look at the state and display accordingly. When the thread has finished loading the document it should post a message to the document. In the handler, set the state to loaded and call UpdateAllViews() to give the views the chance to update with the new document data.

Example: This will print "loading" while the doc is loading and "loaded" and loaded after it finished in the view.

enter image description here

In resource.h:

#define IDD_NotifyDocumentFinished        101

In the document header:

public:
   enum DocState
   {
      None,
      Failed,
      Loading,
      Loaded
   };

   DocState GetDocState() const {return m_state;}
private:
   DocState m_state;
   void StartLoading();

In the document implementation:

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

   m_state = Loading;

   StartLoading();

   return TRUE;
}

UINT LongRunningFunction(LPVOID param)
{
   Sleep(3000);

   HWND hWnd = AfxGetApp()->m_pMainWnd->GetSafeHwnd();

   NMHDR hdr = {hWnd, IDD_NotifyDocumentFinished, 0};
   ::SendMessage(hWnd, WM_NOTIFY, 0, reinterpret_cast<LPARAM>(&hdr));

   return 0;
}

void CMFCDocViewAsyncDoc::StartLoading()
{
   AfxBeginThread(&LongRunningFunction, nullptr);
}

BOOL CMFCDocViewAsyncDoc::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
   if(HIWORD(nCode) == WM_NOTIFY)
   {
      WORD wCode = LOWORD(nCode);
      AFX_NOTIFY * notify = reinterpret_cast<AFX_NOTIFY*>(pExtra);
      if(notify->pNMHDR->idFrom == IDD_NotifyDocumentFinished)
      {
         m_state = Loaded;
         UpdateAllViews(nullptr);
      }
   }

   return TRUE;
}

In the view:

void CMFCDocViewAsyncView::OnDraw(CDC* pDC)
{
    CMFCDocViewAsyncDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if (!pDoc)
        return;

    CMFCDocViewAsyncDoc::DocState state = pDoc->GetDocState();
   CString sstate;
   switch(state)
   {
   case CMFCDocViewAsyncDoc::None:
      sstate = "None";
      break;
   case CMFCDocViewAsyncDoc::Failed:
      sstate = "Failed";
      break;
   case CMFCDocViewAsyncDoc::Loading:
      sstate = "Loading";
      break;
   case CMFCDocViewAsyncDoc::Loaded:
      sstate = "Loaded";
      break;
   }
   pDC->TextOut(50, 50, sstate);
}

Update: Also look here for a similar, more detailed example http://www.codeproject.com/Articles/14706/Notifying-the-Document.

Upvotes: 7

Related Questions