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.
Related
I'm learning how to draw in Winforms. I've created a Form, with a panel with scrollbars. Upone Event Paint I draw an ellipse. This is fairly straightforward:
this.panel1.AutoScroll = true;
this.panel1.AutoScrollMinSize = = new System.Drawing.Size(500, 300);
private void OnPaint(object sender, PaintEventArgs e)
{
Rectangle ellipse = new Rectangle(Point.Empty, new Size(400, 400));
ellipse.Offset(this.panel1.AutoScrollPosition);
using (Pen myPen = new System.Drawing.Pen(System.Drawing.Color.Red))
{
e.Graphics.DrawEllipse(myPen, ellipse);
}
}
private void OnPanelScroll(object sender, ScrollEventArgs e)
{
this.panel1.Invalidate();
}
This works fine, but the complete image is redrawn when resizing or scrolling the panel.
A long time ago, in the time of MFC there was the notion of ViewPort / SetViewPortOrg / mapping modes / etc.. Scrolling and resizing did not require a recalculation of the complete image. Once you had drawn the image you didn't have to redraw as long as the complete image was not changed. All you had to do was move the viewport, or change the mapping mode
Does .NET have something similar that can do the scrolling for me? Maybe I should not draw on a panel, but on another subclass of a ScrollableControl?
I have a litle problem doing something I want.
What I want is quite simple.
I'm making a game tower defence for school.
When I clicked on a tower to place I want to see the tower when moving over my picturebox where everything happens in. This is no problem when I move it does what I want. But when I stop moving my Mouse I want the image stay on the position where it last was ( MouseHover).
But i don't get the mousehover event to do what I want. When I stop moving my mouse the image disapears and nothing is shown in my picturebox.
This is what i currently have for my MouseMove & MouseHover:
Note: I have already tried to use the MousePosition in the mousehover event but this doesn't work either. I hope someone can help me out on this ?
private void picGameArea_MouseMove(object sender, MouseEventArgs e)
{
Pen myPen = new Pen(Color.Black);
if (myGameLogic.tmpTower != null)
{
Xpos = e.X;
YPos = e.Y;
Graphics paper;
paper = picGameArea.CreateGraphics();
paper.DrawImage(myGameLogic.tmpTower.myImage, e.X - 25, e.Y -25, 50, 50);
paper.DrawEllipse(myPen, e.X - myGameLogic.tmpTower.Range, e.Y - myGameLogic.tmpTower.Range , myGameLogic.tmpTower.Range * 2, myGameLogic.tmpTower.Range * 2);
picGameArea.Invalidate();
}
}
private void picGameArea_MouseHover(object sender, EventArgs e)
{
Graphics paper;
paper = picGameArea.CreateGraphics();
if (myGameLogic.tmpTower != null)
{
paper.DrawImage(myGameLogic.tmpTower.myImage, Xpos - 25, YPos - 25, 50, 50);
}
picGameArea.Invalidate();
}
The image disappears because your next Paint event will redraw picGameArea and inside that event nothing will redraw your image of tmpTower.
You have 2 options:
move tmpTower drawing inside Paint event.
enable polling to redraw what is drawn on top by using timer.
There are mistakes in your code:
CreateGraphics required disposing, put it inside using or call paper.Dispose();
there is no need to handle MouseHover at all, this event is used for something like showing ToolTip, it only triggers once if mouse is inside control for some time.
calling Invalidate event will queue paint (it's like asking please, redraw my control some time later, when you have time), instead use Refresh before painting (and remove Invalidate).
Last one actually will fix your issue too...
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.
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 have a problem:
I have 3 picture boxes with 3 different images as in Image
what can i set to pictureBox3 so both images look same.....
EDITED:
I want to move pictureBox3 on pictureBox2,
So there is no Option to merge them to single image
Make sure the image in pictureBox3 is transparent. Set the BackColor to transparent. In code, set the Parent property of the pictureBox3 to be pictureBox2. Adjust the Location coordinates of pictureBox3 since they will be relative to the coordinates of pictureBox2 once you've changed the Parent.
private void Form1_Load(object sender, EventArgs e)
{
pictureBox3.Parent = pictureBox2;
pictureBox3.Location =
new Point(
pictureBox3.Location.X
- pictureBox2.Location.X,
pictureBox3.Location.Y
- pictureBox2.Location.Y);
}
In designer you will not see the transparency, but at runtime you will.
Update
In the image, the left side shows the designer view, the right side is the runtime version.
Another update
I really don't understand how it would be possible that this doesn't work for you. I suppose there must be something we are doing different. I'll describe the exact steps to take to create a working sample. If you follow the exact same steps, I wonder if we'll get the same results or not. Next steps describe what to do and use two images I found on the net.
Using Visual Studio 2008, create a New Project using template Windows Forms Application. Make sure the project is targeted at the .NET Framework 3.5.
Set the Size of the Form to 457;483.
Drag a PictureBox control onto the form. Set its Location to 0;0 and its Size to 449;449.
Click the ellipsis besides its Image property, click the Import... button and import the image at http://a.dryicons.com/files/graphics_previews/retro_blue_background.jpg (just type the URL in the File name text box and click Open). Then click OK to use the image.
Drag another PictureBox onto the form, set its Location to 0;0 and its Size to 256;256. Also set its BackColor property to Transparent.
Using the same method as described above, import image http://www.axdn.com/redist/axiw_i.png which is a transparent image.
Now place the following code in the form's OnLoad event handler:
private void Form1_Load(object sender, EventArgs e)
{
pictureBox2.Parent = pictureBox1;
}
That's it! If I run this program I get a transparent image on top of another image.
I'll add another example that according to the updated requirement allows for moving image3.
To get it working, put an image with transparency in Resources\transp.png
This uses the same image for all three images, but you can simply replace transparentImg for image1 and image2 to suitable images.
Once the demo is started the middle image can be dragged-dropped around the form.
public partial class Form1 : Form
{
private readonly Image transparentImg; // The transparent image
private bool isMoving = false; // true while dragging the image
private Point movingPicturePosition = new Point(80, 20); // the position of the moving image
private Point offset; // mouse position inside the moving image while dragging
public Form1()
{
InitializeComponent();
//
// pictureBox1
//
this.pictureBox1.Location = new System.Drawing.Point(0, 0);
this.pictureBox1.Name = "pictureBox1";
this.pictureBox1.Size = new System.Drawing.Size(231, 235);
this.pictureBox1.TabIndex = 0;
this.pictureBox1.TabStop = false;
this.pictureBox1.Paint += new System.Windows.Forms.PaintEventHandler(this.pictureBox1_Paint);
this.pictureBox1.MouseDown += new System.Windows.Forms.MouseEventHandler(this.pictureBox1_MouseDown);
this.pictureBox1.MouseMove += new System.Windows.Forms.MouseEventHandler(this.pictureBox1_MouseMove);
this.pictureBox1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.pictureBox1_MouseUp);
this.Controls.Add(this.pictureBox1);
transparentImg = Image.FromFile("..\\..\\Resources\\transp.png");
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
var g = e.Graphics;
g.DrawImageUnscaled(transparentImg, new Point(20, 20)); // image1
g.DrawImageUnscaled(transparentImg, new Point(140, 20)); // image2
g.DrawImageUnscaled(transparentImg, movingPicturePosition); // image3
}
private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
var r = new Rectangle(movingPicturePosition, transparentImg.Size);
if (r.Contains(e.Location))
{
isMoving = true;
offset = new Point(movingPicturePosition.X - e.X, movingPicturePosition.Y - e.Y);
}
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (isMoving)
{
movingPicturePosition = e.Location;
movingPicturePosition.Offset(offset);
pictureBox1.Invalidate();
}
}
private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
isMoving = false;
}
}
This code will do the trick:
using (Graphics g = Graphics.FromImage(pictureBox1.Image))
{
g.DrawImage(pictureBox2.Image,
(int)((pictureBox1.Image.Width - pictureBox2.Image.Width) / 2),
(int)((pictureBox1.Image.Height - pictureBox2.Image.Height) / 2));
g.Save();
pictureBox1.Refresh();
}
It will draw the image from pictureBox2 on the existing image of pictureBox1.
For starters, set the BackColor property of PictureBox3 to Transparent. This should work in almost all cases.
You should also use an image with a transparent background instead of white so you do not have the white borders around your purple circle. (Recommended image format: PNG)
Update
Following the replies I got, it appears setting the BackColor to Transparent doesn't work. In that case, it's best you handle the Paint event of the PictureBox and do the painting of the new image yourself as Albin suggested.
You might do some hack by overriding OnPaint and stuff, example here.
But I'd recommend to merge the pictures in pictureBox2 and 3 into a single image before displaying them in a single pictureBox.