I have a C# UserControl. Within the it I have overriden the OnPaint method. Then I draw a circle inside it.
Bitmap GraphicsImage = new Bitmap(24, 24, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
Graphics.FromImage(GraphicsImage).Clear(btnColor);
Graphics graphics = e.Graphics;
SolidBrush myBrush = new SolidBrush(btnColor);
Pen myPen = new Pen(btnColor);
// Draw the button in the form of a circle
graphics.DrawEllipse(myPen, 0, 0, 40, 40);
graphics.FillEllipse(myBrush, new Rectangle(0, 0, 40, 40));
Here is the Image
What I want is to trigger the mouse click event only when the mouse is inside the circle, because the usercontrol is bigger.
You can trigger all click events on the user control, and check if the mouse position is inside the circle
private void yourcontrol_Click(object sender, EventArgs e)
{
Point pt = yourcontrol.PointToClient(System.Windows.Forms.Control.MousePosition);
//check if point is in the circle with
if (Math.Sqrt(Math.Pow(pt.X - xCenterOfCircle, 2) + Math.Pow(pt.Y - yCenterOfCOircle, 2)) < radius)
{
//do something
}
}
xCenterOfCircle has to be the x position of the center of your circle and yCenterOfCircle the y position. I assume in your example it is the center of your control and the radius would be half the size of your control.
I would do that by overriding OnMouseDown:
/// <summary>
/// Raises the <see cref="E:System.Windows.Forms.Control.MouseClick"/> event.
/// </summary>
/// <param name="e">An <see cref="T:System.Windows.Forms.MouseEventArgs"/> that contains the event data. </param>
protected override void OnMouseClick(MouseEventArgs e)
{
var centerX = Left + Width/2.0;
var centerY = Top + Height/2.0;
var dist = (e.X - centerX)*(e.X - centerX) + (e.Y - centerY)*(e.Y - centerY);
if(dist <= (radius*radius))
{
base.OnMouseClick(e);
}
}
So, if the mouse is within a given radius of the center of the control (using Pythagoras's theorem, pass on the click event. Otherwise, ignore it, as the control hasn't actually been clicked.
You can then add event handlers to control.Click as normal.
Edit: to duplicate the example code exactly, change:
var centerX = 20; //(40-0) / 2
var centerY = 20;
Related
I have an empty class Box that i make a matrix of like you se below and i want to draw this matrix on winform how can i do that ?
For example if Box[i,j] have red color and i change the color of the box like thisBox[i.j].color = Color.Black så should my winform change the color of Box[i,j] to black after it draw the whole matrix.
Box[,] boxes = new Box[100, 100];
MainForm form;
Timer timer;
public Game()
{
form = new MainForm();
timer = new Timer();
}
public void Run(int size)
{
form.Paint += new PaintEventHandler(Draw);
timer.Tick += new EventHandler(TimerEventHandler);
timer.Interval = 1000 / 25;
timer.Start();
form.Visible = true;
}
private void TimerEventHandler(Object obj, EventArgs args)
{
form.Refresh();
}
private void Draw(Object obj, PaintEventArgs args)
{
}
Here's an approach to doing it (not the only way):
First, give Box a method to draw itself to a Graphics context:
class Box
{
public Color color { get; set; } = Color.Red;
// Note: Conventionally, property names begin with a capital letter.
/// <summary>
/// Draw this block to the given graphics context.
/// </summary>
/// <param name="g">The graphics context to draw to.</param>
/// <param name="x">X-coordinate of the block within the graphics context.</param>
/// <param name="y">Y-coordinate of the block within the graphics context.</param>
/// <param name="width">Width of the area in which to draw the block.</param>
/// <param name="height">Height of the area in which to draw the block.</param>
public void DrawTo(Graphics g, int x, int y, int width, int height)
{
// Fill in the color of this block:
Brush brush = new SolidBrush(color); // fill style, color etc.
g.FillRectangle(brush, x, y, width, height);
// Black outline:
Pen pen = new Pen(Color.Black); // line style, color, etc.
g.DrawRectangle(pen, x, y, width, height);
// look up the documentation of the System.Drawing.Graphics class for other methods for drawing.
}
}
Now, if you wanted to draw the matrix to a bitmap image, you could do this:
private void DrawMatrix(System.Drawing.Bitmap bm)
{
Graphics g = Graphics.FromImage(bm);
try
{
for (int y = 0; y < MatrixHeight; y++)
for (int x = 0; x < MatrixWidth; x++)
{
boxes[x, y].DrawTo(g, x * CellWidth, y * CellHeight, CellWidth, CellHeight);
}
}
finally
{
g.Dispose();
}
}
// Size of the matrix:
const int MatrixWidth = 100;
const int MatrixHeight = 100;
// Size of each cell in pixels:
const int CellWidth = 20;
const int CellHeight = 20;
But that would be too slow to do each frame (for a 100x100 matrix).
You could update an off-screen image like this if you were updating only individual cells whenever they changed.
You could then place a PictureBox control on the form, and set its Image property to the Bitmap updated here. You can create that bitmap (parameter to this method) with new Bitmap(MatrixWidth * CellWidth, MatrixHeight * CellHeight) .
Alternatively, you could draw to the form's canvass in the OnPaint event handler (the Draw method in your question).
Assuming that the window scrolls (and not all of the matrix is visible within the viewport at any time),
you would loop through only the visible cells and make them draw themselves to the form's Graphics context.
The form's Graphics context is the property Graphics on the event arguments object. This is what you need for the Box.Draw method above. When drawing each cell, you have to take account of its position in the grid and how the window is scrolled, in order to calculate the coordinates to be passed to that method.
On each invocation of the OnPaint event, only part of the window has to be redrawn. This part is given by the property ClipRectangle . (Windows breaks down the area to be redrawn into one or more rectangles, and does a call for each rectangle.)
(ClipRectangle could be the whole window.)
Drawing outside of this is allowed but can be inefficient.
So, for efficiency, you should loop through only all cells that fall at least partly within ClipRectangle, calculate their position within the visible area and call their Box.Draw.
The coordinates used with the Graphics class are the visible coordinates, so (0,0) is the top left of the window, regardless of scrolling.
See these properties for the scroll position:
Form.HorizontalScroll.Value
Form.VerticalScroll.Value
I'm trying to create a square that is located where the user clicks. I have pSize, pX, and pY as variables to represent the location and size of the square. But when I click on the form, the square is at least 70 pixels of in X and Y coordinates where the mouse clicked. I did (this is in the form click function):
pX = Cursor.Position.X;
pY = Cursor.Position.Y;
Graphics g = this.CreateGraphics();
SolidBrush brush = new SolidBrush(Color.Black);
g.FillRectangle(brush, pX, pY, pSize, pSize);
Here is a picture of what is happening:
The screenshot doesn't show my cursor but it is in the top left corner. I also noticed that every time I start the program, the amount the square is offset changes so this time it's relatively distant while next time it could be only about 25 pixels away in both axes.
Could someone tell me what I am doing wrong and/or what I can do about it? Thanks.
You're currently getting the Cursor position. Which is relative to the screen, which is why it is a different offset when you move the form around.
To get the position relative to your Form you need to use the Mouse Click position (sounds similar which is how this tricks people).
You need to make sure your Click event raises a MouseEventHandler:
this.MouseClick += new System.Windows.Forms.MouseEventHandler(this.DrawSquare);
Then you need to get the coordintaes from the event handler:
private void DrawSquare(object sender, MouseEventArgs e)
{
int pX = e.X;
int pY = e.Y;
int pSize = 10;
Graphics g = this.CreateGraphics();
SolidBrush brush = new SolidBrush(Color.Black);
g.FillRectangle(brush, pX, pY, pSize, pSize);
}
I think you don't need the cursor position, but the click position instead.
Try this:
private void Form2_MouseClick(object sender, MouseEventArgs e)
{
int pX = e.X;
int pY = e.Y;
Graphics g = this.CreateGraphics();
SolidBrush brush = new SolidBrush(Color.Black);
g.FillRectangle(brush, pX, pY, 10, 10);//Size just for testing purposes
}
i have PictureBox, how can I draw a shape/line that fires on MouseEnter event and change color or do more.
private void ImgViewer_Paint(object sender, PaintEventArgs e)
{
var graph = e.Graphics;
using (var pen = new Pen(Color.FromArgb(0, 255, 0)))
graph.DrawLine(pen, x1, y1, x2, y2);
}
this code is not enough, i guess
If you know the equation of the shape you could calculate whether the mouse is within or outside the shape area. Note that this is easy if the shape is consisted of the straight lines or circles (ellipses) for which the geometrical equations are relatively simple. For instance if your shape is a triangle with x and y coordinates (10,10), (50,10) and (30,50) than you should derive the equations of the lines using the equation of the line in two points:
y-y1 = ((y2-y1)/(x2-x1))*(x-x1)
the equations of the lines of our triangle would be:
y=1
y=2*x-10
y=-2*x+110
We should draw that triangle on some canvas, let's say on the PictureBox with FixedSingle border. Add the Paint event handler
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
Point[] p = new Point[3];
p[0] = new Point(10,10);
p[1] = new Point(50,10);
p[2] = new Point(30,50);
e.Graphics.DrawLines(Pens.Black, p);
e.Graphics.FillPolygon(Brushes.Red, p);
}
We should add the MouseMove event handler for the PictureBox
bool inside = false;
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Y > 10 && e.Y < 2 * e.X - 10 && e.Y < -2 * e.X + 110)
{
if (!inside)
{
inside = true;
HandleMouseEnter();
}
}
else
inside = false;
}
void HandleMouseEnter()
{
MessageBox.Show("Mouse inside");
}
In if statement whether the mouse cursor is within the triangle (note that the coordinate origin in C# is on the top-left corner but it is similar to the real geometry). The HandleMouseEnter is the method that handles the mouse enter.
You could use similar approach for an arbitrary shape but you should have geometry equations that describe it.
Is it possible to rotate a button or any control at a particular angle in WinForms? If so, how?
If you really want to (I have no idea why one would..*) you could try to use a Button subclass, maybe like that:
public partial class TurnButton : Button
{
public TurnButton()
{
InitializeComponent();
}
int angle = 0; // current rotation
Point oMid; // original center
protected override void OnLayout(LayoutEventArgs levent)
{
base.OnLayout(levent);
if (oMid == Point.Empty) oMid = new Point(Left + Width / 2, Top + Height / 2);
}
protected override void OnPaint(PaintEventArgs pe)
{
int mx = this.Size.Width / 2;
int my = this.Size.Height / 2;
SizeF size = pe.Graphics.MeasureString(Text, Font);
string t_ = Text;
Text = "";
base.OnPaint(pe);
if (!this.DesignMode)
{
Text = t_; pe.Graphics.TranslateTransform(mx, my);
pe.Graphics.RotateTransform(angle);
pe.Graphics.TranslateTransform(-mx, -my);
pe.Graphics.DrawString(Text, Font, SystemBrushes.ControlText,
mx - (int)size.Width / 2, my - (int)size.Height / 2);
}
}
protected override void OnClick(EventArgs e)
{
this.Size = new Size(Height, Width);
this.Location = new Point(oMid.X - Width / 2, oMid.Y - Height / 2);
angle = (angle + 90) % 360;
Text = angle + "°";
base.OnClick(e);
}
}
(* I have no idea why I wrote that, either ;-)
You can't rotate controls. That's simply not supported by the native API controls that WinForms uses.
And one might wonder why it even should be supported. What could you possibly be trying to do that you'd need to rotate a button control? It would be much easier to draw it in a different place with a different shape in the first place, rather than trying to rotate an existing control. (Do note that you can also resize and reposition a control at run-time, if that would fit your needs. Investigate the Size and Location properties.)
The only workaround is to draw the control's image to a bitmap, hide the control, and draw the bitmap onto the form in the location you want it to appear. Of course, that won't result in a control that the user can interact with. They won't be able to click an image of a button, because it's not a real button. If that's acceptable to you, you should probably be using an image in the first place, rather than a button.
This is similar to the question asked here:
Rotating a .NET panel in Windows Forms
The quick summary of answers from that question is that while it may be possible to do it, it would be very, very complicated.
A possible workaround in some cases would be this:
Use a tabControl , and resize it so you only have the button left. Set the allignment to left/right, and you have your button rotated 90/270 degrees.
public class VerticalButton : Button
{
public string VirticalText { get; set; }
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
StringFormat stringFormat = new StringFormat();
stringFormat.FormatFlags = StringFormatFlags.DirectionVertical;
SolidBrush solidBrush = new SolidBrush(this.ForeColor);
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
pe.Graphics.DrawString(VirticalText, this.Font, solidBrush,
new Rectangle(0, 0, Width, Height), stringFormat);
}
}
I am having trouble drawing a line within a group box in a simple windows form.
here is my code:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
DrawLShapeLine(groupBox1.CreateGraphics(), 10, 10, 20, 40);
}
public void DrawLShapeLine(System.Drawing.Graphics g, int intMarginLeft, int intMarginTop, int intWidth, int intHeight)
{
Pen myPen = new Pen(Color.Black);
myPen.Width = 2;
// Create array of points that define lines to draw.
int marginleft = intMarginLeft;
int marginTop = intMarginTop;
int width = intWidth;
int height = intHeight;
int arrowSize = 3;
Point[] points =
{
new Point(marginleft, marginTop),
new Point(marginleft, height + marginTop),
new Point(marginleft + width, marginTop + height),
// Arrow
new Point(marginleft + width - arrowSize, marginTop + height - arrowSize),
new Point(marginleft + width - arrowSize, marginTop + height + arrowSize),
new Point(marginleft + width, marginTop + height)
};
g.DrawLines(myPen, points);
}
}
If I attach the DrawLShapeLine method to a button click event, it draws fine, but it does not draw on load of the form.
Please advice.
Quick & dirty:
How about creating a panel with the width of 1 pixel and give it a backgroundcolor?
Hook up an event handler for the Paint event of the GroupBox and call DrawLShapeLine from within that event handler instead. You should then use the Graphics object supplied by in event arguments:
private void groupBox1_Paint(object sender, PaintEventArgs e)
{
DrawLShapeLine(e.Graphics, 10, 10, 20, 40);
}
As your code looks now it will attempt to paint in the GroupBox when the form requires painting. The group box may be painted at any other occasion, which will the line you paint disappear.
Another option would be to use the line control that is available in Visual Basic Power Packs.
http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/d9e082c8-5386-4481-a744-1c9029805696/
If you have Visual Studio 2008 SP1, or Visual Studio 2010, you won't need to download anything.
If you do not see the Visual Basic PowerPacks control in the Toolbox, right click in the Toolbox and select Show All in the context menu.
Add a label with no text, a 3D border and a height of 2 (you have to set the height in the properties page, not with the GUI)!
I'm not sure if something else is going on, but you should draw the line on the GroupBox's Paint event, not the Form's.
The System.Drawing.Pen can be used to draw line in Windows Form.
Graphics surface = CreateGraphics();
Pen pen1 = new Pen(Color.Black, 2);
surface.DrawLine(pen1, this.Width / 2, 0, this.Width / 2, this.Height);