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