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.