问题
I'm trying to print data from a database using printdocument and i got it to where it prints the data from the specified range but there are still 2 things that go wrong
the things that still don't work like intended are
- on printing if a page is almost fully used it adds a blank page with only a header printed on it
- and if a page is full the next page starts with the first item of the range again (FIXED by using multiple lists because i couldnt figure out how to make a list with sublists)
this is the code i have to call the printdocument
private void button3_Click(object sender, EventArgs e)//print
{
range();
try
{
if (artikel == true)
{
itemperpage = totalnumber = 0;
printPreviewDialog1.Document = printDocument1;
print = true;
printDocument1.Print();
// this.Close();
}
}
catch (Exception q) { MessageBox.Show("" + q); }
}
this is the code of the printdocument
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
if (artikel == true)
{
Font messageFont = new Font("Arial", 14, System.Drawing.GraphicsUnit.Point);
float currentY = 10;// declare one variable for height measurement
e.Graphics.DrawString(" I N K O O P A R T I K E L E N ", DefaultFont, Brushes.Black, 10, currentY);//this will print one heading/title in every page of the document
currentY += 15;
SqlCommand artprint = new SqlCommand("select * from ART WHERE ART BETWEEN @range AND @range2 ORDER BY ART ASC", Connectie.connMEVO_ART);
if (comboBox1.SelectedIndex <= comboBox2.SelectedIndex)
{
artprint.Parameters.Add("@range", SqlDbType.VarChar).Value = comboBox1.SelectedItem;
artprint.Parameters.Add("@range2", SqlDbType.VarChar).Value = comboBox2.SelectedItem;
}
else if (comboBox2.SelectedIndex <= comboBox1.SelectedIndex)
{
artprint.Parameters.Add("@range", SqlDbType.VarChar).Value = comboBox2.SelectedItem;
artprint.Parameters.Add("@range2", SqlDbType.VarChar).Value = comboBox1.SelectedItem;
}
drART = artprint.ExecuteReader();
try
{
while (totalnumber <= amount - 1 && drART.Read())
{// check the number of items
tostring();//SQL data to string
row = row + Environment.NewLine + "ART LEV LTD MVRD SGR INK CRNI " + " VALUTA KOR ";
row = row + Environment.NewLine + aa + " " + a + " " + b + " " + c + " " + d + " " + m + " " + f + " " + g + " " + h;
row = row + Environment.NewLine + "EH2 EH1 EF OMS ";
row = row + Environment.NewLine + j + " " + k + " " + l + " " + i;
row = row + Environment.NewLine;
e.Graphics.DrawString(row, DefaultFont, Brushes.Black, 50, currentY);//print each item
Debug.WriteLine("" + row);
row = "";
currentY += 80; // set a gap between every item
totalnumber += 1; //increment count by 1
if (itemperpage <= 12)
{
itemperpage += 1; // increment itemperpage by 1
e.HasMorePages = false; // set the HasMorePages property to false , so that no other page will not be added
}
else // if the number of item(per page) is more than 12 then add one page
{
itemperpage = 0; //initiate itemperpage to 0 .
e.HasMorePages = true; //e.HasMorePages raised the PrintPage event once per page .
return;//It will call PrintPage event again
}
}
}
catch (Exception ef)
{
MessageBox.Show("" + ef);
}
artprint.Dispose();
}
}
private void tostring()
{
aa = drART["ART"].ToString();
for (int ss = aa.Length; ss < 8; ss++) { aa = aa + " "; }
a = drART["LEV"].ToString();
for (int ss = a.Length; ss < 10; ss++) { a = a + " "; }
b = drART["LTD"].ToString();
for (int ss = b.Length; ss < 10; ss++) { b = b + " "; }
c = drART["MVRD"].ToString();
for (int ss = c.Length; ss < 10; ss++) { c = c + " "; }
d = drART["SGR"].ToString();
for (int ss = d.Length; ss < 10; ss++) { d = d + " "; }
m = drART["INK"].ToString();
for (int ss = m.Length; ss < 10; ss++) { m = m + " "; }
f = drART["CRNI"].ToString();
for (int ss = f.Length; ss < 10; ss++) { f = f + " "; }
g = drART["VALUTA"].ToString();
for (int ss = g.Length; ss < 3; ss++) { g = g + " "; }
h = drART["KOR"].ToString();
for (int ss = h.Length; ss < 10; ss++) { h = h + " "; }
i = drART["OMS"].ToString();
j = drART["EH2"].ToString();
for (int ss = j.Length; ss < 3; ss++) { j = j + " "; }
k = drART["EH1"].ToString();
for (int ss = k.Length; ss < 3; ss++) { k = k + " "; }
l = drART["EF"].ToString();
for (int ss = l.Length; ss < 10; ss++) { l = l + " "; }
}
回答1:
You wrote that you have used sources from the web and I guess it shows. We all do that but it is really important to be rather suspicious about their quality. And of course combining two sources most likely will not work at all..
I'll first go over a couple of issues with your code and then show you an example of how to print items from a DB on several pages..
Please do yourself a favor and chose the names of your variables with care! Yes, that takes a little longer but it will pay, believe me. (And if you can't come up with anything convincing, step back! This is usually an indication that you don't understand the problem well enough..)
Let's look at some: There is currentY
, which is pretty good. And there are itemperpage
, totalnumber
and amount
, all of which are terrible! They either are totally unclear or even misleading!
Let's set up a few ones with better names:
// layout variables
int itemsPerPage = 40; // how many items fit on one page
int itemHeight = 25; // height of one item in the chosen unit
// print job variables
int totalNumber = -1; // total number of records/items coming from the dbms
int itemsToPrint = 75; // total number of items we want to print
// print progress variables
int itemsPrinted = 0; // number of items printed so far
int pagesPrinted = 0; // number of pages printed
Another issue is the unit you chose, or to be precise the lack of choosing one. The default GraphicsUnit is Display
which for printing amounts to 1/100 inch. If you like that I suggest to write it in code to document it. This unit will be used during the whole layout process so it should be visible in the code! I personally prefer Millimeters. There are several other units..pick yours but document it!
One more thing you should look into are String.Format() and string.PadLeft(). They are so much nicer than the loop you have in the tostring()
method!
Now let's look at your specific problems:
The one with an empty page at the end of the job will go away once we use enough variables with names one can actually understand without learning them..
The other problem is the way you set up the DB-reading: You need to set up the reader where you start the printing and not in the PrintPage
loop. Note: These actually are two loops: one is visible as the while (...reader.Read() )
and the other, outer loop is implict: The whole PrintPage
event itself is the loop body.
First let's look at the range()
code. I have turned it into a function that returns the maximum number of records. I fetch the count in a first step. Then I set up the DataReader
for the real reading, which will happen in the PrintPage
event: I enumerate the fields so I have control over their order and don't pull in stuff I don't need.
Note that I am using MySql
but the classes are pretty much the same, just with a MySQl
-prefix. I don't include the connect code here. And of course I read from a database of my own..
MySqlConnection DBC = new MySqlConnection("");
MySqlCommand CMD = null;
MySqlDataReader DR = null;
int range ()
{
CMD = new MySqlCommand("select count(0) from test.artists ", DBC);
var count = CMD.ExecuteScalar();
int counter = Convert.ToInt32(count);
CMD = new MySqlCommand("select artist_ID, artist, genres from test.artists ", DBC);
DR = CMD.ExecuteReader();
return (int)counter;
}
Now let's look at the print command, using the variables from above:
private void cb_print_Click(object sender, EventArgs e)
{
totalNumber = range();
try
{
if (DBC.State == ConnectionState.Open)
{
pagesPrinted = 0;
printPreviewDlg.Document = printDocument1;
printPreviewDlg.ShowDialog();
}
} catch (Exception q) { MessageBox.Show("" + q); }
DR.Dispose();
}
Note that the Reader
is created in the range
call!
Finally the PrintPage
event:
private void printDocument1_PrintPage(object sender,
System.Drawing.Printing.PrintPageEventArgs e)
{
// my page unit
e.Graphics.PageUnit = GraphicsUnit.Millimeter;
// starting a new page
int itemsOnPage = 0;
pagesPrinted++;
float currentY = 12;
e.Graphics.DrawString(String.Format(
"HEADER - printing {0} items of {1} - Page {2}",
itemsToPrint, totalNumber, pagesPrinted),
Font, Brushes.Black, 1, currentY);
currentY += 30; // header height
try
{
// page is not full and we want to print more items
while (itemsOnPage < itemsPerPage && itemsPrinted < itemsToPrint - 1
&& DR.Read())
{
string row = String.Format("{0,5:000} Artist: {1,20} ({2}) ",
DR[0], DR[1], DR[2]);
e.Graphics.DrawString(row, DefaultFont, Brushes.Black, 50, currentY);
// Console.WriteLine("" + row);
currentY += itemHeight;
itemsPrinted++;
itemsOnPage++;
// we want to print more items but now the page is full
if ( itemsPrinted < itemsToPrint && itemsOnPage >= itemsPerPage)
{
itemsOnPage = 0;
e.HasMorePages = true;
return;
}
else
{
// this will only be used after all data are printed
e.HasMorePages = false;
}
}
} catch (Exception ef)
{
MessageBox.Show("" + ef);
}
}
回答2:
Had this issue too. In my situation the reason was a line e.HasMorePages = true;
( e is PrintPageEventArgs ) in event handler void PrintDoc_PrintPage(object sender, PrintPageEventArgs e)
. Every time you declare e.HasMorePages = true;
a new page is prepared for printing. So, my correct code was:
private void PrintDoc_PrintPage(object sender, PrintPageEventArgs e)
{
if (currentPagePrinting <= maxPagePrinting) {
//...
// do some drawing on e.Graphics as you like
//...
currentPagePrinting++;
e.HasMorePages = (currentPagePrinting <= maxPagePrinting);
} else
{
e.HasMorePages = false;
// also set this value to 1 in PrintDoc_BeginPrint event handler (declare it)
currentPagePrinting = 1;
}
}
来源:https://stackoverflow.com/questions/28624951/printdocument-adds-blank-page