I need to allow to the user to draw lines over an bitmap. Lines should be drawn interactively, I mean something performed using typical code giving to the user a visual feedback about what is drawn:
private void MainPictureBox_MouseDown( object sender, MouseEventArgs e)
{
DrawingInProgress = true ;
Origin = new Point (e.X, e.Y);
}
private void MainPictureBox_MouseUp(object sender, MouseEventArgs e)
{
DrawingInProgress = false ;
}
private void MainPictureBox_MouseMove(object sender, MouseEventArgs e)
{
if (!DrawingInProgress) return ;
End = new Point (e.X, e.Y);
using( Pen OverlayPen = new Pen( Color .Red, 1.0f))
using (Graphics g = MainPictureBox.CreateGraphics())
{
g.DrawLine(OverlayPen, Origin, End);
}
}
Of course I keep track of the points using List.Add within MainPictureBox_MouseUp in order to draw lines in the Paint event (code not shown for the sake of simplicity)
Without the background image things could be done nicely simply overwriting the previous line with the background color, something like:
g.DrawLine(BackgroundColorPen, Origin, PreviousEnd);
g.DrawLine(OverlayPen, Origin, End);
but this is not possible with a not uniform background.
Invalidating the rectangle defined by the points: Origin, PreviousEnd then using Update() makes the rendering quite messy. I am wondering how to perform this task and those are possible ways to do so i am considering:
Draw the lines over a transparent bitmap then draw the bitmap over the Picturebox. I guess that with big images this is simply unfeasible for performances reason.
Using the Picture.BackgroundImage for the bitmap then drawing on the Picture.Image but I unable to figure out how this could really saave the day
Using double buffering? How?
Stacking a different control (a panel?) over the pictureBox, making it transparent (is it possible?) then drawing over it.
Could someone give a hint in the best direction? I am really getting lost.
The solutions working for me has been the following:
Create a transparent panel;
Put it over the bitmap having them overlap completely;
Draw on the panel using proper mouse events;
There is no need to cancel the previous shape, of course: it was a misleading question. It is sufficient to distinguish permanent shapes recorded in proper lists fed to the Paint event from the transient shape previously drawn that will be not drawn again in the next Paint event;
Make absolutely sure that all drawings are performed in the Paint event using the Graphics provided by the PaintEventArgs. Thanks to #HansPassant to have stressed this in a different post.
Related
C# Beginner here.
I'm making a 2D Tanks game, and everything's working out nicely so far.
Both of my tanks are Picture Boxes, and so is my Missile.
The image of the missile and tanks in the PictureBoxes have transparent BackColour properties. The problem is, the background of the missile & tanks are not transparent while on top of the other picturebox (pbBackground). It looks like this.
I'm aware that using different PB's is an inefficient way of going about it, but I've come pretty far and I don't really know any better. Anyways, as you can see, the Missile and Tank PB backgrounds show the form colour. When I downloaded the images, the backgrounds were transparent, I'm sure of it. How do I go about making the background of my PB's truly transparent? (Matching the background of the Overlapped PB?)
I saw this but it doesn't really match my scenario and I don't understand the solution.
UPDATE: Okay, I followed Tommy's advice, is this the right way to go about moving it along pbBackground in a timer constantly changing MissileX and MissileY? Currently this does nothing.
using (Graphics drawmissile = Graphics.FromImage(pbBackground.Image))
{
drawmissile.DrawImage(pbMissile.Image, new Point(MissileX,Convert.ToInt32(MissileY)));
}
PictureBox is opaque. And PictureBox is not efficient.
For making games, you should study Paint event which directly draws on your form.
Bitmap backgroundBitmap = new Bitmap("background");
Bitmap tankBitmap = new Bitmap("tank");
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(backgroundBitmap, 0, 0);
e.Graphics.DrawImage(tankBitmap, 30, 30);
}
private void timer1_Tick(object sender, EventArgs e)
{
this.Invalidate(); //trigger Form1_Paint to draw next frame
}
Don't layer multiple PictureBox instances on top of each other. It will get very confusing, very quickly.
Instead, use one single PictureBox and use Paint to draw your images to it. In this way, you can have much more control over the graphics operations happening.
Have a look at this
private void DrawIt()
{
System.Drawing.Graphics graphics = this.CreateGraphics();
System.Drawing.Rectangle rectangle = new System.Drawing.Rectangle(
50, 50, 150, 150);
graphics.DrawEllipse(System.Drawing.Pens.Black, rectangle);
graphics.DrawRectangle(System.Drawing.Pens.Red, rectangle);
}
In this example they demonstrate how to draw shapes directly onto a Form. You would use your PictureBox there instead. You can also draw images.
There's lots of ways to draw shapes onto a form using a System.Drawing.Graphics object. Try taking a look at this question for a comparison.
Tommy's answer is right, however, if you're dead set on using pictureboxes (a bad idea), set the overlapping picturebox backgroundcolour to Transparent and the Form's Background to whatever image. TIL the Transparent BackColour just uses the form colour / image. Tommy actually has the right answer here, but this is what I did to fix my problem (the lazy way).
In my setup i have 5 pictureboxes which I am trying to layer.
My code for this is:
pbCoin1.Parent = pbMap;
pbCoin1.BackColor = Color.Transparent;
pbCoin2.Parent = pbMap;
pbCoin2.BackColor = Color.Transparent;
pbCoin3.Parent = pbMap;
pbCoin3.BackColor = Color.Transparent;
pbFlashlight.Parent = pbMap;
pbFlashlight.BackColor = Color.Transparent;`
All 5 pictureboxes contain images. The method I am using works fine, but the problem is that the PbCoin 1,2,3 are glitching trough my pbFLashlight(see image).
Can someone provide a solution such that the coins are only visible when the transparent part of the black layer is over it?
Don't use multiple picture boxes. Use a single PictureBox to represent the game area and handle the .Paint event. Do all of your drawing using GDI calls on the Graphics reference that .Paint sends you in e. You will want to call PictureBox.Invalidate at the end of the Paint event to cause it to queue up the next frame
For better performance, create a new Bitmap instance that you keep a reference to for the life of the game. Clear and do all your drawing to that bitmap every frame, then draw that bitmap to the PictureBox in .Paint.
For even better performance than that, don't use WinForms.
The basic pattern should look like this:
private void Canvas_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(...);
Canvas.Invalidate();
}
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
How can a drawing canvas be realised in managed C++ (C++/CLI). I want to be able to "draw" on a PictureBox object by leftclicking the mouse and simulatneous moving the mouse. The PictureBox is cleared with white color and i want to draw with Black pixels.
First of all, consider this an exception. While it's perfectly fine to ask for help with assignments and homework related stuff here (just make sure to mention it), you should really try to show some code or work you've done already. Don't just go like "I need some code, thanks." because that's not how this site is meant to work.
Please don't just copy and paste this code. Understand it first, then use it or apply what you've learned to your own code.
Back to the actual problem: There are multiple ways to approach this, but the basic concept is always the same (even if you try to create some vector drawing program). The following lines ommit classes and namespaces for readability. If you keep the standard using directives, this shouldn't be an issue for you (most stull will be in System.Drawing). Note that I'll implement everything directly into the form. You could as well create a custom user control for this (which might be the better/cleaner approach).
First you'll need some control to actually display your drawing. Using the standard control PictureBox is perfectly fine for this.
Next you'll need some image to actually draw to. Add a private Bitmap member to your user form.
In this example we'll use the mouse cursor to draw a line. A line is always drawn between the previous position of the cursor and the current position. Due to this we'll have to store the previous position in a Point.
Given the previous two points, you'll need the following two members somewhere in your form (or user control):
private Bitmap bitmap;
private Point oldPosition;
In your form's Load event you'll have to create the Bitmap object. I'm also using Graphics to clear the Bitmap to white and then display it using the PictureBox:
private void Form1_Load(object sender, EventArgs e)
{
bitmap = new Bitmap(pictureBox1.Width, pictureBox1.Height);
using (Graphics g = Graphics.FromImage(bitmap))
g.Clear(Color.White);
pictureBox1.Image = bitmap;
}
Next up, we'll have to reset the previous cursor position, whenever the user clicks somewhere in the PictureBox. For this I add a very simple MouseDown event to it:
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
oldPosition = e.Location;
}
Last but not least, the actual drawing happens in an MouseMove event. For this to work properly, you'll have to do one check and three working steps:
Determine whether the user actually wants to draw (is the left mouse button pressed?).
Draw the line into the bitmap.
Update the previous cursor position to the new position.
Display the results.
The code for this could look like this:
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
using (Graphics g = Graphics.FromImage(bitmap))
g.DrawLine(Pens.Black, oldPosition, e.Location);
oldPosition = e.Location;
pictureBox1.Image = bitmap;
}
}
If everything works as expected, you should be able to draw freehandedly on your PictureBox:
I think I'm not clear, but I am practicing doing some pictures on a form. It's very simple code so I think it's not worth it to post it.
I want to draw some semi-transparent rectangles close to the borders of the form, which I have already managed to do. The problem is that when I re-size the form the rectangles just stay at their original positions, and don't "follow" the new position of the borders.
Make sure to do your drawing in the form's Paint event. That way, it will happen each time the control is redrawn: on a resize for example.
Here's a good example: http://msdn.microsoft.com/en-us/library/system.windows.forms.control.paint.aspx
public MyForm()
{
this.Paint += this.PaintRectangles;
}
private void PaintRectangles(object sender, PaintEventArgs e)
{
// use e.Graphics to draw stuff
}
I have drawn a circle in windows form
Pen pen = new Pen(Color.Black, 3);
Graphics gr = this.CreateGraphics();
gr.DrawEllipse(pen, 5,5,20,20);
How to delete it...
You have to clear your Graphic:
Graphics.Clear();
But all drawn figures will be cleared. Simply, you will then need to redraw all figures except that circle.
Also, you can use the Invalidate method:
Control.Invalidate()
It indicates a region to be redrawn inside your Graphics. But if you have intersecting figures you will have to redraw the figures you want visible inside the region except the circle.
This can become messy, you may want to check out how to design a control graph or use any graph layout library.
You can invalidate the draw region you want to refresh for example:
this.Invalidate();
on the form...
Assuming you're subscribing to the Paint event or overriding the protected OnPaint routine, then you will need to perform something like this:
bool paint = false;
protected override void OnPaint(object sender, PaintEventArgs e)
{
if (paint)
{
// Draw circle.
}
}
Then when you want to stop painting a circle:
paint = false;
this.Invalidate(); // Forces a redraw
You can make a figure of same dimensions using the backColor of your control in which you are drawing
use after your code to clear your figure.
Pen p = new Pen(this.BackColor);
gr.DrawEllipse(p, 5,5,20,20);
In fact, you can delete your circle and nothing but your circle.
Everything you need is something like a screenshot of the "before state" of the area you want to clear, to make a TextureBrush from it. You can achieve that step by something like this:
Bitmap _Background = new Bitmap(this.Width, this.Height);
Graphics.FromImage(_Background).CopyFromScreen(this.Left, this.Top, 0, 0, this.Size);
The first line will give you a bitmap in your windows forms size. The second line will save a screenshot of it in the _Background-bitmap.
Now you create a TextureBrush out of it:
Brush brsBackground = new TextureBrush(_Background);
The next thing you need are the dimensions of your circle, so you should save them into a variable, if they are not a fix value. When you got them at hand, you can clear the specific area like this:
Graphics gr = this.CreateGraphics();
gr.FillEllipse(brsBackground, 5, 5, 20, 20); // values referred to your example
Done!
Even complex figures are able to be deleted by this, like a GraphicsPath for example:
GraphicsPath gp = new GraphicsPath(); // any kind of GraphicsPath
gr.FillRegion(brsBackground, new Region(gp));
You don't "delete" it per se, there's nothing to delete. It's a drawing, you draw something else over it or you can call the Graphics.Clear() method.
If u are using Invalidate() and is not working, make a panel.Refresh().
That will work on you.
just make another control with the attributes etc. that you want, make the visibility to false and set the region of the control to the other control like this:
pen.Region = pen2.Region;
It is very simple to delete a drawn circle from c.
There is only four steps:-
Open turbo app
go to the command where you had drawn the circle
drag the command
click on delete button