Dict.CN 在线词典, 英语学习, 在线翻译

学海苦作舟,书山勤为径

留下点回忆

常用链接

统计

积分与排名

Denoise

English study

Web技术

数据压缩

一些连接

最新评论

怎么样在VC中使用Static控件来显示图像?

   在使用MFC编写程序时,经常需要显示图像;根据GDI的要求,需要一个DC(设备内容)作为显示的基础;实际上任何Windows的窗口都可以作为一个DC,我们可以通过APIMFC的函数来得到,例如:

            HDC GetDC (HWND);---这里的HWND是窗口的句柄

            CDC * CWnd::GetDC ();---这里的CWnd实际上是任何从CWnd的类

当我们使用MFC的单文档或多文档框架时,我们可以使用CView作为图像显示的DC,这个时候我们将绘制图像的操作放在OnDraw中就可以了;当窗口无效或更新的时候,框架会自动调用该函数来重新绘制图像;这里没有什么问题,我们主要来谈谈另外一种模式:当你需要在一个基于Dialog程序或一个CDialog控件中显示图像的问题。

实际上什么控件都可以作为图像显示的DC,他们可以是按钮、图片控件、Static控件等,只要有窗口的控件都可以得到DC。这里仅以Static控件作为图像显示的控件来介绍。

首先看我程序的基本逻辑:

Static1.JPG


源文件后面的按钮是用来选择位图文件的;而下面的图像显示区域是用来显示图像的Static控件;当设置好要显示的图像文件以后,图像就自动在Static中画出来。

l         第一次

一开始,我在CDialog对应的按钮处理程序中调用显示图像的代码,代码如下(IDC_PICVIEWStaticID):

staticcodes.JPG
然后在
CImageCntDlg::OnPaint中也调用ShowImage(TRUE);然后编译运行。一开始还可以,选择BMP文件之后也可以正确选择,但当激活另一个程序(也就是隐藏了该窗口),然后再激活这个程序,这个时候发现Static中图像显示闪烁一下后变成灰色的背景。到底什么发生了?

l         到底什么发生了?

上面的现象告诉我们,即使我们将ShowImage放在CDialogWM_PAINT处理消息中,在某些情况下仍然不能正确的处理。

从现象看,我们的图像应该是先画出来了,但然后又被清除了;感觉是PAINT的消息处理不正确。

没有办法,自己想不同那么就使用工具。VC自带的Spy++是个很好的工具,打开Spy++;运行程序,然后打开某个图像,这个时候在Spy++中找到对应的窗口,然后观察与该窗口相关的消息;如图:

 

staticspy.JPG


这个时候我们切换程序窗口,先让其被覆盖,然后再显示;观察Spy++的结果,发现这样几条记录:

staticspy1.JPG
可以看到在
WM_PAINT消息之后,窗口又收到了很多WM_CTLCOLORBTNWM_CTLCOLORSTATIC等多条消息,查询MSDN知道这些是主窗体收到的绘制窗口上空间的消息;实际上,主窗体在处理WM_PAINT消息的时候也需要绘制发送消息给各个控件有机会绘制自己;而对应的消息是控件本身的WM_PAINT消息。

好了,终于找到原因了,我们在CDialogOnPaint中调用ShowImage之后不久,OnPaint也主动通知各控件重绘,结果这个时候Static上的图像给覆盖了。

l         定义自己的Static控件

知道原因就好办了,只需要将ShowImage放到适当的地方就可以了。这里需要自己从CStatic继承一个自己的类,然后重写其OnPaint函数,在其中显示图像。代码如下:

void CImageWnd::OnPaint()
{
    HDC hDC 
= ::GetDC(m_hWnd);
    PAINTSTRUCT paintStruct;
    ::BeginPaint(m_hWnd,
&paintStruct);
    DrawImage(m_strImageName);
    TRACE(
"CImageWnd OnPaint!\n");
    ::EndPaint(m_hWnd,
&paintStruct);
}


void CImageWnd::DrawImage(CString imageName)
{
    
if(imageName == ""return ;    
    m_hBitmap 
= NULL;
    m_hBitmap 
=(HBITMAP)::LoadImage  (NULL,imageName.GetBuffer(),
           IMAGE_BITMAP, 
00, LR_DEFAULTCOLOR | LR_LOADFROMFILE);
    
if(m_hBitmap == NULL) return ;
    CDC 
* pDC = GetDC();
    CDC cdc;
    cdc.CreateCompatibleDC(pDC);
    cdc.SelectObject(m_hBitmap);
    
int startLeft = 0,startTop = 0;
    BITMAP bmpInfo;
    GetObject(m_hBitmap, 
sizeof(BITMAP), &bmpInfo);
    GetClientRect(
&m_picViewRect);
    startLeft 
= (m_picViewRect.right-bmpInfo.bmWidth)/2;
    
if(startLeft <0) startLeft = 0;
    startTop 
= (m_picViewRect.bottom-bmpInfo.bmHeight)/2;
    
if(startTop<0) startTop = 0;

    pDC
->BitBlt(startLeft,startTop,
       m_picViewRect.right
-startLeft,
       m_picViewRect.bottom
-startTop,&cdc,0,0,SRCCOPY);
}

 

另外CImageWnd头文件如此定义:

class CImageWnd : public CStatic

{

    DECLARE_DYNAMIC(CImageWnd)

public:

    CImageWnd();

    
virtual ~CImageWnd();

    
void ShowImage(CString imageName)

    
{

       SetImageName(imageName);

       DrawImage(imageName);

    }


    
void DrawImage(CString imageName);

    
void SetImageName(CString imageName)

    
{

       m_strImageName 
= imageName;

    }


protected:

    afx_msg 
void OnPaint();

    DECLARE_MESSAGE_MAP()

protected:

    HBITMAP m_hBitmap;

    RECT   m_picViewRect;

    CString m_strImageName;

}
;


   在原来调用ShowImage(TRUE)的地方这样调用m_picView.ShowImage(filename);m_picViewStatic对应的CImageWnd类型成员)。

好了,编译测试。这次发现切换没有问题了;但当我们打开文件选择对话框,然后在窗口上面覆盖Static左右拖动的时候发现,一会以后图像不在显示了。那么这次又为什么

实际上上面的写法有问题的,只是赶时间随手写的。

l         追踪最后的凶手

没有办法,我插入了许多日志来观察变量的设置情况,结果发现DrawImage 中的m_hBitmap变量在一段时间后变成0了,那么肯定显示不了图像了。

想了想,GDI资源中HANDLE有一定的数目限制,这里只创建HANDLE,而从没有释放过,所以一段时间之后HANDLE的上限达到,而不能再创建新的HANDLE了。那么就删除不用的HANDLE吧。

l         最后的代码

 1void CImageWnd::DrawImage(CString imageName)
 2{
 3    if(imageName == ""return ;
 4    TRACE("Begin CImageWnd::DrawImage1 imageName= %s!\n",imageName.GetBuffer());
 5    if((m_hBitmap&&imageName != m_strImageName)||
 6        (m_hBitmap == NULL))
 7    {
 8        DeleteObject(m_hBitmap);
 9        m_hBitmap = NULL;
10        m_hBitmap =(HBITMAP)::LoadImage(NULL,imageName.GetBuffer(),
11            IMAGE_BITMAP, 00, LR_DEFAULTCOLOR | LR_LOADFROMFILE);
12    }

13    TRACE("Begin CImageWnd::DrawImage2 m_hBitmap=%d!\n",m_hBitmap);
14    if(m_hBitmap == NULL) return ;
15    TRACE("Begin CImageWnd::DrawImage3!\n");
16
17    CDC * pDC = GetDC();
18    CDC cdc;
19    cdc.CreateCompatibleDC(pDC);
20    cdc.SelectObject(m_hBitmap);
21    int startLeft = 0,startTop = 0;
22    BITMAP bmpInfo;
23    GetObject(m_hBitmap, sizeof(BITMAP), &bmpInfo);
24    GetClientRect(&m_picViewRect);
25    startLeft = (m_picViewRect.right-bmpInfo.bmWidth)/2;
26    if(startLeft <0) startLeft = 0;
27    startTop = (m_picViewRect.bottom-bmpInfo.bmHeight)/2;
28    if(startTop<0) startTop = 0;
29
30    pDC->BitBlt(startLeft,startTop,
31        m_picViewRect.right-startLeft,
32        m_picViewRect.bottom-startTop,&cdc,0,0,SRCCOPY);
33    TRACE("End of CImageWnd::ShowImage!\n");
34    //DeleteObject(m_hBitmap);
35    //m_hBitmap = NULL;
36}


好了,在编译运行。这次一切正常。

 

通过这个例子,我们了解几个问题

1.  CDialog首先画自己,然后再画控件

2.  选择合适的时候重绘图像

3.  GDI对象的有限的,达到一定数目之后就不能创建了,所有需要释放,以免资源浪费

欢迎讨论。

posted on 2006-02-19 00:11 笨笨 阅读(13133) 评论(12)  编辑 收藏 引用

评论

# re: 怎么样在VC中使用Static控件来显示图像? 2006-02-19 00:54 picker

这种方法不太符合ms的期望。自画dialog中的static,一般的做法是,在dialog的消息处理函数中响应WM_CTLCOLORSTATIC(wParam:static的hdc,lParam:static的hwnd)消息,自定义的处理完毕后,返回一个画刷,static的default处理函数会用这个画刷刷一遍static,如果不要static因再刷一遍而毁了自画的结果,那么就返回GetStockObject(NULL_BRUSH),mfc中也是一样的  回复  更多评论   

# re: 怎么样在VC中使用Static控件来显示图像? 2006-02-19 15:15 笨笨

picker说的有道理啊!我也试验了你的说法,完全可以。但我想这里是否符合MS的期望不是太重要,单独继承一个控件的好处是明显的,代码被分开处理了,控件只处理自身相关的东西,也就是其背景由其自身来话,更符合思维习惯了。谢谢你的建议,我有学习了一种新做法。  回复  更多评论   

# re: 怎么样在VC中使用Static控件来显示图像? 2006-05-08 16:27 zjy

请问
DrawImage(..) 是在什么头文件中定义, 为什么我的程序老是说这个函数没定义,望指教  回复  更多评论   

# re: 怎么样在VC中使用Static控件来显示图像? 2006-05-09 08:41 笨笨

上面有写,仔细看看  回复  更多评论   

# re: 怎么样在VC中使用Static控件来显示图像? 2006-06-18 21:38 uu

看贴要留言!  回复  更多评论   

# re: 怎么样在VC中使用Static控件来显示图像? 2008-04-26 16:51 Paranoia

可否将ShowImage放在CDialog::OnPaint();后  回复  更多评论   

# re: 怎么样在VC中使用Static控件来显示图像?[未登录] 2008-04-30 10:35 michael

多谢了!最近也在搞这个,头都大了,多交流啊  回复  更多评论   

# re: 怎么样在VC中使用Static控件来显示图像? 2008-07-22 11:10 wxw

开始图片刷不出来,当点击一下对话框后才能显示图片,如果对话框失去焦点后,再获得焦点,还是要从新点击才有图片显示。不知道为何?????
  回复  更多评论   

# re: 怎么样在VC中使用Static控件来显示图像? 2008-07-22 11:11 wxw

还有就是定义的paint()从来没有调用过,楼主能说明一下么,本人愚钝,第一次做界面上的东西。  回复  更多评论   

# re: 怎么样在VC中使用Static控件来显示图像? 2008-07-22 16:46 wxw

问题解决了,多谢了。  回复  更多评论   

# re: 怎么样在VC中使用Static控件来显示图像? 2008-11-03 22:12 zxx

请教:可不可以在一个循环体中对多个static框的caption同时设置?
如下语句可以吗:
for(i=IDC_STATICL00;i<IDC_STATICL25;++ )
((CStatic*)GetDlgItem(i))->SetFont(Font,true);
编译报错,要怎么做?
因为不想对24个static框一条一条写。
有好的办法嘛?  回复  更多评论   

# re: 怎么样在VC中使用Static控件来显示图像? 2013-05-04 22:50 飞天狐

为什么编译时说没有定义:right,bottom,等变量啊,(在vs2010平台,变量类型为:CStatic,)  回复  更多评论   


只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理