I am trying to create a elipse in a textbox on double click. But it doesnt seem to happen.
panel.MouseClick += create_terms;
private void create_terms(object sender, EventArgs arg)
{
if (Phys_terms_check.Checked == true)
{
MouseEventArgs e = (MouseEventArgs)arg;
Graphics g = CreateGraphics();
SolidBrush p = new SolidBrush(Color.Red);
Pen erase = new Pen(Color.White);
Panel panel = (Panel)sender;
g.FillEllipse(p, e.X+panel.Left,e.Y+panel.Top,10,10);
}
}
The e.x and e.y seem to be giving relative coordinates from the sender. How to get point relative to the form.
add sender's top and left coordinates.
g.FillEllipse(p, e.X + textbox.Left, e.Y + textbox.Top, 10, 10);
but, this won't show, because textbox paint event fill fire and repaint textbox.
First of all: TextBoxes are old legacy and rather special Controls that do not support all things normal controls let you do.
Among the things that won't work are
Setting a BackgroundImage
Owner-drawing them
The latter includes any drawing in its Paint/OnPaint events.
You can code and hook-up the Paint event, but it won't get called.
You still can draw onto a TextBox using CreateGraphics, if you do it right, but as always with this function the result is non-persistent and will go away as soon as the system refreshes the TextBox itself, which is super-fast: as soon as you move your cursor over it the circle you draw may disappear..
The code to do it would have to look similar to this:
Graphics g = yourTextBox.CreateGraphics();
g.FillEllipse(Brushes.Red, yourTextBox.Width - 22, 2, 11, 11);
But as I said this will not persist, so it has little or no value.
If you want to draw onto something with a visible Text property you can use a Label:
private void yourLabel_Paint(object sender, PaintEventArgs e)
{
e.Graphics.FillEllipse(Brushes.Red, yourLabel.Width - 22, 2, 11, 11);
}
The result looks the same, but only the dot in the Label will persist, e.g. a Minimize-Maximize of the form.. In fact the dot in the TextBox didn't even survive calling my screenshot program, so I had use resort to pressing the Print-Key !
For drawing circles upon mouseclicks onto normal controls see this post!
Related
I'm trying to make a little graphics program that has a circle of diameter 100 on the screen and from the center of it, a line is coming out of it that is always attached to the mouse pointer until such time that the user does a click, and then the line is permanently drawn. It's exactly like MSPaint's line, except that starting point is always the center of the circle.
I tried a few things that DON'T work.
I can get the line to appear only after a mouse-click. That's not what I want. I want the line to always be present and pivoting from the circle-center until the mouse is clicked and then it's then permanently on the screen.
I can get a smeary thing where the line is always being drawn. It makes a sort of star shape, but that's not what I want either.
Basically, I want the same functionality that you have in MSPaint when you draw a line. What am I supposed to do? Draw the line and then erase it a second later, and then draw it again when the mouse is in a new position? I tried something like that, but it does a thing where it erases the background a little bit, and then the line is only drawn when the mouse is in motion, but not when the mouse is stationary.
If anyone can provide a code snippet, that'd be great. Or just some pseudo-code.
Is this the right pseudo code?
Start:
Left click and a line appears from center of circle to mouse tip
Line stays there until a new mouse coordinate is made (how do I keep track)?
Line from center of circle to original location gets erased
New line is made to new location of mouse coordinates.
I think this something of a state-machine to use what I learned in digital class. How are states implemented in C#?
Any help would be appreciated, and thanks to everyone that can understand my question even though I'm probably not using the proper terminology.
So short answer is you will need some custom painting. The longer answer involves custom drawing, and event handling.
The other piece of code you need is a list of some sort to hold all of the lines. The code below creates a user control and does the custom painting without relying on a state machine. To test it, create a new project add a user control called UserControl1, and add it to a form. Make sure you tie into the listed events.
I tried to comment the relevant sections and this shows a quick and dirty way to do what you appear to be trying to do.
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
namespace CustomDrawingAndEvents
{
public partial class UserControl1 : UserControl
{
private struct MyLine
{
public Point mStart;
public Point mEnd;
public MyLine(Point xStart, Point xEnd)
{
mStart = xStart;
mEnd = xEnd;
}
}
private List<MyLine> mLines;
private Point mCircleCenter;
private Point mMousePosition;
public UserControl1()
{
InitializeComponent();
mLines = new List<MyLine>();
//Double Buffer to prevent flicker
DoubleBuffered = true;
//Create the center for our circle. For this just put it in the center of
//the control.
mCircleCenter = new Point(this.Width / 2, this.Height / 2);
}
private void UserControl1_MouseClick(object sender, MouseEventArgs e)
{
//User clicked create a new line to add to the list.
mLines.Add(new MyLine(mCircleCenter, e.Location));
}
private void UserControl1_MouseMove(object sender, MouseEventArgs e)
{
//Update mouse position
mMousePosition = e.Location;
//Make the control redraw itself
Invalidate();
}
private void UserControl1_Paint(object sender, PaintEventArgs e)
{
//Create the rect with 100 width/height (subtract half the diameter to center the rect over the circle)
Rectangle lCenterRect = new Rectangle(mCircleCenter.X - 50, mCircleCenter.Y - 50, 100, 100);
//Draw our circle in the center of the control with a diameter of 100
e.Graphics.DrawEllipse(new Pen(Brushes.Black), lCenterRect);
//Draw all of our saved lines
foreach (MyLine lLine in mLines)
e.Graphics.DrawLine(new Pen(Brushes.Red), lLine.mStart, lLine.mEnd);
//Draw our active line from the center of the circle to
//our mouse location
e.Graphics.DrawLine(new Pen(Brushes.Blue), mCircleCenter, mMousePosition);
}
}
}
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 working on a simple windows forms paint application. I am having problem in clearing the panel. The code i am using to draw is
private void panel1_Paint(object sender, PaintEventArgs e)
{
Graphics g = Graphics.FromImage(tempDraw);
Pen myPen = new Pen(foreColor, lineWidth);
g.DrawLine(myPen, x1, y1, x2, y2);
myPen.Width = 100;
myPen.Dispose();
e.Graphics.DrawImageUnscaled(tempDraw, 0, 0);
g.Dispose();
}
How to clear the panel?
Are drawing in the paint handler of the Panel instance? If not then calling Invalidate on the panel would do.
But you will probably be persisting the drawing items and so to clear them you would need to delete what has been drawn and then call Invalidate. You could also fill the Panel with a particular color using FillRect but that would be a dirty workaround and not fit your final design.
You should also check out CodeProject.com for examples like this one to give you an idea on what needs to be handled when creating a drawing app like this.
EDIT:
Per the edited answer, you cannot clear the panel with the existing logic. You are painting inside Paint handler of the form which will happen any time it needs to be redrawn. This means that you should change your approach. You need some sort of condition inside the Paint handler which decides whether or not it will paint anything at all. This is where the persistence of drawing objects comes in. If you want to create a drawing program then you will have to handle the mouse Down, Up and Move events over the panel objects and store the data in a points array. (As an example of one type of drawing.) Then in your Paint handler if the Points[] is not empty you draw the points. Otherwise you draw nothing... which ends up in an empty container. Then if you need to clear the drawing you delete the contents of the Points array and call Invalidate on the Panel. That will clear the persisted data and repaint to nothing.
You can use
Panel1.Invalidate();
But there is a problem with this, after you call this function it clears all the graphics from the panel, but it also recalls the function i.e.
private void panel1_Paint(object sender, PaintEventArgs e)
{
//This function is recalled after Panel1.Invalidate();
}
So solution is to make your paint code in some other function
private void MyDrawing()
{
Graphics g = Graphics.FromImage(tempDraw);
// if above line doesn't work you can use the following commented line
//Graphics g = Graphics.Panel1.CreateGraphics();
Pen myPen = new Pen(foreColor, lineWidth);
g.DrawLine(myPen, x1, y1, x2, y2);
myPen.Width = 100;
myPen.Dispose();
Panel1.Graphics.DrawImageUnscaled(tempDraw, 0, 0);
g.Dispose();
}
You'll have to draw over the panel again with whatever base colour you're using eg. white\grey with the Graphics.FillRectangle method:
// Create solid brush.
SolidBrush whiteBrush = new SolidBrush(Color.White);
// Create location and size of rectangle.
// Fill rectangle to screen.
e.Graphics.FillRectangle(whiteBrush, panel.Location.X, panel.Location.Y, panel.Width, panel.Height);
this.Invalidate();
On a blank winform code can be added to show lines that intersect (crosshairs) at the mouse pointer. The problem is that the lines don't show (or are partially hidden) by controls on the form (ie listview, splitcontainer, buttons).
How would I modify the code below to show on-top (bring to front...) of all the controls present on the form?
int lastX = 0;
int lastY = 0;
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
Region r = new Region();
r.Union(new Rectangle(0, lastY, this.Width, 1));
r.Union(new Rectangle(lastX, 0, 1, this.Height));
this.Invalidate(r);
this.Update();
Graphics g = Graphics.FromHwnd(this.Handle);
g.DrawLine(Pens.Chocolate, 0, e.Y, this.Width, e.Y);
g.DrawLine(Pens.Chocolate, e.X, 0, e.X, this.Height);
lastX = e.X;
lastY = e.Y;
}
private void Form1_MouseLeave(object sender, EventArgs e)
{
this.Invalidate();
}
You need a transparent window that's on top of all the other controls. The only way to get one is by overlapping the form with another form, made transparent with its TranparencyKey property. You'll find sample code for this in my answer in this thread.
Please just try first sending to back(Control.SendToBack()) the controls on the form (ie listview, splitcontainer, buttons). Put this at the FormLoad event. I have experimented the same nightmare with a Windows MDI application.
Hope that helps,
Enumerate through the desired controls and call the .BringToFront(); function on them.
listBox1.BringToFront();
According to the documentation, the region object should be in world co-ordinates, you're passing in client co-ordinates. Use Control.PointToScreen to map the rectangles' top left coordinate to world space.
I'd also be tempted to defer the drawing to the OnPaint method.
Can I delete the old rectangle which I have drawn and draw a new rectangle?
private void panel1_MouseClick(object sender, MouseEventArgs e)
{
Graphics g = this.panel1.CreateGraphics();
Pen pen = new Pen(Color.Black, 2);
g.DrawRectangle(pen, 100,100, 100, 200);
g.dispose();
}
No, you cannot "delete" something that's already been drawn. You can overwrite it with something else, but drawing with Graphics objects is like painting in real-life: once the paint is dry, you can only paint over it with another colour, you can't "erase" it.
You probably shouldn't be drawing things in response to a MouseClick, either. It's best to only draw things in response to a Paint event. What I would do in this situation is add a Rectangle structure to a list on the MouseClick and then call panel1.Invalidate() to ask it to redraw itself. Then in the Paint event for the panel, do the drawing there.
This will kill two birds with one stone, because you will be able to "erase" thing by simply removing them from the list of stuff to draw.
This is usually done by maintaining a collection of objects you want drawn. The mouse click should update this collection and then tell the window (or the affect region) to refresh. This has the enormous advantage of preserving whatever you've drawn if the window is moved off-screen, hidden behind other windows, minimized, etc.
For a rudimentary solution, create a hierarchy of drawable shape types derived from a common abstract Shape class, and use, e.g., a List for the collection. The base Shape class will have an abstract Draw method that the derived classes override.
For a more industrial-strength solution, look around for 2-D scene graphs.
One can use Graphics.Save() and Graphics.Restore(state) methods for that. For example:
private void SaveRestore2(PaintEventArgs e)
{
// Translate transformation matrix.
e.Graphics.TranslateTransform(100, 0);
// Save translated graphics state.
GraphicsState transState = e.Graphics.Save();
// Reset transformation matrix to identity and fill rectangle.
e.Graphics.ResetTransform();
e.Graphics.FillRectangle(new SolidBrush(Color.Red), 0, 0, 100, 100);
// Restore graphics state to translated state and fill second
// rectangle.
e.Graphics.Restore(transState);
e.Graphics.FillRectangle(new SolidBrush(Color.Blue), 0, 0, 100, 100);
}
http://msdn.microsoft.com/en-us/library/system.drawing.graphics.restore.aspx
Also, depending on the application, you might look at using DrawReversibleFrame. You can change the rectangle location by calling the Offset method.
Instead of calling g.DrawRectangle(pen, 100,100, 100, 200); , maintain the rectangle as a object which will be drawn by the graphics object. Each time you will update this rectangle object with new one and graphics object will draw the new one.
The refresh should clear the old rectangle and graphics will draw the new one.
You can just use VisualBasic PowerPacks, it is included with my version of Visual Studio 2008
Here's a sample code that will draw a rectangle over a TextBox, i.e. I am giving it a custom border
Dim x = TextBox1.Location.X
Dim y = TextBox1.Location.Y
Dim width = TextBox1.Width
Dim height = TextBox1.Height
Dim ShapeContainer1 As New Microsoft.VisualBasic.PowerPacks.ShapeContainer
Me.Controls.Add(ShapeContainer1)
Dim RectangleShape1 As New Microsoft.VisualBasic.PowerPacks.RectangleShape
ShapeContainer1.Shapes.AddRange(New Microsoft.VisualBasic.PowerPacks.Shape() {RectangleShape1})
RectangleShape1.Location = New System.Drawing.Point(x - 1, y - 1)
RectangleShape1.Size = New System.Drawing.Size(width + 1, height + 1)
RectangleShape1.BorderColor = Color.MistyRose
ShapeContainer1.Refresh()
Code is self describing but if you'd have any problem, just leave a message...
I think using DrawReversibleFrame is the right solution.
The first call draw the rectangle, the second call undraw it and so on.
Here is a sample code, a clic on the button will make the rectangle appear/disapper.
Rectangle pRect = new Rectangle(10, 10, 20, 20);
private void rect_Click(object sender, EventArgs e)
{
ControlPaint.DrawReversibleFrame(pRect, this.BackColor, FrameStyle.Thick);
}