Datagridview issue with printing alternate rows - c#

Hello all I am trying to print data from datagridview in such a way the alternate rows should print left and right. I am able to print but they are not getting in line. Here is the code I tried to print.
Complete code you can find here
https://drive.google.com/open?id=0ByVjmdncQgagRjZvME9STWJoVHc
using (Font font = new Font("Consolas", 13f))
{
float x = 0;
float y = 0;
for (int row = linesPrinted; row < DGV.Rows.Count; row++)
{
for (int col = 0; col < DGV.ColumnCount; col++)
{
x = 0;
string text = DGV[col, row].FormattedValue.ToString();
if (row % 2 == 0)
{
x += 10;
y += 5;
e.Graphics.DrawString(text, font, Brushes.Black, new PointF(x, y));
}
else
{
x += 45;
y += 5;
e.Graphics.DrawString(text, font, Brushes.Black, new PointF(x, y));
}
}
}
I am getting the output as follows
is getting printed after country1 what I need is it should align to top

Since it appears you are trying to print TWO different rows on the same line, then you are going to have to keep track of where columns 1’s “Y” value starts, this will facilitate lining up column 2. The other issue is the current code simply prints one page when there is more than one page to print.
When looping through the DataGridView printing the rows, and we reach the end of the page, we need to call the PrintPage method again. In its current form, the PrintPage method will start printing from the first row (0) of the DataGridView and it will do this for every new page. An infinite loop will ensue because each new page simply starts over with the first row. A global int variable dgvRowIndex should fix this so that each time the next page is printed we won’t start at the beginning.
I had a difficult time printing multiple pages, however #LarsTech solution worked well at Print multiple datagridview pages
To help, below is a method that simply prints ONE row of the data grid view. The method takes a rowIndex to identify which row to use in the DataGridView, a printX value to indicate the starting left print value, a printY value to indicate the vertical position to start printing this row, a lineIncrement used as a “leading” value for the text and lastly the PrintPageEventArgs variable e to draw the text to.
private void printRow(int rowIndex, float printX, float printY, float lineIncrement, PrintPageEventArgs e) {
for (int col = 0; col < dataGridView1.ColumnCount; col++) {
string text = dataGridView1[col, rowIndex].FormattedValue.ToString();
e.Graphics.DrawString(text, font, Brushes.Black, new PointF(printX, printY));
printY += lineIncrement;
}
}
Now that we have a method to print a single row from the DatagridView into a column in the print document, it should be easier to print the columns as rows and line the rows up. First as mentioned earlier, we need a global variable that keeps track of the DataGridViews row index while looping through it. This global variable dgvRowIndex is used each time the PrintPage method is called. I also added the font variable as a global variable.
// global variables
int dgvRowIndex = 0;
Font font = new Font("Consolas", 13f);
Below is the PrintPage method to print each page. Most variable’s names are self-explanatory. pageHeight is used to determine when we need a new page. Two columns, LeftColX and RightColX are the two horizontal (X) starting points for each column. curY keeps track of the vertical (Y) position on the print page and is used to compare its value to pageHeight and start a new page if needed. lineIncrement is used as a “leading’ value for each new line. spaceBefore is used to add extra vertical space between the two printed rows. Lastly, as stated before, we need to keep track the vertical “Y” value when a new “column 1” is printed. column2YValue is used for this and is sent to the PrintRow method as the starting “Y” value when printing the second column.
When the while loop starts looping through the rows of the DataGridView , a check is made to see if this row is in column 1 or column 2. If dgvRowIndex is in column 1, then we need to save this starting “Y” column2YValue to use when printing the second column. If dgvRowIndex is in column2 then we simply call printRow method again, but this time we pass over the starting “Y” value from the previous column column2YValue. Extra vertical space is added, dgvRowIndex is incremented to go to the next row, then a check is made to see if we need to add another page. Here a simple check is made to see if the current “Y” value curY is past the margin, if it is, then we need to indicate a new page is needed then return. The return simply starts the PrintPage method over, this should work as expected since we now have a global variable dgvRowIndex to avoid starting at the beginning of the DataGridView. Hope this helps.
private void printDocument1_PrintPage(object sender, PrintPageEventArgs e) {
float pageHeight = e.MarginBounds.Height;
float leftColX = 10;
float rightColX = 145;
float curY = 10;
float lineIncrement = 16;
float spaceBefore = 24;
float column2YValue = curY;
while (dgvRowIndex < dataGridView1.Rows.Count) {
if (dgvRowIndex % 2 == 0) {
column2YValue = curY;
printRow(dgvRowIndex, leftColX, curY, lineIncrement, e);
} else {
printRow(dgvRowIndex, rightColX, column2YValue, lineIncrement, e);
curY += spaceBefore;
}
dgvRowIndex++;
if (curY > pageHeight) {
e.HasMorePages = true;
return;
}
curY += spaceBefore;
}
}
Edit to address multiple columns and multiple rows.
As I commented, in order to print multiple columns the code will need to keep track of the print “X” value on the page. The idea is to keep printing on the same (print) row (y value) until the “X” value goes past the right margin.
The other change addresses where to start the next print group/grid row. When the code calls printRow it is not known before that call how many columns the grid row may have. If the grid has three, four or 10 columns, then the code will print that number of lines. If there are 10 columns in each grid row… there will be 10 “print” rows for each row in the grid. Since printRow is going to loop through the columns to print, it is used to return this “next” “Y” print row value.
Tracing the code below, loops through the grids rows, first the current row is printed, and the next “print” row nextRowYStart is set based on how many columns are in the grid.
The next if statement checks if there is enough room on the right of the page to print another column. If there is enough room on the page for another column, then curX simply moves to the next column. If there is not enough room, the “X” value curX is reset to the left most (starting) column, curY is set to nextRowYstart to move down to the next print row, and some vertical space is added. A check is then made to see if the new print rows “Y” value curY goes past the bottom margin. If it does, then we start a new page. Otherwise, simply print the next row of the grid.
The code assumes you want to print all the columns, however if you wanted to print only the first X columns, a stopColumn variable is added so you can decide how many of the first columns to print. I am guessing this may be what you are looking for. Hope it helps.
private void printDocument1_PrintPage(object sender, PrintPageEventArgs e) {
//int stopColumn = 3;
int stopColumn = dataGridView1.Columns.Count;
float pageHeight = e.MarginBounds.Height;
float pageWidth = e.MarginBounds.Width;
float startX = 50;
float startY = 50;
float columnWidth = 150;
float curX = startX;
float curY = startY;
float lineIncrement = 16;
float spaceBefore = 24;
float nextRowYStart = curY;
while (dgvRowIndex < dataGridView1.Rows.Count) {
nextRowYStart = printRow(dgvRowIndex, curX, curY, lineIncrement, stopColumn, e);
if (curX + columnWidth > pageWidth) {
// start a new PRINT group/row
curX = startX;
curY = nextRowYStart;
curY += spaceBefore;
if (curY > pageHeight + spaceBefore) {
dgvRowIndex++;
e.HasMorePages = true;
return;
}
} else {
curX += columnWidth;
}
dgvRowIndex++;
}
}
Updated printRow method
private float printRow(int rowIndex, float printX, float printY, float lineIncrement, int stopCol, PrintPageEventArgs e) {
for (int col = 0; col < dataGridView1.ColumnCount; col++) {
if (col < stopCol) {
string text = dataGridView1[col, rowIndex].FormattedValue.ToString();
e.Graphics.DrawString(text, font, Brushes.Black, new PointF(printX, printY));
printY += lineIncrement;
}
else {
break;
}
}
return printY;
}

Related

draw picture box and get coordinates

i'm newbie here and also in c#.
my project is to create a box in grid view.
then when click desired box, i'll get the box coordinate or position and box will change the colour.
when click another box, the previous box colour will change to original.
the box will resize when total size for rows x cols more than panel2 size.
i wanna extend the function of code by add new button NEXT, when click, then next picture box will be highlight and also coordinate will update. how to relate new button with existing picture box?
for (int cols = 0; cols < COLUMNS; cols++)
{
for (int rows = 0; rows < ROWS; rows++)
{
PictureBox newPic = new PictureBox();
newPic.Height = HEIGHT;
newPic.Width = WIDTH;
newPic.BackColor = Color.Maroon;
int x = cols * (HEIGHT + SPACE);
int y = rows * (WIDTH + SPACE);
newPic.Location = new Point(x + SPACE, y + SPACE);
newPic.Click += NewPic_Click;
items.Add(newPic);
this.panel2.Controls.Add(newPic);
}
}
Just for color switching, you only need the PictureBox which has been clicked on. It is stored in the sender parameter.
I you want the coordinates, you need to store some information on the PictureBox. You don't want to specify 50 handlers.
The way I would do is; to make use of the Tag property of a Control.
Your for-loop would be something like:
for (int cols = 0; cols < COLUMNS; cols++)
{
for (int rows = 0; rows < ROWS; rows++)
{
PictureBox newPic = new PictureBox();
newPic.Height = HEIGHT;
newPic.Width = WIDTH;
newPic.BackColor = Color.Maroon;
// instead of the coordinates, store the indices (for col, row)
newPic.Tag = new Point(cols, rows);
// I would use the Width on the cols, instead of the Height.
int x = cols * (WIDTH + SPACE);
int y = rows * (HEIGHT + SPACE);
newPic.Location = new Point(x + SPACE, y + SPACE);
newPic.Click += NewPic_Click;
items.Add(newPic);
this.panel2.Controls.Add(newPic);
}
}
And your handler would be something like:
// a field to store the previous selected picturebox.
private PictureBox _currentPictureBox = null;
private void NewPic_Click(object sender, EventArgs e)
{
// the picturebox which has been clicked, is stored in the sender object, but you need to cast it to a PictureBox.
PictureBox pb = (PictureBox)sender;
// just for the extra use of the Tag.
var location = (Point)pb.Tag;
// location.X Contains the Column index
// location.Y Contains the Row index
// did we have a previous picturebox?
if(_currentPictureBox != null)
{
// change the previous pictureBox back to Maroon
_currentPictureBox.BackColor = Color.Maroon;
}
// change the current to blue
pb.BackColor = Color.Blue;
// store the new one as the current. (so we can revert it)
_currentPictureBox = pb;
}
I haven't tested it, only in a notepad. So there might be some typo's. But I hope you get the idea.

Vertical line across multiple line charts with value display for each chart in Winforms

I am trying to create an application which will have four line charts on a single form. When user will drag mouse over these charts, there should be one vertical line crossing each chart and the current value will be shown for each chart. Is there any way how this can be done in C#/.NET and WinForms?
Here is an example of what I am trying to achieve:
I suggest to put your data into one MSChart control with four separate ChartAreas.
For this you need to set their positions because the default layout would be 2x2.
Then you add a VerticalLineAnnotation and make it movable.
In its moving events you trigger the Paint event of the chart, where you calculate the necessary data, i.e. the values to display and positions where to display them.
Here is an example:
The Paint event is coded like this:
private void chart_Paint(object sender, PaintEventArgs e)
{
double xv = VL.X; // get x-value of annotation
for (int i = 0; i < chart.ChartAreas.Count; i++)
{
ChartArea ca = chart.ChartAreas[i];
Series s = chart.Series[i];
int px = (int )ca.AxisX.ValueToPixelPosition(xv);
var dp = s.Points.Where(x => x.XValue >= xv).FirstOrDefault();
if (dp != null)
{
int py = (int )ca.AxisY.ValueToPixelPosition(s.Points[0].YValues[0]) - 20;
e.Graphics.DrawString(dp.YValues[0].ToString("0.00"),
Font, Brushes.Black, px, py);
}
}
}
Note the use of two axis functions to convert between two (of the three) coordinate systems in a chart: We start with data values and go to pixels. The third system is percentages, which we'll meet below when setting up the chartareas..
Also note that for simplicty's sake I assume that there is one Series per ChartArea; so I can use the same index. You could also find the respective Series by seaching for the Series with the right ChartArea.Name field (*).
Feel free to set a different y-position and of course font, formatting etc..
To bring it to live we code these two events:
private void chart_AnnotationPositionChanging(object sender,
AnnotationPositionChangingEventArgs e)
{
chart.Invalidate();
}
private void chart_AnnotationPositionChanged(object sender, EventArgs e)
{
chart.Invalidate();
}
The chart setup including test data creation is a little longer..:
First we declare a class level variable for the annotation. Of course we could also grab it from the chart.Annotations collection..:
VerticalLineAnnotation VL = null;
private void setupbutton_Click(object sender, EventArgs e)
{
chart.ChartAreas.Clear();
chart.Series.Clear();
for (int i = 0; i < 4; i++)
{
ChartArea ca = chart.ChartAreas.Add("CA" + (i+1));
ca.Position = new ElementPosition(0, i*23 + 5, 90, 25);
Series s = chart.Series.Add("S" + (i+1));
s.ChartType = SeriesChartType.Line;
s.MarkerStyle = MarkerStyle.Circle; // make the points stand out
s.MarkerSize = 3;
s.ChartArea = ca.Name; // where each series belongs (*)
for (int j = 0; j < 50; j++) // a few test data
{
s.Points.AddXY(j, Math.Sin((( (j + 1) *(i + 1) ) / 55f) * 10f));
}
}
VL = new VerticalLineAnnotation(); // the annotation
VL.AllowMoving = true; // make it interactive
VL.AnchorDataPoint = chart.Series[0].Points[0]; // start at the 1st point
VL.LineColor = Color.Red;
VL.IsInfinitive = true; // let it go all over the chart
chart.Annotations.Add(VL);
}
If you watch the animation closely you will see the values jump; that is because I only have 50 points. If you wanted to display interpolated values you could do that by finding the other neighbouring point and do some simple math. But in many cases this would be nonsense.
Note that I used some 'magic' numbers when setting the ChartArea.Position. It is in percentages of the Chart and I left a little slack at top and botton and also to the right for the Legend..

Automatically resize a form with a TableLayoutPanel that is populated dynamically with controls in its cells

I've a form with a TableLayoutPanel inside it, it have 1 row and 1 column. I populate this TableLayoutPanel dinamically with controls.
First i create the table with:
private void generateTable(int columnCount, int rowCount)
{
//Clear out the existing controls, we are generating a new table layout
tblLayout.Controls.Clear();
//Clear out the existing row and column styles
tblLayout.ColumnStyles.Clear();
tblLayout.RowStyles.Clear();
//Now we will generate the table, setting up the row and column counts first
tblLayout.ColumnCount = columnCount;
tblLayout.RowCount = rowCount;
for (int x = 0; x < columnCount; x++)
{
//First add a column
tblLayout.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
for (int y = 0; y < rowCount; y++)
{
//Next, add a row. Only do this when once, when creating the first column
if (x == 0)
{
tblLayout.RowStyles.Add(new RowStyle(SizeType.AutoSize));
}
}
}
}
then add controls in its cells:
tblLayout.Controls.Add(my_control, column, row);
i've also create this function to resize the form on the basis of the number of row and column of the TableLayoutPanel.
private void forceSizeLocationRecalc()
{
this.Height = 0;
this.Width = 0;
int col = this.tblLayout.ColumnCount;
int row = this.tblLayout.RowCount;
for (int i = 0; i < row; i++)
{
this.Height += this.tblLayout.GetControlFromPosition(0, i).Height;
}
for (int i = 0; i < col; i++)
{
this.Width += this.tblLayout.GetControlFromPosition(i, 0).Width;
}
}
and call it at the end of the setup of the form.
The problem is, if for example, i've first a tableLayout (col=2, row=3), and the i pass to a case in which table layout is (col = 3, row = 1) the dimension of the form is the same of the previous, so i've to resize the form manually. I would like that my form resizing automatically on the basis of columns number and rows number.
Any idea?
thanks
Assuming I've understood you correctly, ensure tblLayout has it's auto size properties set, and then replace the function forceSizeLocationRecalc with this:
private void forceSizeLocationRecalc()
{
this.Width = this.tblLayout.Width;
this.Height = this.tblLayout.Height;
}
This will then force the form to take on the size of the table layout panel. Obviously you'll still need to call this manually when the table is changed.
You could add this where you construct the table layout panel to prevent having to do that:
this.tblLayout.SizeChanged += delegate { this.forceSizeLocationRecalc(); };
Hope that helps!
Set the TableLayoutPanel and Form AutoSize properties to true.
This means the TableLayoutPanel is auto resized based on the size of it's contents (the controls being added) and the Form is auto resized based on it's contents (the TableLayoutPanel).
If the resizing does not get done automatically or looks jumpy you can use the SuspendLayout, ResumeLayout and PerformLayout methods to control when the resizing occurs.

How to print the values of datagridview in c#? [duplicate]

This question already has answers here:
How to print values from a DataGridView control
(3 answers)
Closed 8 years ago.
i want to print entire rows of an DGV.
this should be done by extracting the values not taking picture...(if you don't understand please comment below)
is there any way i can do this?
Still facing problem...
https://drive.google.com/file/d/0BxqCNfHpYEJlUVg3VW5aZlpHMlk/view?usp=sharing
Here is an absolutely minimal code example.
It prints all rows of a dgv, including simple headers and footers..
You need to add a PrintDocument printDocument1 to your form.
Also a Button cb_printPreview.
// a few variables to keep track of things across pages:
int nextPageToPrint = -1;
int linesOnPage = -1;
int linesPrinted = -1;
int maxLinesOnPage = -1;
private void printDocument1_PrintPage(object sender,
System.Drawing.Printing.PrintPageEventArgs e)
{
// for each fresh page:
linesOnPage = 0;
nextPageToPrint++;
// a short reference to the DataGridView we want to print
DataGridView DGV = yourDataGridView;
// I prefer mm, pick your own unit!
e.Graphics.PageUnit = GraphicsUnit.Millimeter;
// I want to print the columns at these fixed positions
// the first one is the left margin,
// the last one a dummy at the right page margin
int[] tabStops = new int[4] {15, 30, 100, 190};
// I want only one column to be right-aligned:
List<int> rightAlignedCols = new List<int>() { 1 };
// we need to keep track of the horizontal position
// this is also the top margin:
float y = 35f;
// we add a little space between the lines:
float leading = 1.5f;
// we will need to measure the texts we print:
SizeF size = Size.Empty;
// we use only one font:
using (Font font = new Font("Consolas", 13f))
{
// a simple header:
e.Graphics.DrawString("List " + printDocument1.DocumentName,
font, Brushes.Black, 50, y);
y += size.Height + 15;
// I loop over the all rows:
for (int row = linesPrinted; row < DGV.Rows.Count; row++)
{
// I print a light gray bar over every second line:
if (row % 2 == 0)
e.Graphics.FillRectangle(Brushes.Gainsboro,
tabStops[0], y - leading / 2, e.PageBounds.Width - 25, size.Height);
// I print all (3) columns in black, unless they're empty:
for (int col = 0; col < DGV.ColumnCount; col++)
if (DGV[0, row].Value != null)
{
string text = DGV[col, row].FormattedValue.ToString();
size = e.Graphics.MeasureString(text, font, 9999);
float x = tabStops[col];
if (rightAlignedCols.Contains(col) )
x = tabStops[col + 1] - size.Width;
// finally we print an item:
e.Graphics.DrawString(text, font, Brushes.Black, x, y);
}
// advance to next line:
y += size.Height + leading;
linesOnPage++;
linesPrinted++;
if (linesOnPage > maxLinesOnPage) // page is full
{
e.HasMorePages = true; // more to come
break; // leave the rows loop!
}
}
e.Graphics.DrawString("Page " + nextPageToPrint, font,
Brushes.Black, tabStops[3] -20, y + 15);
}
}
private void cb_printPreview_Click(object sender, EventArgs e)
{
PrintPreviewDialog PPVdlg = new PrintPreviewDialog();
PPVdlg.Document = printDocument1;
PPVdlg.ShowDialog();
}
private void printDocument1_BeginPrint(object sender,
System.Drawing.Printing.PrintEventArgs e)
{
// create a demo title for the page header:
printDocument1.DocumentName = " Suppliers as of "
+ DateTime.Now.ToShortDateString();
// initial values for a print job:
nextPageToPrint = 0;
linesOnPage = 0;
maxLinesOnPage = 30;
linesPrinted = 0;
}
The actual printing can be triggered from the PrintPreviewDialog.
There is also a PrintDialog and a PrintPreview control for more options.
Obviously one can add many more things, including graphics, multiple fonts etc, but this should give you an idea.. For full tutorials please look refer to the WWW & MSDN!

Resizing label width depending on the width of its content

I'm working on a code-editor and want to auto-adjust the width of a label as the number increases. For example, for 1-9 (1 digit) there's a specific width. Then when it gets to 10-99 (2 digits), width of label increases. Then again for then 100-999 (3 digits), etc.
The result should be something like this:
Here is my code:
private void timer_countline_Tick(object sender, EventArgs e)
{
updateNumberLabel();
}
private void updateNumberLabel()
{
// we get index of first visible char and number of first visible line
Point pos = new Point(0, 0);
int firstIndex = rtb.GetCharIndexFromPosition(pos);
int firstLine = rtb.GetLineFromCharIndex(firstIndex);
// now we get index of last visible char and number of last visible line
pos.X = ClientRectangle.Width;
pos.Y = ClientRectangle.Height;
int lastIndex = rtb.GetCharIndexFromPosition(pos);
int lastLine = rtb.GetLineFromCharIndex(lastIndex);
// this is point position of last visible char, we'll use its Y value for calculating numberLabel size
pos = rtb.GetPositionFromCharIndex(lastIndex);
// finally, renumber label
numberLabel.Text = "";
for (int i = firstLine; i <= lastLine + 1; i++)
numberLabel.Text += i + 1 + "\n";
}
You can use TextRenderer for doing what you want. Please check the following code lines (You should add the code lines to TextChanged event of your label):
Please remember that the AutoSize property of your controls must set to False.
This is for changing Width of your control to fit with width of its contents.
yourLabelName.Width = TextRenderer.MeasureText(yourLabelName.Text, yourLabelName.Font).Width;
This is for changing Height of your control to fit with height of its contents.
yourLabelName.Height = TextRenderer.MeasureText(yourLabelName.Text, yourLabelName.Font).Height;
Update 1:
For changing your panel Width for showing all contents in it horizontally, you can use the followng code line:
yourPanelName.Width = yourLabelName.Left + yourLabelName.Width;
For changing your panel Height for showing all contents in it vartically, you can use the followng code line:
yourPanelName.Height = yourLabelName.Top + yourLabelName.Height;
Update 2:
If you are used SplitContainer control, you must change the properties of your SplitContainer as follows:
FixedPanel = none
IsSplitterFixed = False
Then you can use the following lines of code for achieve to what you want:
For changing your SplitContainer panel Width for showing all contents in it horizontally, you can use the followng code line:
int yourLabelNameWidth = TextRenderer.MeasureText(yourLabelName.Text, yourLabelName.Font).Width;
yourSplitContainerName.SplitterDistance = yourLabelName.Left + yourLabelNameWidth;
yourLabelName.Width = yourLabelName.Left + yourLabelNameWidth;

Categories

Resources