Introduction
Following code is based on a codeproject article Printing of DataGridView. I added the following alterations:
- Converted the C# code to C++/CLI
- Resolved some typecasting conflicts
- Simplified the column width calculation algorithm
- Added more code comments and formatted the code to make it more readable
- Made the 'number lines of text per row' user variable
- Added DateTime conversion mechanism
- Added connection to an OLE database instead of server-side SQL.
Assume you have a form with the following components:
- a DataGridView object with pre-formatted columns that represent your database's columns
- a Get patient Details button that will retreive the data from your database and display it in the DatGridView
- a Print Preview button
- a Print button that will spawn a PrintDialog.
- a PrintDocument component with 2 events attached: BeginPrint and PrintPage
- a MaskedTextBox enabling the user to enter 2 numeric digits only
As stated, the original project used a serverside SQL database, the code of which is still present but commented out. This version uses a clientside MS Access database (mdb) file.
Code
First the initialisation section. All code generated by the Form Designer has been omitted. Note that the database here has 6 columns, one of which is a DataTime, the other ones are text:
#pragma once namespace PrintDataGrid { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; // using namespace System::Data::SqlClient; using namespace System::Data::OleDb; // for Access database using namespace System::Text; public ref class Form1 : public System::Windows::Forms::Form { private: // determines number lines of text each row should allow: static int iRowHeight = 0; String^ strConnection; // DB connection string StringFormat^ strFormat; // to format the grid rows. ArrayList^ arrColumnLefts; // left coordinates of columns ArrayList^ arrColumnWidths; // column widths int iCellHeight; // datagridview cell dimensions int iTotalWidth; int iRow; // counter bool bFirstPage; // to check whether we are printing first page bool bNewPage; // to check whether we are printing a new page int iHeaderHeight; // header height private: System::Windows::Forms::DataGridView^ dataGridView1; private: System::Windows::Forms::Button^ button1; private: System::Windows::Forms::Button^ button2; private: System::Windows::Forms::Button^ button3; private: System::Windows::Forms::Label^ label1; private: System::Windows::Forms::MaskedTextBox^ maskedTextBox1; private: System::Windows::Forms::DataGridViewTextBoxColumn^ Last_Name; private: System::Windows::Forms::DataGridViewTextBoxColumn^ Initials; private: System::Windows::Forms::DataGridViewTextBoxColumn^ First_Name; private: System::Windows::Forms::DataGridViewTextBoxColumn^ Mobile; private: System::Windows::Forms::DataGridViewTextBoxColumn^ Email; private: System::Windows::Forms::DataGridViewTextBoxColumn^ DOB; private: System::Windows::Forms::DataGridViewTextBoxColumn^ Address; private: System::Drawing::Printing::PrintDocument^ printDocument1; public: Form1(void) { InitializeComponent(); InitializeVariables(); } protected: ~Form1() { if (components){ delete components; } } private: void InitializeVariables() { // For SQL server side database: // strConnection = "data source=localhost;Integrated Security=SSPI;Initial Catalog=yourDatabase;" // For client side OLE database: strConnection = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=yourPath\\yourDatabase.mdb"; arrColumnLefts = gcnew ArrayList(); arrColumnWidths = gcnew ArrayList(); iCellHeight = 0; iTotalWidth = 0; iRow = 0; bFirstPage = false; bNewPage = false; iHeaderHeight = 0; } private: /// Required designer variable. System::ComponentModel::Container ^components; #pragma region Windows Form Designer generated code ////// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// void InitializeComponent(void) { ... }
The actual event handlers and methods:
// Patient Details button clicked private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) { Cursor->Current = Cursors::WaitCursor; // SQL: //SqlConnection ^ sqlConnection; //SqlCommand ^ sqlCommand; //SqlDataReader ^ sqlReader; // OLE DB: OleDbConnection^ oleDbConnection; OleDbCommand ^ oleDbCommand; OleDbDataReader^ oleDbDataReader; try{ String^ strQuery = "SELECT Last_Name, Initials, First_Name, Mobile, Email, DOB, Address FROM VI_Patient"; // SQL: //sqlConnection = gcnew SqlConnection(strConnection); //sqlConnection.Open(); //sqlCommand = gcnew SqlCommand(strQuery, sqlConnection); //sqlReader = sqlCommand->ExecuteReader(); //while (sqlReader->Read()){ // OLE DB: oleDbConnection = gcnew OleDbConnection(strConnection); oleDbConnection->Open(); oleDbCommand = gcnew OleDbCommand(strQuery, oleDbConnection); oleDbDataReader = oleDbCommand->ExecuteReader(); while(oleDbDataReader->Read()){ //Reformat dateTime String ^ thisDate = String::Empty; if (oleDbDataReader->GetFieldType(5)->ToString() =="System.DateTime"){ if (oleDbDataReader[5] != System::DBNull::Value){ DateTime^ dt = (DateTime^)oleDbDataReader[5]; thisDate = dt->ToString("dd MMM yyyy"); } } // array<System::Object^>^ row = { oleDbDataReader[0], oleDbDataReader[1], oleDbDataReader[2], oleDbDataReader[3], oleDbDataReader[4], thisDate, oleDbDataReader[6] }; this->dataGridView1->Rows->Add(row); } } catch(Exception^ e){ MessageBox::Show(e->Message, "Error", MessageBoxButtons::OK, MessageBoxIcon::Error); return; } finally{ Cursor->Current = Cursors::Default; oleDbConnection->Close(); if(oleDbDataReader != nullptr){ oleDbDataReader->Close(); oleDbDataReader = nullptr; } if(oleDbCommand != nullptr){ oleDbCommand = nullptr; } } } // Print button clicked private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) { iRowHeight = Convert::ToInt32(maskedTextBox1->Text); if(iRowHeight > 0){ // open the print dialog PrintDialog^ printDialog = gcnew PrintDialog(); printDialog->Document = printDocument1; printDialog->UseEXDialog = true; // get the document if(System::Windows::Forms::DialogResult::OK == printDialog->ShowDialog()){ printDocument1->DocumentName = "Patient Page Print"; printDocument1->Print(); } } else{ MessageBox::Show("row height should be at least 1", "Warning", MessageBoxButtons::OK, MessageBoxIcon::Warning); } } // Initialize the printing. // Set page alignments and calculate total width private: System::Void printDocument1_BeginPrint(System::Object^ sender, System::Drawing::Printing::PrintEventArgs^ e) { try{ strFormat = gcnew StringFormat(); // horizontal alignment strFormat->Alignment = StringAlignment::Near; // vertical alignment strFormat->LineAlignment = StringAlignment::Center; // text needs trimming to the nearest character, and an ellipsis is inserted at the end of a trimmed line strFormat->Trimming = StringTrimming::EllipsisCharacter; arrColumnLefts->Clear(); arrColumnWidths->Clear(); iCellHeight = 0; iRow = 0; bFirstPage = true; bNewPage = true; // calculating total width iTotalWidth = 0; for each(DataGridViewColumn^ dgvGridCol in dataGridView1->Columns){ iTotalWidth += dgvGridCol->Width; } } catch(Exception^ e){ MessageBox::Show(e->Message, "Error", MessageBoxButtons::OK, MessageBoxIcon::Error); } } private: System::Void printDocument1_PrintPage(System::Object^ sender, System::Drawing::Printing::PrintPageEventArgs^ e) { try{ // e->MarginBounds gives printed page dimensions within the margins int iLeftMargin = e->MarginBounds.Left; int iTopMargin = e->MarginBounds.Top; // flag to indicate more pages need printing: bool bMorePagesToPrint = false; int iTmpWidth = 0; // for the first page, set cell width and header height if(bFirstPage){ for each (DataGridViewColumn ^ GridCol in dataGridView1->Columns){ // set column widths relative to the margin bounds: iTmpWidth = CalculateColWidth(GridCol->Width, e->MarginBounds.Width); // MeasureString measure string when drawn with specified font iHeaderHeight = (int)(e->Graphics->MeasureString( GridCol->HeaderText, GridCol->InheritedStyle->Font, iTmpWidth).Height) + 11; // save width and height of headers arrColumnLefts->Add(iLeftMargin); arrColumnWidths->Add(iTmpWidth); // move the margin iLeftMargin += iTmpWidth; } } // loop through each line of the DataGridView while(iRow < dataGridView1->Rows->Count){ DataGridViewRow ^ gridRow = dataGridView1->Rows[iRow]; // set cell height iCellHeight = (gridRow->Height * (iRowHeight)) + 7; // 7 is margin int iCount = 0; // set some flags when page needs changing: if(iTopMargin + iCellHeight >= e->MarginBounds.Height + e->MarginBounds.Top){ bNewPage = true; bFirstPage = false; bMorePagesToPrint = true; break; } else{ if(bNewPage){ // draw the header e->Graphics->DrawString( "Patient Details Summary", gcnew System::Drawing::Font(dataGridView1->Font, FontStyle::Bold), Brushes::Black, (float)e->MarginBounds.Left, (float)e->MarginBounds.Top - e->Graphics->MeasureString( "Patient Details Summary", gcnew System::Drawing::Font(dataGridView1->Font, FontStyle::Bold), e->MarginBounds.Width).Height - (float)13.0 ); // draw today's dateTime String ^ strDate = DateTime::Now.ToLongDateString() + " " + DateTime::Now.ToShortTimeString(); e->Graphics->DrawString( strDate, gcnew System::Drawing::Font(dataGridView1->Font, FontStyle::Bold), Brushes::Black, e->MarginBounds.Left + (e->MarginBounds.Width - e->Graphics->MeasureString( strDate, gcnew System::Drawing::Font(dataGridView1->Font, FontStyle::Bold), e->MarginBounds.Width).Width), e->MarginBounds.Top - e->Graphics->MeasureString( "Patient Details Summary", gcnew System::Drawing::Font(dataGridView1->Font, FontStyle::Bold), e->MarginBounds.Width).Height - 13 ); // Draw the DataGridView's header row iTopMargin = e->MarginBounds.Top; for each(DataGridViewColumn ^ gridCol in dataGridView1->Columns){ // Fill the header rectangle e->Graphics->FillRectangle( gcnew SolidBrush(Color::LightGray), Rectangle( (int)arrColumnLefts[iCount], iTopMargin, (int)arrColumnWidths[iCount], iHeaderHeight ) ); // Draw the header rectangle e->Graphics->DrawRectangle( Pens::Black, Rectangle( (int)arrColumnLefts[iCount], iTopMargin, (int)arrColumnWidths[iCount], iHeaderHeight ) ); // Draw the header text e->Graphics->DrawString( gridCol->HeaderText, gridCol->InheritedStyle->Font, gcnew SolidBrush( gridCol->InheritedStyle->ForeColor), Rectangle( (int)arrColumnLefts[iCount], iTopMargin, (int)arrColumnWidths[iCount], iHeaderHeight ), strFormat ); iCount++; } bNewPage = false; iTopMargin += iHeaderHeight; } // END if(bNewPage) iCount = 0; // Draw text of subsequent rows on this page for each(DataGridViewCell ^ cel in gridRow->Cells){ if(cel->Value != nullptr){ e->Graphics->DrawString( cel->Value->ToString(), cel->InheritedStyle->Font, gcnew SolidBrush(cel->InheritedStyle->ForeColor), Rectangle( (int)arrColumnLefts[iCount], iTopMargin, (int)arrColumnWidths[iCount], iCellHeight ), strFormat ); } // Draw cell borders e->Graphics->DrawRectangle( Pens::Black, Rectangle( (int)arrColumnLefts[iCount], iTopMargin, (int)arrColumnWidths[iCount], iCellHeight ) ); iCount++; } } // END else iRow++; iTopMargin += iCellHeight; } // END while // if more lines are present, // if more lines are present, invoke printDocument1_PrintPage() again if (bMorePagesToPrint){ e->HasMorePages = true; } else{ e->HasMorePages = false; } } // END try catch(Exception ^ e){ MessageBox::Show(e->Message, "Error", MessageBoxButtons::OK, MessageBoxIcon::Error); } } // Print Preview button clicked private: System::Void button3_Click(System::Object^ sender, System::EventArgs^ e) { iRowHeight = Convert::ToInt32(maskedTextBox1->Text); if(iRowHeight > 0){ // open the print preview dialog - this will do all the required work PrintPreviewDialog ^ objPPdialog = gcnew PrintPreviewDialog(); objPPdialog->Document = printDocument1; objPPdialog->ShowDialog(); } else{ MessageBox::Show("row height should be at least 1", "Warning", MessageBoxButtons::OK, MessageBoxIcon::Warning); } } private: int CalculateColWidth(int gridColWidth, int marginWidth) { int thisWidth = 1; // Math::Floor returns the largest integer less than or equal to the specified double. thisWidth = (int)(System::Math::Floor( (double)( (double)gridColWidth / (double)iTotalWidth * (double)marginWidth ) ) ); return thisWidth; }
The code has been tested in Visual Studio 2008. When using a pdf printer such as Cute Pdf, you can create presentable pdf files of your database tables. Again, this code is mainly the work of a previous article in C# written by Life with .NET, I only converted it to C++/CLI and made a few alterations.