尽管本章的初衷是用滚动条来显示一张bmp图片,但是他涉及到3大主题
1. 运行时在对话框加载一张bitmap图片
2. 通过滚动条技术显示一张原始大小的bitmap图片
3. 通过双缓存技术实现绘图不闪烁
代码下载:
http://download.csdn.net/detail/zang141588761/9604527
运行时在对话框加载一张bitmap图片
使用类生成向导,为IDC_STATIC1创建一个名为m_st1 的CStatic控件变量
在对话框的头文件中MyDlg.h,加入如下代码
1. public:
2. CRect rectStaticClient;
3. int sourcex, sourcey,offsetx,offsety;
4. protected:
5. CDC m_dcMem; // Compatible Memory DC for dialog
6. HBITMAP m_hBmpOld; //Handle of old bitmap to save
7. HBITMAP m_hBmpNew; // Handle of new bitmap from file
8. BITMAP m_bmInfo; //Bitmap Information structure
在对话框的实现文件中MyDlg.cpp,加入如下代码
1. BOOL CMyDlg::OnInitDialog()
2. {
3.
4. // TODO: Add extrainitialization here
5. CClientDC dc(this);
6. m_dcMem.CreateCompatibleDC(&dc);
7. return TRUE; // return TRUEunless you set the focus to a
8. // control
9. }
重写对话框的OnPaint函数
1. void MyDlg::OnPaint()
2. {
3. if (IsIconic())
4. {
5. CPaintDC dc(this); // device context for painting
6.
7. SendMessage(WM_ICONERASEBKGND,(WPARAM) dc.GetSafeHdc(),0);
8.
9. // Center icon inclient rectangle
10. int cxIcon=GetSystemMetrics(SM_CXICON);
11. int cyIcon = GetSystemMetrics(SM_CYICON);
12. CRect rect;
13. GetClientRect(&rect);
14. int x=(rect.Width()- cxIcon+ 1)/2;
15. int y = (rect.Height()- cyIcon+1)/2;
16.
17. // Draw the icon
18. dc.DrawIcon(x, y, m_hIcon);
19. }
20. else
21. {
22. //Add the following code
23. CPaintDC dc(this);
24. dc.BitBlt(offsetx,offsety,m_size.cx,m_size.cy,
25. &m_dcMem, sourcex, sourcey,SRCCOPY);
26. CDialog::OnPaint();
27.
28. }
29. }
1. 写入如下代码,在对话框中你想要加载图片的地方
m_hBmpNew= (HBITMAP)LoadImage(
2. AfxGetInstanceHandle(), // handle to instance
3. filename, // name or identifier of the image,
4. // say,"C:\\NewFolder\\1.bmp"
5. IMAGE_BITMAP, // image types
6. 0, // desired width
7. 0, // desired height
8. LR_LOADFROMFILE);
9.
10. if( m_hBmpNew== NULL)
11. {
12. AfxMessageBox("Load ImageFailed");
13. }
14.
15. // put the HBITMAPinfo into the CBitmap
16. // (but not the bitmap itself)
17. else{
18. m_st1.GetClientRect(&rectStaticClient);
19. rectStaticClient.NormalizeRect();
20. m_size.cx=rectStaticClient.Size().cx;
21. m_size.cy=rectStaticClient.Size().cy;
22. m_size.cx= rectStaticClient.Width(); //zero based
23. m_size.cy = rectStaticClient.Height(); // zero based
24.
25. // Convert toscreen coordinates using static as base,
26. // then to DIALOG (instead of static)client coords
27. // using dialog asbase
28. m_st1.ClientToScreen(&rectStaticClient);
29. ScreenToClient(&rectStaticClient);
30.
31. m_pt.x = rectStaticClient.left;
32. m_pt.y= rectStaticClient.top;
33. GetObject( m_hBmpNew,sizeof(BITMAP),&m_bmInfo);
34. VERIFY(m_hBmpOld=(HBITMAP)SelectObject(m_dcMem, m_hBmpNew
35. ) );
36. offsetx= m_pt.x; //
37. offsety=m_pt.y; //
38. InvalidateRect(&rectStaticClient);
通过滚动条技术显示一张原始大小的bitmap图片
通过类生成向导,创建水平、垂直滚动条成员变量如下:
1. CScrollBar m_vbar; //For vertical scroll bar
2. CScrollBar m_hbar; //For horizontalscroll bar
你需要创建两个SCROLLINFO 结构体来存储水平与垂直滚动条参数,所以在对话框头文件中声明如下:
1. public:
2. CRect rectStaticClient;
3. int sourcex, sourcey,offsetx,offsety;
4. SCROLLINFO horz,vert;
在初始化的时候隐藏滚动条
1. BOOL CMyDlg::OnInitDialog()
2. {
3.
4. // TODO: Add extrainitialization here
5. CClientDC dc(this);
6. m_dcMem.CreateCompatibleDC(&dc);
7. m_vbar.ShowWindow(false); //Hide vertical scroll bar
8. m_hbar.ShowWindow(false); //Hide horizontalscroll bar
9. return TRUE; //return TRUEunless you set the
10. //focus to acontrol
11. }
当你加载图片到预先定义的图片空间会有4中情况
1) 加载图片的宽度与长度都大于图片控件。
1. m_size.cx = rectStaticClient.Width(); // zero based
2. m_size.cy= rectStaticClient.Height(); // zero based
3. GetObject( m_hBmpNew,sizeof(BITMAP),&m_bmInfo);
4.
Max. verticalScrollingRange=m_bmInfo.bmHeight-m_size.cy
5. Max.HorizontalScrollingRange=m_bmInfo.bmWidth-m_size.cx;
6. m_hbar.ShowWindow(true);
7. m_vbar.ShowWindow(true);
2) 宽度大于图片控件,高度小于等于图片控件
Max.HorizontalScrollingRange=m_bmInfo.bmWidth-m_size.cx;
offsety= m_pt.y+((m_size.cy- m_bmInfo.bmHeight)/2);
m_hbar.MoveWindow(offsetx,offsety+ m_bmInfo.bmHeight m_size.cx,18);,
m_hbar.ShowWindow(true);
m_vbar.ShowWindow(false);
3) 高度大于图片控件,宽度小于等于图片控件
Max.VerticalScrollingRange=m_bmInfo.bmHeight-m_size.cy;
offsetx= m_pt.x+((m_size.cx- m_bmInfo.bmWidth)/2);
m_vbar.MoveWindow(offsetx+ m_bmInfo.bmWidth,offsety,18,
m_size.cy);
m_hbar.ShowWindow(false);
m_vbar.ShowWindow(true);
4) 高度与宽度都小于等于图片控件
offsetx= m_pt.x+((m_size.cxn- m_bmInfo.bmWidth)/2);
offsety= m_pt.y+((m_size.cy- m_bmInfo.bmHeight)/2);
m_hbar.ShowWindow(false);
m_vbar.ShowWindow(false);
//Horizontal Scroll Info Structure
horz.cbSize=sizeof(SCROLLINFO);
horz.fMask = SIF_ALL;
horz.nMin=0;
horz.nMax = m_bmInfo.bmWidth-m_size.cx;
horz.nPage=0;
horz.nPos = 0;
horz.nTrackPos=0;
m_hbar.SetScrollInfo(&horz);
//Vertical Scroll Info Structure
vert.cbSize=sizeof(SCROLLINFO);
vert.fMask = SIF_ALL;
vert.nMin=0;
vert.nMax = m_bmInfo.bmHeight-m_size.cy;
vert.nPage=0;
vert.nTrackPos=0;
m_vbar.SetScrollInfo(&vert);
InvalidateRect(&rectStaticClient);
记住,取决于你加载的图片,滚动条的位置可能不同。所以在加载另一幅图片时,释放保存图片的空间和初始化滚动条的位置。
1. if(m_hBmpNew!= NULL)
2. DeleteObject(m_hBmpNew); //Release memoryholding bitmap
3. m_vbar.MoveWindow(offsetx+m_size.cx,offsety,18,m_size.cy);
4. // Reset position of vertical scroll bar
5. m_hbar.MoveWindow(offsetx,offsety+m_size.cy,m_size.cx,18);
6. // Reset position of horizontal scroll bar
7. if(m_hBmpNew!= NULL)
8. DeleteObject(m_hBmpNew); //Release memoryholding bitmap
9. sourcex=sourcey=0; //Set starting position of Source
10. //bitmap to becopied to (0,0)
11. sm_hBmpNew = (HBITMAP) LoadImage(
12. AfxGetInstanceHandle(), // handle to instance
13. Path of Bitmap, // name or identifier of the image
14. // (say"C:\\New Folder\\
15. //bitmap.bmp")
16. IMAGE_BITMAP, // image types
17. 0, // desired width
18. 0, // desired height
19. LR_LOADFROMFILE);
20. if( m_hBmpNew== NULL){
21. AfxMessageBox("Load ImageFailed");}
22.
23. // put the HBITMAPinfo into the CBitmap (but not the
24. // bitmap itself)
25. else{
26. m_st1.GetClientRect(&rectStaticClient);
27. rectStaticClient.NormalizeRect();
28. m_size.cx=rectStaticClient.Size().cx;
29. m_size.cy=rectStaticClient.Size().cy;
30. m_size.cx= rectStaticClient.Width(); //zero based
31. m_size.cy = rectStaticClient.Height(); // zero based
32.
33. // Convert toscreen coordinates using static as base,
34. // then to DIALOG (instead of static)client coords
35. // using dialog asbase
36. m_st1.ClientToScreen(&rectStaticClient);
37. ScreenToClient(&rectStaticClient);
38.
39. m_pt.x = rectStaticClient.left;
40. m_pt.y= rectStaticClient.top;
41. GetObject( m_hBmpNew,sizeof(BITMAP),&m_bmInfo);
42. VERIFY(m_hBmpOld=(HBITMAP)SelectObject(m_dcMem, m_hBmpNew
43. ) );
44.
45. offsetx= m_pt.x;
46. offsety=m_pt.y;
47. m_vbar.MoveWindow(offsetx+m_size.cx,offsety,18,m_size.cy);
48. // Reset position of vertical scroll bar
49. m_hbar.MoveWindow(offsetx,offsety+m_size.cy,m_size.cx,18);
50. // Reset position of horizontal scroll bar
51. horz.cbSize = sizeof(SCROLLINFO);
52. horz.fMask= SIF_ALL;
53. horz.nMin = 0;
54. horz.nMax= m_bmInfo.bmWidth-m_size.cx;
55. horz.nPage =0;
56. horz.nPos=0;
57. horz.nTrackPos=0;
58. if(m_bmInfo.bmWidth<=m_size.cx)
59. {
60. if((m_size.cx-m_bmInfo.bmWidth)==0)
61. offsetx= m_pt.x;
62. else
63. offsetx= m_pt.x+((m_size.cx-m_bmInfo.bmWidth)/2);
64. m_vbar.MoveWindow(offsetx+ m_bmInfo.bmWidth,offsety,18,
65. m_size.cy);
66. m_hbar.ShowWindow(false);
67. }
68. else
69. m_hbar.ShowWindow(true);
70. m_hbar.SetScrollInfo(&horz);
71. vert.cbSize = sizeof(SCROLLINFO);
72. vert.fMask= SIF_ALL;
73. vert.nMin = 0;
74. vert.nMax= m_bmInfo.bmHeight-(m_size.cy);
75. vert.nPage = 0;
76. vert.nTrackPos=0;
77. if(m_bmInfo.bmHeight<=m_size.cy)
78. {
79. if((m_size.cy-m_bmInfo.bmHeight)==0)
80. offsety= m_pt.y;
81. else
82. offsety= m_pt.y+((m_size.cy-m_bmInfo.bmHeight)/2);
83. m_hbar.MoveWindow(offsetx,offsety+m_bmInfo.bmHeight,
84. m_size.cx,18);
85. m_vbar.ShowWindow(false);
86. }
87. else
88. m_vbar.ShowWindow(true);
89. m_vbar.SetScrollInfo(&vert);
90.
91. InvalidateRect(&rectStaticClient);
处理WM_VSCROLL 和 WM_HSCROLL 消息
1. void CMyDlg::OnVScroll(UINT nSBCode, UINT nPos,CScrollBar*
2. pScrollBar)
3. {
4. // TODO: Add your message handler code hereand/or call default
5. switch (nSBCode)
6. {
7. case SB_TOP:
8. sourcey = 0;
9. break;
10. case SB_BOTTOM:
11. sourcey = INT_MAX;
12. break;
13. case SB_THUMBTRACK:
14. sourcey = nPos;
15. break;
16. }
17.
18. m_vbar.SetScrollPos(sourcey);
19. InvalidateRect(&rectStaticClient);
20. CDialog::OnVScroll(nSBCode, nPos, pScrollBar);
21. }
22.
23. void CMyDlg::OnHScroll(UINT nSBCode, UINT nPos,CScrollBar*
24. pScrollBar)
25. {
26. // TODO: Add your message handler code hereand/or call default
27. switch (nSBCode)
28. {
29. case SB_TOP:
30. sourcex = 0;
31. break;
32. case SB_BOTTOM:
33. sourcex = INT_MAX;
34. break;
35. case SB_THUMBTRACK:
36. sourcex= nPos;
37. break;
38. }
39. m_hbar.SetScrollPos(sourcex);
40. InvalidateRect(&rectStaticClient);
41. CDialog::OnHScroll(nSBCode, nPos, pScrollBar);
通过双缓存技术实现绘图不闪烁
为了消除在绘图的时候闪烁,你需要绘制一个内存DC,并通过BitBlt或StretchBlt函数拷贝到真正的DC。这种技术称为双缓存技术。
重载OnEraseBackground
1. BOOL MyDlg::OnEraseBkgnd(CDC* pDC)
2. {
3. // TODO: Add yourmessage handler code here and/or call default
4. if(erase)
5. return false;
6. else
7. return CDialog::OnEraseBkgnd(pDC);
8. }
重置erase 标记位为false在OnPaint函数中
1. else
2. {
3. CPaintDC dc(this);
4. dc.BitBlt(offsetx,offsety,m_size.cx,m_size.cy,
5. &m_dcMem, sourcex, sourcey,SRCCOPY);
6. erase=false;
7. CDialog::OnPaint();
8.
9. }
实现效果图如下: