基于DAO的学生成绩管理软件

2016-06-19

前面我已经写过几篇关于数据库的文档,这一篇还是它的后继篇。前面都是用ODBC开发的,现在改用DAO,DAO有一个缺点,没有同步支持ACCESS的最新版本,还要转换到早期的版本,不知道微软是咋搞的。废话少说,首先看一下软件的用法:

1、对话框中有两个菜单项用于创建班级和增减科目,一项下面有两个子项。其中"增加班级"只有在选中对话框"组合控件"中的一个班级然后按"开始"按钮后,才可以使用,因为只有先得到表的结构,才能按此结构创建新表。而其它三个子项恰相反,只有程序刚打开还没有选择班级按"开始按钮"或选择"组合控件"中的"关闭班级"项按"开始"按钮后才可以使用,这是因为只有把所有班级记录集关闭后,这三项才能安全工作。

2、增加记录与以前有一点不同,打开增加记录对话框后,大家可以看到不同,因为科目是随"增减科目"变化的,所以增加记录的科目要动态变化。

3、修改记录只有选中列表控件中的一条记录后,按"修改记录"按钮才可以。

4、其它的与以前版本相同。

下面看一下开发过程:

因为科目是可以变化的,所以我没有用一个具体的CDaoRecordset类,因此大部分的功能是用SQL语句实现的。

1、首先看一下OnInitDialog():

BOOL CListDlg::OnInitDialog()
{
	CDialog::OnInitDialog();
   
    SetWindowText("学生成绩管理");
	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);
	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}
   

	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	GetDlgItem(IDC_STRAT)->EnableWindow(FALSE);
 this->Enable(FALSE);

	m_pDB=new CDaoDatabase;
	CString sPath;
	GetModuleFileName(NULL,sPath.GetBufferSetLength(MAX_PATH+1),MAX_PATH);
	sPath.ReleaseBuffer ();
	int nPos;
	nPos=sPath.ReverseFind (''\\'');
	sPath=sPath.Left (nPos);
	nPos=sPath.ReverseFind(''\\'');
    sPath=sPath.Left (nPos);
	CString lpszFile = sPath + "\\db6.mdb";
	try{
		m_pDB->Open(lpszFile);
	}
	catch(CDaoException* e)
	{
		AfxMessageBox(e->m_pErrorInfo->m_strDescription,MB_ICONEXCLAMATION);
		delete m_pDB;
		e->Delete();
		return FALSE;
	}
    
 CDaoTableDefInfo tabInfo;
	int nTableDefCount = m_pDB->GetTableDefCount();
	for (int i = 0; i < nTableDefCount; i++)
	{
		m_pDB->GetTableDefInfo(i,tabInfo);
		if (tabInfo.m_lAttributes & dbSystemObject)
			continue;
		m_Com.AddString(tabInfo.m_strName);

	}
	m_pRecordSet=new CDaoRecordset(m_pDB);
 m_bTF=TRUE;
	m_bCR=FALSE;
	return TRUE;  // return TRUE  unless you set the focus to a control
}      

它的主要任务是打开数据库和表的名字

2、下面是最为重要的OnStatr():

void CListDlg::OnStrat() 
{  
	if(m_pRecordSet->IsOpen())
    m_pRecordSet->Close();
  
    m_List1.DeleteAllItems();
    if(nField!=0)
	{
	for(long i=0;iOpen(dbOpenDynaset, strSQL);
		m_pRecordSet->m_strFilter.Empty();
	}
	catch(CDaoException *e)
	{   
		AfxMessageBox(e->m_pErrorInfo->m_strDescription,MB_ICONEXCLAMATION);
		delete m_pRecordSet;
		m_pDB->Close();
		delete m_pDB;
		e->Delete();
		return ;
	}
	 
	if(m_pRecordSet != NULL)
	{
		m_pImageList = new CImageList();
		m_pImageList->Create(IDB_BITMAP4, 16, 1, RGB(255,0,0));
		m_List1.SetImageList(m_pImageList, LVSIL_SMALL);	/* set extended stlyes*/
		
	DWORD dwExStyle = LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES | /*LVS_EX_SUBITEMIMAGES |*/
		m_List1.SetExtendedStyle(LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES);
		LVS_EX_HEADERDRAGDROP | LVS_EX_TRACKSELECT;

		LV_COLUMN lvColumn;
		lvColumn.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
		lvColumn.fmt = LVCFMT_LEFT;	lvColumn.cx = 67;
	    nField=m_pRecordSet->GetFieldCount();
		for(int i = 0; i < nField; i++) // set up columns
		{    
			CDaoFieldInfo m_fieldinfo;
			m_pRecordSet->GetFieldInfo(i, m_fieldinfo);//get field name
			int len = m_fieldinfo.m_strName.GetLength();
			CString temp = m_fieldinfo.m_strName;
			TCHAR* szBuffer = new TCHAR[len + 1];
			strcpy(szBuffer, temp.GetBuffer(len));
			temp.ReleaseBuffer();
			lvColumn.pszText = szBuffer;
		    m_List1.InsertColumn(i, &lvColumn);//insert column		
			delete szBuffer;
		}
		/*set number of items in ListView*/
		m_pRecordSet->MoveFirst();
		m_pRecordSet->MoveLast();
     long count =m_pRecordSet->GetRecordCount();//Get number of records
	   this->OnGetdispinfoList( count, nField);
	
	}
	GetDlgItem(IDC_STRAT)->EnableWindow(FALSE);
	this->Enable(TRUE);
}      

3、而OnGetdispinfoList(long row,long column) 最为有用,可以在很多方面调用:

void CListDlg::OnGetdispinfoList(long row,long column) 
{  
   
	if(!m_pRecordSet)
	return;
    COleVariant varValue;
	for(long i=0;iSetAbsolutePosition(i);//Set the file to desired index

		}
		catch(CDaoException* e)
		{
		AfxMessageBox(e->m_pErrorInfo->m_strDescription, 
				    MB_ICONEXCLAMATION );
		e->Delete();
		return;
		}
	
    	for(long j=0;jGetFieldValue(j, varValue);
		   	 else
			 m_pRecordSet->GetFieldValue(0, varValue);
		}
		catch(CDaoException* e)
		{
			AfxMessageBox(e->m_pErrorInfo->m_strDescription, 
				    MB_ICONEXCLAMATION );
			e->Delete();
			return;
		}
	

		const VARIANT* variant = LPCVARIANT(varValue);
		
		if(variant->vt & VT_BYREF)
			return;
		CString st;
		switch(variant->vt)
		{  
			case VT_ERROR:{	
							st="Error";					
							break;
						  }
			case VT_I2:{ 	
				            st.Format("%d", variant->iVal);
							break;
					   }
			case VT_I4:{ 
				           st.Format( "%d", variant->lVal);
							break;
					   }
			case VT_R4:{   
						   st.Format( "%.2f", variant->fltVal);
                           
						    break;}
					
			case VT_R8:{	st.Format( "%.2f", variant->dblVal);
							break;
					   }
			case VT_CY:{	COleCurrency c(varValue);
						   st = c.Format();//ie. 1.00
							break;
					   }
			case VT_DATE:{	COleDateTime t(variant->date);
							st = t.Format( "%B %d, %Y" );//Day of Week, Month Day, Year
							
							break;
						 }
			case VT_BSTR:{   st = V_BSTRT( &varValue );//convert BSTR to CString
							
							break;
						}
			case VT_BOOL:{	if(variant->boolVal)
							st="TRUE";
							else
								st= "FALSE";
							break;
						 }
			case VT_UI1:{st=(CString)((char*)variant->bVal);
							break;
						}

				default:{
							
							break;
						}

		}
      if(j==0)		
      m_List1.InsertItem(i,st,0);
      else
      m_List1.SetItemText(i,j,st);
		}
  	}
}      

其它的可以看源码。

注意:

(1) 这个程序没有实现以前的成绩统计的功能,其实这些功能的实现不要用表,建按钮实现就可,比较简单,有兴趣可以一试。这里把重点放在"增减科目"上。

(2) 程序完后,时间紧张,没有优化,大家有兴趣可以优化一下。下一步,我的重点放在这上面,准备用COM把很多部分做成组件,欢迎有兴趣的读者和我交流。

(3) 界面也没有花时间美化。