|
Hello,
First of all, it-s a great job. Thanks
I'm just coding some enhancements to answer to customer needs. The first enhancement i've done is the dynamic support for frozen columns.
All frozen columns defined in your DGV are automatically added on each page parts. Meanwhile, I adjust the number of frozen cols to be sure to add at least one non-frozen col on each part (else we can create a neverending print job )
<code>
/// <summary>
/// Scan all the rows and columns to be printed and calculate the
/// overall individual column width (based on largest column value),
/// the header sizes, and determine all the row heights.
/// </summary>
/// <param name="g">The graphics context for all measurements</param>
private void measureprintarea(Graphics g)
{
int i, j;
rowheights = new List<float>(rowstoprint.Count);
colwidths = new List<float>(colstoprint.Count);
headerHeight = 0;
footerHeight = 0;
// Store frozen cols to repeat them on each page's part
int frozenColsCount = 0;
// temp variables
DataGridViewColumn col;
DataGridViewRow row;
//-----------------------------------------------------------------
// measure the page headers and footers, including the grid column header cells
//-----------------------------------------------------------------
// measure the column headers
Font headerfont = dgv.ColumnHeadersDefaultCellStyle.Font;
if (null == headerfont)
headerfont = dgv.DefaultCellStyle.Font;
// set initial column sizes based on column titles
for (i = 0; i < colstoprint.Count; i++)
{
col = (DataGridViewColumn)colstoprint[i];
// measure the title for each column, keep widths and biggest height
SizeF size = g.MeasureString(col.HeaderText, headerfont);
colwidths.Add(size.Width);
colheaderheight = (colheaderheight < size.Height ? size.Height : colheaderheight);
if ( col.Frozen )
frozenColsCount++;
}
//-----------------------------------------------------------------
// measure the page number
//-----------------------------------------------------------------
if (pageno)
{
StringFormat format = managestringformat(pagenumberalignment, pagenumberformatflags,
dgv.DefaultCellStyle, overridetitleformat);
pagenumberHeight = (g.MeasureString("Page", pagenofont, printWidth, format)).Height;
}
//-----------------------------------------------------------------
// Calc height of header.
// Header height is height of page number, title, subtitle and height of column headers
//-----------------------------------------------------------------
// note that we dont count the page number height if it's not on a separate line
if (pagenumberontop && !pagenumberonseparateline)
{
headerHeight += pagenumberHeight;
}
if (!String.IsNullOrEmpty(title))
{
StringFormat format = managestringformat(titlealignment, titleformatflags,
null, overridetitleformat);
headerHeight += (g.MeasureString(title, titlefont, printWidth, format)).Height;
}
if (!String.IsNullOrEmpty(subtitle))
{
StringFormat format = managestringformat(subtitlealignment, subtitleformatflags,
null, overridetitleformat);
headerHeight += (g.MeasureString(subtitle, subtitlefont, printWidth, format)).Height;
}
headerHeight += colheaderheight;
//-----------------------------------------------------------------
// measure the footer, if one is provided. Include the page number if we're printing
// it on the bottom
//-----------------------------------------------------------------
if (!String.IsNullOrEmpty(footer))
{
StringFormat format = managestringformat(footeralignment, footerformatflags,
null, overridefooterformat);
footerHeight += (g.MeasureString(footer, footerfont, printWidth, format)).Height;
}
// note we don't count the page number height if it's not on a separate line
if (!pagenumberontop && !pagenumberonseparateline)
{
footerHeight += pagenumberHeight;
}
footerHeight += footerspacing;
//-----------------------------------------------------------------
// measure the grid to be printed ... this gets us all the row heights
// and an accurate measure of column widths for the printed area
//-----------------------------------------------------------------
Font defaultfont = dgv.DefaultCellStyle.Font;
Font cellfont;
for (i = 0; i < rowstoprint.Count; i++)
{
row = (DataGridViewRow) rowstoprint[i];
rowheights.Add(0);
// add row headers if they're visible
if (dgv.RowHeadersVisible)
{
SizeF rhsize = g.MeasureString(row.HeaderCell.EditedFormattedValue.ToString(),
headerfont);
rowheaderwidth = (rowheaderwidth < rhsize.Width) ? rhsize.Width : rowheaderwidth;
}
// calculate widths for each column. We're looking for the largest width needed for
// all the rows of data.
for (j = 0; j < colstoprint.Count; j++)
{
col = (DataGridViewColumn) colstoprint[j];
if (row.Cells[col.Name].HasStyle && (null != row.Cells[col.Name].Style.Font))
cellfont = row.Cells[col.Name].Style.Font;
else
cellfont = defaultfont;
// get the raw size of the string.
SizeF size = g.MeasureString(row.Cells[col.Name].EditedFormattedValue.ToString(),
cellfont);
// Handle fixed size cells and > printwidth cells where the width of the
// data won't fit. (I.E. need to stretch the row down the page)
if ((0 < colwidthsoverride[j]) || (size.Width > printWidth))
{
// set column width
if (0 < colwidthsoverride[j])
colwidths[j] = colwidthsoverride[j];
else if (size.Width > printWidth)
colwidths[j] = printWidth;
// remeasure the string with the new limits and proper formatting for wrapping.
// Use an absurd height value so that we can get the real number of lines printed
int chars, lines;
StringFormat format = managestringformat(cellalignment, cellformatflags,
col.InheritedStyle, overridecellformat);
g.MeasureString(row.Cells[col.Name].EditedFormattedValue.ToString(),
cellfont, new SizeF(colwidths[j], 2147483647), format,
out chars, out lines);
// set row height
float tempheight = lines * size.Height;
rowheights[i] = (rowheights[i] < tempheight ? tempheight : rowheights[i]);
}
else
{
colwidths[j] = (colwidths[j] < size.Width ? size.Width : colwidths[j]);
rowheights[i] = (rowheights[i] < size.Height ? size.Height : rowheights[i]);
}
}
}
//-----------------------------------------------------------------
// Break the columns accross page sets. This is the key to printing
// where the total width is wider than one page.
//-----------------------------------------------------------------
// assume everything will fit on one page
pagesets = new List<PageDef>();
pagesets.Add(new PageDef(printmargins, colstoprint.Count));
int pset = 0;
// Account for row headers
pagesets[pset].coltotalwidth = rowheaderwidth;
// split columns into page sets
float columnwidth;
for (i = 0; i < colstoprint.Count; i++)
{
// get initial column width
columnwidth = (colwidthsoverride[i] >= 0)
? colwidthsoverride[i] : colwidths[i];
// See if the column width takes us off the page - Except for the
// first column. This will prevent printing an empty page!! Otherwise,
// columns longer than the page width are printed on their own page
if (printWidth < (pagesets[pset].coltotalwidth + columnwidth) && i != 0)
{
pagesets.Add(new PageDef(printmargins, colstoprint.Count));
pset++;
// Account for row headers
pagesets[pset].coltotalwidth = rowheaderwidth;
// Adding frozen columns
frozenColsCount = AddFrozenColsForPageSet( pagesets[pset], frozenColsCount, i );
}
// update page set definition
pagesets[pset].colstoprint.Add(colstoprint[i]);
pagesets[pset].colwidths.Add(colwidths[i]);
pagesets[pset].colwidthsoverride.Add(colwidthsoverride[i]);
pagesets[pset].coltotalwidth += columnwidth;
}
//-----------------------------------------------------------------
// Adjust column widths and table margins for each page
//-----------------------------------------------------------------
for (i=0; i<pagesets.Count; i++)
AdjustPageSets(g, pagesets[i]);
}
/// <summary>
/// Add frozen columns on every new page created.
/// We adjust nummber of frozen coluns to be sure to print at least on non-frozen one.
/// </summary>
/// <param name="pageset">new page set in which we add frozen columns</param>
/// <param name="frozenColsCount">number of defined frozen columns</param>
/// <param name="nextCol">first non frozen column to add to this page set</param>
/// <returns>corrected number of frozen columns</returns>
private int AddFrozenColsForPageSet( PageDef pageset, int frozenColsCount, int nextCol ) {
float totalWidth = 0;
float columnwidth;
if ( frozenColsCount >= nextCol ) {
frozenColsCount -= 2;
if ( frozenColsCount < 1 )
frozenColsCount = 1;
}
// compute resulting width after add of next column
totalWidth = ( colwidthsoverride[nextCol] >= 0 )
? colwidthsoverride[nextCol] : colwidths[nextCol];
for ( int i = 0; i < frozenColsCount; i++ ) {
// get initial column width
columnwidth = ( colwidthsoverride[i] >= 0 )
? colwidthsoverride[i] : colwidths[i];
if ( printWidth < ( totalWidth + columnwidth ) ) {
return frozenColsCount;
}
// update page set definition
pageset.colstoprint.Add( colstoprint[i] );
pageset.colwidths.Add( colwidths[i] );
pageset.colwidthsoverride.Add( colwidthsoverride[i] );
pageset.coltotalwidth += columnwidth;
totalWidth += columnwidth;
}
return frozenColsCount;
}
</code>
modified on Monday, April 7, 2008 5:13 AM
|
|
|
|
|
Interesting concept, and now that you mention it, I can see someone wanting to do this. If you can send me your C# code file (my email is in the article header above), I'll take a look at fully integrating this update into the main code base.
Thanks for a great idea and the effort to make it work!
Steve G.
|
|
|
|
|
how to print multi-subtitle at different line?
|
|
|
|
|
Currently, DGVPrinter supports one title line and one subtitle line. To do multiple subtitles you'd need to change measureprintarea and printDoc_PrintPage (as well as adding the extra subtitle properties).
Steve G.
|
|
|
|
|
I am not sure if this was the problem with some previous versions, but with the one I downloaded you can easily print more lines, just use \r\n to move to next line.
|
|
|
|
|
Ding!
Sometimes the answer is so obvious that you just can't find it...
Steve G.
|
|
|
|
|
Hi
I am getting the Index out of range error. When i set the page number from 2 to 3 from the print dialog.
Exception comes into this method "printDoc_PrintPage"
while ((printpos + nextrowheight) < staticheight)
{
lastrowprinted++;
if (lastrowprinted >= rowstoprint.Count - 1)
//if (rowheights.Count-1 > lastrowprinted)
{
printpos += rowheights[lastrowprinted];
nextrowheight = (lastrowprinted < rowheights.Count) ? rowheights[lastrowprinted] : 0;
}
}
Please help me to resolved this.
If you help than it's nice to me. Please replay me at my id hiteshmaster151@yahoo.com or you can chat with me
Thanks
Hitesh
|
|
|
|
|
Good catch! I've fixed the bug and uploaded the new code. I've also found and fixed the bug where the page "part" number was too high.
Enjoy!
Steve G.
|
|
|
|
|
Hi
Thanks for your replay.
I have updated your code but it is printing blank page when i select the page 2 to 2 from print dialog
Please help me resolved this bug
Thanks
Hitesh Patel
|
|
|
|
|
That will happen when the page number selected to print is past the end of your data.
Steve G.
|
|
|
|
|
Hi
again thanks for your replay
but i have two page if i can see it with all pages.
and when i give print to all or selected page it will print two pages and it is blank
I want to use your code in my application. So Please help me
Thanks
Hitesh Patel
|
|
|
|
|
Hi steve
Please replay
Thanks
Hitesh
|
|
|
|
|
I had emailed you on 4/1 since I couldn't post to CodeProject. Here's the email that I'm guessing you didn't get...
I seem to have hit a problem with the CodeProject site - or perhaps
it's their maintenance cycle, so I'm having to email you directly...
I'm not certain what you could be running into. DGVPrinter will print
all the rows in the grid when you print and choose "All". Does your
blank page have a header and a footer or is it completely empty? The
only time I know of that you can get a completely empty page is when
you choose to print a range with the start page beyond the end of your
data.
What does the data in the grid look like?
____________________________________________________________________
Steve G.
Complex problems have simple, easy to understand, wrong answers.
|
|
|
|
|
Hello!
Nice work, and nice article, I use something like your solution for years, befor I find: DataGridView Extension, and buy the silver pack for one (1) euro
Check it out: http://www.completit.com/Products/DGVE/Overview.aspx[^]
It's NOT a product preview, or advertisment.
It's just an infomration from Me to You.
|
|
|
|
|
Yes, and if MS had done the job right, neither this nor my DGVPrinter would be required!
Steve G.
|
|
|
|
|
I think they do it never
|
|
|
|
|
Text wrapping within fixed width columns is now supported. I.E. if you just have a lot of data, you can get lines to wrap with a cell. To make this work you need to do two things. First, override the cell formatting to remove the nowrap flag, and second, set a fixed width for the column.
<br />
print.CellFormatFlags = StringFormatFlags.LineLimit | StringFormatFlags.NoClip;<br />
print.ColumnWidths.Add("descriptionDataGridViewTextBox", 200);<br />
Steve G.
|
|
|
|
|
I've received the request a number of times, so I finally broke down and implemented printing where the width of the columns is larger than one page. If the total column width is larger than one page, you'll get a second set of pages for the excess - or a third, or a fourth, etc.
I've also simplified some portions of the code and adjusted the printout so that the border of the column headers is not overlaid by the row background.
Take it for a spin and let me know if y'all find anything!
Steve G.
|
|
|
|
|
Steve,
How difficult would it be to do the following...
If a column is going to be too wide for the page make it the last column on the page and wrap the text in that row down the page. Subsequent columns could then follow on new pages.
I suppose the main issue would be determining the width to use of the column in question.
Top job !
Thanks, Jim.
|
|
|
|
|
Let me clarify what's happening: Normally, when the total width of columns is larger than one page, the columns that go over the limit are moved and printed in whole on another page. The ONLY time I do any truncating is if there is a SINGLE column wider than the print area. That individual column gets printed on it's own page and chopped at the page limit.
Does that help clear things up? As for wrapping a column, I'll have to look into that...
Steve G.
|
|
|
|
|
Thanks
We often have data in columns (200+ chars) that the datagridview of course wraps within the view and it would be really useful to have it printed that way as well.
Cheers, Jim.
|
|
|
|
|
Download the latest version and try it out. You'll need to turn off the NoWrap flag, and set a fixed width to get wrapping:
print.CellFormatFlags = StringFormatFlags.LineLimit | StringFormatFlags.NoClip;<br />
print.ColumnWidths.Add("descriptionDataGridViewTextBox", 200);<br />
Let me know how this works out for you...
Steve G.
|
|
|
|
|
Steve,
Looking good, thanks !
Few things
1. Would it be possible to specify a different value for the fixed width of the first column ?
OK Understand now what the printer.ColumnWidths.Add("Field", 50); statement is doing !!
2. Currently at the bottom of the page I see the page number and a part number, the part number starts with a value of 2 on page 1 !!
3. Doesn't seem to work for the print preview call.
I would like to say again , Top effort !
Thanks, Jim
Here is the code I'm using to call DGVPrinter.
<br />
DGVPrinter printer = new DGVPrinter();<br />
printer.Title = "DataGridView Report";<br />
printer.SubTitle = "An Easy to Use DataGridView Printing Object";<br />
printer.SubTitleFormatFlags = StringFormatFlags.LineLimit | StringFormatFlags.NoClip;<br />
printer.PageNumbers = true;<br />
printer.PageNumberInHeader = false;<br />
printer.PorportionalColumns = true;<br />
printer.HeaderCellAlignment = StringAlignment.Near;<br />
printer.Footer = "Your Company Name Here";<br />
printer.FooterSpacing = 15;<br />
<br />
printer.CellFormatFlags = StringFormatFlags.NoWrap | StringFormatFlags.LineLimit | StringFormatFlags.NoClip;<br />
printer.ColumnWidths.Add("Field", 50);<br />
printer.ColumnWidths.Add("Data", 100);
<br />
printer.PrintPreviewDataGridView(dg_print);<br />
printer.PrintDataGridView(dg_print);<br />
On the line
// print column headers
printcolumnheaders(e.Graphics, ref printpos, pagesets[currentpageset]);
I get an "ArgumentOutOfRangeException" was unhandled, this when actually doing the print.
pagesets Count=1
currentpageset = 1 also
modified on Wednesday, March 26, 2008 6:45 PM
|
|
|
|
|
Hmmm.... I'm not able to reproduce any of these errors.
Is there some way to send me the code/program that you're using to drive DGVPrinter & let me take a look at it?
Thanks,
Steve G.
|
|
|
|
|
Steve,
Nothing really strange re the DataGridView, it's initialised as follows
<br />
dg_print.ColumnCount = 2;<br />
dg_print.ColumnHeadersVisible = true;<br />
dg_print.ColumnHeadersDefaultCellStyle = columnHeaderStyle;<br />
dg_print.Columns[0].Name = "Field";<br />
dg_print.Columns[1].Name = "Data";<br />
dg_print.RowCount = 0;<br />
<br />
dg_print.AutoSizeRowsMode = DataGridViewAutoSizeRowsMode.AllCells;<br />
dg_print.DefaultCellStyle.WrapMode = DataGridViewTriState.True;<br />
and filled with code like this
<br />
dg_print.Rows.Add(String.Format("{0,-15}", "Plan Number : "), planno);<br />
dg_print.Rows.Add(String.Format("{0,-15}", "Source Entry : "), dsp_sourceentry.Text);<br />
dg_print.Rows.Add(String.Format("{0,-15}", "Reg Date : "), dsp_regdate.Text);<br />
dg_print.Rows.Add(String.Format("{0,-15}", "Description : "), dsp_plandesc.Text);<br />
the row Description can contain data 200+ chars in length.
Jim.
|
|
|
|
|