我要用VC做一个SDI的数据采集程序。我参考了人家用VC做的软件。点击菜单会出现表格显示的各组数据,再右键点击表格的上下文菜单在表格的下面可以画出数据平均值曲线图。请问这是怎么做出来的?    点击表格的Title可以选中本列或本行数据。选中列数据或行数据后,再右键点击上下文菜单可以插入列、删除列。点击单元格可以修改数据。
      
     这是用LineTo(),TextOut()等函数在视图类中画出来的表格和曲线(如果是这样的话,好象不好插入列、删除列、修改单元格数据),还是用第三方控件像DataGrid,FlexGrid等做的(如果是这样的话,应该用FormView,并把背景换成白色,跟视图窗口一样。)????

解决方案 »

  1.   

    TO  楼上的3位热心人:
       
      要么我把人家做的程序发给你们,你们运行一下程序,帮我分析是用什么技术做的.好吗?
      留下大家的EMAIL地址!!!
      

  2.   

    楼主发给我的邮件已收到,你要实现的功能并不是简单几行代码能实现.1 如果只是简单的表格可以用DATAGRID,但这个控件没有打印功能,所以要打印的话建议用truedbgrid来替换DATAGRID吧.在VC中使用控件最好在"DIALOG"中用,如果是SDI的话就用FORMVIEW吧.2 绘制数据曲线我以前做过,好像也有个控件是专门绘画工业曲线的,我实现这个功能的时候是自己编码计算实现的,所以没有那个控件,也提供不了给你,好像是在WWW.VCCODE.COM上看到的!!
      

  3.   

    请楼主发一个给我看看。
    [email protected]
      

  4.   

    TO   j805:
       请看后回复我!  请留下大家的EMAIL地址,帮我分析是用什么技术做的.好吗?
      

  5.   

    to j805(这儿):
      如果说是画出来的.那么怎么修改某一个单元格里的内容呢?有怎么插入,删除一行呢? 怎么获得某一方格的句柄?
      

  6.   

    Step 1: Derive a class from CListCtrl
    In the code below, CMyListCtrl is the name used for the derived class. You can also derive a class from CListView if you need this functionality in a CView rather than in a control. If you are already working with a sub-class of CListCtrl, you can make the modifications to that class. 
    Step 2: Define HitTestEx()
    Define an extended HitTest function for the CMyListCtrl class. This function will determine the row index that the point falls over and also determine the column. The HitTestEx() has already been listed and explained in an earlier section and is listed here again for completeness. We need this function if the user interface to initiate the edit is a mouse click or a double click. See the section "Detecting column index of the item clicked". 
    // HitTestEx - Determine the row index and column index for a point
    // Returns - the row index or -1 if point is not over a row
    // point - point to be tested.
    // col - to hold the column index
    int CMyListCtrl::HitTestEx(CPoint &point, int *col) const
    {
    int colnum = 0;
    int row = HitTest( point, NULL );

    if( col ) *col = 0; // Make sure that the ListView is in LVS_REPORT
    if( (GetWindowLong(m_hWnd, GWL_STYLE) & LVS_TYPEMASK) != LVS_REPORT )
    return row; // Get the top and bottom row visible
    row = GetTopIndex();
    int bottom = row + GetCountPerPage();
    if( bottom > GetItemCount() )
    bottom = GetItemCount();

    // Get the number of columns
    CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
    int nColumnCount = pHeader->GetItemCount(); // Loop through the visible rows
    for( ;row <=bottom;row++)
    {
    // Get bounding rect of item and check whether point falls in it.
    CRect rect;
    GetItemRect( row, &rect, LVIR_BOUNDS );
    if( rect.PtInRect(point) )
    {
    // Now find the column
    for( colnum = 0; colnum < nColumnCount; colnum++ )
    {
    int colwidth = GetColumnWidth(colnum);
    if( point.x >= rect.left 
    && point.x <= (rect.left + colwidth ) )
    {
    if( col ) *col = colnum;
    return row;
    }
    rect.left += colwidth;
    }
    }
    }
    return -1;
    }Step 3: Add function to initiate the edit
    The user interface to initiate an edit for a sub item may be click on an already selected row, a double click or even a push button. We define a helper function to set up the edit control. The helper function takes only the row and column index of the subitem. EditSubLabel() ensures that the row as well as the column is visible before it creates the edit control. It then creates the edit control of the right size and with the proper text justification. The edit control created is of the class CInPlaceEdit which we will define later. 
    // EditSubLabel - Start edit of a sub item label
    // Returns - Temporary pointer to the new edit control
    // nItem - The row index of the item to edit
    // nCol - The column of the sub item.
    CEdit* CMyListCtrl::EditSubLabel( int nItem, int nCol )
    {
    // The returned pointer should not be saved // Make sure that the item is visible
    if( !EnsureVisible( nItem, TRUE ) ) return NULL; // Make sure that nCol is valid
    CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
    int nColumnCount = pHeader->GetItemCount();
    if( nCol >= nColumnCount || GetColumnWidth(nCol) < 5 )
    return NULL; // Get the column offset
    int offset = 0;
    for( int i = 0; i < nCol; i++ )
    offset += GetColumnWidth( i ); CRect rect;
    GetItemRect( nItem, &rect, LVIR_BOUNDS ); // Now scroll if we need to expose the column
    CRect rcClient;
    GetClientRect( &rcClient );
    if( offset + rect.left < 0 || offset + rect.left > rcClient.right )
    {
    CSize size;
    size.cx = offset + rect.left;
    size.cy = 0;
    Scroll( size );
    rect.left -= size.cx;
    } // Get Column alignment
    LV_COLUMN lvcol;
    lvcol.mask = LVCF_FMT;
    GetColumn( nCol, &lvcol );
    DWORD dwStyle ;
    if((lvcol.fmt&LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
    dwStyle = ES_LEFT;
    else if((lvcol.fmt&LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
    dwStyle = ES_RIGHT;
    else dwStyle = ES_CENTER; rect.left += offset+4;
    rect.right = rect.left + GetColumnWidth( nCol ) - 3 ;
    if( rect.right > rcClient.right) rect.right = rcClient.right; dwStyle |= WS_BORDER|WS_CHILD|WS_VISIBLE|ES_AUTOHSCROLL;
    CEdit *pEdit = new CInPlaceEdit(nItem, nCol, GetItemText( nItem, nCol ));
    pEdit->Create( dwStyle, rect, this, IDC_IPEDIT );
    return pEdit;
    }
      

  7.   

    Step 4: Handle the scroll messages
    The CInPlaceEdit class is designed to destroy the edit control and delete the object when it loses focus. Clicking on the scrollbars of the ListView control does not take away the focus from the edit control. We therefore add handlers for the scrollbar messages which force focus away from the edit control by setting the focus to the list view control itself. void CMyListCtrl::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
    {
    if( GetFocus() != this ) SetFocus();
    CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
    }void CMyListCtrl::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
    {
    if( GetFocus() != this ) SetFocus();
    CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
    }
    Step 5: Handle EndLabelEdit
    Like the built in edit control for the first column, our edit control also sends the LVN_ENDLABELEDIT notification when the edit is completed. If this notification message isn’t already being handled, add a handler so that any changes made with the edit control can be accepted. void CMyListCtrl::OnEndLabelEdit(NMHDR* pNMHDR, LRESULT* pResult)
    {
    LV_DISPINFO *plvDispInfo = (LV_DISPINFO *)pNMHDR;
    LV_ITEM *plvItem = &plvDispInfo->item; if (plvItem->pszText != NULL)
    {
    SetItemText(plvItem->iItem, plvItem->iSubItem, plvItem->pszText);
    }
    *pResult = FALSE;
    }
    Step 6: Add means for the user to initiate the edit
    A suggested method for starting an edit of a subitem is to click on a subitem when the item already has the focus. You may choose to provide a different method. The code below is the handler for the WM_LBUTTONDOWN message. An edit control is created to edit a subitem if the subitem is clicked after the item already has the focus. The code checks for the LVS_EDITLABELS style before creating the edit control. It also does not activate the edit control for the first column since editing of the first column is already supported by the list view control. 
    void CMyListCtrl::OnLButtonDown(UINT nFlags, CPoint point)
    {
    int index;
    CListCtrl::OnLButtonDown(nFlags, point); int colnum;
    if( ( index = HitTestEx( point, &colnum )) != -1 )
    {
    UINT flag = LVIS_FOCUSED;
    if( (GetItemState( index, flag ) & flag) == flag && colnum > 0)
    {
    // Add check for LVS_EDITLABELS
    if( GetWindowLong(m_hWnd, GWL_STYLE) & LVS_EDITLABELS )
    EditSubLabel( index, colnum );
    }
    else
    SetItemState( index, LVIS_SELECTED | LVIS_FOCUSED ,
         LVIS_SELECTED | LVIS_FOCUSED); 
    }
    }
    Step 7: Subclass the CEdit class
    We need to subclass the CEdit class to provide for our special requirement. The main requirements placed on this class is that 
    It should send the LVN_ENDLABELEDIT message when edit is complete 
    It should expand to accommodate the text 
    It should destroy itself when the edit is complete 
    The edit should be terminated when the user presses the Escape or the Enter key or when the edit control loses focus. 
    The listing of the header file precedes that of the implementation file. The CInPlaceEdit declares four private variables. These are used when the control sends the LVN_ENDLABELEDIT notification. 
    // InPlaceEdit.h : header file
    ///////////////////////////////////////////////////////////////////////////////
    // CInPlaceEdit windowclass CInPlaceEdit : public CEdit
    {
    // Construction
    public:
    CInPlaceEdit(int iItem, int iSubItem, CString sInitText);// Attributes
    public:// Operations
    public:// Overrides
    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CInPlaceEdit)
    public:
    virtual BOOL PreTranslateMessage(MSG* pMsg);
    //}}AFX_VIRTUAL// Implementation
    public:
    virtual ~CInPlaceEdit(); // Generated message map functions
    protected:
    //{{AFX_MSG(CInPlaceEdit)
    afx_msg void OnKillFocus(CWnd* pNewWnd);
    afx_msg void OnNcDestroy();
    afx_msg void OnChar(UINT nChar, UINT nRepCnt, UINT nFlags);
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    //}}AFX_MSG DECLARE_MESSAGE_MAP()
    private:
    int m_iItem;
    int m_iSubItem;
    CString m_sInitText;
    BOOL    m_bESC;   // To indicate whether ESC key was pressed
    };/////////////////////////////////////////////////////////////////////////////
    The listing of the implementation file now follows. The CInPlaceEdit constructor simply saves the values passed through its arguments and initializes m_bESC to false. The overridden PreTranslateMessage() is to ascertain that certain key strokes do make it to the edit control. The escape key and the enter key are normally pre-translated by the CDialog or the CFormView object, we therefore specifically check for these and pass it on to the edit control. The check for GetKeyState( VK_CONTROL) makes sure that key combinations such as Ctrl-C, Ctrl-V and Ctrl-X get forwarded to the edit control. 
    OnKillFocus() sends of the LVN_ENDLABELEDIT notification and destroys the edit control. The notification is sent to the parent of the list view control and not to the list view control itself. When sending the notification, the m_bESC member variable is used to determine whether to send a NULL string. The OnNcDestroy() function is the appropriate place to destroy the C++ object. The OnChar() function ends the edit if the Escape or the Enter key is pressed. It does this by setting focus to the list view control which force the OnKillFocus() of the edit control to be called. For any other character, the OnChar() function lets the base class function take care of it before it tries to determine if the control needs to be resized. The function first gets the extent of the new string using the proper font and then compares it to the current dimension of the edit control. If the string is too long to fit within the edit control, it resizes the edit control after checking the parent window ( the list view control ) to determine if there is space for the edit control to grow. 
      

  8.   

    The OnCreate() function creates the edit control and initializes it with the proper values. // InPlaceEdit.cpp : implementation file
    //#include "stdafx.h"
    #include "InPlaceEdit.h"#ifdef _DEBUG
    #define new DEBUG_NEW
    #undef THIS_FILE
    static char THIS_FILE[] = __FILE__;
    #endif/////////////////////////////////////////////////////////////////////////////
    // CInPlaceEditCInPlaceEdit::CInPlaceEdit(int iItem, int iSubItem, CString sInitText)
    :m_sInitText( sInitText )
    {
    m_iItem = iItem;
    m_iSubItem = iSubItem;
    m_bESC = FALSE;
    }CInPlaceEdit::~CInPlaceEdit()
    {
    }
    BEGIN_MESSAGE_MAP(CInPlaceEdit, CEdit)
    //{{AFX_MSG_MAP(CInPlaceEdit)
    ON_WM_KILLFOCUS()
    ON_WM_NCDESTROY()
    ON_WM_CHAR()
    ON_WM_CREATE()
    //}}AFX_MSG_MAP
    END_MESSAGE_MAP()/////////////////////////////////////////////////////////////////////////////
    // CInPlaceEdit message handlersBOOL CInPlaceEdit::PreTranslateMessage(MSG* pMsg)
    {
    if( pMsg->message == WM_KEYDOWN )
    {
    if(pMsg->wParam == VK_RETURN
    || pMsg->wParam == VK_DELETE
    || pMsg->wParam == VK_ESCAPE
    || GetKeyState( VK_CONTROL)
    )
    {
    ::TranslateMessage(pMsg);
    ::DispatchMessage(pMsg);
    return TRUE;      // DO NOT process further
    }
    } return CEdit::PreTranslateMessage(pMsg);
    }
    void CInPlaceEdit::OnKillFocus(CWnd* pNewWnd)
    {
    CEdit::OnKillFocus(pNewWnd); CString str;
    GetWindowText(str); // Send Notification to parent of ListView ctrl
    LV_DISPINFO dispinfo;
    dispinfo.hdr.hwndFrom = GetParent()->m_hWnd;
    dispinfo.hdr.idFrom = GetDlgCtrlID();
    dispinfo.hdr.code = LVN_ENDLABELEDIT; dispinfo.item.mask = LVIF_TEXT;
    dispinfo.item.iItem = m_iItem;
    dispinfo.item.iSubItem = m_iSubItem;
    dispinfo.item.pszText = m_bESC ? NULL : LPTSTR((LPCTSTR)str);
    dispinfo.item.cchTextMax = str.GetLength(); GetParent()->GetParent()->SendMessage( WM_NOTIFY, GetParent()->GetDlgCtrlID(), 
    (LPARAM)&dispinfo ); DestroyWindow();
    }void CInPlaceEdit::OnNcDestroy()
    {
    CEdit::OnNcDestroy(); delete this;
    }
    void CInPlaceEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
    {
    if( nChar == VK_ESCAPE || nChar == VK_RETURN)
    {
    if( nChar == VK_ESCAPE )
    m_bESC = TRUE;
    GetParent()->SetFocus();
    return;
    }
    CEdit::OnChar(nChar, nRepCnt, nFlags); // Resize edit control if needed // Get text extent
    CString str; GetWindowText( str );
    CWindowDC dc(this);
    CFont *pFont = GetParent()->GetFont();
    CFont *pFontDC = dc.SelectObject( pFont );
    CSize size = dc.GetTextExtent( str );
    dc.SelectObject( pFontDC );
    size.cx += 5;     // add some extra buffer // Get client rect
    CRect rect, parentrect;
    GetClientRect( &rect );
    GetParent()->GetClientRect( &parentrect ); // Transform rect to parent coordinates
    ClientToScreen( &rect );
    GetParent()->ScreenToClient( &rect ); // Check whether control needs to be resized
    // and whether there is space to grow
    if( size.cx > rect.Width() )
    {
    if( size.cx + rect.left < parentrect.right )
    rect.right = rect.left + size.cx;
    else
    rect.right = parentrect.right;
    MoveWindow( &rect );
    }
    }int CInPlaceEdit::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
    if (CEdit::OnCreate(lpCreateStruct) == -1)
    return -1; // Set the proper font
    CFont* font = GetParent()->GetFont();
    SetFont(font); SetWindowText( m_sInitText );
    SetFocus();
    SetSel( 0, -1 );
    return 0;
    }
      

  9.   


       请大家继续讨论,过几天我会分分我会给大家的!   请留下大家的EMAIL地址,我把程序发给大家帮我分析是用什么技术做的.好吗?
      

  10.   

    up![email protected]
    俺也在做曲线绘制。
    可以参考参考,探讨探讨。
      

  11.   

    换了163的邮箱,楼主发的程序已收到,大致看了一下。用vc作的话,可以:
    1、单文档多视图,连接数据库。
    2、关于试验数据曲线图部分。根据它在菜单上也能弹出对话框作曲线图来看,应该是封装了一个绘图模板,比如DLL,可以在dialog或formview上通过指定rect然后调用绘图dll就可以自适应大小绘制(这个应该不难)。如果是动态变化的曲线图那要费一些功夫设计算法。
    3、试验记录里的试验数据,可以是个formview,做背景设置就行。然后lineto、textout什么的,编辑单元格数据可以create一个跟单元格大小相同的数据,与删除添加列一样通过OnLButtonDown来响应应该就可以了。也就是楼上的楼上的楼上。那个浪子的方法吧(俺没test,看他老人家的几个step,就是这个意思罢)
    4、或许应该还有更好的方法。
      

  12.   

    笔误:编辑单元格数据可以create一个跟单元格大小相同的edit。