我想在一个picture控件上画图,从网上下载程序已经实现
我从这个网址:http://blog.csdn.net/zerocnd/archive/2009/08/31/4503939.aspx,复制一个程序:BYTE *g_pBits; //这里是位图阵列数据
HDC g_hMemDC;
HBITMAP g_hBmp, g_hOldBmp;
CDC *pDC;
CStatic *pic;
int width, height;
CRect rect;
int i, x0, y0;pDC=GetDlgItem(IDC_PICTURE_1)->GetDC();
pic=(CStatic*)GetDlgItem(IDC_PICTURE_1);
pic->GetClientRect(&rect);
width=rect.Width();
height=rect.Height();
g_hMemDC=::CreateCompatibleDC(pDC->m_hDC);BYTE bmibuf[sizeof(BITMAPINFO)+256*sizeof(RGBQUAD)];
memset(bmibuf, 0, sizeof(bmibuf));
BITMAPINFO *pbmi=(BITMAPINFO*)bmibuf;pbmi->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
pbmi->bmiHeader.biWidth=width;
pbmi->bmiHeader.biHeight=height;
pbmi->bmiHeader.biPlanes=1;
pbmi->bmiHeader.biBitCount=24;
pbmi->bmiHeader.biCompression=BI_RGB;g_hBmp=::CreateDIBSection(g_hMemDC, pbmi, DIB_RGB_COLORS, (void**)&g_pBits, 0, 0);
g_hOldBmp=(HBITMAP)::SelectObject(g_hMemDC, g_hBmp);
BitBlt(g_hMemDC, 0, 0, width, height, pDC->m_hDC, 0, 0, SRCCOPY);//修改图像内容:g_pBits
for(i=0; i<width*height*3; i++)
{
g_pBits[i]=0;
}x0=width/2;
y0=height/2;g_pBits[y0*width*3+x0*3]=255;
g_pBits[y0*width*3+x0*3+1]=255;
g_pBits[y0*width*3+x0*3+2]=255;
BitBlt(pDC->m_hDC, 0, 0, width, height, g_hMemDC, 0, 0, SRCCOPY);
SelectObject(g_hMemDC, g_hOldBmp);   现在已经可以正常绘图。但是,如果这个picture被别的窗口覆盖后,或切换到别的窗口然后再回来,那么先前画的图就没有了。
   请问各位达人,如何解决这个问题?多谢了。

解决方案 »

  1.   

    你这段代码在OnPaint中调用了么?
      

  2.   

    如果在OnPaint实现的(调用那段代码)
    那么 来回切换时 系统会自动调用到那些代码 就没有这个问题了 
      

  3.   

        回复两位,没有在OnPaint中调用。我看了一些网上的文章,似乎要在picture控件的OnPaint()中调用自己的绘图函数,但是,我不知道如何找到picture控件的OnPaint()函数,还请指点?
      

  4.   

    晕。要响应picture控件的OnPaint,就需要派生子类来完成。你在对话框的OnPaint中调用试试吧。
      

  5.   

    绘图操作要在OnPaint或者 OnDraw函数中写上。
    不然重新刷新的时候,OnPaint会调用 ,但你的绘画的内容又不在这里写上,当然就看不到你的内容了。
      

  6.   

        谢谢各位指点。
         现在的问题是这样的,我从另外的线程接收数据,然后处理数据,再以曲线的形式输出到屏幕。如果在OnPaint中绘图,那么当屏幕被遮挡并重新可见后,系统会调用OnPaint进行绘图,但是这个时候,我的输出数据帧未必处理好,可能一半数据已更新,另一半还正在更新,那么屏幕输出的数据就会有错。所以我觉得这个OnPaint函数要和我的数据处理线程同步。
         那么这样处理似乎有点复杂。因为我的数据处理线程已经和上面的数据发送线程建立了同步,现在又要加上一个线程同步,并且这个程序对实时性要求高,所以觉得头痛。
         有没有更简便的方法?
      

  7.   

       是啊。我调用OnPaint的时候,肯定是把数据处理完了,然后再调用OnPaint绘图。但是系统也可能调用OnPaint,并且它调用的时间是随机的,这个时候我的数据就不一定处理好了。
      

  8.   

      我想用关键代码段试试。不管是处理数据、还是OnPaint绘图,一个在处理的时候,另外一个只有等待。不知道这样同步的效率高不高?
      

  9.   

    使用缓存,就是你画的时候在memory dc/bitmap上画,画好之后再调用显示窗口的OnPaint函数,然后再把你memory dc/bimap上的图bitblt到显示窗口上,这样应该可以避免你说的没画好显示错误的问题。
    至于怎么用memory dc/bitmap有很多资料。原理就是创建一个兼容你显示窗口的DC和bitmap对象,然后在这这个兼容的dc和bitmap上画图,最后再更新到现实窗口上。
      

  10.   

    现在已经可以正常绘图。但是,如果这个picture被别的窗口覆盖后,或切换到别的窗口然后再回来,那么先前画的图就没有了。
    ====================================================================
    如果是Picture这个控件,在Onpaint中画,不会出现这种情况
      

  11.   

    你可以保存位图,比如作为类成员变量,重绘之后不销毁,然后OnPaint里只调用BitBlt函数把位图贴到picture控件就行了。
      

  12.   

       走投无路了。
       按照各位的指点,在OnPaint外的函数中处理数据,在OnPaint中绘图。但是现在出不了图。
       在对话框中包含一个picture控件,对话框的按钮相应函数:
    CRect rect;
    my_pic *pPic=(my_pic*)GetDlgItem(IDC_PICTURE_1);
    pPic->pDC=pPic->GetDC();
    pPic->GetClientRect(&rect);
    pPic->width=(rect.Width()/4)*4;//水平像素数量必须是4的倍数
    pPic->height=rect.Height(); pPic->hMemDC=::CreateCompatibleDC(pPic->pDC->m_hDC); //创建内存dc
    BYTE bmibuf[sizeof(BITMAPINFO)+256*sizeof(RGBQUAD)]; memset(bmibuf, 0, sizeof(bmibuf));
    BITMAPINFO *pbmi=(BITMAPINFO*)bmibuf;
    pbmi->bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
    pbmi->bmiHeader.biWidth=pPic->width;
    pbmi->bmiHeader.biHeight=pPic->height;
    pbmi->bmiHeader.biPlanes=1;
    pbmi->bmiHeader.biBitCount=24;
    pbmi->bmiHeader.biCompression=BI_RGB; //创建位图
    pPic->hBmp=::CreateDIBSection(pPic->hMemDC, pbmi, DIB_RGB_COLORS, (void**)&pPic->src, 0, 0);
    //选人内存dc
    pPic->hOldBmp=(HBITMAP)::SelectObject(pPic->hMemDC, pPic->hBmp); //在这里不断修改Bmp的内容
    memset(pPic->src, 0, pPic->width*pPic->height*3);
    pPic->Invalidate(false); //绘图完毕
    Sleep(10);
    ::SelectObject(pPic->hMemDC, pPic->hOldBmp);       在picture控件的OnPaint():
          CPaintDC dc(this);
    ::BitBlt(dc.m_hDC, 0, 0, width, height, hMemDC, 0, 0, SRCCOPY);
           但是按按钮后,就是没有黑色的图。请各位大人指点。
     
      

  13.   


    Class XXX
    {
        CBitmap bitmap;//内存位图
        //...
    };BOOL XXX::OnInitDialog()
    {
        //...
        memDC.CreateCompatibleDC(&picture控件控制变量);
        bitmap.CreateCompatibleBitmap(picture控件控制变量.GetDC(), Width, Height);
        //你也可以试试用别的方法创建位图。
        memDC.SelectObject(&bitmap);
        //...
    }void XXX::Render()
    {
        memDC.MoveTo(...);
        memDC.LineTo(...);
        //绘制你的东西,对bitmap进行操作
    }按钮响应函数:
    {
        //...
        Render();
        //...
    }void XXX::OnPaint()
    {
        CPaintDC dc(&picture控件控制变量
        // TODO: 在此处添加消息处理程序代码
        CDialog::OnPaint();
        dc.BitBlt(0,0,vWidth,vHeight,&memDC,LeftTop.x,LeftTop.y,SRCCOPY);
        //这里直接把bitmap上贴到dc上。
        //因为bitmap没有销毁,所以只要OnPaint调用,就会显示,而且不用重新画。
        //最后在析构函数里把memDC和bitmap销毁就行了。
    }
      

  14.   

       to Swkjd: 按照你的方法,已经看到图像了。谢谢。
       但是不知道能不能这样,把上面的这些函数都放在一个picture控件内实现?
       实现这个要求的目的是,上级调用函数可以通过调用这个picture控件,所有的和绘图相关的操作都由这个picture控件完成,上级调用函数可以不管绘图相关的事情。   在上面的OnInitDialog()函数内实现的初始化memDC等功能,不知道在这个picture控件的哪个函数内实现?我尝试在picture控件的OnCreate函数实现,但不能显示图像。
          请问这个要求能不能实现?
      

  15.   

        我的目的就是做一个派生于CStatic的控件,这个控件实现绘图功能。
        现在基本达到这个目的,但是有个很头痛的问题,就是运行时,窗口切换到别的窗口,如果再切换过来,程序就好像死了一样,要等程序运行完后,才显示整个窗口和最后一帧的图像。但是如果不切换,则可以正常显示每一帧图像。
        我看别的绘图控件,采用定时器的方法。定时器定时启动OnPaint函数,在OnPaint函数内进行数据处理、绘图,他这样做是没有问题的。我上面的程序没有采用定时器。
        但是因为我的控件要从别的模块获取数据,所以感觉这个定时器的时长不方便设置,因为别的模块发送数据的时间不一定很准时,所以我一直想采用线程同步的方法。
        请教各位上面故障的原因,还有这样的控件是否非要采用定时器?
      

  16.   

       现在是在别的函数里处理数据,将数据复制到位图,然后Invalidate, 调用OnPaint,利用BitBlt将位图输出。
       如果窗口不切换,那么运行正常;窗口切换,则不显示图像。
      

  17.   

    你说要全部集成到picture控件中我没试过,不过我的代码意思是不管不管什么程序调用,不管什么时候绘图,只需调用Render函数,在内存中绘制,OnPaint函数只管BitBlt贴图,不做其它事情。我自己测试是窗口切换后正常刷新的。
    你是不是在窗口切换回来时重新调用Render函数,或者相关计算函数对图像进行了重绘?如果是,这点是不必要的,因为bitmap是类成员变量,位图没销毁,切换窗口时调用OnPaint只是将之前画好的位图加载,所以不用重绘,基本不耗时间。你只需要在上层系统要求你重绘的时候调用Render函数,完了在Invalidate一下就行了。比如:
    接收到上级要求重绘信息(比如人为按下重绘按钮,或者数据变化要求重绘),响应函数
    {
        //...
        Render();
        //...
        Invalidate();
    }
    此时位图已经绘制完成,之后系统会调用OnPaint函数将位图贴上。我觉得没必要把初始化写到picture控件里。而且切换窗口之后是不需要重绘的,直接贴图就行了。
      

  18.   

    如果你一定要做picture的派生类,你说写到create函数里没反应,会不会是因为create函数运行完后,这个picture实体还不存在,所以初始化没完成。你要不试试写到其它地方,比如说重载UpdateWindow,将初始化写到里面,或者实在不行就写一个Init()函数,在绘制这个控件的父窗口里(比如OnInitDialog()里)调用。这样应该可以实现封装。
      

  19.   

    老问题了,绘图放在WM_PAINT中