PrintDocument HasMorePages don't work - c#

I want to print the data from data grid. The code works well for the first page, but the commented lines do not works well and don't move to next page. Can anyone help fix this?
private void DrawFactorA4(object sender, PrintPageEventArgs ev)
{
for (int j = 0; j < GrdRDocument.Rows.Count; j++)
{
i += 2;
//draw data grid
s++;
if(s == 10)
{
//ev.HasMorePages = true; //this line doesn't work
s = 0;
i = 0;
}
else
{
ev.HasMorePages = false;
}
}
}
_
private void BtnPrint_Click(object sender, EventArgs e)
{
printFont = new Font("Arial", 12);
IEnumerable<PaperSize> paperSizes =
pd.PrinterSettings.PaperSizes.Cast<PaperSize>();
sizeA4 = paperSizes.First<PaperSize>(size => size.Kind == PaperKind.A4);
pd.DefaultPageSettings.Landscape = true;
pd.DefaultPageSettings.PaperSize = sizeA4;
pd.PrintPage += new PrintPageEventHandler(this.DrawFactorA4);
printPreviewDialog.Document = pd;
printPreviewDialog.ShowDialog();
}

Stop and read what you have:
printFont = new Font("Arial", 12);
Fonts are unmanaged resources; here you're instantating one and never disposing it. Maybe that's harmless in this particular situation, but this is a bad habit to get in to.
pd.PrintPage += new PrintPageEventHandler(this.DrawFactorA4);
DrawFactorA4 is going to be called for every page in your document. Inside DrawFactorA4:
for (int j = 0; j < GrdRDocument.Rows.Count; j++)
You iterate through every Row in GrdRDocument, regardless the number of rows or the size of your page. That is wrong; you have to stop after the page is filled. By the way, I hope GrdRDocument is a local copy of immutable data and you're not passing UI controls to the printing thread.
s++;
if(s == 10)
{
//ev.HasMorePages = true; //this line doesn't work
s = 0;
Your commented line would work fine. The problem is you set ev.HasMorePages = true and then ignore it; you set s = 0 and keep iterating; next iteration s!=10 so you:
ev.HasMorePages = false;
Read the PrintDocument docs; it has an example of printing more than one page. You should create a class to store all the unmanaged resources and page state. Make it IDisposable so they get disposed. Iterate through only the rows or whatever you want to print on each page. Something like:
class PrintStuff : IDisposable
{
readonly IEnumerable<Whatever> data;
readonly PrintDocument pd;
Font font;
private int currentIndex;
public PrintStuff(IEnumerable<Whatever> data)
{
this.data = data;
pd = new PrintDocument();
pd.BeginPrint += OnBeginPrint;
pd.PrintPage += OnPrintPage;
pd.EndPrint += OnEndPrint;
}
public void Print()
{
pd.Print();
}
public void Dispose()
{
pd.Dispose();
}
private void OnBeginPrint(object sender, PrintEventArgs args)
{
font = new Font(FontFamily.GenericSansSerif, 12F);
currentIndex = 0;
}
private void OnEndPrint(object sender, PrintEventArgs args)
{
font.Dispose();
}
private void OnPrintPage(object sender, PrintPageEventArgs args)
{
var x = Convert.ToSingle(args.MarginBounds.Left);
var y = Convert.ToSingle(args.MarginBounds.Top);
var lineHeight = font.GetHeight(args.Graphics);
while ((currentIndex < data.Count())
&& (y <= args.MarginBounds.Bottom))
{
args.Graphics.DrawWhatever(data.ElementAt(currentIndex), font, Brushes.Black, x, y);
y += lineHeight;
currentIndex++;
}
args.HasMorePages = currentIndex < data.Count();
}
}

Related

How to access to a specific PictureBox.EventHandler that was created programmatically?

I am making a Scorebar from 1-10 where every number is one picture of a small grey cube.
Like this:
🟩🟩🟩🟩🟩⬛⬛⬛⬛⬛ (This would be a score of 5)
Now I want to change the cube images to green ones, when the MouseDown Event is triggered, but I don't know who to tell the program.
Pic of Scorebar
private void BtnDebug_Click(object sender, EventArgs e)
{
int xPos = 200;
int yPos = 100;
PictureBox[] ScoreGameplay = new PictureBox[100];
for (int i = 0; i < 10; i++)
{
ScoreGameplay[i] = new PictureBox();
ScoreGameplay[i].Name = "ScoreGameplay" + i;
ScoreGameplay[i].Size = new Size(18, 18);
ScoreGameplay[i].Location = new Point(xPos, yPos);
ScoreGameplay[i].Image = Image.FromFile(#"img\icons\score_empty.png");
ScoreGameplay[i].MouseEnter += new EventHandler(Score_MouseEnter);
ScoreGameplay[i].MouseLeave += new EventHandler(Score_MouseLeave);
ScoreGameplay[i].MouseDown += new MouseEventHandler(Score_MouseDown);
this.Controls.Add(ScoreGameplay[i]);
xPos += 18;
}
This part works without an issue, but here we go:
private void Score_MouseDown(object sender, EventArgs e)
{
if (sender is PictureBox pBox)
{
// ???
}
}
How do I tell know which Index of the Array in BtnDebug_Click has triggered the MouseDown?
For example:
The 7th PictureBox has been clicked; now I want to change the images from PictureBoxes 1-7 to the green ones.
Anyone has a smart solution for this?
Simply you can do that
private void BtnDebug_Click(object sender, EventArgs e)
{
int xPos = 200;
int yPos = 100;
PictureBox[] ScoreGameplay = new PictureBox[100];
for (int i = 0; i < 10; i++)
{
ScoreGameplay[i] = new PictureBox();
ScoreGameplay[i].Name = $"ScoreGameplay{i}";
ScoreGameplay[i].Size = new Size(18, 18);
ScoreGameplay[i].Location = new Point(xPos, yPos);
ScoreGameplay[i].Image = Image.FromFile(#"img\icons\score_empty.png");
ScoreGameplay[i].MouseEnter += new EventHandler(Score_MouseEnter);
ScoreGameplay[i].MouseLeave += new EventHandler(Score_MouseLeave);
ScoreGameplay[i].MouseDown += new MouseEventHandler(Score_MouseDown);
this.Controls.Add(ScoreGameplay[i]);
xPos += 18;
}
}
and in Score_MouseDown
private void Score_MouseDown(object sender, EventArgs e)
{
string imageName = ((PictureBox)sender).Name; // get the name of the clicked image
string imageIndex = imageName.Substring(13); // get the text after 13 chars
}

Problem with refreshing canvas in windows form application

I'm trying to make sorting visualization algorithm in c# but I got a problem with the canvas refreshing.
I tried to refresh the canvas every time I redraw but its not looks good. I'm sure there is another way to do it and I hope someone can help me.
In this picture you can see the black rectangles that I want to delete from the canvas
This is my code :
private void GenerateArrayButton_Click(object sender, EventArgs e)
{
MyCanvas.Refresh();
Random random = new Random();
int xPosition = 0 , yPosition = MyCanvas.Height/2;
const int k_RectangleWight = 2;
for(int i = 0; i < k_SizeOfArray; i++)
{
int rectangleHeight = random.Next(MyCanvas.Height / 2);
m_UnsortedArray[i] = new Rectangle(xPosition,yPosition, k_RectangleWight, rectangleHeight);
xPosition += 5;
}
draw(m_UnsortedArray, Pens.Black);
}
private void draw(Rectangle[] i_ArrayToDraw, Pen i_PenColor)
{
var graphics = MyCanvas.CreateGraphics();
graphics.DrawRectangles(i_PenColor, i_ArrayToDraw);
graphics.Dispose();
}
private void SortingButton_Click(object sender, EventArgs e)
{
bubbleSort();
draw(m_UnsortedArray, Pens.Green);
}
private void bubbleSort()
{
for(int i = 0; i < m_UnsortedArray.Length; i++)
{
for(int j = 0; j < m_UnsortedArray.Length - 1; j++)
{
if(m_UnsortedArray[j].Height > m_UnsortedArray[j + 1].Height)
{
swap(ref m_UnsortedArray[j], ref m_UnsortedArray[j+1]);
}
}
draw(m_UnsortedArray,Pens.Black);
}
}
private void swap(ref Rectangle i_Rectangle1, ref Rectangle i_Rectangle2)
{
// Swap the position of the rectangle
var location = i_Rectangle1.Location;
i_Rectangle1.Location = i_Rectangle2.Location;
i_Rectangle2.Location = location;
// Swap the position of the current rectangle in the array
var copyRect = i_Rectangle1;
i_Rectangle1 = i_Rectangle2;
i_Rectangle2 = copyRect;
}
}
The drawing canvas in question MyCanvas whether its a PictureBox or a Panel or the Form itself, provides specific events for the painting routines, particularly the Paint event in this context. The event has a PaintEventArgs which provides a free Graphics object to do your drawings. Meaning, you don't need to create extra Graphics objects like in your draw method. Now let's draw those rectangles.
Class level fields:
using System.Threading.Tasks;
//...
public partial class Form1 : Form
{
private const int k_RectangleWight = 2;
private const int k_SizeOfArray = 100; //assign the right value.
private Rectangle[] m_UnsortedArray;
Random rand = new Random();
private Pen MyPen;
Handle the Paint event of the MyCanvas control.
public Form1()
{
InitializeComponent();
//You can add normal event handler instead if you prefer so.
MyCanvas.Paint += (s, e) =>
{
if (MyPen != null)
e.Graphics.DrawRectangles(MyPen, m_UnsortedArray);
};
}
In the GenerateArrayButton_Click event, create the rectangles, assign the drawing pen, and call the Invalidate() method of the drawing canvas.
private void GenerateArrayButton_Click(object sender, EventArgs e)
{
m_UnsortedArray = new Rectangle[k_SizeOfArray];
var xPosition = 0;
var yPosition = MyCanvas.Height / 2;
for(var i = 0; i < k_SizeOfArray; i++)
{
var rectangleHeight = rand.Next(MyCanvas.Height / 2);
m_UnsortedArray[i] = new Rectangle(
xPosition,
yPosition,
k_RectangleWight,
rectangleHeight);
xPosition += 5;
}
MyPen = Pens.Black;
MyCanvas.Invalidate();
}
At this point, you will get something drawn like this:
Now the second part. Your methods for swapping the rectangles:
private async void bubbleSort()
{
for (int i = 0; i < m_UnsortedArray.Length; i++)
{
for (int j = 0; j < m_UnsortedArray.Length - 1; j++)
if (m_UnsortedArray[j].Height > m_UnsortedArray[j + 1].Height)
swap(ref m_UnsortedArray[j], ref m_UnsortedArray[j + 1]);
await Task.Delay(30);
MyCanvas.Invalidate();
}
}
private void swap(ref Rectangle i_Rectangle1, ref Rectangle i_Rectangle2)
{
var location = i_Rectangle1.Location;
i_Rectangle1.Location = i_Rectangle2.Location;
i_Rectangle2.Location = location;
var copyRect = i_Rectangle1;
i_Rectangle1 = i_Rectangle2;
i_Rectangle2 = copyRect;
}
In the click event of the SortingButton, you just need to:
private void SortingButton_Click(object sender, EventArgs e)
{
MyPen = Pens.Green;
bubbleSort();
}
}
... and you will get:

C# Snow effect on MDI Container

I have a form set as the IsMdiContainer attribute to true.
I'm trying to make it in the container "snows".
I am trying to adapt a code that works well on a normal WinForms, but not good on MDI container. This is what I managed to do:
public partial class SnowTest : Form
{
int num;
int[] x;
int[] y;
int[] v;
int[] s;
Random random = new Random();
System.Drawing.Graphics graphics;
Rectangle rectangle;
private MdiClient MDIContainer;
private Graphics asd;
public SnowTest()
{
InitializeComponent();
}
private void SnowTest_Load(object sender, EventArgs e)
{
SelectMdiContainer();
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.UpdateStyles();
snow();
Timer1.Start();
}
private void SelectMdiContainer()
{
foreach (Control control in this.Controls)
{
// #2
MdiClient client = control as MdiClient;
if (!(client == null))
{
MDIContainer = client;
break;
}
}
}
private void snow()
{
num = 2000;
x = new int[num];
y = new int[num];
v = new int[num];
s = new int[num];
int i = 0;
for (i = 0; i <= num - 1; i++)
{
Insnow(i);
}
}
private void Insnow(int i)
{
x[i] = random.Next(0, this.Width - 1);
y[i] = random.Next(0, this.Height * 5 / 7);
v[i] = random.Next(5, 20);
s[i] = (random.Next(1, 3) * 100 + random.Next(50, 200)) / 100;
}
private void Timer1_Tick(object sender, EventArgs e)
{
for (int i = 0; i <= num - 1; i++)
{
y[i] = y[i] + v[i];
if (y[i] >= this.Height)
{
Insnow(i);
}
}
Invalidate();
SnowOnContainer();
}
private void SnowOnContainer()
{
graphics = MDIContainer.CreateGraphics();;
int i = 0;
for (i = 0; i <= num - 1; i++)
{
graphics.FillEllipse(Brushes.White, x[i], y[i], s[i], s[i]);
}
}
private void SnowTest_Resize(object sender, EventArgs e)
{
rectangle = new Rectangle(0, 0, this.Width, this.Height);
}
}
The Timer1 is set with a Interval of 100
The script runs and generates the "snow" but it does not remove it after a while and also the whole form creates lag.
What is the best way to achieve my goal?
In the normal form is pretty much structured in the same way, it lacks the function that select the container (SelectMdiContainer()), the function SnowOnContainer() and the global variable MDIContainer.
In the form, however, there is the paint event and consequently below carry as structured
private void SnowTest_Paint(object sender, PaintEventArgs e)
{
graphics = e.Graphics;
int i = 0;
for (i = 0; i <= num - 1; i++)
{
graphics.FillEllipse(Brushes.White, x[i], y[i], s[i], s[i]);
}
}
EDIT
I'm doing some tests. Currently I removed the function SnowOnContainer() and added the Paint event for the container.
private void SelectMdiContainer()
{
foreach (Control control in this.Controls)
{
// #2
MdiClient client = control as MdiClient;
if (!(client == null))
{
MDIContainer = client;
MDIContainer.Paint += OnMdiContainerPaint; //Added this Event
break;
}
}
}
The event is structured as that of the normal form but applied to the container
private void OnMdiContainerPaint(object sender, PaintEventArgs e)
{
graphics = MDIContainer.CreateGraphics();
int i = 0;
for (i = 0; i <= num - 1; i++)
{
graphics.FillEllipse(Brushes.White, x[i], y[i], s[i], s[i]);
}
}
Finally I did some research and I've added this code to avoid Flickering animation. as shown in this topic How to fix the flickering in User controls
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
In doing so it appears to be working properly with a fluid animation, but as soon as I resize the form, or I reduce to an icon returns lag

Design an algorithm for guessing a card number

I am making a card guessing game.there are 100 cards place in 10rows and 10 columns each card with a number and user have to find a number he is thinking of. i want to devise an algorithm to determine whether a given number is written on one of the cards by turning up less than 20 cards.I hav created the buttons dynamically, now im having hard time making a logic to search through them.This is my code.
namespace WindowsFormsApplication3
{
public partial class Form1 : Form
{
int rememberlast = 0, move = 0;
object savelastobject;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
int sizee = 50, where = 0;
this.Height = 0;
this.Width = 0;
var generatedNum = new List<int>();
var random = new Random();
while (generatedNum.Count < 100)
{
var tempo = random.Next(0, 100);
if (generatedNum.Contains(tempo)) continue;
generatedNum.Add(tempo);
}
char[] text2 = text.ToCharArray();
for (int x = 0; x < 10; x++)
{
for (int y = 0; y < 10; y++)
{
Button ta = new Button();
ta.Name = x.ToString()+y.ToString();
ta.Width = sizee;
ta.Height = sizee;
ta.Tag = generatedNum[where];
where++;
ta.BackColor = Color.Red;
ta.Location = new Point(70 * y, 70 * x);
Controls.Add(ta);
ta.Click += new System.EventHandler(this.button_Click);
this.Width += 90 * x / 13;
this.Height += 90 * x / 8;
}
}
}
private void button_Click(object sender, EventArgs e)
{
Control me = (Control)sender;
rememberlast = int.Parse(me.Tag.ToString());
savelastobject = me;
me.Text = me.Tag.ToString();
me.Enabled = true;
me.BackColor = Color.Gray;
move++;
label2.Text = move.ToString();
me.Enabled = true;
me.Text = me.Tag.ToString();
me.BackColor = Color.Gray;
me.Refresh();
Thread.Sleep(1000);
if (move == 20)
{
MessageBox.Show("Total Moves Consumed");
Application.Restart();
}
if (me.Tag.ToString() == textBox1.Text)
{
//Control him = (Control)savelastobject;
//him.BackColor = Color.LightBlue;
me.BackColor = Color.LightBlue;
MessageBox.Show("You Win");
Application.Restart();
}
}
}
}

Trying to create a new page when horizontal pos. of i extends past right margin

I am trying add a page when horizontal or the x position is greater than a counter in order to keep a right side margin. When I run the code I end up in an infinate loop of hundreds of pages all displaying the same first page graphics. Thinking it might have to do with my lack of understanding HasMorePages. I could use some help. Thanks.
public static class PrintWave
{
public static void PrintPreWave()
{
PrintDocument pd = new PrintDocument();
if (WaveTools.MySettings == null)
{
pd.DefaultPageSettings.Landscape = true;
}
else
{
pd.DefaultPageSettings = WaveTools.MySettings;
}
pd.OriginAtMargins = true;
pd.PrintPage += new PrintPageEventHandler(OnPrintPage);
PrintDialog dlg = new PrintDialog();
PrintPreviewDialog printPreviewDlg = new PrintPreviewDialog();
printPreviewDlg.Document = pd;
Form p = (Form)printPreviewDlg;
p.WindowState = FormWindowState.Maximized;
printPreviewDlg.ShowDialog();
}
private static void OnPrintPage(object sender, PrintPageEventArgs e)
{
string MyTag = string.Empty;
MyTag = WaveActions.ActiveId;
Wave MyWave = WaveHolder.FindWave(MyTag);
int MyCount = 0;
int xOffset = e.MarginBounds.Location.X;
int yOffset = e.MarginBounds.Location.Y;
if (MyWave != null)
{
Graphics g = e.Graphics;
g.SetClip(e.PageBounds);
Pen MyPen = new Pen(WaveTools.WaveColor, WaveTools.PenWidth);
float dx = (float)e.PageBounds.Width / MyWave.NumSamples;
float dy = (float)e.PageBounds.Height / 255;
if (MyWave.Normal == false)
{
g.ScaleTransform(dx, dy);
}
for (int i = 0; i < MyWave.NumSamples - 1; i++)
{
g.DrawLine(MyPen, i, MyWave.Data[i], i + 1, MyWave.Data[i + 1]);
MyCount = MyCount + 1;
if (MyCount > e.MarginBounds.Width)
{
e.HasMorePages = true;
MyCount = 0;
return;
}
else
{
e.HasMorePages = false;
return;
}
}
}
}
}
}
for (int i = 0; i < MyWave.NumSamples - 1; i++)
That's the core problem statement, you start at 0 every time PrintPage gets called. You need to resume where you left off on the previous page. Make the i variable a field of your class instead of a local variable. Implement the BeginPrint event to set it to zero.
The else clause inside the loop need to be deleted.

Categories