源代碼運行效果圖如下:
想必大家都用過WINDOWS自帶的畫筆,這是一個小巧易用的軟件。在業余時間,我模擬畫筆自己開發了一個類似的程序(當然不如畫筆那麼功能豐富)。它主要完成的功能有畫直線、曲線、圓、橢圓、矩形、多邊形;支持剪貼板的操作;支持撤銷、重復;保存成位圖文件;打開位圖文件。這個例子是用MFC開發的,為了方便使用,最後將轉換成控件。
建立單文檔工程Demo,下面將分四部分介紹相關功能的實現。
一、 繪圖功能
本程序包含多種圖元:直線、曲線、圓、橢圓、矩形等,使用不同的圖元類實現,這些圖元類均派生於同一個基類:CDrawObject。這樣可以大大簡化不同圖元的處理過程。
雖然不同的圖元具有不同的表現形式和數據結構,但它們具有許多相同的特性,如都具有顏色定義功能,都有圖元繪制函數等,在基類中對這些共有的數據成員和成員函數進行描述。圖元基類的定義如下:
class CDrawObject : public CObject
{
private:
COLORREF m_PenColor;//圖元顏色
int m_iPenWidth; //畫筆寬度
bool m_bFill; //是否填充
public:
bool m_bSelected;
long m_nStyle;//圖元類型
CDrawObject(){m_bSelected = false;};
void SetPenColor(COLORREF color);//設置圖元顏色
COLORREF GetPenColor();//獲得圖元顏色
void SetPenWidth(int width) {m_iPenWidth = width;};
int GetPenWidth() {return m_iPenWidth;};
void SetFill(bool fill) { m_bFill = fill;};
bool GetFill() {return m_bFill;};
virtual void Draw(CDC* pDC) {};
virtual void MoveAt(CDC* pDC, long x, long y) {};
virtual void EndPoint(CDC* pDC) {};
virtual void NewPoint(long x, long y){};
//圖形對象第一點坐標,如果返回false則結束繪圖
virtual int AddPoint(long x, long y){return 0;};
};
各圖元的具體實現函數請參考源代碼。
繪圖功能的實現主要是在視類中完成的。首先建立相應的菜單和工具欄按鈕用來設置圖元的樣式、顏色、畫筆的粗細、是否填充等等。還要重載OnLButtonDown(按下鼠標左鍵)、OnMouseMove(鼠標移動)、OnLButtonUp(松開鼠標左鍵)三個函數。創建過程如下:
1、 按下左鍵,創建新的圖元類實例;
2、 跟蹤鼠標移動修改圖元,獲得所見即所得的視覺效果;
3、 松開左鍵,繪制結束。
二、 剪貼板操作
和剪貼板操作相關的函數主要有以下幾個:
打開剪貼板:OpenClipboard();
清空剪貼板:EmptyClipboard();
保存至剪貼板:SetClipboardData (CF_BITMAP, bitmap.GetSafeHandle() );
取出剪貼板的內容:GetClipboardData(CF_BITMAP);
關閉剪貼板:CloseClipboard();
至於視覺效果的實現,本來應該用"橡皮筋類",這裡偷了點兒懶,直接畫了矩形:)。剪切和拷貝的實現方法基本相同,剪切時需要清空選中的矩形區域。粘貼時需要先判斷剪切板中有無內容。
三、 撤銷和重復
為了實現撤銷和重復,我自己定義了一個類Stack,該類的主要功能類似於一個棧,可以在初始化時定義棧的大小,可以彈出棧頂元素、增加新元素等等,除此以外還保存了一個表示當前位置的指針m_iCurPos。撤銷時該指針向前移動,重復時向後移動,如果撤銷後又有了新操作,則當前長度應改至m_iCurPos,即棧中m_iCurPos之後的元素無效。 至於棧中保存的內容,則是在每次操作後調用自己定義的SaveInStack()函數,將屏幕內容保存到一個HBITMAP類型的變量中。(這個方法有點兒笨,不過我實在想不出更好的方法了:(。但這個方法的實際效果還是不錯的。)
四、 打開和保存
有了前面的基礎,著部分比較容易實現。
打開文件的主要函數是:
HBITMAP hbmp = (HBITMAP)LoadImage(NULL, _T(path), IMAGE_BITMAP,
0, 0, LR_CREATEDIBSECTION | LR_DEFAULTSIZE | LR_LOADFROMFILE);
pDC->BitBlt(0, 0, bm.bmWidth, bm.bmHeight, &dc, 0, 0, SRCCOPY);
具體實現過程請參考源代碼。
以上是MFC程序的實現過程,接下來我們把它轉化成控件。
五、轉化成控件
1、新建控件
啟動Microsoft Visual C++ 6.0,單擊File下拉菜單下的New命令,在Profects標簽下選擇MFC ActiveX ControlWizard。輸入工程名WinPainter;
2、引入ActivDoc.h和ActivDoc.cpp文件;
3、在CwinPainterCtrl類的構造函數中添加
AddDocTemplate(new CActiveXDocTemplate(
RUNTIME_CLASS(CDemo1Doc),
RUNTIME_CLASS(CMainFrame),
RUNTIME_CLASS(CDemo1View)));
4、 將剛才開發的MFC工程中的視類、文檔類、框架類的頭文件、實現文件,還有圖元類以及Stack類的文件統統包含進來,這時各類的消息函數可以正常響應,但菜單、工具欄等資源需要重新定義;
5、 在CWinPainterCtrl類中調用視類的函數,舉例:
CDemo1View* m_pView;
m_pView = (CDemo1View *)(GetFrameWnd()->GetActiveView());
ASSERT(m_pView);
m_pView->OnEditCopy();
調用MFC中其它類的函數與此類似。
6、 增加控件的屬性和方法。
至此,控件開發完畢。
控件運行測試效果圖如下:
參考文獻: 王宏 李玉東 李罡 《Visual C++ 實戰演練》 人民郵電出版社