I want to draw free hand in a form (picture box) on visual studio and copy the same figure (that I draw) on another panel/picture box.
Also they should not be dots forming one line but a continuous line. Please help.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
Pen p_white;
bool draw = true;
private Graphics objgraphics;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
Pen p_black = new Pen(new SolidBrush(Color.Black));
if (draw)
{
objgraphics = panel1.CreateGraphics();
}
}
/*private void panel1_MouseDown(object sender, MouseEventArgs e)
{
bool draw = true;
}*/
private void panel1_MouseMove_1(object sender, MouseEventArgs e)
{
Rectangle rEllipse = new Rectangle();
switch (e.Button)
{
case MouseButtons.Left:
rEllipse.X = e.X;
rEllipse.Y = e.Y;
rEllipse.Width = 5;
rEllipse.Height = 5;
objgraphics.DrawEllipse(System.Drawing.Pens.Black, rEllipse);
break;
case MouseButtons.Right:
rEllipse.X = e.X;
rEllipse.Y = e.Y;
rEllipse.Width = 3;
rEllipse.Height = 3;
objgraphics.DrawEllipse(System.Drawing.Pens.Black, rEllipse);
break;
default:
return;
}
}
/*private void panel1_MouseUp(object sender, MouseEventArgs e)
{
bool draw = false;
} */
private void form_Paint(object sender, EventArgs e)
{
}
private void panel2_Paint(object sender, PaintEventArgs e)
{
Pen p_black = new Pen(new SolidBrush(Color.Black));
if (draw)
{ objgraphics = panel1.CreateGraphics();
}
}
private void button2_Click(object sender, EventArgs e)
{
this.Close();
}
}
}
Looking at your code I'm afraid I have to say: This is all wrong.
Sorry to be so blunt, but you must never use control.CreateGraphics!!
The first thing to do is to throw away the Graphics objgraphics object.
It is (almost) always wrong to store a Graphics object!
Instead, you have to use the one you get from the e.Graphics parameter in the Paint events of your controls.
Note that Graphics doesn't contain any graphics, it is a tool used to draw onto an associated Bitmap or a control's surface.
The next thing is to do is to understand about drawing freehand lines. Often one can see the code you have; but it is useless and only an example of how many stupid things you find in introductions. Forget it. It will always look like crap as the circles simply never look smooth and as soon as you move the mouse faster the pseudo-lines completely fall apart.
There is a nice method DrawCurve that will draw smooth lines. You feed it a Pen and an array of Points.
This is what we will use.
Let's return to the basics: How to create a graphic? Now we know that you need to call DrawCurve in the Paint event:
e.Graphics.DrawCurve(somePen, somePointsArray);
This brings up the next questions:
what's somePen
what's somePointsArray
There is a hidden third question:
what about drawing more lines?
The first one is simple; you create a Pen with a stroke width of 5.5 pixels as
Pen somePen = new Pen(Color.Blue, 5.5f);
If you want to you can give it a linestyle (dashes), too.
Now for the array: In its simplest form this is easy as well.
In the MouseMove event you store the current Location in a list of points. First, we declare it at class level:
List<Point> currentLine = new List<Point>();
Then we start filling it as long as the left button is pressed:
private void panel1_MouseMove_1(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
currentLine.Add(e.Location);
panel1.Invalidate();
}
}
Note the last line: Calling Invalidate on a control triggers the system to invoke the Paint event. It may look complicated but this is the only correct way as it guarantees that the very same drawing will also happen when some other reason makes it necessary.
We need to draw because we have changes the data that should be drawn. But there are many outside reasons, most notoriously the Minimize/maximize sequence that will clear the drawing and trigger the Paint event, too. So we need to cooperate with the way windows draws its controls! Only this way the graphics will persist.
Also, note we don't use an array as we don't know how many Points we will need. Instead, we use a List and later cast it to Array.
Let's code the Paint event for our simple case:
private void panel1_Paint(object sender, PaintEventArgs e)
{
using (Pen somePen = new Pen(Color.Blue, 5.5f) )
if (currentLine.Count > 1) e.Graphics.DrawCurve(yourPen , currentLine.ToArray());
}
Note that I have created the Pen in a using clause. This is a cheap and safe way to ensure that the Pen is disposed of properly.
Also note how we cast the List to an Array!
The above code alone would work and allow you free-hand drawing a line.
But what about the next line? It should not connect to the first one so we can't just add more points!
So we need not just one list of points but more than that, in fact, a list of lists of points is called for:
List<List<Point>> curves = new List<List<Point>>();
And we add the current curve to it whenever the mouse is released:
private void panel1_MouseUp(object sender, MouseEventArgs e)
{
if (currentLine.Count > 1) curves.Add(currentLine.ToList()); // copy!!
currentLine.Clear();
panel1.Invalidate();
}
Note how I use a cast from List to List to enforce a copy or else only the reference would be assigned and then, in the next line cleared..
Again we trigger the Paint event as the final thing to do.
We now should change the Paint event to display all the lines, both the one currently being drawn and all the earlier ones..:
private void panel1_Paint(object sender, PaintEventArgs e)
{
using (Pen somePen = new Pen(Color.Blue, 5.5f) )
{
if (currentLine.Count > 1) e.Graphics.DrawCurve(somePen, currentLine.ToArray());
foreach (List<Point> lp in curves)
if (lp.Count > 1) e.Graphics.DrawCurve(somePen, lp.ToArray());
}
}
Now we are basically done with the free-hand drawing part.
So we return to your original question: How can you copy the drawing to a second Panel?
Well, you have stored everything in the curves data structure.
So you have two options: Either simply use the same data in the panel2_Paint event or if you need to copy and change the data to, maybe adapt to a different size.
Here are the things still missing:
Saving the data, saving the drawing (Serialize, DrawToBitMap)
Drawing with varying pens & colors (create a drawAction class to store all you need)
Using other tools like Line or Rectangle (create a drawAction class)
Clearing the drawing (see below)
Undo and Redo (look into Stacks an Queues)
Caching when there are a great number of lines ( draw the first portion into a BackgroundImage Bitmap)
Here is a clearing code:
curves.Clear(); currentLine .Clear(); panel1.Invalidate();
I noted that your original code lets you draw with two different stroke widths using the left and right button. This alone shows that this code is not very good. Who would a) think of that and b) be satisfied with only two stroke widths.
Please read this post where I explain a little about creating a class that can store a pen width, a color etc so that you can change then between lines you draw.
Related
My goal is very simple. Imagine opening MSPaint, clicking the line tool, holding mouse down, and dragging it around. It anchors the starting coordinates where you clicked mouse down and constantly draws and redraws a line to your current position.
Except me trying to do this in C# isn't working as well as I would hope.
[DllImport("user32.dll")]
static extern IntPtr GetDC(IntPtr hWnd);
[DllImport("User32.dll")]
static extern int ReleaseDC(IntPtr hwnd, IntPtr dc);
protected override void OnPaint(PaintEventArgs e)
{
endingPoint = GetMouseCoords();
DrawLine(startingPoint, endingPoint);
}
private void DrawLine(Point startingCoords, Point endingCoords)
{
IntPtr desktop = GetDC(IntPtr.Zero);
Pen pen = new Pen(Brushes.Red, 3);
using (Graphics g = Graphics.FromHdc(desktop))
{
g.DrawLine(pen, startingCoords.X, startingCoords.Y, endingCoords.X, endingCoords.Y);
g.Dispose();
}
ReleaseDC(IntPtr.Zero, desktop);
}
Using it this way, I only get the line drawn once. However, if I move the DrawLine() to a more static event like MouseUp, it will draw it, then disappear after about a quarter of a second.
What would be the best way to accomplish my goal here?
I would think that whatever event is being used to make the line disappear is what I would want to attach the drawing of the line to in the first place.
You need to have two drawing calls:
One for the non-persistent line that follows the cursor in the MouseMove using someControls.CreateGraphics
the other for the persisting line, triggered in the MouseUp, where
you store the coordinates and
call Invalidate on your canvas control and
draw in the Paint event of your canvas using its e.Graphics object.
Here is a minimal example code:
List<Point> allPoints = new List<Point>();
Point mDown = Point.Empty;
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
mDown = e.Location;
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
allPoints.Add(e.Location);
pictureBox1.Invalidate();
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button.HasFlag(MouseButtons.Left))
{
pictureBox1.Refresh();
using (Graphics G = pictureBox1.CreateGraphics())
G.DrawLine(Pens.Red, mDown, e.Location);
}
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (allPoints.Count > 1) e.Graphics.DrawLines(Pens.Black, allPoints.ToArray());
}
Note that this uses a PictureBox as the canvas control. It is the control meant for this kind of interaction. Your code seems to draw onto the desktop, which doesn't belong to you. Drawing onto it in a persistent manner is not anything like what you would do with the/any Paint application.
Also note that my example stores a list of points and draws them as one non-closed Polyline. To draw them closed exchange DrawLines for DrawPolygon! To draw several such polylines or polygons you need to..
..decide on the user interface for it, maybe add segment points only while the control-key is pressed and otherwise finish the current polyline
store the points in a List<List, Point>>
Also note that this is one of the rare examples where using control.CreateGraphics is called for, as you actually do want a non-persistent drawing while the user moves the mouse.
In most other cases the Winforms graphics basic rule #1 applies:
Never use control.CreateGraphics! Never try to cache a Graphics object! Either draw into a Bitmap bmp using a Graphics g = Graphics.FromImage(bmp) or in the Paint event of a control, using the e.Graphics parameter..
I got a question about the Graphics object. I want to draw an consecutive line like MS paint. I don't know how to implement such thing. I do know how to start a line from the mouse location. This I do on a picturebox and add the new Point(e.X, e.Y). The otherline could not be the same ofcourse else there would be no line visible. I could not make the other Point(10, 10) or something like that. Because then it would create a line always from the same point.
Does anyone know how to draw consecutive lines(with curves)
Does it has something to do with the mouse_down and mouse_up event? I am really stuck with this problem for a long time. If anyone of you have the time to explain me method that would work, that would be great!
Thanks in advance!
I just implemented simple paint for you. Just create a new project and copy this code below to Form1.cs file. Comments in code should explain how it works.
public partial class Form1 : Form
{
private Bitmap bmp; // Place to store our drawings
private List<Point> points; // Points of currently drawing line
private Pen pen; // Pen we will use to draw
public Form1()
{
InitializeComponent();
DoubleBuffered = true; // To avoid flickering effect
bmp = new Bitmap(640, 480); // This is our canvas that will store drawn lines
using (Graphics g = Graphics.FromImage(bmp))
g.Clear(Color.White); // Let's make it white, like paper
points = new List<Point>(); // Here we will remember the whole path
pen = new Pen(Color.Black);
MouseDown += OnMouseDown; // Start drawing
MouseMove += OnMouseMove; // Drawing...
MouseUp += OnMouseUp; // Stop drawing
Paint += OnPaint; // Show the drawing
}
void OnPaint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(bmp, 0, 0); // Show what is drawn
if (points.Count > 0)
e.Graphics.DrawLines(pen, points.ToArray()); // Show what is currently being drawn
}
void OnMouseDown(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
return;
points.Clear();
points.Add(e.Location); // Remember the first point
}
void OnMouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
return;
points.Add(e.Location); // Add points to path
Invalidate(); // Force to repaint
}
void OnMouseUp(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left)
return;
SaveToBitmap(); // Save the drawn line to bitmap
points.Clear(); // Our drawing is saved, we can clear the list of points
}
private void SaveToBitmap()
{
if (points.Count == 0)
return;
using (Graphics g = Graphics.FromImage(bmp))
g.DrawLines(pen, points.ToArray()); // Just draw current line on bitmap
}
}
Result:
Broadly:
On MouseDown, capture the mouse and store the current location. This location is your initial Point value.
On MouseMove, add the current location to your list of points.
On MouseUp, complete your curve as appropriate, stop capturing the mouse.
When rendering, convert your list of Point values to an array and pass it to the Graphics.DrawLines() method. As a possible optimization, once the user is done drawing, permanently convert the list to an array. Alternatively, use a Bitmap instance as your rendering cache.
Note that you can configure the Pen object used to draw the lines to apply special effects, like end caps and mitered joints.
To draw curves, use the Graphics.DrawBeziers() method instead. Note that in this case, the points captured during the mouse events should be every third point in the array passed to the method. The two points between each of those points are the control points for each curve.
You should probably start with DrawLines(), as it's much simpler. Once you have that working nicely, then you can complicate your life with the DrawBeziers() method. At a minimum, you will have to automatically compute default control points for use with the method. Preferably, you will give the user a way to edit the control points, so that they can customize the curve.
I am creating an application for an industrial touch screen computer with no hardware to brag about. The operator of this touch screen computer is among other things supposed to be able to unlock and drag buttons around on a form with a background image.
However, as many of you already might know, moving controls on a parent control with a background image isn't pretty. The dragging is slow and instead of experiencing a smooth dragging, the operator will see a button jumping after through hoops in the wake of the mouse pointer as you move the pointer across the screen.
This is the current code for moving the button:
private void btnButton_MouseMove(object sender, MouseEventArgs e)
{
// Do not proceed unless it is allowed to move buttons
if (!this._AllowButtonsToBeMoved)
return;
if (this._IsBeingDragged)
{
var btn = (sender as Button);
var newPoint = btn.PointToScreen(new Point(e.X, e.Y));
newPoint.Offset(this._ButtonOffset);
btn.Location = newPoint;
}
}
I am not looking to solve this exact problem, I'd rather eliminate it to save some time. What I wish to implement in order to eliminate this, is a more resource efficient way to move the box around. I'm thinking that moving a dotted rectangle instead of the button, then dropping it where I want the button must be way more efficient than dragging the button around the screen, causing who knows how many repaint operations.
Does anyone have any better suggestions? If not, then I would very much appreciate pointers on how to proceed with creating and moving this rectangle around the screen, as I am having some difficulty finding good sources of information regarding how to approach this on good ol' Google.
Update, 26/11/13
I'm attempting Luaan's suggestion regarding overriding the form's OnPaint, however I am unsure as to how exactly I can add the rendering of the button in this code. Any ideas?
protected override void OnPaint(PaintEventArgs e)
{
if (_IsBeingDragged)
{
e.Graphics.DrawImage(this._FormPaintBuffer, new Point(0, 0));
}
else
{
base.OnPaint(e);
}
}
This is a standard case of Winforms being too programmer-friendly. Details that any game programmer pays careful attention to but are way too easy to miss. It allows you to set a BackgroundImage and it will take anything you throw at it. That usually works just fine, except when you need the image to render quickly. Like you do in this case.
Two things you need to do to make it draw ~fifty times quicker:
Resize the bitmap yourself to fit the form's ClientSize so it doesn't have to be done repeatedly every time the image needs to be painted. The default Graphics.InterpolationMode property value produces very nice looking images but it is not cheap. Do note that this can take a significant memory hit, the reason it isn't done automatically.
Pay attention to the pixel format of the image. There's only one that draws fast, the one that can be blitted directly to the video adapter without having the value of every single pixel converted to the frame buffer format. That is PixelFormat.Format32bppPArgb on all video adapters in use in the past 10+ years. Big difference, it is ten times faster than all the other ones. You never get that format out of a bitmap that you created with a painting program so explicitly converting it is required. Again heed the memory hit.
It takes but a little scrap of code to get both:
private static Bitmap Resample(Image img, Size size) {
var bmp = new Bitmap(size.Width, size.Height,
System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
using (var gr = Graphics.FromImage(bmp)) {
gr.DrawImage(img, new Rectangle(Point.Empty, size));
}
return bmp;
}
In the somewhat unlikely case you still have painting trails or stuttering you need to consider a very different approach. Like the one that's used in the Winforms designer. You use layers, an extra borderless transparent window on top of the original. You get one with the TransparencyKey property.
1) If you want to keep everything the way it is, you might want to implement some sort of double buffering for the background. When the form is redrawn, it always has to redraw pretty much the whole thing, while actually doing some logic (ie. JPG/PNG is slower to draw than a BMP). If you store the Paint canvas in a Bitmap, you can draw the whole form except for that one button in a Bitmap and draw only that as background while you're dragging the button - this way you get around all the draw logic of the form and its controls, which should be vastly faster.
2) You can only draw an outline of the button being moved. The trick is that you draw lines in XOR mode - the first time you draw the rectangle it adds the outline, and the next time you draw it in the same location, it disappears. This way you don't have to redraw the form all the time, just the few pixels that form the rectangle. The support for XOR lines in C# isn't the best, but you can use the ControlPaint.DrawReversibleLine method.
To drag a control you need to use the .DrawToBitmap function of the control and set the appropriate styles of the form. I haven't done it in the sample code but you'll need a "design mode" and a "normal mode". To drag the control you simply click it, drag it and click again. You can get fancy and make the Bitmap holding the control transparent so as to accommodate rounded edges.
For this example, make a standard C# Windows Forms application (Form1) and drop a button (button1) onto the form then place this code after the Form1 constructor in your Form class. Make sure to change the location of the background bitmap in code.
private Bitmap b = null;
private bool IsDragging = false;
private Point down = Point.Empty;
private Point offset = Point.Empty;
private void button1_MouseUp(object sender, MouseEventArgs e)
{
IsDragging = true;
button1.Visible = false;
down = button1.PointToScreen(e.Location);
offset = e.Location;
this.Invalidate();
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
if (IsDragging)
{
IsDragging = false;
down = new Point(down.X - offset.X, down.Y - offset.Y);
button1.Location = down;
button1.Visible = true;
down = Point.Empty;
this.Invalidate();
}
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (IsDragging)
{
down.X += (e.X - down.X);
down.Y += (e.Y - down.Y);
this.Invalidate();
}
}
private void Form1_Load(object sender, EventArgs e)
{
try
{
b = new Bitmap(button1.Width, button1.Height, System.Drawing.Imaging.PixelFormat.Format32bppPArgb);
button1.DrawToBitmap(b, new Rectangle(0, 0, button1.Width, button1.Height));
button1.MouseUp += new MouseEventHandler(button1_MouseUp);
this.MouseUp += new MouseEventHandler(Form1_MouseUp);
this.MouseMove += new MouseEventHandler(Form1_MouseMove);
this.DoubleBuffered = true;
this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint, true);
this.UpdateStyles();
this.BackgroundImage = Image.FromFile(#"C:\Users\Public\Pictures\Sample Pictures\desert.jpg");
this.BackgroundImageLayout = ImageLayout.Stretch;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
protected override void OnPaint(PaintEventArgs e)
{
if (IsDragging)
{
e.Graphics.DrawImage(b, new Point(down.X - offset.X, down.Y - offset.Y));
}
base.OnPaint(e);
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (b != null)
{
b.Dispose();
}
}
How can I clear the fill of a rectangle? I only want to keep the border.
g.FillRectangle(Brushes.Transparent, x, y, w, h);
Didn't work, neither did aRGB with alpha, I want to delete the fill so there's only the border left.
So what you want is
g.DrawRectangle(Pens.Black,x,y,w,h);
I think
EDIT: due to a change in the OP requirements this is not exactly the answer he wants, though it is not incorrect, therefore I choose to leave it here, for now.
you must set new clip for your graphics after set clip clear it, then restore clip to normal.
g.SetClip(new Rectangle(x,y,w,h), CombineMode.Replace);
g.Clear(Color.Transparent);
Ok so you are after a selection tool, you might have wanted to tell us that in the first place.
Create a new windows form application.
in the form events use mousedown, mouseup and mousemove
public Point MouseXY = new Point(0, 0);
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
MouseXY = e.Location;
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
int width = e.Location.X - MouseXY.X;
int height = e.Location.Y-MouseXY.Y;
this.Refresh();
CreateGraphics().DrawRectangle(Pens.Blue, new Rectangle(MouseXY, new Size(width,height)));
}
}
private void Form1_MouseUp(object sender, MouseEventArgs e)
{
this.Refresh();
}
This code is not perfect and I don't pretend it is. What this will do is draw a blue rectangle that starts where you click and follows your mouse. It does not draw a negative rectangle, you would have to determine whether your mouse is currently to the left or up from your starting point then draw the rectangle accordingly, but I think you can figure that out on your own. as well the rectangle is not persistent, though I do not believe you would want it to be.
i am trying to create slider movement graphics. The code explain better this.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
namespace temp
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private System.Drawing.Graphics g;
private System.Drawing.Pen pen1 = new System.Drawing.Pen(Color.Black, 1F);
//timer for animation
private void Form1_Load(object sender, EventArgs e)
{
Timer a = new Timer();
a.Interval = 60;
a.Tick += new EventHandler(a_Tick);
a.Start();
}
void a_Tick(object sender, EventArgs e)
{
button1_Click(this, null);
}
//draws randomly generated point array.
int cnt = 0;
private void button1_Click(object sender, EventArgs e)
{
rAr();
g = pictureBox1.CreateGraphics();
g.Clear(Color.Violet);
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.SmoothingMode = SmoothingMode.HighQuality;
g.DrawCurve(pen1, points2);
cnt++;
}
Random r = new Random();
Point[] p = new Point[100];
Point[] points2 = new Point[100];
int c = 4;
//fills new random point array
private void rAr()
{
points2.CopyTo(p, 0);
int cc = 1;
for (int i = points2.Length - 1; i > 0; i--)
{
points2[i - 1] = new Point(100 - cc, p[i].Y);
cc++;
}
points2[99] = new Point(100, r.Next(1, 50));
}
}
}
this code works fine put the problem is that it takes to much cpu and automation is not very smooth. is there some other way to achieve same thing put with much less cpu.
Thanks for replays
(Premature) Optimisations
In addition to the other answers already posted, you are attempting to draw a spline curve through 100 points.
This is probably a bad idea for two reasons: Firstly, fitting a curve to 100 points isn't a particularly fast thing to do under any circumstances. Secondly, as the points are all 1 pixel apart, there is likely to be no visual benefit to using a curve.
Therefore, there are several more things you could do that might improve the speed:
Try using a PolyLine rather than a curve,
If you need to use a curve, then you probably don't need to use 100 points across 100 pixels of the drawing - using 10 or 20 positions across that width may give you better results, and will significantly reduce the curve-fitting work that .net has to do.
You may be able to also remove some intermediate points that lie on the line/curve, so that you have fewer lines to draw. e.g. if you have points at (10,57) (11, 57) (12,57) then you can drop the middle point and just draw a straight line from (10,57) to (12,57).
Wrong Algorithm?
But wait! Before you optimise the code - is it actually the right solution to the problem?
It sounds as if the content is not meant to "change", but simply scroll sideways. In which case, only the new pixels introduced at one side of the image are actually "changing" with each frame - the rest simply move sideways. In that case, you can scroll (move) the region of the graphic that contains the "old" curve image, and then just draw the extra pixel or two that have "scrolled into view". Scrolling in this manner can be achieved in several different ways (e.g. blitting in the graphics, or if the content of the entire window is scrolling, by using windows Form scrolling commands)
In the same way, if the data is not changing, but simply "scrolling", then rebuilding the array for every frame is unnecessarily expensive. Using a circular buffer you could simply add a new point to the "end" of the array and delete one from the beginning without having to reallocate the array or copy all the intervening points at all.
So it's all about using the right tool for the job.
You should move all of the graphics code in Button1 to your picturebox's paint event, something like this:
private void button1_Click(object sender, EventArgs e)
{
rAr();
pictureBox1.Invalidate();
cnt++;
}
private void picturebox1_Paint(object sender PaintEventArgs e)
{
using graphics g = e.Graphics()
{
g.Clear(Color.Violet);
g.PixelOffsetMode = PixelOffsetMode.HighQuality;
g.SmoothingMode = SmoothingMode.HighQuality;
g.DrawCurve(pen1, points2);
}
}