2013年8月8日 星期四

單文檔(SDI)顯示不同的控制頁面(CFormView):

 

新建一個基於CFormView的單文檔應用程式,再添加一個控制頁面和與之對應的基於CFormView的類,然後通過在主框架類裡添加控制代碼和功能表控制實現這兩個控制頁面的動態切換,下面就是具體的實現過程:

(一) 用 "MFC AppWizard(exe) "建立一個新專案 "SwitchForm ",並在第二步的創建類型上選擇為 "Single documnet "單文檔模式,第三、四、五、六步均取預設狀態,最後一步選擇 "CFormView "作為view類的基類。

點按 "完成 "按鈕,生成了初始工程 "SwitchForm "。

點選單 "Insert "、 "Resource… ",在彈出的 "Insert Resource "對話方塊中 "Dialog "樹裡的"IDD_FORMVIEW ",點擊 "New "按鈕,生成了一個新的表單,將其ID號改為 "IDD_NEXTFORM "。在原有的表單上加一個靜態框 "這是第一個表單 ";在新建的表單上也添加一個靜態框 "這是第二個表單 "。

(三) 在菜單資源的 "IDR_MAINFRAME "上添加一級功能表 "表單切換 ",及其二級功能表 "第一個表單 "、 "第二個表單 ",其標識號分別為 "ID_FIRSTFORM "和 "ID_SECONDFORM "。修該 "第一個表單 "的屬性為 "Checked ",表明程式初始時顯示的是第一個表單。

(四) 在 "ClassView "屬性頁裡的 "SwitchForm classes "上右鍵,在彈出功能表上選擇 "New Class…",彈出 "New Class "對話方塊,選擇 "Dialog ID: "為我們剛添加的新表單 "IDD_NEXTFORM ",選擇 "Base class: "為 "CFormView ",類名取為 "CNextFormView ",這樣就把第二個表單對應的視圖類添加到了工程。

(五) 在框架類裡添加函數SwitchToForm():

void CMainFrame::SwitchToForm(int nForm)

{

// 獲取原來的活動表單的視圖控制碼

CView* pOldActiveView = GetActiveView();

// 獲取由 "nForm "標識的表單所對應的視圖控制碼

CView* pNewActiveView = (CView*) GetDlgItem(nForm);

// 若視圖控制碼為空,則創建一新的。

if (pNewActiveView == NULL) {

if (nForm == IDD_SWITCHFORM_FORM)

pNewActiveView = (CView*)new CSwitchFormView;

if (nForm == IDD_NEXTFORM)

pNewActiveView = (CView*)new CNextFormView;

CCreateContext context;

context.m_pCurrentDoc = pOldActiveView-> GetDocument();

pNewActiveView-> Create(NULL,

NULL,

0L,

CFrameWnd::rectDefault,

this,

nForm,

&context);

pNewActiveView-> OnInitialUpdate();

}

// 選擇pNewActiveView為活動表單

SetActiveView(pNewActiveView);

// 顯示活動表單,隱藏非活動表單

pNewActiveView-> ShowWindow(SW_SHOW);

pOldActiveView-> ShowWindow(SW_HIDE);

int ID;

if (pOldActiveView-> GetRuntimeClass() == RUNTIME_CLASS(CSwitchFormView))

ID = IDD_SWITCHFORM_FORM;

if(pOldActiveView-> GetRuntimeClass() == RUNTIME_CLASS(CNextFormView))

ID = IDD_NEXTFORM;

// 設置表單的ID號

pOldActiveView-> SetDlgCtrlID(ID);

pNewActiveView-> SetDlgCtrlID(AFX_IDW_PANE_FIRST);

RecalcLayout();

}

(六)添加兩個功能表相對應的命令回應函數和更新函數如下:

void CMainFrame::OnFirstform()

{

// 通過IsKindOf函數確定當前使用中視窗是否是第一個視窗,如是,則無須切換,

// 否則將通過SwitchToForm函數將當前使用中視窗切換到”DD_SWITCHFORM_FORM "

// 標識的第二個表單。

if (GetActiveView()-> IsKindOf(RUNTIME_CLASS(CSwitchFormView)))

return;

SwitchToForm(IDD_SWITCHFORM_FORM);

}

void CMainFrame::OnUpdateFirstform(CCmdUI *pCmdUI)

{

// 通過IsKindOf函數判斷當前使用中視窗是否是第一個表單,如是則將其選中。

pCmdUI-> SetCheck(GetActiveView()-> IsKindOf(RUNTIME_CLASS(CSwitchFormView)));

}

void CMainFrame::OnSecondform()

{

if (GetActiveView()-> IsKindOf(RUNTIME_CLASS(CNextFormView)))

return;

SwitchToForm(IDD_NEXTFORM);

}

void CMainFrame::OnUpdateSecondform(CCmdUI *pCmdUI)

{

pCmdUI-> SetCheck(GetActiveView()-> IsKindOf(RUNTIME_CLASS(CNextFormView)));

}

然後再在該檔開始處添加對兩個視圖類的引用:

#include "SwitchFormDoc.h "

#include "SwitchFormView.h "

#include "NextFormView.h "

在此須注意:應在兩個視類的引用之前添加對文檔類的引用,否則會引起編譯錯誤。另外,由於視

類的構造函數在聲明時都確省的聲明為保護型的,在框架類中無法引用,所以還要將兩個視類的類

聲明改動如下:

class CNextFormView : public CFormView

{

DECLARE_DYNCREATE(CNextFormView)

// protected: 將 protected 改為 public

public:

CNextFormView();

virtual ~CNextFormView();

……

};

class CSwitchFormView : public CFormView

{

// protected: 將 protected 改成 public

public:

CSwitchFormView();

DECLARE_DYNCREATE(CSwitchFormView)

……

};

三、 編譯運行

編譯運行程式,開始時的表單上有 "這是第一個表單的字樣 ",功能表也只有 "第一個表單 "是被選中的,當前的活動表單是第一個表單;點擊功能表 "第二個表單 ",視圖中的表單上的字樣變成了 "這是第二 個表單 ",同時選中的功能表也由 "第一個表單 "變成了 "第二個表單 ",實現了通過功能表將表單進行動態切換。

總結:此程式中關鍵的是SwitchToView函數,在此函數中,程式搜索所有當前文檔的顯示視窗來查找與CruntimeClass變數匹配的視圖類。如果找到,該視窗被啟動。通過與之類似的方法,還可以實現在多文檔模式下的單檔(文檔)多視(視圖),通過不同的視圖以不同的方式顯示來自同一份文檔的資料,以更好的滿足程式的需要。

關於ID_FILE_SAVE與ID_FILE_SAVE_AS彈出另存為對話方塊的處理

 

Question:

程式(MFC)準備序列化一些資料進行保存,但在按"檔/保存"後會彈出一個另存為的對話方塊,而我想做的是在按下"保存"後直接個根據我指定的路徑與檔案名進行保存,不彈出對話方塊,這要怎麼處理?

Answer:

切換到資源Tree view,選擇到功能表資源中"File->Save"功能表項目,然後滑鼠按右鍵彈出功能表,選擇"添加事件處理常式"(Add Event Handler..)後,彈出事件處理對話方塊,在其中可以設置處理這一個命令事件回應的類,一般是CXXDoc類,點擊“確定”後,MFC嚮導會自動構造命令處理函數的框架,在這裡面可以自己構造保存內容。這樣,另存為對話方塊就不會再打開。

#pragma pack(n)

 
最近有一個專案,本來都順順的!但放了幾天假回來!忽然出現一個bug,查了好久,原來是有一個寫底層的同事!在處理#pragma pack(1)  (對齊)時,忘了把它還原了#pragma pack(pop)!
........................
#pragma pack(push)
#pragma pack(1)
typedef struct s_xxxx
{
   ....
}t_xxxxx;
#pragma pack(pop)
........................
下次若遇到HEAP CORRUPTION DETECTED

或是下面的圖片,而且找不到原因,可以找看看是否遇到這種情形!








在花壇遇到變形金鋼出任務

 
 Meet Transformers

在機車行的門口,科博文跟大黃蜂站在門的兩側出任務!
呵呵!老板好讚!




彰員路(137縣道)





修改 cstatic text color

 
 
// Message Map
BEGIN_MESSAGE_MAP(CDlgDlg, CDialog)
    ...
    ON_WM_CTLCOLOR()
END_MESSAGE_MAP()
HBRUSH CDlgDlg::OnCtlColor( CDC* pDC, CWnd* pWnd, UINT nCtlColor )
{
    // Call base class version at first. Or else it will override your changes.
    HBRUSH hbr = CDialog::OnCtlColor( pDC, pWnd, nCtlColor );
    // Check whether which static label its.
    if( pWnd->GetDlgCtrlID() == IDC_STATIC_OK )
    {
        // Set color as red.
        pDC->SetTextColor( RGB( 255, 0, 0 ));
        pDC->SetBkMode( TRANSPARENT );
    }
    return hbr;
}

MFC clistbox 顯示水平 scrollbar

//送出要用水平軸的訊息
::SendMessage(m_list.m_hWnd,LB_SETHORIZONTALEXTENT,1024,0);
//1024是最長寬度,可以每次在新增字串以後再重新計算需要多少;
m_list.SetScrollRange(SB_HORZ,0,1024);
//強制顯示水平軸
m_list.ShowScrollBar(SB_HORZ);
1. 以上三行請加在initial的地方,CListBox 在 CDialog 裡就放在 OnInitDialog();在CFormView 裡就 OnInitialUpdate();
2. 請記得一定要把Horizontal scroll勾起來,
CListBox 使用 AddString() 是將字串加到最後面,預設是看不到最新的資料的(因為不會自動往下捲)。在每次 AddString() 後加上以下這行程式碼就會自動顯示新增的一行
m_list.SetTopIndex(m_list.GetCount()-1);

如何得到Window系統文件路徑

TCHAR szPath[MAX_PATH];
   SHGetFolderPath(NULL,   CSIDL_COMMON_DOCUMENTS   ,   NULL,   0,   szPath);