Windows Form drawn ellipse Select and Delete Particularly selected - c#

I have drawn Some Ellipses on Windows Form.
Is it Possible for 'c# Window Form' to 'mouse click' on an ellipse and to delete it?..
Code from Comments
public void DrawCircle_Paint(object sender, PaintEventArgs e)
{
Pen pen = new Pen(Color.Black, 3);
Graphics gr = this.CreateGraphics();
gr.DrawEllipse(pen, 40, 45, 20, 20);
Pen pen2 = new Pen(Color.Black, 3);
Graphics gr1 = this.CreateGraphics();
gr.DrawEllipse(pen2, 30, 25, 38, 20);
Pen pen3 = new Pen(Color.Black, 3);
Graphics gr2 = this.CreateGraphics();
gr.DrawEllipse(pen3, 35, 36, 68, 15);
Pen pen4 = new Pen(Color.Black, 3);
Graphics gr3 = this.CreateGraphics();
gr.DrawEllipse(pen4, 50, 60, 67, 35);
}

Yes its possible.
I assume that you know the positions and dimensions of the ellipses and have them stored in a list. Then you can use the MouseDown and iterate through the ellipses. How to check, if a point is in an ellipse, can be found here for example.
If you found a clicked ellipse remove it from your list and repaint everything.
Update:
To your code. You dont need to call CreateGraphics all the time. The graphics object is given in the PaintEventArgs (e.Graphics). Also you dont need to create a pen every time.
public void DrawCircle_Paint(object sender, PaintEventArgs e)
{
Graphics gr = e.Graphics;
using(Pen p = new Pen(Color.Black, 3))
{
gr.DrawEllipse(pen, 40, 45, 20, 20);
gr.DrawEllipse(pen2, 30, 25, 38, 20);
gr.DrawEllipse(pen3, 35, 36, 68, 15);
gr.DrawEllipse(pen4, 50, 60, 67, 35);
}
}

I've just tried this demo, well there are fairly much work. It's just a demo because there are still many features lacked and also the memory leakage problem is not ensured to not occur. Drawing some thing directly using some methods of Graphics object won't help you support some interaction (like hittesting). You have to draw a GraphicsPath or a Region using the methods FillPath or FillRegion. In this demo, I used GraphicsPath, is has 2 interesting methods to help us implement hittesting which are IsVisible and IsOutlineVisible, in this case we just use IsVisible. Now is the code for you:
public class Ellipse : IDisposable
{
GraphicsPath gp = new GraphicsPath();
RectangleF rect;
public Ellipse(Point center, float rx, float ry)
{
Center = center;
RadiusX = rx;
RadiusY = ry;
Visible = true;
rect = new RectangleF(Center.X - RadiusX, Center.Y - RadiusY, RadiusX * 2, RadiusY * 2);
gp.AddEllipse(rect);
BackColor = Color.Green;
SelectedBackColor = Color.LimeGreen;
BorderColor = Color.Transparent;
}
public event EventHandler Click;
public event EventHandler MouseEnter;
public event EventHandler MouseLeave;
Point center;
float rx, ry;
Control canvas;
bool entered;
bool visible;
Color backColor;
Color borderColor;
Color selectedColor;
bool selected;
public bool Selected
{
get { return selected; }
set
{
if (selected != value)
{
selected = value;
if (canvas != null) canvas.Invalidate(Rectangle.Ceiling(rect));
}
}
}
public bool Visible
{
get { return visible; }
set
{
if (visible != value)
{
visible = value;
if(canvas != null) canvas.Invalidate(Rectangle.Ceiling(rect));
}
}
}
public Control Canvas
{
get { return canvas; }
set
{
if (canvas != value)
{
if(canvas != null) DetachCanvas(canvas);
if (value != null)
{
AttachCanvas(value);
value.Invalidate(Rectangle.Ceiling(rect));
}
canvas = value;
}
}
}
public Point Center
{
get { return center; }
set
{
if (center != value)
{
int dx = value.X - center.X;
int dy = value.Y - center.Y;
rect.Offset(dx, dy);
center = value;
gp.Reset();
gp.AddEllipse(rect);
if (canvas != null) canvas.Invalidate(Rectangle.Ceiling(rect));
}
}
}
public float RadiusX
{
get { return rx; }
set
{
if (rx != value)
{
rect.Width = rx * 2;
rx = value;
gp.Reset();
gp.AddEllipse(rect);
if (canvas != null) canvas.Invalidate(Rectangle.Ceiling(rect));
}
}
}
public float RadiusY
{
get { return ry; }
set
{
if (ry != value)
{
rect.Height = ry * 2;
ry = value;
gp.Reset();
gp.AddEllipse(rect);
if (canvas != null) canvas.Invalidate(Rectangle.Ceiling(rect));
}
}
}
public Color BorderColor
{
get { return borderColor; }
set
{
if (borderColor != value)
{
borderColor = value;
if (canvas != null) canvas.Invalidate(Rectangle.Ceiling(rect));
}
}
}
public Color SelectedBackColor
{
get { return selectedColor; }
set
{
if (selectedColor != value)
{
selectedColor = value;
if (canvas != null) canvas.Invalidate(Rectangle.Ceiling(rect));
}
}
}
public Color BackColor
{
get { return backColor; }
set
{
if (backColor != value)
{
backColor = value;
if (canvas != null) canvas.Invalidate(Rectangle.Ceiling(rect));
}
}
}
private void Render(Graphics g)
{
using(Pen p = new Pen(BorderColor))
using (Brush b = new SolidBrush(selected ? SelectedBackColor : BackColor))
{
SmoothingMode sm = g.SmoothingMode;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.FillPath(b, gp);
g.DrawPath(p, gp);
g.SmoothingMode = sm;
}
}
private void CanvasPaint(object sender, PaintEventArgs e)
{
if (Visible) Render(e.Graphics);
}
private void CanvasMouseMove(object sender, MouseEventArgs e)
{
var cv = sender as Control;
if (gp.IsVisible(e.Location))
{
var handler = MouseEnter;
if (handler != null && Visible)
{
handler(this, EventArgs.Empty);
}
entered = true;
}
else if (entered)
{
var handler = MouseLeave;
if (handler != null && Visible)
{
handler(this, EventArgs.Empty);
}
entered = false;
}
}
private void CanvasMouseClick(object sender, MouseEventArgs e){
var cv = sender as Control;
if (gp.IsVisible(e.Location))
{
var handler = Click;
if (handler != null && Visible) handler(this, EventArgs.Empty);
}
}
private void AttachCanvas(Control canvas)
{
canvas.Paint += CanvasPaint;
canvas.MouseMove += CanvasMouseMove;
canvas.MouseClick += CanvasMouseClick;
}
private void DetachCanvas(Control canvas)
{
canvas.Paint -= CanvasPaint;
canvas.MouseMove -= CanvasMouseMove;
canvas.MouseClick -= CanvasMouseClick;
}
public void Dispose()
{
Visible = false;
if (Canvas != null) Canvas.Invalidate(Rectangle.Ceiling(rect));
Canvas = null;
gp.Dispose();
}
}
Usage: I implemented only 3 events: MouseEnter, MouseLeave and Click. These events are very similar to the events we use with normal control. Here is the demo code for you to test, it will render the ellipse on a form:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
var e = new Ellipse(new Point(400, 400), 100, 100) { Canvas = this };
e.MouseEnter += (s, ev) => {
e.BackColor = Color.Red;
};
e.MouseLeave += (s, ev) => {
e.BackColor = Color.Green;
};
e.Click += (s, ev) => {
e.Visible = false; //Hide the ellipse
};
}
}
Note that you can create your own class for other shapes.
UPDATE: I updated the Ellipse class with some modification to avoid memory leakage, to delete the ellipse you just need to call Dispose, it will delete the ellipse and you can't use it anymore, for your requirement with a List<Ellipse>, try this:
//You have to handle `Ellipse.Click` to set the `Selected` manually, this is by design
//do this for every ellipses
Ellipse selectedEllipse;
//Use this handler for all the Click event of your ellipses
private void Ellipse_Click(object sender, EventArgs e){
var ellipse = sender as Ellipse;
if(ellipse == selectedEllipse) return;
if(selectedEllipse != null) selectedEllipse.Selected = false;
ellipse.Selected = true;
selectedEllipse = ellipse;
}
List<Ellipse> ellipses = ...; //this is your List<Ellipse>
for(int i = ellipses.Count - 1; i >= 0; i--){
if(ellipses[i].Selected){
ellipses[i].Dispose();
ellipses.RemoveAt(i);
}
}
The code to handle the Ellipse.Click event for every Ellipse can be modified to allow multiple selected. That's why it's not hard-coded to be selected when begin clicked.

try it !
Bitmap Painting;
Pen Pen_Drawing;
Graphics Graph_Drawing;
Pen_Drawing.Dispose();
Graph_Drawing.Dispose();
Painting.Dispose();
Painting = new Bitmap(500, 500);
Pen_Drawing = new Pen(Color.FromArgb(95, 158, 160), 1f);
Graph_Drawing = Graphics.FromImage(Painting);

Related

How to animate dots in UserControl Paint event?

In paint event because i want to be able to control the dots size colors and more properties.
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
public partial class LoadingLabel : UserControl
{
public LoadingLabel()
{
InitializeComponent();
}
private void LoadingLabel_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
e.Graphics.FillEllipse(Brushes.Red, 1, 1, 20, 20);
Thread.Sleep(1);
e.Graphics.FillEllipse(Brushes.Red, 1, 1, 0, 0);
Thread.Sleep(1);
}
}
I tried first to make a simple dot that is disappearing after some time and then show again but it's not working i see a red still dot(point).
later when this will work i want to make 3 dots animating like a loading animation.
This is what I've tried:
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
public partial class LoadingLabel : UserControl
{
private bool animate = false;
public LoadingLabel()
{
InitializeComponent();
timer1.Enabled = true;
}
private void LoadingLabel_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
if (animate == false)
{
e.Graphics.FillEllipse(Brushes.Red, 1, 1, 20, 20);
}
else
{
e.Graphics.FillEllipse(Brushes.Red, 5, 1, 20, 20);
}
}
int count = 0;
private void timer1_Tick(object sender, EventArgs e)
{
count++;
if(count == 10 && animate == false)
{
animate = true;
}
if(count == 20 && animate)
{
animate = false;
count = 0;
}
this.Invalidate();
}
}
the result is the first point draw then the second point draw but the first one is gone:
it looks like the point is moving to the right and back to the left.
but i want a loading effect with 3 points. and not moving point.
This is working with 3 points but it looks too complicated for 3 points. and if i want 100 points?
maybe i should use a loop inside the paint event ?
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
public partial class LoadingLabel : UserControl
{
private int numofpoints = 0;
public LoadingLabel()
{
InitializeComponent();
timer1.Enabled = true;
}
private void LoadingLabel_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
if(numofpoints == 0)
{
e.Graphics.FillEllipse(Brushes.Red, 1, 1, 20, 20);
}
if(numofpoints == 1)
{
e.Graphics.FillEllipse(Brushes.Red, 5, 1, 20, 20);
}
if(numofpoints == 2)
{
e.Graphics.FillEllipse(Brushes.Red, 10, 1, 20, 20);
}
}
int count = 0;
private void timer1_Tick(object sender, EventArgs e)
{
count++;
if(count == 10)
{
numofpoints = 0;
}
if(count == 20)
{
numofpoints = 1;
}
if(count == 30)
{
numofpoints = 2;
count = 0;
}
this.Invalidate();
}
}
Another update of what I've tried:
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
public partial class LoadingLabel : UserControl
{
private List<PointF> points = new List<PointF>();
public LoadingLabel()
{
InitializeComponent();
points.Add(new PointF(0, 0));
timer1.Enabled = true;
}
private void LoadingLabel_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
for (int i = 0; i < points.Count; i++)
{
e.Graphics.FillEllipse(Brushes.Red, points[i].X, points[i].Y, 20, 20);
}
}
int count = 0;
private void timer1_Tick(object sender, EventArgs e)
{
count++;
if (count < 3)
{
points.Add(new PointF(count * 20, 0));
//points = new List<PointF>();
}
//this.Invalidate();
}
}
If i will make the instance in the tick event it will not draw anything. if i will use the Invalidate line it will make the points to be like blinking.
what i want is to create a loading effect animation.
the result as the code now is still 3 points, and i want to animate them like in the link.
Something like this:
Since, based on the image you have posted, you want to animate a series of Dots, where only the active one changes color, your UserControl can define Properties that allow to specify the number of Dots, the Color of a Dot and the Color of the active Dot.
A Timer can be used to change the current active Dot, so the paint procedure knows when to change the color of one of the Dots.
The UserControl is automatically resized when the number of Dots specified changes.
Also when the UserControl is first created, it sets its MinimumSize, so the Dots are always visible.
You can expand on this template, adding more features.
Note these lines in the Constructor of the UserControl:
components = new Container();
dotsTimer = new Timer(components) { ... };
This instructs the Timer Component to add itself to the Components of the Parent container, so when the Parent is disposed, the Timer is also disposed and its event handler(s) removed.
Setting DoubleBuffered = true; avoids flickering when the Dots are drawn.
Call the Start() method to start the animation and the Stop() method to, well, stop it.
using System.ComponentModel;
using System.Drawing;
using System.Drawing.Drawing2D;
public partial class LoadingLabel : UserControl {
private int m_NumberOfDots = 5;
private Color m_DotColor = Color.Cyan;
private Color m_DotActiveColor = Color.Blue;
private float dotSize = 20.0f;
private float dotSpacing = 20.0f;
private int currentDot = 0;
private Timer dotsTimer = null;
public LoadingLabel()
{
InitializeComponent();
components = new Container();
dotsTimer = new Timer(components) { Interval = 200 };
dotsTimer.Tick += DotsTimer_Tick;
DoubleBuffered = true;
Padding = new Padding(5);
}
[DefaultValue(5)]
public int NumberOfDots {
get => m_NumberOfDots;
set {
value = Math.Max(3, Math.Min(value, 7));
if (m_NumberOfDots != value) {
m_NumberOfDots = value;
bool running = dotsTimer.Enabled;
Stop();
SetMinSize();
if (running) Start();
}
}
}
[DefaultValue(typeof(Color), "Cyan")]
public Color DotColor {
get => m_DotColor;
set {
m_DotColor = value;
Invalidate();
}
}
[DefaultValue(typeof(Color), "Blue")]
public Color DotActiveColor {
get => m_DotActiveColor;
set {
m_DotActiveColor = value;
Invalidate();
}
}
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
for (int dot = 0; dot < m_NumberOfDots; dot++) {
var color = dot == currentDot ? DotActiveColor : DotColor;
var pos = Padding.Left + (dotSize + dotSpacing) * dot;
using (var brush = new SolidBrush(color)) {
e.Graphics.FillEllipse(brush, pos, Padding.Top, dotSize, dotSize);
}
}
base.OnPaint(e);
}
protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
SetMinSize();
}
protected override void OnHandleDestroyed(EventArgs e) {
Stop();
base.OnHandleDestroyed(e);
}
private void DotsTimer_Tick(object sender, EventArgs e) {
currentDot += 1;
currentDot %= m_NumberOfDots;
Invalidate();
}
public void Start() => dotsTimer.Start();
public void Stop() {
dotsTimer.Stop();
currentDot = 0;
Invalidate();
}
private void SetMinSize() {
var width = Padding.Left + Padding.Right +
(dotSize * m_NumberOfDots) + (dotSpacing * (m_NumberOfDots - 1)) + 1;
var height = Padding.Top + Padding.Bottom + dotSize + 1;
MinimumSize = new Size((int)width, (int)height);
Size = MinimumSize;
}
}
This is how it works:
On demand, this is the PasteBin of the custom ComboBox Control used to select a Color.
A single call to the Paint method/event should draw the control as it is supposed to look at that instant. If you wish to add animation, you should make the control redraw itself repeatedly and use some internal state to keep track of the animation.

After adding label to picturebox contols, events don't fire

I'm having a weird behavior with a class I created inheriting from PictureBox.
It's a class made to behave like a button (basically what I care about is the mouse enter, mouse leave, mouse down, mouse up events).
Everything works perfectly until I add the TextLabel (code below) to the control of my customized class. It shows the label, centered and everything as I wanted, but the events I mentioned before (and every other event in that matter) becomes disabled, for some reason it won't fire them.
I would like to know what is the reason for that behavior and if there is any fix for it.
public class RoundedButton : PictureBox
{
private readonly Image r_BasicImage = RoundedButtonCheck2.Properties.Resources.basicRoundedButtonIcon as Image;
private readonly Image r_HoverImage = RoundedButtonCheck2.Properties.Resources.hoverRoundedButtonIcon as Image;
private readonly Image r_ClickImage = RoundedButtonCheck2.Properties.Resources.clickRoundedButtonIcon as Image;
private string m_Text;
private Font m_Font;
public Color m_TextColor;
private Label LabelText = new Label();
public RoundedButton()
{
this.Width = 130;
this.Height = 40;
this.Image = r_BasicImage;
this.BackColor = Color.Transparent;
this.SizeMode = PictureBoxSizeMode.StretchImage;
this.MouseDown += RoundedButton_MouseDown;
this.MouseUp += RoundedButton_MouseUp;
this.MouseEnter += RoundedButton_MouseEnter;
this.MouseLeave += RoundedButton_MouseLeave;
LabelText.Font = ButtonFont;
ButtonTextColor = Color.Black;
//PROBLEMATIC CODE HERE:
***********this.Controls.Add(LabelText);***************
}
public Color ButtonTextColor
{
get
{
return m_TextColor;
}
set
{
m_TextColor = value;
LabelText.ForeColor = m_TextColor;
}
}
public Font ButtonFont
{
get
{
if (m_Font == null)
{
m_Font = new Font("Calibri Light", 12);
}
return m_Font;
}
set
{
m_Font = value;
LabelText.Font = ButtonFont;
adjustLabel();
}
}
public string ButtonText
{
get
{
return m_Text;
}
set
{
m_Text = value;
LabelText.Text = m_Text;
adjustLabel();
}
}
private void adjustLabel()
{
const int MARGIN = 10;
LabelText.AutoSize = true;//needed for autosize calculation of the label;
Size newSize = new Size(LabelText.Size.Width, LabelText.Size.Height); ;
LabelText.AutoSize = false;//after auto-calculated size of the label, set to false in order for centering label works well.
this.MinimumSize = newSize;
this.Size = new Size(newSize.Width + MARGIN, newSize.Height + MARGIN);
LabelText.TextAlign = ContentAlignment.MiddleCenter;
LabelText.Dock = DockStyle.Fill;
}
private void RoundedButton_MouseLeave(object sender, EventArgs e)
{
RoundedButton hoveredButton = sender as RoundedButton;
if (hoveredButton != null)
{
hoveredButton.Image = r_BasicImage;
hoveredButton.SizeMode = PictureBoxSizeMode.StretchImage;
}
}
private void RoundedButton_MouseEnter(object sender, EventArgs e)
{
RoundedButton hoveredButton = sender as RoundedButton;
if (hoveredButton != null)
{
hoveredButton.Image = r_HoverImage;
hoveredButton.SizeMode = PictureBoxSizeMode.StretchImage;
}
}
private void RoundedButton_MouseUp(object sender, MouseEventArgs e)
{
RoundedButton clickedButton = sender as RoundedButton;
if (clickedButton != null)
{
clickedButton.Image = r_BasicImage;
clickedButton.SizeMode = PictureBoxSizeMode.StretchImage;
}
}
private void RoundedButton_MouseDown(object sender, MouseEventArgs e)
{
RoundedButton clickedButton = sender as RoundedButton;
if (clickedButton != null)
{
clickedButton.Image = r_ClickImage;
clickedButton.SizeMode = PictureBoxSizeMode.StretchImage;
}
}
}

Drawn Line in PictureBox isn't moving (MouseEvents)

I'm using the answer from another StackOverflow thread here.
My situation is very similar, expect I'm drawing my Lines inside a PictureBox. I modified the above answer by adding a PictureBox to the constructor. I can see that the lines are drawn in my PictureBox, but it won't move.
Am I missing something really obvious here? I've tried a lot of different things, but nothing is working.
LineMover.cs
public class LineMover : Form
{
public LineMover(PictureBox pb)
{
this.DoubleBuffered = true;
pb.Paint += new PaintEventHandler(LineMover_Paint);
pb.MouseMove += new MouseEventHandler(LineMover_MouseMove);
pb.MouseDown += new MouseEventHandler(LineMover_MouseDown);
pb.MouseUp += new MouseEventHandler(LineMover_MouseUp);
this.Lines = new List<GraphLine>()
{
new GraphLine (10, 10, 100, 200),
new GraphLine (10, 150, 120, 40),
};
}
void LineMover_MouseUp(object sender, MouseEventArgs e)
{
if (Moving != null)
{
this.Capture = false;
Moving = null;
}
RefreshLineSelection(e.Location);
}
void LineMover_MouseDown(object sender, MouseEventArgs e)
{
RefreshLineSelection(e.Location);
if (this.SelectedLine != null && Moving == null)
{
this.Capture = true;
Moving = new MoveInfo
{
Line = this.SelectedLine,
StartLinePoint = SelectedLine.StartPoint,
EndLinePoint = SelectedLine.EndPoint,
StartMoveMousePoint = e.Location
};
}
RefreshLineSelection(e.Location);
}
void LineMover_Paint(object sender, PaintEventArgs e)
{
e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
foreach (var line in Lines)
{
var color = line == SelectedLine ? Color.Red : Color.Black;
var pen = new Pen(color, 2);
e.Graphics.DrawLine(pen, line.StartPoint, line.EndPoint);
}
}
void LineMover_MouseMove(object sender, MouseEventArgs e)
{
if (Moving != null)
{
Moving.Line.StartPoint = new PointF(Moving.StartLinePoint.X + e.X - Moving.StartMoveMousePoint.X, Moving.StartLinePoint.Y + e.Y - Moving.StartMoveMousePoint.Y);
Moving.Line.EndPoint = new PointF(Moving.EndLinePoint.X + e.X - Moving.StartMoveMousePoint.X, Moving.EndLinePoint.Y + e.Y - Moving.StartMoveMousePoint.Y);
}
RefreshLineSelection(e.Location);
}
private void RefreshLineSelection(Point point)
{
var selectedLine = FindLineByPoint(Lines, point);
if (selectedLine != this.SelectedLine)
{
this.SelectedLine = selectedLine;
this.Invalidate();
}
if (Moving != null)
this.Invalidate();
this.Cursor =
Moving != null ? Cursors.Hand :
SelectedLine != null ? Cursors.SizeAll :
Cursors.Default;
}
public List<GraphLine> Lines = new List<GraphLine>();
GraphLine SelectedLine = null;
MoveInfo Moving = null;
static GraphLine FindLineByPoint(List<GraphLine> lines, Point p)
{
var size = 10;
var buffer = new Bitmap(size * 2, size * 2);
foreach (var line in lines)
{
//draw each line on small region around current point p and check pixel in point p
using (var g = Graphics.FromImage(buffer))
{
g.Clear(Color.Black);
g.DrawLine(new Pen(Color.Green, 3), line.StartPoint.X - p.X + size, line.StartPoint.Y - p.Y + size, line.EndPoint.X - p.X + size, line.EndPoint.Y - p.Y + size);
}
if (buffer.GetPixel(size, size).ToArgb() != Color.Black.ToArgb())
return line;
}
return null;
}
//public static void Main()
//{
// Application.Run(new LineMover());
//}
}
public class MoveInfo
{
public GraphLine Line;
public PointF StartLinePoint;
public PointF EndLinePoint;
public Point StartMoveMousePoint;
}
public class GraphLine
{
public GraphLine(float x1, float y1, float x2, float y2)
{
this.StartPoint = new PointF(x1, y1);
this.EndPoint = new PointF(x2, y2);
}
public PointF StartPoint;
public PointF EndPoint;
}
And my Form1.cs
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
LineMover myLineMover = new LineMover(pictureBox1);
}
}

C# createGraphics, pen only working initialized within panel paint event

I need to draw random shapes with a random color and random position.
Now i figured out how to use a paint event but it only seems to work when I initialize then Pen within the paint event
private void ShapesPanel_Paint(object sender, PaintEventArgs e)
{
_graphics = ShapesPanel.CreateGraphics();
_pen = new Pen(Color.Black, 1);
}
This works, but I want the random colors and every shape has it own generated random color.
I have this foreach which works:
foreach (var shape in _shapes)
{
shape.DrawAble(_graphics);
}
Now I want to have the drawing to have to shapes color:
foreach (var shape in _shapes)
{
_pen = new Pen(shape.Color, 3);
shape.DrawAble(_graphics);
}
And this will give no drawings at all.
Someone familiar?
Thanks
form class
public partial class ShapesForm : Form
{
private Shape _shape;
private Graphics _graphics;
private Pen _pen;
private Random _random;
private int _red, _blue, _green;
private Color _color;
private int _x, _y;
private Point _point;
private int _randomShape;
private double _size;
private double _radius;
private List<Shape> _shapes;
public ShapesForm()
{
InitializeComponent();
_shapes = new List<Shape>();
_random = new Random();
}
private void ShapesPanel_Paint(object sender, PaintEventArgs e)
{
_graphics = ShapesPanel.CreateGraphics();
_pen = new Pen(Color.Black, 1);
}
private void AddShapeButton_Click(object sender, EventArgs e)
{
_red = _random.Next(0, 255);
_green = _random.Next(0, 255);
_blue = _random.Next(0, 255);
_color = Color.FromArgb(1, _red, _green, _blue);
_x = _random.Next(0, 100);
_y = _random.Next(0, 100);
_point = new Point(_x, _y);
_radius = _random.Next(0, 20);
_size = _random.Next(0, 20);
_randomShape = _random.Next(0, 2);
switch(_randomShape)
{
case 0:
_shape = new Circle(_point, _color, _radius);
_shapes.Add(_shape);
UpdateShapeListBox();
DrawShapes();
break;
case 1:
_shape = new Square(_point, _color, _size);
_shapes.Add(_shape);
UpdateShapeListBox();
DrawShapes();
break;
}
}
public void UpdateShapeListBox()
{
ShapesListBox.Items.Clear();
foreach (var shape in _shapes)
{
ShapesListBox.Items.Add(shape.ToString());
}
}
public void DrawShapes()
{
ShapesPanel.Refresh();
foreach (var shape in _shapes)
{
_pen = new Pen(shape.Color, 3);
shape.DrawAble(_graphics);
}
}
}
This is the sort of logic you want to implement:
private void ShapesPanel_Paint(object sender, PaintEventArgs e)
{
foreach (var shape in _shapes)
{
shape.DrawAble(e.Graphics);
}
}
// in the Shape class
public void DrawAble(Graphics g)
{
var pen = new Pen(this.Color, 3);
g.DrawRect( ... ); // or whatever
}
You should use e.Graphics from the paint handler, and only while the paint handler is running.
The paint handler will normally be called whenever necessary. If you want to repaint because your shapes have changed, call ShapesPanel.Invalidate().
You need to do all of your drawing in the Paint handler only, using e.Graphics.
If you want to draw something, call Invalidate() to raise a Paint event, then make sure your Paint handler will draw everything you want.

How to move dynamically added graphics line in winforms

I am displaying two lines dynamically in form and want to move line vertically.
I tried to move using mousemove event but it's moving both lines together.
So is it possible to move dynamically added line individually on the form?
Here is my code
Graphics g;
int Y = 250;
public Form2()
{
InitializeComponent();
}
private void Form2_Load(object sender, EventArgs e)
{
}
private void Form2_Paint(object sender, PaintEventArgs e)
{
g = e.Graphics;
g.DrawLine(Pens.Red, new Point(0, Y), new Point(1500, Y));
g.DrawLine(Pens.LimeGreen, new Point(0, Y+50), new Point(1500, Y+50));
}
public void Form2_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
Y = e.Location.Y;
Refresh();
}
}
Any input will be greatly appreciated.
You may use following code to solve your purpose.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace TestWin
{
public partial class Form3 : Form
{
public Form3()
{
InitializeComponent();
this.DoubleBuffered = true;
this.Paint += new PaintEventHandler(LineMover_Paint);
this.MouseMove += new MouseEventHandler(LineMover_MouseMove);
this.MouseDown += new MouseEventHandler(LineMover_MouseDown);
this.MouseUp += new MouseEventHandler(LineMover_MouseUp);
this.Lines = new List<GraphLine>()
{
new GraphLine (10, 10, 100, 200),
new GraphLine (10, 150, 120, 40),
};
}
void LineMover_MouseUp(object sender, MouseEventArgs e)
{
if (Moving != null)
{
this.Capture = false;
Moving = null;
}
RefreshLineSelection(e.Location);
}
void LineMover_MouseDown(object sender, MouseEventArgs e)
{
RefreshLineSelection(e.Location);
if (this.SelectedLine != null && Moving == null)
{
this.Capture = true;
Moving = new MoveInfo
{
Line = this.SelectedLine,
StartLinePoint = SelectedLine.StartPoint,
EndLinePoint = SelectedLine.EndPoint,
StartMoveMousePoint = e.Location
};
}
RefreshLineSelection(e.Location);
}
void LineMover_Paint(object sender, PaintEventArgs e)
{
e.Graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.High;
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
foreach (var line in Lines)
{
var color = line == SelectedLine ? Color.Red : Color.Black;
var pen = new Pen(color, 2);
e.Graphics.DrawLine(pen, line.StartPoint, line.EndPoint);
}
}
void LineMover_MouseMove(object sender, MouseEventArgs e)
{
if (Moving != null)
{
Moving.Line.StartPoint = new PointF(Moving.StartLinePoint.X + e.X - Moving.StartMoveMousePoint.X, Moving.StartLinePoint.Y + e.Y - Moving.StartMoveMousePoint.Y);
Moving.Line.EndPoint = new PointF(Moving.EndLinePoint.X + e.X - Moving.StartMoveMousePoint.X, Moving.EndLinePoint.Y + e.Y - Moving.StartMoveMousePoint.Y);
}
RefreshLineSelection(e.Location);
}
private void RefreshLineSelection(Point point)
{
var selectedLine = FindLineByPoint(Lines, point);
if (selectedLine != this.SelectedLine)
{
this.SelectedLine = selectedLine;
this.Invalidate();
}
if (Moving != null)
this.Invalidate();
this.Cursor =
Moving != null ? Cursors.Hand :
SelectedLine != null ? Cursors.SizeAll :
Cursors.Default;
}
public List<GraphLine> Lines = new List<GraphLine>();
GraphLine SelectedLine = null;
MoveInfo Moving = null;
static GraphLine FindLineByPoint(List<GraphLine> lines, Point p)
{
var size = 10;
var buffer = new Bitmap(size * 2, size * 2);
foreach (var line in lines)
{
//draw each line on small region around current point p and check pixel in point p
using (var g = Graphics.FromImage(buffer))
{
g.Clear(Color.Black);
g.DrawLine(new Pen(Color.Green, 3), line.StartPoint.X - p.X + size, line.StartPoint.Y - p.Y + size, line.EndPoint.X - p.X + size, line.EndPoint.Y - p.Y + size);
}
if (buffer.GetPixel(size, size).ToArgb() != Color.Black.ToArgb())
return line;
}
return null;
}
public class MoveInfo
{
public GraphLine Line;
public PointF StartLinePoint;
public PointF EndLinePoint;
public Point StartMoveMousePoint;
}
public class GraphLine
{
public GraphLine(float x1, float y1, float x2, float y2)
{
this.StartPoint = new PointF(x1, y1);
this.EndPoint = new PointF(x2, y2);
}
public PointF StartPoint;
public PointF EndPoint;
}
}
}
Both of your lines take there position from the same variable Y (declared at the top of the source code) to do what you want you will need to firstly create a second variable to keep track of where the lines are individually.
Your second problem is going to be deciding which line you want to move perhaps alternating which line is moved based on which mouse button is down during Form2_MouseMove() .

Categories

Resources