home *** CD-ROM | disk | FTP | other *** search
/ Mastering MFC Development / MMD.ISO / labs / c10 / lab01 / ex03 / dataview.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1997-02-20  |  14.0 KB  |  520 lines

  1. // DataView.cpp : implementation file
  2. //
  3.  
  4. #include "stdafx.h"
  5. #include "Browser.h"
  6. #include "DataView.h"
  7. #include "BrowserDoc.h"
  8. #include "Mainfrm.h"
  9. #include "ChildFrm.h"
  10. #include "VarDecoder.h"
  11. #include "FindDlg.h"
  12. #include "QueryDlg.h"
  13.  
  14. #ifdef _DEBUG
  15. #define new DEBUG_NEW
  16. #undef THIS_FILE
  17. static char THIS_FILE[] = __FILE__;
  18. #endif
  19.  
  20. #define USERMSG_HEADCLICK WM_USER + 1     //To solve problem with LVN_COLUMNCLICK
  21.  
  22. /////////////////////////////////////////////////////////////////////////////
  23. // CDataView
  24.  
  25. IMPLEMENT_DYNCREATE(CDataView, CListView)
  26.  
  27. CDataView::CDataView()
  28.     : m_pDB( NULL ), m_pQuery( NULL ), 
  29.     m_pRecordset( NULL ), m_pTable( NULL )
  30.  
  31. {
  32. }
  33.  
  34. CDataView::~CDataView()
  35. {
  36.     try
  37.     {
  38.         if ( m_pRecordset )           //No check for dirty records
  39.             m_pRecordset->Close( );    //This is just a browser
  40.         if ( m_pQuery )
  41.             m_pQuery->Close( );
  42.         if ( m_pTable )
  43.             m_pTable->Close( );
  44.     }
  45.     catch ( CException* ex )
  46.     {
  47.         ex->ReportError( );
  48.         ex->Delete( );
  49.     }
  50.     delete m_pRecordset;   //Release the memory regardless 
  51.     delete m_pQuery;        //of other problems
  52.     delete m_pTable;
  53. }
  54.  
  55.  
  56. BEGIN_MESSAGE_MAP(CDataView, CListView)
  57.     //{{AFX_MSG_MAP(CDataView)
  58.     ON_COMMAND(ID_PAGE_DOWN, OnPageDown)
  59.     ON_COMMAND(ID_PAGE_FIRST, OnPageFirst)
  60.     ON_COMMAND(ID_PAGE_LAST, OnPageLast)
  61.     ON_COMMAND(ID_PAGE_UP, OnPageUp)
  62.     ON_NOTIFY_REFLECT(LVN_COLUMNCLICK, OnColumnclick)
  63.     //}}AFX_MSG_MAP
  64.     ON_MESSAGE( USERMSG_HEADCLICK, OnHeadClick )
  65. END_MESSAGE_MAP()
  66.  
  67. /////////////////////////////////////////////////////////////////////////////
  68. // CDataView drawing
  69.  
  70. void CDataView::OnDraw(CDC* pDC)
  71. {
  72.     CDocument* pDoc = GetDocument();
  73.     // TODO: add draw code here
  74. }
  75.  
  76. /////////////////////////////////////////////////////////////////////////////
  77. // CDataView diagnostics
  78.  
  79. #ifdef _DEBUG
  80. void CDataView::AssertValid() const
  81. {
  82.     CListView::AssertValid();
  83. }
  84.  
  85. void CDataView::Dump(CDumpContext& dc) const
  86. {
  87.     CListView::Dump(dc);
  88. }
  89. #endif //_DEBUG
  90.  
  91. /////////////////////////////////////////////////////////////////////////////
  92. // CDataView message handlers
  93.  
  94. BOOL CDataView::PreCreateWindow(CREATESTRUCT& cs) 
  95. {
  96.     // TODO: Add your specialized code here and/or call the base class
  97.  
  98.     return CListView::PreCreateWindow(cs);
  99. }
  100.  
  101. //Function receives a pointer to the CDaoDatabase object, table or query name
  102. //and whether it is a table or a query.  If a query, it calls a function to
  103. //get any parameters.  Then it opens recordset.  
  104. BOOL CDataView::SetData( CDaoDatabase * pDB, const CString & name, const int nType )
  105. {     
  106.     //In place for exercise 1
  107.     //return FALSE;  //remove for exercise 2
  108.  
  109.     //Set database and table names as title bar
  110.     CString str = GetDocument( )->GetTitle( );
  111.     ( ( CChildFrame * ) GetParentFrame( ) )
  112.         ->m_strCaption = str + ": " + name;     //Store database: tablename
  113.   
  114.     m_pDB = pDB;  //Store database pointer in view CDataView::m_pDB
  115.  
  116.     try
  117.     {
  118.         if ( TABLE == nType )
  119.         {
  120.             m_pRecordset = new CDaoRecordset( m_pDB );
  121.             CString sql = "Select * from [" + name + "]";
  122.             m_pRecordset->Open( dbOpenDynaset, sql );
  123.         }
  124.  
  125.         else if ( QUERY == nType )
  126.         {
  127.             m_pQuery = new CDaoQueryDef( m_pDB );
  128.             m_pQuery->Open( name );
  129.             if ( dbQSelect == m_pQuery->GetType( ) )
  130.             {
  131.                 SetParams( m_pQuery );
  132.                   m_pRecordset = new CDaoRecordset( m_pDB );
  133.                 m_pRecordset->Open( m_pQuery );
  134.             }    //End if Select-Query
  135.             else 
  136.             {
  137.                 AfxMessageBox( "Not a Select Query" );
  138.                 return FALSE;
  139.             }
  140.         } //End Query-type
  141.     }    //End try block
  142.     catch ( CException * ex )
  143.     {
  144.         ex->ReportError( );
  145.         ex->Delete( );
  146.         delete m_pTable;  //Clean up mess
  147.         delete m_pRecordset;
  148.         delete m_pQuery;
  149.         m_pTable = NULL;  
  150.         m_pRecordset = NULL;
  151.         m_pQuery = NULL;
  152.  
  153.         return FALSE;
  154.     }
  155.  
  156.     LoadHeaders( ); //Fill in the view's column headers
  157.     LoadData( );    //Load the report view rows
  158.  
  159.     return TRUE;
  160. }
  161.  
  162. CBrowserDoc  * CDataView::GetDocument( )
  163. {
  164.     return ( CBrowserDoc * ) m_pDocument; //Cast to correct pointer type
  165. }
  166.  
  167. void CDataView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView) 
  168. {
  169.     // TODO: Add your specialized code here and/or call the base class
  170.  
  171.     CMainFrame * pFrame = ( CMainFrame * ) AfxGetMainWnd( );
  172.     
  173.     if ( bActivate )      //This view is activated so we load its toolbar
  174.         pFrame->m_wndToolBar.LoadToolBar(IDR_DATATYPE);
  175.     else if ( pFrame == ( CMainFrame * ) pActivateView ) //Load mainframe toolbar
  176.         pFrame->m_wndToolBar.LoadToolBar(IDR_MAINFRAME); //if mainframe activates
  177.     pFrame->RecalcLayout( );  //Compute client area
  178.  
  179.     CListView::OnActivateView(bActivate, pActivateView, pDeactiveView);
  180. }
  181.  
  182. void CDataView::LoadHeaders()
  183. {
  184.     CListCtrl & lstctrl = GetListCtrl( );  //Get view's list control
  185.     
  186.     lstctrl.DeleteAllItems( );  //Clean it out
  187.  
  188.     //Make the column headers
  189.     
  190.     lstctrl.CWnd::ModifyStyle( NULL, LVS_REPORT ); //Set for report view
  191.     
  192.     try
  193.     {
  194.         int width;    //Width of fieldname text
  195.         int align;    //Alignment style (left, center, right)
  196.         CDaoFieldInfo fInfo;   //Structure of field information
  197.         
  198.         int columns = m_pRecordset->GetFieldCount( );  //How many fields
  199.         for ( int i = 0 ; i < columns ; i ++ )
  200.         {
  201.             m_pRecordset->GetFieldInfo( i, fInfo );    
  202.             //Align text and memo fields to the left, others to the right
  203.             align =        
  204.                 dbText == fInfo.m_nType || dbMemo == fInfo.m_nType
  205.                 ? LVCFMT_LEFT : LVCFMT_RIGHT;
  206.             width = lstctrl.GetStringWidth( fInfo.m_strName );
  207.             //Make the column twice the label width
  208.             lstctrl.InsertColumn( i, fInfo.m_strName, align, 2 * width );
  209.         }
  210.     }
  211.     catch( CException * ex )
  212.     {
  213.         ex->ReportError( );
  214.         ex->Delete( );
  215.     }
  216. }
  217.  
  218.  
  219. //Fill list view with data.  If a non-null pointer is passed to the
  220. //function, use CDaoRecordset::FindNext to locate data, otherwise use
  221. //CDaoRecordset::MoveNext
  222.  
  223. void CDataView::LoadData( CString * pstrCriteria )
  224. {
  225.     CListCtrl & lstctrl = GetListCtrl( );  //The view's list control
  226.     lstctrl.DeleteAllItems( );  //Clean it out
  227.     
  228.     int rows = lstctrl.GetCountPerPage( ); //How many rows can fit on the page
  229.  
  230.     try
  231.     {
  232.        COleVariant var;
  233.        CString str;
  234.  
  235.        int columns = m_pRecordset->GetFieldCount( );  //How many fields
  236.  
  237.     // Move through records
  238.         for ( int r = 0 ; r < rows && ! m_pRecordset->IsEOF( ) ; r ++ )
  239.         {
  240.             for ( int c = 0; c < columns ;  c ++ )      //Move across fields
  241.             {
  242.                 m_pRecordset->GetFieldValue( c, var );
  243.                 
  244.                 //Converter function from VarDecoder.cpp (not an MFC file)
  245.                 str = ::strVARIANT( var ); 
  246.  
  247.                 // Add field value to list control
  248.                 if (0 == c )  //First item in row so we need to use InsertItem
  249.                     lstctrl.InsertItem( r, ( LPCTSTR ) str );
  250.                 else  //row exists, so we use SetItemText
  251.                     lstctrl.SetItemText( r, c, ( LPCTSTR ) str  );
  252.             }  //End this record
  253.  
  254.             if ( pstrCriteria )     //We're passed a criteria string
  255.             {
  256.                 //Try to find another record, if not found then
  257.                 if ( ! m_pRecordset->FindNext( ( LPCTSTR ) * pstrCriteria ) ) 
  258.                     break;  //stop the looping on rows of data
  259.             }
  260.             else  //No criteria, so we just take the next record
  261.                 m_pRecordset->MoveNext( );         
  262.         }
  263.         if ( m_pRecordset->IsEOF( ) )
  264.             m_pRecordset->MoveLast( );    //Leave cursor on a record
  265.     }
  266.     catch( CException* ex )
  267.     {
  268.         ex->ReportError( );
  269.         ex->Delete( );
  270.     }
  271. }
  272.  
  273. void CDataView::OnPageDown() 
  274. {
  275.     // TODO: Add your command handler code here
  276.     try 
  277.     {
  278.         if ( m_pRecordset->IsBOF( ) )  //If past the top on a previous move
  279.             m_pRecordset->MoveFirst( );    //move to the first record
  280.         LoadData( );  //Start loading from current position
  281.     }
  282.     catch ( CException * ex )
  283.     {
  284.         ex->ReportError( );
  285.         ex->Delete( );
  286.     }
  287. }
  288.  
  289. void CDataView::OnPageFirst() 
  290. {
  291.     // TODO: Add your command handler code here
  292.     try 
  293.     {
  294.         m_pRecordset->MoveFirst( );
  295.         LoadData( ) ;
  296.     }
  297.     catch ( CException * ex )
  298.     {
  299.         ex->ReportError( );
  300.         ex->Delete( );
  301.     }
  302. }
  303.  
  304. void CDataView::OnPageLast() 
  305. {
  306.     // TODO: Add your command handler code here
  307.     
  308.     CListCtrl & lstctrl = GetListCtrl( );  //The view's list control
  309.     int rows = lstctrl.GetCountPerPage( ); //How many rows can fit on the page
  310.     
  311.     try 
  312.     {
  313.         m_pRecordset->MoveLast( );
  314.         m_pRecordset->Move( 1 - rows );     //Show a full page
  315.         if ( m_pRecordset->IsBOF( ) )      //Small table
  316.             m_pRecordset->MoveFirst( );
  317.         LoadData( ) ;
  318.     }
  319.     catch ( CException * ex )
  320.     {
  321.         ex->ReportError( );
  322.         ex->Delete( );
  323.     }
  324. }
  325.  
  326. void CDataView::OnPageUp()                                 
  327. {
  328.     // TODO: Add your command handler code here
  329.  
  330.     CListCtrl & lstctrl = GetListCtrl( );    //The view's list control
  331.     int rows = lstctrl.GetCountPerPage( );    //How many rows in list
  332.     int items = lstctrl.GetItemCount( );    //Items actually displayed
  333.  
  334.     try 
  335.     {    //From bottom of current page to top of previous page
  336.         m_pRecordset->MoveNext( );
  337.         if ( m_pRecordset->IsEOF( ) ) //Show last page if at EOF
  338.             m_pRecordset->Move( - rows - items );
  339.         else  //Go past current page to top of preceeding page
  340.             m_pRecordset->Move( - 2 * rows );
  341.         
  342.         if ( m_pRecordset->IsBOF( ) )  //Easy to do on small table
  343.             m_pRecordset->MoveFirst( );
  344.     
  345.         LoadData( ) ;
  346.     }
  347.     catch ( CException * ex )
  348.     {
  349.         ex->ReportError( );
  350.         ex->Delete( );
  351.     }    
  352. }
  353.  
  354. void CDataView::OnColumnclick(NMHDR* pNMHDR, LRESULT* pResult) 
  355. {
  356.     NM_LISTVIEW* pNMListView = (NM_LISTVIEW*)pNMHDR;
  357.     // TODO: Add your control notification handler code here
  358.     
  359.     //The listview tries to read the dialog's first message, so we
  360.     //post a message to give the listview time to finish its
  361.     //processing.  This is a work-around.
  362.     PostMessage( USERMSG_HEADCLICK, (UINT) pNMHDR );
  363.     
  364.     * pResult = 0;
  365. }
  366.  
  367. //Message handler to get message sent by CDataView::OnColumnclick
  368. long CDataView::OnHeadClick( UINT uP, long )
  369.  
  370. {
  371.     NM_LISTVIEW * pNMListView = ( NM_LISTVIEW * ) uP;
  372.  
  373.     CListCtrl & lstctrl = GetListCtrl( );  //View's list control
  374.     char caFieldName[100];    //Buffer to hold field name
  375.  
  376.     LV_COLUMN lc;            //List view column information structure                 
  377.     lc.mask = LVCF_TEXT;    //We're after the text data member
  378.     lc.pszText = caFieldName;  //Tell the structure about the buffer
  379.     lc.cchTextMax = sizeof caFieldName;    //Tell the structure the size
  380.  
  381.     int column = pNMListView->iSubItem;    //Find out which column was clicked
  382.  
  383.     if ( ! lstctrl.GetColumn( column, & lc ) )    //Ask for the information
  384.     {
  385.         MessageBox( "Cannot execute find" ); 
  386.         return FALSE;
  387.     }
  388.  
  389.      CString strField = _T( lc.pszText ); //Field name from column structure
  390.                         //_T casts negotiates ANSI and Unicode differences
  391.     try
  392.     {                                                
  393.         CFindDlg dlg;
  394.         CDaoFieldInfo fInfo;
  395.  
  396.         m_pRecordset->GetFieldInfo( column, fInfo );
  397.         BOOL bTextType =           //Is the field a text or memo type?
  398.             dbText == fInfo.m_nType || dbMemo == fInfo.m_nType;
  399.         if ( bTextType ) //If a text or memo field set the radio button to Like
  400.             dlg.m_nMatchType = CFindDlg::LIKE;  
  401.         else
  402.             dlg.m_nMatchType = CFindDlg::EXACT; //Must be numeric, use Exact
  403.  
  404.         //If the user doesn't enter a string, 
  405.         //we reset the list to all records
  406.         if ( IDOK == dlg.DoModal( ) )
  407.         {
  408.             if ( "" == dlg.m_strFind )    //Set the filter, and requery
  409.             {
  410.                 m_pRecordset->m_strFilter = "TRUE = TRUE"; //Every record
  411.                 m_pRecordset->Requery( );
  412.                 LoadData( );
  413.                 return TRUE;
  414.             } //End empty string criteria
  415.  
  416.             //Build a query string from the dialog information
  417.             //Enclose field names in brackets [Field Name], use
  418.             //wildcards to get LIKE and contains behavior
  419.             //Use quotes for text criteria "Text criteria"
  420.             //Exact:    [Field Name] = "Text criteria"
  421.             //Like:        [Field Name] LIKE "Text criteria*"
  422.             //Contains:    [Field Name] LIKE "*Text criteria*"
  423.             //Numeric:    [Field Name] = 12345
  424.  
  425.             CString strQuery;    
  426.             switch ( dlg.m_nMatchType )
  427.             {
  428.             case CFindDlg::EXACT :
  429.                 if ( bTextType ) //Text needs quote delimiters
  430.                     strQuery.Format( "[%s] = \"%s\" ", strField, dlg.m_strFind );
  431.                 else   //Numeric entries don't use quotes
  432.                      strQuery.Format( "[%s] = %s ", strField, dlg.m_strFind ); 
  433.                 break;
  434.             case CFindDlg::LIKE :  //Append an * to the search string
  435.                     strQuery.Format( "[%s] LIKE \"%s*\" ", strField, dlg.m_strFind );
  436.                 break;
  437.             case CFindDlg::CONTAINS :  //Prepend and append asterisks to the search string
  438.                     strQuery.Format( "[%s] LIKE \"*%s*\" ", strField, dlg.m_strFind );
  439.                 break;
  440.             }
  441.  
  442.             if ( m_pQuery )  //We use find method to load data
  443.             {
  444.                 FindQueryData( strQuery ); //Tries to bookmark current position
  445.             }
  446.             else  //Use the filter string we created and requery
  447.             {
  448.                 m_pRecordset->m_strFilter = strQuery; 
  449.                 m_pRecordset->Requery( );
  450.                 LoadData( );
  451.             }
  452.         
  453.             return TRUE;
  454.         }  //End if IDOK 
  455.     }
  456.     catch ( CException * ex )
  457.     {
  458.         ex->ReportError( );
  459.         ex->Delete( );
  460.     }
  461.     return FALSE;
  462. }
  463.  
  464. //Determine whether the select query requires any parameters.  If so,
  465. //determine the parameter names and types, and get them from user
  466. void CDataView::SetParams( CDaoQueryDef * pQDef )
  467. {
  468.     try
  469.     {
  470.         int paramCount = pQDef->GetParameterCount( );
  471.         if ( 0 == paramCount )
  472.             return;   //Nothing to do
  473.         
  474.         CDaoParameterInfo daoInfo;  //Information structure
  475.         CQueryDlg dlg;   //Dialog to fetch parameter values
  476.  
  477.         for ( int i = 0 ; i < paramCount; i ++ ) //Get the params
  478.         {
  479.             pQDef->GetParameterInfo( i, daoInfo );
  480.             dlg.m_strParamName = daoInfo.m_strName;  //Show the param name in the dialog
  481.             //In this section we create variants from the user's entry
  482.             if ( IDOK == dlg.DoModal( ) )  //Show the dialog
  483.             {
  484.                 if ( dbDate == daoInfo.m_nType ) //If a date parameter
  485.                 {
  486.                     COleVariant var ( dlg.m_strParamValue ); //Create a variant
  487.                     COleDateTime dt ( var );        //Convert variant to COleDateTime
  488.                     pQDef->SetParamValue( i, dt );    //Set date param
  489.                 }
  490.                 else    
  491.                 {        //See article Q140599 regarding the casts
  492.                     COleVariant var ( _T( dlg.m_strParamValue ), VT_BSTRT );
  493.                     pQDef->SetParamValue( i, var );
  494.                 }
  495.             }
  496.         }
  497.     }
  498.     catch ( CException * ex )
  499.     {
  500.         ex->ReportError( );
  501.         ex->Delete( );
  502.     }    
  503. }
  504.  
  505. void CDataView::FindQueryData( CString & strCriteria )
  506. {
  507.     CListCtrl & lstctrl = GetListCtrl( );  //The view's list control
  508.     
  509.     try
  510.     {
  511.     //Code for exercise 4 goes in this block
  512.  
  513.     }
  514.     catch ( CException * ex )
  515.     {
  516.         ex->ReportError( );
  517.         ex->Delete( );
  518.     }    
  519. }
  520.