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.
Related
My problem is that within my Windows Form Application, I want to draw an Ellipse everytime the mouse is clicked within a specific picture box, and I want the previously drawn ellipses to remain present in the picture box.
In its current state, once the mouse is clicked, the previously drawn ellipse will be replaced with the new one drawn at the cursor's new location.
Ball.Paint draws an ellipse.
Here is the relevant code to the problem:
private Ball b;
private void pbField_Paint(object sender, PaintEventArgs e)
{
if (b != null)
b.Paint(e.Graphics);
}
private void pbField_MouseClick(object sender, MouseEventArgs e)
{
int width = 10;
b = new Ball(new Point(e.X - width / 2, e.Y - width / 2), width);
Refresh();
}
If there is any more needed code or information I am able to provide it.
You need some sort of data structure to store prior ellipses. One possible solution is below:
private List<Ball> balls = new List<Ball>(); // Style Note: Don't do this, initialize in the constructor. I know it's legal, but it can cause issues with some code analysis tools.
private void pbField_Paint(object sender, PaintEventArgs e)
{
if (b != null)
{
foreach(Ball b in balls)
{
b.Paint(e.Graphics);
}
}
}
private void pbField_MouseClick(object sender, MouseEventArgs e)
{
int width = 10;
b = new Ball(new Point(e.X - width / 2, e.Y - width / 2), width);
balls.Add(b);
Refresh();
}
If you want more than one ball to be painted, you need to keep track of a list of balls, rather than just b. Every time the control is refreshed, it is expected to redraw all its contents. That means that in pbField_Paint, you need to be ready to draw as many balls as have been added to the scene.
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();
}
}
I am looking to draw an ellipse on the screen during the MouseMove() event but am wanting it to only draw the most recent ellipse from a start point to the current mouse position. At the moment it is drawing an ellipse for every mouse position that is getting registered. I would be able to draw it easily enough without showing the ellipse if i simply used the MouseDown() and MouseUp() events, but I am wanting the user to be able to see the ellipse as they move the mouse around, so they can know exactly where they are placing it. Does anyone know how I could achieve this?
My current code is as follows:
private void pnlDraw_MouseDown(object sender, MouseEventArgs e)
{
initialX = e.X;
initialY = e.Y;
previousX = e.X;
previousY = e.Y;
isPainting = true;
}
private void pnlDraw_MouseMove(object sender, MouseEventArgs e)
{
if (isPainting)
{
switch (currentDrawType)
{
case DRAWTYPE.ELLIPSE:
{
DrawEllipse(e);
break;
}
default:
{
break;
}
}
}
}
private void DrawEllipse(MouseEventArgs e)
{
pen = new Pen(Color.Black);
Graphics graphics = pnlDraw.CreateGraphics();
graphics.DrawEllipse(pen, initialX, initialY, e.X - initialX, e.Y - initialY);
previousX = e.X;
previousY = e.Y;
graphics.Dispose();
}
Any help would be greatly appreciated!
The first thing you should do when you are drawing something on screen is to clear what was previously displayed. Otherwise, you simply draw on top of existing drawing.
In WinForm, it's normally handled in the OnBackgroundPaint() method, which is a simple way of saying that it's there you should clear the background.
It should looks like this:
e.Graphics.FillRectangle(new SolidBrush(MyBackgroundColor), e.ClipRectangle);
or if it's not a drawing event:
graphics.FillRectangle(new SolidBrush(MyBackgroundColor), 0, 0, MyWidth, MyHeight);
And should be called before drawing anything on top of it.
It is also a guaranty that your drawing zone is ready to be drawn on it.
I've been having problems with onPaint event handler for a pictureBox. I've simplified the code, so right now I'm trying to achieve this: load the bitmap from path into pictureBox1 and draw a small rectangle around mouse cursor when moving over the pictureBox1.
private int mouseX;
private int mouseY;
private String path;
public Form1()
{
InitializeComponent();
path = "images\\image.jpg";
}
private void Form1_Load(object sender, EventArgs e)
{
pictureBox1.Image = new Bitmap(path);
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
mouseX = e.X;
mouseY = e.Y;
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawRectangle(Pens.Black, mouseX - 10, mouseY - 10, 20, 20);
pictureBox1.Invalidate();
}
When I run the application the bitmap is loaded successfully and the rectangle is being drawn nicely wherever I drag the mouse. Anyway there are two problems:
1) When the Form is loaded everything is white but pictureBox until I move the window somewhere. It means menuStrip, toolStrip eg is cleared with white color. When I add pictureBox.Invalidate(); into Form1_Load it seems to take care of that problem (everything is loaded and visible) but on the toolStrip there are some buttons and comboBoxes that are interacting, changing its visibility and this still causes problems. I'd like to reduce the funcionality of the Paint only on pictureBox.
2) The rectangle is drawn the moment the Form is loaded despite the mouse cursor didn't enter pictureBox. Also when cursor leaves the pictureBox the last drawn rectangle stays there. I've tried to remove it but I couldn't figure it out.
Thanks for any suggestions, it seems to me that something fundamental is missing there but don't know what.
2) MouseMove works only when cursor is over picture box. Try to use MouseEnter and MouseLeave in order to handle when to start drawing rectangle and when to stop doing that.