Event handling for multiple pages printing - c#

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;
}

Related

Why does my program draw all the lines from the top left corner? How to change this?

I have written a program that is supposed to connect 10 points generated by mouseclicks, with lines.
I am the following problems:
it draws all lines from the top left corner.
I can think of the solution being something along the lines of setting the first point as the first mouseclick, but i do not know how to do this.
code:
public partial class Form1 : Form
{
Point[] punten = new Point[10];
private int kliks = 0;
private int lijst = 0;
public Form1()
{
InitializeComponent();
}
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
kliks = kliks + 1;
lijst = kliks;
punten[lijst] = e.Location;
if (lijst < 9)
{
punten[lijst] = e.Location;
}
else
{
Pen pen = new Pen(Color.Blue);
Graphics papier = this.CreateGraphics();
papier.DrawLines(pen, punten);
}
}
}
}
The problem for you is using a fixed-size array. Each entry defaults to a point (0,0) and so the Drawlines() method draws the 10 elements, even the ones not set by mouseclicks. This results in the final few points begin at the origin (0,0). In addition, there is a buggy implementation of which clicks to keep and how many have happened in using kliks and lijst for the same thing.
This being C#, it is better to learn how to separate your data model that keeps track of your mouselicks and your display codes that draws the lines.
PointHistory.cs
Consider the following class that uses an array of Point to keep track of the mouseclicks. The class is initialized with the maximum number of points to keep (here 10 for example). There are methods for adding a point AddPoint(point) and to remove the oldest point with RemoveFirstPoint(). The number of points defined is viewed with the .Count property.
Finally, the GetPoints() method, returns a copy of the array with only the elements that have been defined. This method is used in the code that draws the lines.
using System;
using System.Linq;
public class PointHistory
{
private readonly Point[] points;
private readonly int maxPoints;
private int count;
public PointHistory(int pointCapacity)
{
count = 0;
maxPoints = pointCapacity;
points = new Point[pointCapacity];
}
public Point[] GetPoints() { return points.Take(count).ToArray(); }
public int Count { get => count; }
public int Capacity { get => maxPoints; }
public void AddPoint(Point point)
{
if (count < maxPoints)
{
points[count++] = point;
}
}
public void RemoveFirstPoint()
{
if (count > 0)
{
for (int i = 0; i < count - 1; i++)
{
points[i] = points[i + 1];
}
count--;
}
}
}
Form1.cs
Another issue is your drawing, which needs to happen in Paint event handler. Use your MouseClick event to modify the list of points, and the Paint event to do the drawing.
public partial class Form1 : Form
{
readonly PointHistory history;
public Form1()
{
InitializeComponent();
history = new PointHistory(10);
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.MouseClick += Form1_MouseClick;
this.Paint += Form1_Paint;
}
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
history.AddPoint(e.Location);
}
else if (e.Button == MouseButtons.Right)
{
history.RemoveFirstPoint();
history.AddPoint(e.Location);
}
this.Invalidate();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
if (history.Count >= 2)
{
e.Graphics.DrawLines(Pens.Blue, history.GetPoints());
}
foreach (var point in history.GetPoints())
{
e.Graphics.FillEllipse(Brushes.Blue, point.X - 2, point.Y - 2, 4, 4);
}
}
}
I have added some extra code to add little dots at the mouse clicks for a better visual effect.
Left button clicks try to add points to the history of mouseclicks, up to the limit set in the constructor for PointHistory. The right mousebutton cicks removes the oldest point first before adding the new point.
Based on the comments, I want to present a version below that does not define a separate class for the logic, and everything is done internally withing the Form1 class. Also no advanced libraries such as Linq are used
public partial class Form1 : Form
{
private readonly Point[] points;
private int count;
public Form1()
{
InitializeComponent();
points = new Point[10];
count = 0;
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
this.Paint += Form1_Paint;
this.MouseClick += Form1_MouseClick;
}
public Point[] GetPoints()
{
Point[] results = new Point[count];
for (int i = 0; i < count; i++)
{
results[i] = points[i];
}
return results;
// Alternative to the loop above
//
//Array.Copy(points, results, count);
//return results;
}
public void AddPoint(Point point)
{
if (count < points.Length)
{
points[count++] = point;
}
}
public void RemoveFirstPoint()
{
if (count > 0)
{
for (int i = 0; i < count - 1; i++)
{
points[i] = points[i + 1];
}
count--;
}
}
private void Form1_MouseClick(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
AddPoint(e.Location);
}
else if (e.Button == MouseButtons.Right)
{
RemoveFirstPoint();
AddPoint(e.Location);
}
this.Invalidate();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
Point[] mouseClicks = GetPoints();
if (count >= 2)
{
e.Graphics.DrawLines(Pens.Blue, mouseClicks);
}
foreach (var point in mouseClicks)
{
e.Graphics.FillEllipse(Brushes.Blue, point.X - 2, point.Y - 2, 4, 4);
}
}
}
The main method to look at is GetPoints() which returns an array of points that have been defined by mouse clicks and ignores any points that have not been defined yet.
There is an alternate implementation commented with uses Array.Copy() instead of a loop which would make for a better imlpementation.

Drawing stock chart with DataPointCollection.DataBindY in C#

How can I draw a stock chart with List<> and DataPointCollection.DataBindY at once?
I know how to add stock data on a chart but I don't want looping and repeating drawing by Add because actual list is much longer.
To draw a chart at once I tried DataPointCollection.DataBindY but it doesn't work. It needs IEnumerable[] but I don't know how to change List<> to IEnumerable[].
public partial class Form1 : Form
{
double[] Candle;
List<double[]> CandleList = new List<double[]>();
public Form1()
{
InitializeComponent();
Candle = new double[] { 5, 0, 3, 1 };
CandleList.Add(Candle);
Candle = new double[] { 10, 5, 7, 9 };
CandleList.Add(Candle);
}
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < CandleList.Count; i++)
{
chart1.Series[0].Points.Add(CandleList[i]); // works well
}
}
private void button2_Click(object sender, EventArgs e)
{
chart1.Series[0].Points.DataBindY(CandleList); // System.ArgumentException
}
}
Because a list is an iEnumerable, iEnumerable[] means an array of lists.
private void button1_Click(object sender, EventArgs e)
{
List<double>[] Lists = new List<double>[4];
for (int i = 0; i < 4; i++)
{
Lists[i] = new List<double>();
}
Lists[0].Add(5); // high
Lists[0].Add(10);
Lists[1].Add(0); // low
Lists[1].Add(5);
Lists[2].Add(3); // close
Lists[2].Add(8);
Lists[3].Add(2); // open
Lists[3].Add(7);
chart1.Series[0].Points.DataBindY(Lists);
}

Draw Path Of Shape C#

So I basically have have a picturebox set to a certain location (61, 361) and I have the below code in a timer, so whenever it is enabled it will increment the location of the x and y axis with a certain amount. I just need help to code it so it will trace a path preferably like a dotted line if possible. Thanks in advance for the help. It moves in a parabolic shape btw.
private void SimulationTimer_Tick(object sender, EventArgs e)
{
Ball.Location = new Point(Ball.Location.X + x, Ball.Location.Y - y);
}
Hope this helps:
private void SimulationTimer_Tick(object sender, EventArgs e)
{
System.Drawing.Point current =new System.Drawing.Point();
current = Ball.Location;
Ball.Location = new Point(Ball.Location.X + x, Ball.Location.Y - y);
PictureBox dot = new PictureBox();
dot.BackColor = Color.Red;
dot.Location = current;
dot.Height= 5;
dot.Width = 5;
this.Controls.Add(dot);
}
you can just modify the dot above to what you need
To achieve a path following the changes you have applied to your picturebox you
save each point in a List
NOTE: As you wrote "pictureBox" i assumed you are using Forms and not WPF
public partial class Form1 : Form
{
private List<Point> _points; // List of Points
private Timer _timer; // The Timer
private Graphics _g; // The Graphics object which is responsible for drawing the anything onto the Form
public Form1()
{
_points = new List<Point>();
_timer = new Timer();
_timer.Tick += Timer_Tick;
InitializeComponent();
_g = CreateGraphics();
}
private void Form1_Load(object sender, EventArgs e)
{
_timer.Start();
}
private void Timer_Tick(object sender, EventArgs e)
{
_points.Add(pictureBox1.Location); // Add the current location to the List
// Invoke the GUI Thread to avoid Exceptions
pictureBox1.Invoke(new Action(() =>
{
pictureBox1.Location = new Point(pictureBox1.Location.X + 2, pictureBox1.Location.Y + 2);
}));
Pen blackPen = new Pen(Color.Black, 3);
Invoke(new Action(() =>
{
for (int i = 1; i < _points.Count; i++) // Start at 1 so if you hav less than 2 points it doesnt draw anything
{
_g.DrawLine(blackPen, _points[i - 1], _points[i]);
}
}));
}
}
For a dotted Line you could only draw every second segment of the Line, but that's something you can figure out yourself.
Preview:

Print multiple datagridview pages

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);
}
}

timer to change background button

i have button and i want the button background change when i aim my cursor at the button
so i use mouseenter and mouseleave,it work but i want smoother effect when it change the background
sorry for bad english, and i'm green at c#
please help me
my script
private void button1_Click(object sender, EventArgs e)
{
}
private void button1_MouseEnter(object sender, EventArgs e)
{
button1.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.administrator_icon));
}
private void button1_MouseLeave(object sender, EventArgs e)
{
button1.BackgroundImage = ((System.Drawing.Image)(Properties.Resources.alhaq));
}
}
The idea is the same as in this answer. Instead of using a Timer to change the alpha of the BackColor you will want to change the Index of an ImageList of your Button.
Changing it is very similar to the other post:
private void button3_MouseEnter(object sender, EventArgs e)
{
timer1.Interval = 25;
timer1.Start();
}
private void button3_MouseLeave(object sender, EventArgs e)
{
timer1.Stop();
button3.ImageIndex = 0;
index = 1;
}
int index = 1;
private void timer1_Tick(object sender, EventArgs e)
{
index++; ;
button3.ImageIndex = index;
if (index >= imageList1.Images.Count)
{ timer1.Stop(); button3.ImageIndex = 1; index = 1; }
}
The differences are in the ImageList. Unless you want to create it frame by frame, a simple routine to create it dynamically will do the job. First you must assign the inactive and the active images to index 0 and 1 respectively. Then calling this will create step extra images from image nr 1:
public void makeTransPix(int steps)
{
using (Bitmap bmp = new Bitmap( imageList1.Images[1] ) )
{
for (int t = 0; t < steps; t++)
{
Bitmap tBmp = new Bitmap(bmp.Width, bmp.Height);
using (var g = Graphics.FromImage(tBmp))
{
Rectangle R = new Rectangle(0, 0, bmp.Width, bmp.Height);
using (var brush = new SolidBrush(button1.BackColor))
g.FillRectangle(brush, R);
var colorMatrix = new ColorMatrix();
colorMatrix.Matrix33 = t / (float)steps;
var imageAttributes = new ImageAttributes();
imageAttributes.SetColorMatrix(
colorMatrix, ColorMatrixFlag.Default, ColorAdjustType.Bitmap);
g.DrawImage(bmp, R, 0, 0, bmp.Width, bmp.Height, GraphicsUnit.Pixel, imageAttributes);
}
imageList1.Images.Add(tBmp);
}
}
}
You can adjust the steps to vary the speed. You can call it in the Form_Shown event. Make sure all image sizes fit and give your ImageList a ColorDepth of 32!

Categories