GDI對象
在MFC中,CGdiObject類是GDI對象的基類,通過查閱MSDN我們可以看到,CGdiObject類有六個直接的派生類,GDI對象主要也是這六個,分別是:CBitmap、CBrush、CFont、CPalette、CPen和CRgn。
在這六個GDI對象中,最常用的莫過於畫筆和畫刷了,即CPen類和CBrush類。本文就主要講解畫筆的使用。
畫筆的應用實例
雞啄米在這裏直接通過一個波形圖的實例,來詳細講解畫筆的使用方法。
首先介紹此實例要實現的功能:在對話框上有一個Picture控件,將此控件的背景填充爲黑色;啓動一個定時器,每次定時器到時,所有波形數據都前移一個單位,並獲取一個80以內的隨機數作爲波形的最後一個數據,然後以綠色畫筆在繪圖控件上繪製波形。這樣就實現了波形的繪製及動態變化。
下面是具體實施步驟:
1、創建一個基於對話框的MFC工程,名字設爲“Example50”。
2、在自動生成的對話框模板IDD_EXAMPLE50_DIALOG中,刪除“TODO: Place dialog controls here.”靜態文本框,添加一個Picture控件,ID設爲IDC_WAVE_DRAW。
3、爲Picture控件IDC_WAVE_DRAW添加CStatic變量,名稱設爲m_picDraw。
4、在文件Example50Dlg.h文件中CExample50Dlg類聲明的上面添加宏定義:
- #define POINT_COUNT 100
此符號常量的意義是波形的點數,這裏用define將其定義爲符號常量是爲了方便以後可能的修改,假如我們以後想將點數改爲200,則只改此宏定義就可以了:#define POINT_COUNT 200,而如果沒有使用符號常量,在程序中直接使用了100,那麼就需要將所有使用100的位置找出來,並替換爲200,這樣不僅麻煩也很容易出錯,所以最好是將其定義爲符號常量。
5、在CExample50Dlg.h文件中爲CExample50Dlg類添加成員數組:
- int m_nzValues[POINT_COUNT];
此數組用於存放波形數據。
6、在CExample50Dlg類的構造函數中爲數組m_nzValues的元素賦初值:
- CExample50Dlg::CExample50Dlg(CWnd* pParent /*=NULL*/)
- : CDialogEx(CExample50Dlg::IDD, pParent)
- {
- m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
- // 將數組m_nzValues的元素都初始化爲0
- memset(m_nzValues, 0, sizeof(int) * POINT_COUNT);
- }
7、在CExample50Dlg對話框的初始化成員函數CExample50Dlg::OnInitDialog()中,構造隨機數生成器,並啓動定時器。CExample50Dlg::OnInitDialog()修改如下:
- BOOL CExample50Dlg::OnInitDialog()
- {
- CDialogEx::OnInitDialog();
- // Add "About..." menu item to system menu.
- // IDM_ABOUTBOX must be in the system command range.
- ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
- ASSERT(IDM_ABOUTBOX < 0xF000);
- CMenu* pSysMenu = GetSystemMenu(FALSE);
- if (pSysMenu != NULL)
- {
- BOOL bNameValid;
- CString strAboutMenu;
- bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
- ASSERT(bNameValid);
- if (!strAboutMenu.IsEmpty())
- {
- pSysMenu->AppendMenu(MF_SEPARATOR);
- pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
- }
- }
- // Set the icon for this dialog. The framework does this automatically
- // when the application's main window is not a dialog
- SetIcon(m_hIcon, TRUE); // Set big icon
- SetIcon(m_hIcon, FALSE); // Set small icon
- // TODO: Add extra initialization here
- // 以時間爲種子來構造隨機數生成器
- srand((unsigned)time(NULL));
- // 啓動定時器,ID爲1,定時時間爲200ms
- SetTimer(1, 200, NULL);
- return TRUE; // return TRUE unless you set the focus to a control
- }
8、爲CExample50Dlg類添加波形繪製的成員函數CExample50Dlg::DrawWave(CDC *pDC, CRect &rectPicture),參數分別爲設備上下文指針和繪圖的矩形區域。
- void CExample50Dlg::DrawWave(CDC *pDC, CRect &rectPicture)
- {
- float fDeltaX; // x軸相鄰兩個繪圖點的座標距離
- float fDeltaY; // y軸每個邏輯單位對應的座標值
- int nX; // 在連線時用於存儲繪圖點的橫座標
- int nY; // 在連線時用於存儲繪圖點的縱座標
- CPen newPen; // 用於創建新畫筆
- CPen *pOldPen; // 用於存放舊畫筆
- CBrush newBrush; // 用於創建新畫刷
- CBrush *pOldBrush; // 用於存放舊畫刷
- // 計算fDeltaX和fDeltaY
- fDeltaX = (float)rectPicture.Width() / (POINT_COUNT - 1);
- fDeltaY = (float)rectPicture.Height() / 80;
- // 創建黑色新畫刷
- newBrush.CreateSolidBrush(RGB(0,0,0));
- // 選擇新畫刷,並將舊畫刷的指針保存到pOldBrush
- pOldBrush = pDC->SelectObject(&newBrush);
- // 以黑色畫刷爲繪圖控件填充黑色,形成黑色背景
- pDC->Rectangle(rectPicture);
- // 恢復舊畫刷
- pDC->SelectObject(pOldBrush);
- // 刪除新畫刷
- newBrush.DeleteObject();
- // 創建實心畫筆,粗度爲1,顏色爲綠色
- newPen.CreatePen(PS_SOLID, 1, RGB(0,255,0));
- // 選擇新畫筆,並將舊畫筆的指針保存到pOldPen
- pOldPen = pDC->SelectObject(&newPen);
- // 將當前點移動到繪圖控件窗口的左下角,以此爲波形的起始點
- pDC->MoveTo(rectPicture.left, rectPicture.bottom);
- // 計算m_nzValues數組中每個點對應的座標位置,並依次連接,最終形成曲線
- for (int i=0; i<POINT_COUNT; i++)
- {
- nX = rectPicture.left + (int)(i * fDeltaX);
- nY = rectPicture.bottom - (int)(m_nzValues[i] * fDeltaY);
- pDC->LineTo(nX, nY);
- }
- // 恢復舊畫筆
- pDC->SelectObject(pOldPen);
- // 刪除新畫筆
- newPen.DeleteObject();
- }
9、有了定時器和繪圖成員函數,我們就可以在WM_TIMER消息的響應函數中添加對波形數據的定時處理和對波形的定時繪製了。定時器及WM_TIMER消息處理函數的添加方法如果忘記了,可以再到VS2010/MFC編程入門之四十四(MFC常用類:定時器Timer)溫習下。
WM_TIMER消息的處理函數修改如下:
- void CExample50Dlg::OnTimer(UINT_PTR nIDEvent)
- {
- // TODO: Add your message handler code here and/or call default
- CRect rectPicture;
- // 將數組中的所有元素前移一個單位,第一個元素丟棄
- for (int i=0; i<POINT_COUNT-1; i++)
- {
- m_nzValues[i] = m_nzValues[i+1];
- }
- // 爲最後一個元素賦一個80以內的隨機數值(整型)
- m_nzValues[POINT_COUNT-1] = rand() % 80;
- // 獲取繪圖控件的客戶區座標
- // (客戶區座標以窗口的左上角爲原點,這區別於以屏幕左上角爲原點的屏幕座標)
- m_picDraw.GetClientRect(&rectPicture);
- // 繪製波形圖
- DrawWave(m_picDraw.GetDC(), rectPicture);
- CDialogEx::OnTimer(nIDEvent);
- }
10、在對話框銷燬時,定時器應關閉。所以爲CExample50Dlg類添加WM_DESTROY消息的處理函數,並修改如下:
- void CExample50Dlg::OnDestroy()
- {
- CDialogEx::OnDestroy();
- // TODO: Add your message handler code here
- // 關閉定時器
- KillTimer(1);
- }
11、一切準備就緒,編譯運行。最終的效果如下圖:
關於畫筆,雞啄米就講到這裏了,下一節將爲大家簡單講講畫刷的使用。謝謝大家的關注!