OnPaint的使用,让窗口立即重绘的方法,CPaintDC的使用及各种DC的使用

一、CPaintDC的使用及各种DC的使用

摘自
在c++ 编程中常会见到HDC,CDC,CClientDC,CPaintDC,CWindowDC这样的类。
HDC是DC的句柄,API中的一个类似指针的数据类型。
CDC是MFC的DC的一个类。
CDC等设备上下分类,都含有一个类的成员变量:m_nHdc;即HDC类型的句柄。
CDC及其派生类的继承视图:
CObject
public |------CDC
public |------|------CClientDC
public |------|------CPaintDC
public |------|------CWindowDC
public |------|------CMetaFileDC
(注意: 除CMetaFileDC以外的三个派生类用于图形绘制.)

1.CPaintDC:封装BeginPaint和EndPaint两个API的调用。

(1)用于响应窗口重绘消息(WM_PAINT)的绘图输出。
(2)CPaintDC在构造函数中调用BeginPaint()取得设备上下文,在析构函数中调用EndPaint()释放设备上下文。 EndPaint()除了释放设备上下文外,还负责从消息队列中清除WM_PAINT消息。因此,在处理窗口重画时,必须使用CPaintDC,否则 WM_PAINT消息无法从消息队列中清除,将引起不断的窗口重画。
(3)CPaintDC也只能用在WM_PAINT消息处理之中。

2.CClientDC(客户区设备上下文): 处理显示器描述表的相关的窗体客户区域。

构造时自动调用GetDC函数,析构时自动调用ReleaseDC函数.一般应用于客户区窗口的绘制。
当需要处理一个鼠标的单击,然后马上画出一个圆,你不能等到下一个WM_PAINT的消息到来才画图,而是马上,这是就需要CclientDC了。它可以在OnPaint的外面创建一个客户区域DC。

void CMainWindow::OnLButtonDown (UINT nFlags, CPoint point)
{
        CRect rect;
        GetClientRect (&rect);

        CClientDC dc (this);
        dc.MoveTo (rect.left, rect.top);
        dc.LineTo (rect.right, rect.bottom);
        dc.MoveTo (rect.right, rect.top);
        dc.LineTo (rect.left, rect.bottom);
    }
3.CWindowDC: 处理显示器描述表相关的整个窗体区域,包括了框架和控件(子窗体)。

(1)可在非客户区绘制图形,而CClientDC,CPaintDC只能在客户区绘制图形。
(2)坐标原点是在屏幕的左上角,CClientDC,CPaintDC下坐标原点是在客户区的左上角。
(3)关联一特定窗口,允许开发者在目标窗口的任何一部分进行绘图,包含边界与标题,这种DC同WM_NCPAINT消息一起发送。

4.CMetaFileDC:与元文件相关的设备描述表关联。

二、为什么你的窗口不能立即重绘

关于 WM_PAINT事件

  系统会在多个不同的时机发送WM_PAINT消息:当第一次创建一个窗口时,当改变窗口的大小时,当把窗口从另一个窗口背后移出时,当最大化或最小化窗口时,等等,这些动作都是由系统管理的,应用只是被动地接收该消息,在消息处理函数中进行绘制操作;大多数的时候应用也需要能够主动引发窗口中的绘制操作,比如当窗口显示的数据改变的时候,这一般是通过InvalidateRect和InvalidateRgn函数来完成的。InvalidateRect和 InvalidateRgn把指定的区域加到窗口的UpdateRegion中,当应用的消息队列没有其他消息时,如果窗口的Update Region不为空时,系统就会自动产生WM_PAINT消息。
  系统为什么不在调用Invalidate时发送WM_PAINT消息呢?又为什么非要等应用消息队列为空时才发送WM_PAINT消息呢?这是因为系统把在窗口中的绘制操作当作一种低优先级的操作,于是尽可能地推后做。不过这样也有利于提高绘制的效率:两个WM_PAINT消息之间通过 InvalidateRect和InvaliateRgn使之失效的区域就会被累加起来,然后在一个WM_PAINT消息中一次得到更新,不仅能避免多次重复地更新同一区域,也优化了应用的更新操作。像这种通过InvalidateRect和InvalidateRgn来使窗口区域无效,依赖于系统在合适的时机发送WM_PAINT消息的机制实际上是一种异步工作方式,也就是说,在无效化窗口区域和发送WM_PAINT消息之间是有延迟的;有时候这种延迟并不是我们希望的,这时我们当然可以在无效化窗口区域后利用SendMessage发送一条WM_PAINT消息来强制立即重画,但不如使用Windows GDI为我们提供的更方便和强大的函数:UpdateWindow和RedrawWindow。UpdateWindow会检查窗口的Update Region,当其不为空时才发送WM_PAINT消息;RedrawWindow则给我们更多的控制:是否重画非客户区和背景,是否总是发送 WM_PAINT消息而不管Update Region是否为空等。

三、OnPaint()的使用:我从打开一个bmp文件导入一个图片在窗口显示

先了解下CBitmap,HBitmap,Bitmap区别及联系
(1)区别
HBITMAP是bitmap的指针:
  msdn中如是:Handle to a bitmap.typedef HANDLE HBITMAP;
CBitmap是mfc中封装bitmap的类:
  msdn中:Encapsulates(囊括) a Windows graphics device interface (GDI) bitmap and provides member functions to manipulate(操作) the bitmap.
BITMAP是一个结构体,封装着bitmap的一些信息。定义了逻辑位图的高,宽,颜色格式和位值。
  MSDN中如是:This structure defines the type, width, height, color format, and bit values of a bitmap.

(2)三者之间的关系转换:

HBITMAP hBitmap;
CBitmap bitmap;
BITMAP bm;
//下面是三者之间的联系:
bitmap.Attach(hBitmap);//由HBITMAP 得到关联的CBitmap
bitmap.GetBitmap(&bm); // 由CBitmap
BITMAP hBitmap=(HBITMAP)bitmap.GetSafeHandle();//由CBitmap得到相关的HBITMAP

(3)延伸理解下Attach/Detach:
  attach是把一个C++对象与一个WINDOWS对象关联,直到用detach则把关联去掉。
  如果attach了以后没有detach,则C++对象销毁的时候WINDOWS对象跟着一起销毁。attach了以后,C++对象的指针和WINDOWS对象的HWND会有一个映射关系,其作用相当于你直接用一个C++对象去Create一个WINDOWS对象,例如 CEdit edit; edit.create(…) 并且此映射是永久的,知道此对象销毁为止。
  如果用类似GetDlgItem函数也可以返回一个指针,并可以强制转换。GetDlgItem会到映射表里找。有2种映射表,一中是永久的,一种是临时的。直接用C++对象创建的WINDOWS对象或者是通过attach的对象的映射关系都被放到永久表中,否则就在临时表中创建映射。所以GetDlgItem不推荐你保存返回的指针,因为你很难保证你的WINDOWS对象跟C++对象的关联是否放在永久表中。如果映射是放在临时表中,那么在空闲时间会被自动删除。 用attcah完全是为了方便用MFC类的成员函数去操纵WINDOWS对象。

代码

// CRotateBmpDlg 对话框
class CRotateBmpDlg : public CDialogEx
{
// 构造
public:
 CRotateBmpDlg(CWnd* pParent = NULL); // 标准构造函数
 virtual ~CRotateBmpDlg();
// 对话框数据
#ifdef AFX_DESIGN_TIME
 enum { IDD = IDD_ROTATEBMP_DIALOG };
#endif
 protected:
 virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
 //LPVOID lpSrcBits;
 //LONG SrcHeight;
 //LONG SrcWidth;
 HBITMAP m_hBitmap;
 CBitmap m_bitmap;
// 实现
protected:
 HICON m_hIcon;
 // 生成的消息映射函数
 virtual BOOL OnInitDialog();
 afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
 afx_msg void OnPaint();
 afx_msg HCURSOR OnQueryDragIcon();
 DECLARE_MESSAGE_MAP()
public:
 afx_msg void OnBnClickedButton1();
 CString strFilePath;
};

初始化m_hBitmap;m_bitmap;strFilePath;

CRotateBmpDlg::CRotateBmpDlg(CWnd* pParent /*=NULL*/)
 : CDialogEx(IDD_ROTATEBMP_DIALOG, pParent)
{
 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
 strFilePath = "";
 m_hBitmap = NULL;
}
CRotateBmpDlg::~CRotateBmpDlg()
{
 /*if (lpSrcBits)
 {
  delete lpSrcBits;
 }*/
 if (m_bitmap.m_hObject)
 {
  m_bitmap.Detach();
 }
 DeleteObject(&m_hBitmap);
 DeleteObject(&m_bitmap);
}
void CRotateBmpDlg::OnBnClickedButton1()
{
 CFileDialog dlgFileOpen(TRUE, NULL, _T("*.bmp"), OFN_HIDEREADONLY,
  _T("打印文件(*.bmp)|*.bmp|所有文件(*.*)|*.*|"), this);
 if (dlgFileOpen.DoModal() == IDCANCEL)
 {
  return;
 }
 strFilePath = dlgFileOpen.GetPathName();
 CRect rect;
 this->GetClientRect(rect);
 m_hBitmap = (HBITMAP)LoadImage(NULL, strFilePath, IMAGE_BITMAP, rect.Width(), rect.Height() - 60, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
 Invalidate();//使窗口无效,产生WM_PAINT消息,WM_PAINT消息在消息队列的优先级低
//想让窗口立即重绘,可以sendmessage(WM_PAINT)或者
/*InvalidateRect(NULL, TRUE);*/ 
 /*UpdateWindow();*/
}
void CRotateBmpDlg::OnPaint()
{
 if (IsIconic())
 {
  CPaintDC dc(this); // 用于绘制的设备上下文
  SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
  // 使图标在工作区矩形中居中
  int cxIcon = GetSystemMetrics(SM_CXICON);
  int cyIcon = GetSystemMetrics(SM_CYICON);
  CRect rect;
  GetClientRect(&rect);
  int x = (rect.Width() - cxIcon + 1) / 2;
  int y = (rect.Height() - cyIcon + 1) / 2;
  // 绘制图标
  dc.DrawIcon(x, y, m_hIcon);
 }
 else
 {
  if (strFilePath.GetLength() > 4 && m_hBitmap)
  {
   if (m_bitmap.m_hObject)
   {
    m_bitmap.Detach();
   }
   m_bitmap.Attach(m_hBitmap);
   CPaintDC dc(this);//构造会调用BeginPaint()和析构EndPaint(),并从消息队列中清除WM_PAINT消息。
   CRect rect;
   GetClientRect(rect);
   CDC memDC;
   memDC.CreateCompatibleDC(&dc);
   
   BITMAP bmp;
   m_bitmap.GetBitmap(&bmp);
   CBitmap *pBmpOld = memDC.SelectObject(&m_bitmap);
  /* int panelsize = 0;
   if (bitmapinfo.bmBitsPixel < 16)
   {
    panelsize = pow(2, bitmapinfo.bmBitsPixel) * sizeof(RGBQUAD);
   }*/
   dc.StretchBlt(0, 0, bmp.bmWidth, bmp.bmHeight, &memDC, 0, 0, bmp.bmWidth, bmp.bmHeight, SRCCOPY);
   memDC.SelectObject(pBmpOld);
   DeleteDC(memDC);
  }
  else
  {
   CDialogEx::OnPaint();//进去OnPaint()会调用BeginPaint()和EndPaint(),并从消息队列中清除WM_PAINT消息。
  }
 }
}

OnPaint()进去了后一定要清除队列的WM_PAINT这样才不会重复响应,所以每个分支必须调用且只能调用一次CPaintDC()或者CDialogEx::OnPaint();

  • 1
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值