Print multiple datagridview pages - c#

I'd like to print my DataGridView content, but when I have many rows in this DataGridView I don't know how need I use the HasMorePages property.
This is my current code:
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
foreach (DataGridViewRow sor in dataGridView1.Rows)
{
foreach (DataGridViewColumn oszlop in dataGridView1.Columns)
{
szelesseg += oszlop.Width;
e.Graphics.DrawRectangle(Pens.Brown, szelesseg, magassag, oszlop.Width, sor.Height);
szelesseg_lista.Add(szelesseg);
magassag_lista.Add(magassag);
}
foreach (DataGridViewCell cella in sor.Cells)
{
ertekek.Add(cella.Value.ToString());
}
szelesseg = 10;
magassag = magassag + sor.Height;
cella_magassag += sor.Height;
}
int sor_db = ertekek.Count;
for (int i = 0; i < sor_db; i++)
{
e.Graphics.DrawString(ertekek[i], new Font(FontFamily.GenericMonospace, 12, FontStyle.Regular), new SolidBrush(Color.Red), szelesseg_lista[i], magassag_lista[i]);
}
}

The PrintPage event is for every page you want to print. That means your For...Each loop won't work since you are telling the printer to print everything on the current page.
You have to have a variable outside the PrintPage method scope to keep track of which row index you are currently on:
int printIndex;
private void printDocument1_PrintPage(object sender, PrintPageEventArgs e) {
int y = 0;
while (printIndex < dataGridView1.Rows.Count &&
y + dataGridView1.Rows[printIndex].Height < e.MarginBounds.Height) {
// print your stuff, y is where you are on the page vertically
y += dataGridView1.Rows[printIndex].Height;
++printIndex;
}
e.HasMorePages = printIndex < dataGridView1.Rows.Count;
}
Use the BeginPrint event to reset your printIndex value:
private void printDocument1_BeginPrint(object sender, PrintEventArgs e) {
printIndex = 0;
}
If you add another variable, such as int printPage;, you can now know which page you are currently printing, too, by incrementing that value in the PrintPage event.

Try this method
Add this method in to your cs page
protected override PageStatePersister PageStatePersister
{
get
{
return new SessionPageStatePersister(this);
}
}

Related

Event handling for multiple pages printing

The text document have more pages. They need to handle events for print more pages. But my code written for Copy From Screen() method.
code :
public partial class print : Form
{
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
e.Graphics.DrawImage(bmp, 0, 0);
}
private void printbtn_Click(object sender, EventArgs e)
{
printDocument1.DefaultPageSettings.PaperSize = new System.Drawing.Printing.PaperSize("210 X 297", 820, 800);
printPreviewDialog1.Document = printDocument1;
Graphics g = this.CreateGraphics();
bmp = new Bitmap(this.Size.Width, this.Size.Height, g);
Graphics g1 = Graphics.FromImage(bmp);
g1.CopyFromScreen(this.Location.X, this.Location.Y, 0, 0, this.Size);
printPreviewDialog1.ShowDialog();
}
}
It's up to you to break your pages up into pages and to keep track of what has been printed and whether there are more pages to print. Here is a simple example that will print an arbitrary list of text with ten lines to a page. It will keep printing pages with ten lines until there are no more lines to print.
private List<string> lines = new();
private int lineIndex;
private void printDocument1_BeginPrint(object sender, System.Drawing.Printing.PrintEventArgs e)
{
lineIndex = 0;
}
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
for (var i = 0; i < 10 && lineIndex < lines.Count; i++, lineIndex++)
{
e.Graphics.DrawString(lines[lineIndex], Font, Brushes.Black, 0, i * 15);
}
e.HasMorePages = lineIndex < lines.Count;
}
The code resets the line index to zero at the start of each print run. It then prints up to ten lines on the current page and then prints another page if and only if there are still lines to print. It's a simple principle and you need to implement it in the appropriate way for your data.
Another variation is to use the BeginPrint event to process the data and break it into pages there, then print one of those pages per PrintPage event, e.g.
// All the lines to be printed.
private List<string> lines = new();
// The groups of lines to be printed on each page.
private List<string[]> pages;
// The index of the page being printed.
private int pageIndex;
private void printDocument1_BeginPrint(object sender, System.Drawing.Printing.PrintEventArgs e)
{
// Print 10 lines per page.
const int pageLineCount = 10;
pages = new List<string[]>();
// Take groups of up to 10 lines from the line liust and add them to the page list.
for (var pageStartLineIndex = 0; pageStartLineIndex < lines.Count; pageStartLineIndex += pageLineCount)
{
pages.Add(lines.Skip(pageStartLineIndex).Take(pageLineCount).ToArray());
}
// Start printing at the first page.
pageIndex = 0;
}
private void printDocument1_PrintPage(object sender, System.Drawing.Printing.PrintPageEventArgs e)
{
// Get the current page and increment the page index.
var page = pages[pageIndex++];
// Print the current page.
for (var i = 0; i < page.Length; i++)
{
e.Graphics.DrawString(page[i], Font, Brushes.Black, 0, i * 15);
}
// Continue printing if and only if there are more pages to print.
e.HasMorePages = pageIndex < pages.Count;
}

C# DataGridView data move to next cell after edit is done

I made a control on CellEndEdit event to prevent null or empty values. When user edits a cell and clicks to next cell for example and leave that one empty, it gives error and sets CurrentCell property to the empty cell. After i edit empty cell and click TAB value moves to next cell and current cell move next to next cell. I may explained it confusingly so let me show you a it's video record. I also have CellClick event to begin editing when i click the cell.
Here is the code:
private void notTakipPaneli_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
if (String.IsNullOrEmpty(notTakipPaneli[e.ColumnIndex, e.RowIndex].FormattedValue.ToString()))
{
MessageBox.Show("Lütfen hücreyi boş bırakmayınız.");
notTakipPaneli.CurrentCell = notTakipPaneli[e.ColumnIndex, e.RowIndex];
notTakipPaneli.BeginEdit(true);
//for (int i = e.ColumnIndex + 1; i < notTakipPaneli.Rows[e.RowIndex].Cells.Count; i++)
// notTakipPaneli[i, e.RowIndex].Selected = false;
return;
}
if (e.ColumnIndex == (notTakipPaneli.Rows[e.RowIndex].Cells.Count) - 2)
{
double ortalama = 0;
byte vize = Convert.ToByte(notTakipPaneli.Rows[e.RowIndex].Cells[1].FormattedValue);
byte final = Convert.ToByte(notTakipPaneli.Rows[e.RowIndex].Cells[(notTakipPaneli.Rows[e.RowIndex].Cells.Count) - 2].FormattedValue);
byte araSinav = Convert.ToByte(notTakipPaneli.Rows[e.RowIndex].Cells["araSinav"].FormattedValue);
if (araSinav == 0)
ortalama = (vize * 0.4) + (final * 0.6);
else
ortalama = (vize * 0.2) + (araSinav * 0.2) + (final * 0.6);
ortalama = Math.Round(ortalama, 2, MidpointRounding.AwayFromZero);
notTakipPaneli.Rows[e.RowIndex].Cells["notOrtalamasi"].Value = ortalama;
}
}
private void veriKaydet_Click(object sender, EventArgs e)
{
for (int i = 0; i < notTakipPaneli.Rows.Count; i++)
{
for (int j = 0; j < notTakipPaneli.Columns.Count; j++)
{
if (String.IsNullOrEmpty(notTakipPaneli[j, i].FormattedValue.ToString()))
{
MessageBox.Show("Lütfen boş alan bırakmayınız",
"Not Takip Paneli",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
notTakipPaneli[j, i].Selected = true;
return;
}
}
}
}
private void yeniSatirEkle_Click(object sender, EventArgs e)
{
notTakipPaneli.Rows.Add();
}
private void notTakipPaneli_CellClick(object sender, DataGridViewCellEventArgs e)
{
notTakipPaneli.BeginEdit(true);
}
Maybe you can try to wrap that code in a BeginInvoke block.
private void dataGridView1_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
if (String.IsNullOrEmpty(dataGridView1[e.ColumnIndex, e.RowIndex].FormattedValue.ToString()))
{
MessageBox.Show("Lütfen hücreyi boş bırakmayınız.");
this.BeginInvoke(new Action(() => {
dataGridView1.CurrentCell = dataGridView1[e.ColumnIndex, e.RowIndex];
dataGridView1.BeginEdit(true);
}));
return;
}
}

DataGridView OnRowPostPaint draw

Actually, my code is "selecting hover" all the line, when i have the mouse over the cell, but, i want to change it, i want to select all the column, not the line.
Data Grid
How can i manage it?
My code is:
protected override void OnRowPostPaint(DataGridViewRowPostPaintEventArgs e)
{
base.OnRowPostPaint(e);
if (this.RectangleToScreen(e.RowBounds).Contains(MousePosition))
{
using (var b = new SolidBrush(Color.FromArgb(50, Color.Black)))
using (var p = new Pen(Color.MediumVioletRed))
{
var r = e.RowBounds;
r.Width -= 1;
r.Height -= 1;
e.Graphics.FillRectangle(b, e.RowBounds.X, e.RowBounds.Y,e.RowBounds.Width, e.RowBounds.Height);
e.Graphics.DrawRectangle(p, r);
}
}
}
I can find the same method, but to draw vertically...
Thank you
If You just want to select entire column while mouse is over any cell then You don't have to use OnRowPostPaint event , You can make the selection mode for the entire grid to select entire column instead of only one cell or entire row but to do that You need to make the SortMode for the Columns set to anything other than Automatic. here is how to do it:
private void Form1_Load(object sender, EventArgs e)
{
for (int i = 0; i < dataGridView1.ColumnCount; i++)
{
dataGridView1.Columns[i].SortMode = DataGridViewColumnSortMode.NotSortable;
}
dataGridView1.SelectionMode = DataGridViewSelectionMode.FullColumnSelect;
}
then use either OnCellMouseEnter or OnCellMouseMove events to select whole column like this:
private void OnCellMouseMove(object sender, DataGridViewCellMouseEventArgs e)
{
if (e.RowIndex == -1 || e.ColumnIndex == -1)
return;
dataGridView1.CurrentCell = dataGridView1[e.ColumnIndex, e.RowIndex];
}
refer to this Answer for more information on selecting entire column.
Maybe not exactly what you want but maybe this example can get you started:
private void dataGridView1_CellPainting(object sender, DataGridViewCellPaintingEventArgs e)
{
Rectangle newRect = new Rectangle(e.CellBounds.X + 1,
e.CellBounds.Y + 1, e.CellBounds.Width - 4,
e.CellBounds.Height - 4);
using (
Brush gridBrush = new SolidBrush(this.dataGridView1.GridColor),
backColorBrush = new SolidBrush(e.CellStyle.BackColor))
{
using (Pen gridLinePen = new Pen(gridBrush))
{
// Erase the cell.
e.Graphics.FillRectangle(backColorBrush, e.CellBounds);
// Draw the grid lines (only the right and bottom lines;
// DataGridView takes care of the others).
e.Graphics.DrawLine(gridLinePen, e.CellBounds.Left,
e.CellBounds.Bottom - 1, e.CellBounds.Right - 1,
e.CellBounds.Bottom - 1);
e.Graphics.DrawLine(gridLinePen, e.CellBounds.Right - 1,
e.CellBounds.Top, e.CellBounds.Right - 1,
e.CellBounds.Bottom);
// Draw the inset highlight box.
e.Graphics.DrawRectangle(Pens.Blue, newRect);
// Draw the text content of the cell, ignoring alignment.
if (e.Value != null)
{
e.Graphics.DrawString((String)e.Value, e.CellStyle.Font,
Brushes.Crimson, e.CellBounds.X + 2,
e.CellBounds.Y + 2, StringFormat.GenericDefault);
}
e.Handled = true;
}
}
}
The example comes from this page learn.microsoft.com DataGridView.OnCellPainting
This will select all the cells within the hovered column... though it's not really clear what you are asking for.
using System.Windows.Forms;
namespace DatagridViewSelectAllCells_58842788
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn());
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn());
for (int i = 0; i < 10; i++)
{
dataGridView1.Rows.Add($"col1_{i}", $"col2_{i}");
}
dataGridView1.CellMouseEnter += DataGridView1_CellMouseEnter;
}
private void DataGridView1_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
{
if (e.ColumnIndex == -1)//don't do anything if the rowheader is being hovered
{
return;
}
if (dataGridView1.SelectedCells.Count > 1 && e.ColumnIndex == dataGridView1.SelectedCells[0].ColumnIndex)//don't do anything if the column is already selected
{
return;
}
dataGridView1.ClearSelection();//clear the current selection
//select all the cells in that hovered column
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
dataGridView1[e.ColumnIndex, i].Selected = true;
}
}
}
}
here's an updated answer for just changing the color of the cells
using System.Windows.Forms;
using System.Drawing;
namespace DatagridViewSelectAllCells_58842788
{
public partial class Form1 : Form
{
static Color originalColor;
static int highlightedCol = -1;
static DataGridView dataGridView1 = new DataGridView();
public Form1()
{
InitializeComponent();
Controls.Add(dataGridView1);
dataGridView1.Dock = DockStyle.Fill;
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn());
dataGridView1.Columns.Add(new DataGridViewTextBoxColumn());
for (int i = 0; i < 10; i++)
{
dataGridView1.Rows.Add($"col1_{i}", $"col2_{i}");
}
dataGridView1.CellMouseEnter += DataGridView1_CellMouseEnter;
dataGridView1.CellMouseLeave += DataGridView1_CellMouseLeave;
dataGridView1[1, 4].Style.BackColor = Color.DarkOliveGreen;
}
private void DataGridView1_CellMouseLeave(object sender, DataGridViewCellEventArgs e)
{
//WithCellStyle(sender, e, "Leave");
WithDefaultStyle(sender, e, "Leave");
}
private void DataGridView1_CellMouseEnter(object sender, DataGridViewCellEventArgs e)
{
//WithCellStyle(sender, e, "Enter");
WithDefaultStyle(sender, e, "Enter");
}
/*
* to handle cells that have specific style attributes
* like cell dataGridView1[1, 4]
* there would be more to this to keep track of multiple different cell styles on the Leave side of thing
* but that's up to you to handle if you have a need for it.
*/
private static void WithCellStyle(object sender, DataGridViewCellEventArgs e, string eType)
{
switch (eType)
{
case "Enter":
if (originalColor == null)
{
originalColor = ((Control)sender).BackColor;//store the color for leave event
}
highlightedCol = e.ColumnIndex;//set which column is highlighted for leave event
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
dataGridView1[highlightedCol, i].Style.BackColor = Color.Aquamarine;
}
break;
case "Leave":
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
dataGridView1[highlightedCol, i].Style.BackColor = originalColor;
}
break;
default:
break;
}
}
/*
* if you only have default styles
*/
private static void WithDefaultStyle(object sender, DataGridViewCellEventArgs e, string eType)
{
switch (eType)
{
case "Enter":
if (originalColor == null)
{
originalColor = ((Control)sender).BackColor;//store the color for leave event
}
highlightedCol = e.ColumnIndex;//set which column is highlighted for leave event
//change color of all the cells
dataGridView1.Columns[highlightedCol].DefaultCellStyle.BackColor = Color.Aquamarine;//set the new color
break;
case "Leave":
//per defaultstyle
dataGridView1.Columns[highlightedCol].DefaultCellStyle.BackColor = originalColor;
break;
default:
break;
}
}
}
}

c# loop through list view items and display each in a text box a row at a time with a click of a button

I need to displaying items of the 4th column in a textbox row by row after clicking 'next' button. When the user clicks next, the next row should be displayed until the last row.
Below is the code I have so far, that only displays the last row when button 'next' is clicked. I cannot seem to get the loop right.
public partial class RecordingView : Form
{
public RecordingView()
{
InitializeComponent();
}
public void UpdateListView(ListView listView1)
{
foreach (ListViewItem item in listView1.Items)
{
this.listView1.Items.Add((ListViewItem)item.Clone());
}
}
private void RecordingView_Load(object sender, EventArgs e)
{
//Add column header
listView1.Columns.Add("#", 60);
listView1.Columns.Add("Start time", 100);
listView1.Columns.Add("End time", 100);
listView1.Columns.Add("Duration", 100);
listView1.Columns.Add("Text", 350);
}
private void btnNext_Click(object sender, EventArgs e)
{
for (int i = 0; i < listView1.Items.Count; i++)
{
string line = listView1.Items[i].SubItems[4].Text; //picks list item
rtbCurrentLine.Text = line;
}
}
}
Below is my user interface
user interface
kindly help me figure out where I have gone wrong.
I suggest this, declare a global variable that will get your index of next or previous click:
int intIndex = -1;
Then on your btnNext click event handler, increment it per click:
private void btnNext_Click(object sender, EventArgs e)
{
if (intIndex <= listView1.Items.Count)
{
intIndex++;
string line = listView1.Items[intIndex].SubItems[4].Text;
rtbCurrentLine.Text = line;
}
}

Button Click Frequency Array

I need to make a ListBox that displays how often a Button is clicked.
The user chooses how many buttons are available to click. Here is what I've tried:
int clicked;
clicked = int.Parse(((Button)(sender)).Text);
freq_array[clicked]++;
for (int i = 0; i < freq_array[clicked]; i++)
lstFrequencies.Items[clicked] = clicked + "\t\t" + freq_array[clicked];
freq_array uses the 'clicked' variable to add to the frequency that button has been clicked. Or, it's supposed to.
When I debug it, 'clicked' always comes out to 0. I want 'clicked' to equal the text value of the button that's clicked. When I try to run the program, I get an error saying "Input string was not in correct format."
Edit:
I was able to fix my program with help from you guys. I realized I didn't show enough of my code to be clear enough, and I apologize for that. I had to add some things and move things around and got it soon enough. Thank you all.
Here is the code just for those who may need help in the future:
public partial class Form1 : Form
{
int[] freq_array = new int[11];
int[] numList = new int[11];
int oBase = 0;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
invisiblity();
}
private void invisiblity()
{
foreach (Control ctrl in this.Controls)
{
if (ctrl is Button)
if (Char.IsDigit(ctrl.Text[0]))
ctrl.Visible = false;
}
}
private void btnSetBase_Click(object sender, EventArgs e)
{
Form2 frmDialog = new Form2();
frmDialog.ShowDialog(this);
if (frmDialog.DialogResult == DialogResult.OK)
{
oBase = frmDialog.Base;
//lblOutDigits.Text = oBase.ToString();
for (int i = 0; i < oBase; i++)
{
numList[i] = i;
}
}
ShowBaseButtons(oBase);
}
private void ShowBaseButtons(int last_digit)
{
invisiblity();
foreach (Control ctrl in this.Controls)
{
if (ctrl is Button)
if (Char.IsDigit(ctrl.Text[0]))
if (int.Parse(ctrl.Text) <= last_digit - 1)
ctrl.Visible = true;
}
}
private void btnN_Click(object sender, EventArgs e)
{
lblOutDigits.Text += ((Button)(sender)).Text;
int clicked = int.Parse(((Button)(sender)).Text);
freq_array[clicked]++;
}
private void btnShowFreq_Click(object sender, EventArgs e)
{
lstFrequencies.Items.Clear();
for (int i = 0; i < oBase; i++)
lstFrequencies.Items.Add(numList[i] + " \t\t\t" + freq_array[i]);
}
Your code should work as long as your Button Text is actually just a number. Since what you are trying to do is create an index, what I usually do is use the Tag Property of the control, set it to the Index I want in the designer and then cast that to an Int.
i.e.
if (int.TryParse(((Button)sender).Tag.ToString(), out clicked))
freq_array[clicked]++;
I believe what is happening is that you are not initializing your ListBox, This example Code does work using your initial method. Just create a new Form and paste it in and test.
public partial class Form1 : Form
{
ListBox lstFrequencies = new ListBox();
int[] freq_array = new int[10];
public Form1()
{
InitializeComponent();
Size = new Size(400, 400);
lstFrequencies.Location = new Point(150, 0);
lstFrequencies.Size = new Size(150, 200);
Controls.Add(lstFrequencies);
int top = 0;
for (int i = 0; i < 10; i++)
{
Button btn = new Button();
btn.Size = new Size(70, 30);
btn.Location = new Point(5, top);
Controls.Add(btn);
top += 35;
btn.Tag = i;
btn.Text = i.ToString();
btn.Click += new EventHandler(btn_Click);
lstFrequencies.Items.Add(i.ToString());
}
}
void btn_Click(object sender, EventArgs e)
{
int clicked;
clicked = int.Parse(((Button)(sender)).Text);
freq_array[clicked]++;
lstFrequencies.Items[clicked] = clicked + "\t\t" + freq_array[clicked]; //Cleaned up you do not need to iterate your list
// Using my example code
//if (int.TryParse(((Button)sender).Tag.ToString(), out clicked))
//{
// freq_array[clicked]++;
// lstFrequencies.Items[clicked] = clicked + "\t\t" + freq_array[clicked];
//}
}
}
Your code always comes out to 0 because you never assign last clicked value to button text. Try this code:
int clicked = 0;
private void button1_Click(object sender, EventArgs e)
{
clicked = Convert.ToInt32(((Button)sender).Text);
lstFrequencies.Items.Add(((Button)sender).Name + " " + ++clicked);
button1.Text = clicked.ToString(); // you lose this line
}
EDIT: Counter from variable member
int clicked = 0;
private void button1_Click(object sender, EventArgs e)
{
// if you want to display button name, change .Text to .Name
lstFrequencies.Items.Add(((Button)sender).Text + " " + ++clicked);
}

Categories