2015-05-25 50 views
2

我有一個單文檔界面(SDI)Microsoft Foundation Class(MFC)應用程序,需要加載大文檔文件(大約需要2分鐘)。出於這個原因,當我打開文檔時,我的應用程序保持無響應狀態。MFC打開大文檔

但是,我希望我的應用程序能夠在打開文檔時作出響應。問題是,如果我嘗試加載在一個線程我的文檔,我的OnopenDocument功能(在我的文檔)將返回之前,我確實打開文檔:

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 
} 

我怎樣才能做到這一點是無鎖定但只在文檔實際打開後才返回?或者我如何在加載文檔時至少讓我的應用程序響應?

謝謝

+0

你不能希望線程阻塞並同時繼續執行。除非你購買那些昂貴的量子計算機。 – IInspectable

+0

@IInspectable這就是爲什麼我要求一個替代架構 – manatttta

+1

另一種方法是將文件名存儲在OnOpenDocument回調中並按需加載數據。您不能等待加載操作的GUI線程運行完成並同時處理其他消息。您將不得不在文檔加載之前返回,這種或那種方式。 – IInspectable

回答

6

線程創建後返回是OK。您應該爲文檔定義一個狀態,如loadingloaded。當你啓動線程時,狀態應該是loading。視圖應該查看狀態並相應地顯示。當線程完成加載文檔時,它應該向文檔發送消息。在處理程序中,將狀態設置爲loaded並呼叫UpdateAllViews()爲視圖提供更新新文檔數據的機會。

示例:在文檔加載完成並在視圖中完成加載並加載後,將打印「加載」。

enter image description here

在resource.h中:

#define IDD_NotifyDocumentFinished  101 

在文件標頭:

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

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

在文件執行:

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; 
} 

在該視圖中:

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); 
} 

更新:另請參見類似的更詳細的示例http://www.codeproject.com/Articles/14706/Notifying-the-Document

+0

謝謝,但你能更具體嗎?我如何定義文檔狀態? – manatttta

+0

還有另外一個問題。我無法從線程獲取文檔句柄,我不能從線程更新狀態 – manatttta

+0

不是從線程,而是在線程完成時發佈的消息的處理程序中。 –

1

當需要兩分鐘,只加載的東西,那麼有兩個原因:

  1. 你正在做一些愚蠢的事而加載,例如像在索引或圖形渲染等數據上運行昂貴的算法。如果將數據存儲在容器中,分配的開銷也可能變得很大。如果是這種情況,只能按需執行或在後臺異步執行這些操作。例如,您可以在完成後通知主線程(運行UI的主線程)以顯示渲染的圖形。
  2. 數據非常大。在這種情況下,您將不得不重新思考如何訪問這些數據,因爲任何操作可能都很昂貴。最好的辦法是使用異步模型,例如向模型發送請求以執行某些操作。模型然後向UI發送響應以顯示數據。在這種情況下,您需要考慮如何使用戶對此透明。一種方法是在更改請求後將顯示區域變暗,並在響應或進度條之後僅將其變亮。
+0

我的項目文件包含一個可以長到2GB的獨特文件 – manatttta