0

I need to make an interactive graph using MFC

It will be sort of like an equalizer control, where the user should be able to click on a point on the equalizer drag it to change it's y axis value

I am also just starting to learn MFC

Upto this point I have used CPaintDC in the OnPaint() function to draw the graph in a dialog box. For now the graph is very simple, with rectangle boarder, filled with white colour, and 4 points on the graph. I use OnMouseMove() function to know if the cursor is inside the graph area and OnLButtonDown() function to know where the user has clicked. If the user has clicked at a position which implies I want to change the y axis value of the graph point at that location, I repaint the figure using Invalidate() and calling OnPaint() inside OnLButtonDown(). However, every time the graph has to update, I can see a flicker. it is not a problem now, but I will need to extend this graph so that it has at least 64 changeable points, with the ability to change the y axis value for a point by dragging instead of just clicking where I want it to go. Will the flickering problem increase as I increase the number of points and the complexity of the appearance of the graph? The graph will need to have axes, gridlines, labels, etc later on. Is the flickering something I should be concerned about? Is there any way I can prevent it?

----UPDATE ----
This is how I updated my OnPaint() function according to how I understood CodeDreamer's suggestion

void Cgraph_on_dlgboxDlg::OnPaint()
{
     CPaintDC dc_blt(this);
     CDC dc;
     CBitmap bmpDC;

     CRect rcClient;
     GetClientRect(rcClient);

    if (IsIconic())
    {
        // CPaintDC dc(this); // device context for painting

        SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

        // Center icon in client rectangle
        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;

        // Draw the icon
        dc.DrawIcon(x, y, m_hIcon);
    }
    else
    {
        CDialogEx::OnPaint();
    }

     dc.CreateCompatibleDC(&dc);
     bmpDC.CreateCompatibleBitmap(&dc, theGraph.width,theGraph.height );
     dc.SelectObject(&bmpDC);

    CPen pen;
    COLORREF pencolour = RGB(0, 0, 0);
    COLORREF brushcolour = RGB(0, 0, 255);
    COLORREF graphColour = RGB(0, 0, 150);

    // Draw boarder
    pen.CreatePen(PS_SOLID, 3, pencolour);
    // CBrush brush(HS_CROSS, brushcolour);
    dc.SetBkMode(TRANSPARENT);
    dc.SetMapMode(MM_TEXT);
    dc.SetViewportOrg(theGraph.x1, theGraph.y1);
     dc.SelectObject(&pen);

    // Draw graph boundary
    CPoint point1(0,0);
    point1.x = 0;
    point1.y = 0;
    CPoint point2(0,0);
    point2.x = point1.x + theGraph.width;
    point2.y = point1.y + theGraph.height;
    dc.Rectangle(CRect(point1, point2));
    pen.DeleteObject();

    // Draw Horizontal at 0
    pen.CreatePen(PS_SOLID, 1, pencolour);
    dc.SelectObject(&pen);
    dc.MoveTo(0, theGraph.height - ORG_DIST_FROM_BOTTOM);
    dc.LineTo(theGraph.width, theGraph.height - ORG_DIST_FROM_BOTTOM);
    pen.DeleteObject();

    dc.SetViewportOrg(theGraph.x1, theGraph.y1 + theGraph.height - ORG_DIST_FROM_BOTTOM); // dc.SetViewportOrg() always works relative to the clinet origin

    // Draw graph line
    pen.CreatePen(PS_SOLID, 2, graphColour);
    dc.SelectObject(&pen);
    for(int i = 0; i<NUM_OF_SECTIONS_IN_GRAPH; i++){
        dc.MoveTo(graphSamplePoints[i].x, graphSamplePoints[i].y);
        dc.LineTo(graphSamplePoints[i+1].x, graphSamplePoints[i+1].y);
    }

    // draw circles at graph sample points
    for(int i = 0; i<NUM_OF_POINTS_IN_GRAPH; i++){
        CIRCLE(dc, graphSamplePoints[i].x, graphSamplePoints[i].y, GRP_SMP_RAD);        
    }

    // dc_blt.BitBlt(0,0,rcClient.Width(), rcClient.Height(), &dc, 0, 0, SRCCOPY);
    dc_blt.BitBlt(theGraph.x1,theGraph.y1,theGraph.width, theGraph.height, &dc, 0, 0, SRCCOPY);

}

I will need to change the origins of the viewport a number of times and my guess is that this could be one of the reasons for the error. Any suggestions will be welcome.
This is what my output look like without double buffering
enter image description here
This is what it looks like with my attempt at double buffering

enter image description here

4

2 回答 2

2

在这种情况下,一般的解决方案是“双缓冲”。原理是预先创建一个兼容的内存dc用于绘制,绘制结束后输出到屏幕dc。

下面是一个代码示例。

//in OnPaint() function
CPaintDC dc(this);    
CDC dcMem;
CBitmap bmpDC;

CRect rcClient;
GetClientRect(&rcClient);

dcMem.CreateCompatibleDC(pDC);
bmpDC.CreateCompatibleBitmap(pDC, rcClient.Width(), rcClient.Height());
dcMem.SelectObject(&bmpDC);

CRect rect(0, 0, 100, 200);
dcMem.Rectangle(rect);

dc.BitBlt(0, 0, rcClient.Width(), rcClient.Height(), &dcMem, 0, 0, SRCCOPY);

下面是一些参考资料。

介绍

另一个参考

我希望这会对你有所帮助。

于 2013-07-04T07:45:54.647 回答
0

试试CMemDC MFC 类。

在您的 OnPaintFunction 中,您将拥有以下内容:

  CPaintDC   DC(this);
  CMemDC mDC(&DC, this);
  // now use mDC instead of DC

还可以在这里查看更多示例和解释。

于 2013-07-04T08:57:02.767 回答