Making a Custom Drawing Method available throughout entire application - c#

I've added a custom border to the labels in one of my application forms as follows:
private void ColorMe(PaintEventArgs e)
{
Color myColor = Color.FromArgb(104, 195, 198);
Pen myPen = new Pen(myColor, 1);
e.Graphics.DrawRectangle(myPen,
e.ClipRectangle.Left,
e.ClipRectangle.Top,
e.ClipRectangle.Width - 1,
e.ClipRectangle.Height - 1);
base.OnPaint(e);
}
private void lblDisbs_Paint(object sender, PaintEventArgs e)
{
ColorMe(e);
}
Which works nicely. All I have to do it put ColorMe(e) in the Paint Event of each label.
However I want to use this method on all forms throughout the whole application. I tried putting my ColorMe() method in a class to call it from multiple forms that way, but it does not work, saying that 'base has no OnPaint event'.
How should I make this method available throughout the whole application?

Create class LabelWithBorder derive it from Label, override the OnPaint method.
public class LabelWithBorder : Label {
protected override void OnPaint(PaintEventArgs e) {
ColorMe(e);
}
}
Replace all WinForms labels in your app with your label.

You probably shouldn't use the ClipRectangle for drawing in this case, since it would produce malformed rectangles on your control.
If not using Karel Frajtak's solution, which is cleaner, you can try making a static class and then you can call it from any form:
internal static class LabelBorder {
public static void ColorMe(Rectangle r, PaintEventArgs e) {
r.Inflate(-1, -1);
using (Pen p = new Pen(Color.FromArgb(104, 195, 198), 1))
e.Graphics.DrawRectangle(p, r);
}
}
Example:
public Form1() {
InitializeComponent();
label1.Paint += label_Painter;
label2.Paint += label_Painter;
}
void label_Painter(object sender, PaintEventArgs e) {
LabelBorder.ColorMe(((Label)sender).ClientRectangle, e);
}

Related

Call event using button c#

I'm trying to call PaintEventArgs when i press a button, my problem is i don't know how to call one without modify button event
private void button_Click(object sender, EventArgs e /*<= PaintEventArgs*/)
{
func(e);
base.OnPaint(e);
}
You can call Invalidate()
It will cause the control to be redrawn
private void button_Click(object sender, EventArgs e /*<= PaintEventArgs*/)
{
//func(e); It will be called in the OnPaint method
Invalidate();
}
To use PaintEventArgs you need to override OnPaint method
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
func(e);
}
To implement the OnPaint you need to create your own class that inherits the Button
public class BetterButton : Button{
public BetterButton()
{}
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
func(e);
}
}
You ALWAYS do the drawing on the Paint event of the control you want to draw on. That might mean in the overridden OnPaint method or else in the Paint event handler. In order to cause thew Paint event to be raised, you call the Invalidate method of the appropriate control. You can call it with no arguments but, ideally, you would calculate the smallest area that has or might have changed and pass that as a Rectangle or the like.
EDIT:
Here is an example that will draw the last recorded time in the top-left of the form and update that time on each click of a Button:
private DateTime time;
private void button1_Click(object sender, EventArgs e)
{
time = DateTime.Now;
Invalidate();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawString(time.ToString(), Font, Brushes.Black, 10, 10);
}
As you can see, all you need to do on the Click event is update the data and invalidate the form. All the drawing, including the PaintEventArgs, is taken care of in the Paint event handler.

Create your own mouse event in a class

I haven't been learning C# for very long and was wondering if it is possible to create a mouse event from another class?
The intention is that when a mousedown happens, something is drawn on a panel. I would like to do all this from a different class is this possible?
This is what I've already tried:
class Circle: Form1
{
Graphics g;
Pen pen = new Pen(Color.Black);
// Making a event ?
public event EventHandler<MouseEventArgs> MouseDown;
protected void OnClick(MouseEventArgs e)
{
EventHandler<MouseEventArgs> handler = MouseDown;
if (handler != null)
{
handler(this, e);
}
}
public Circle()
{
g = this.panel1.CreateGraphics();
}
public void Mouse_Down(object sender, MouseEventArgs e)
{
// Draw something
g.DrawLine(pen, 0, 0, 50, 50);
}
}
}
So instead of calling the panel1_MouseDown event in this class I want to call it from the circle class:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
// So instead of calling the panel1_MouseDown event in this class I want to call it from the circle class
}
}
}
Yes, you can "do all this from a different class"
Referring to the the example below:
In Form1 constructor create initializes the instance of the Circle class and passes to its constructor an instance of Form1.
Make sure that the control you want to draw on is public (panel1 in the example).
Add MouseDown and Paint events to panel1.
Always use the Paint event to draw graphics - it's the best practice.
More info on Control.Paint Event please: read here
Form1:
public partial class Form1 : Form
{
Circle circle;
public Form1()
{
InitializeComponent();
// Start the measuring time for reauthentication
circle = new Circle(this);
}
}
Circle Class:
class Circle
{
private Form1 Instance;
public Circle(Form1 instance)
{
this.Instance = instance;
// Make sure the access modifier of panel1 is Public and add mousedown event
Instance.panel1.MouseDown += panel1_MouseDown;
// Add a paint event - this is the best practice to draw graphics
Instance.panel1.Paint += panel1_Paint;
}
private int X;
private int Y;
private void panel1_MouseDown(object sender, MouseEventArgs e)
{
this.X = e.X;
this.Y = e.Y;
// Redraw the panel when mouse is down
Instance.panel1.Refresh();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
if (this.X > 0 && this.Y > 0)
{
Graphics g = e.Graphics;
Pen p = new Pen(Color.Red);
g.DrawEllipse(p, X, Y, 50, 50);
}
}
}
Output:

C# WInforms Change border style on button click event

I'm trying to change the border color of a text box (txtUser) on button click event (something like a form validation, if the input is empty then call the method that colors the border red). I did some googling and found this:
void myControl1_Paint(object sender, PaintEventArgs e)
{
ControlPaint.DrawBorder(e.Graphics, this.txtUser.ClientRectangle, Color.Black, ButtonBorderStyle.Solid);
}
But I'm having trouble understaing where or how should I call this method, or methods with (object sender, PaintEventArgs e) as params. Any explanation is appreciated.
You need to inherit from TextBox and then override the OnPaint method. Something like this should work:
public class ValidateEdit : TextBox
{
bool _InError;
public ValidateEdit()
{
SetStyle(ControlStyles.UserPaint, true);
}
public bool InError {
get {
return _InError;
}
set
{
_InError = value;
Refresh();
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
if (InError)
ControlPaint.DrawBorder(e.Graphics, this.DisplayRectangle, Color.Red, ButtonBorderStyle.Solid);
}
}

C# paint graphics from class in picturebox with mouse click

I want to paint a graphics object from a method (paint) I created in a separate class (Paintball). I want it to paint in a picturebox only when I left-click with my mouse and I want the point where I shoot to be stored in a List. When I try the code below, it doesn't shoot. Below is the class Paintball.
{
private List<Point> myClick;
public Paintball()
{
myClick = new List<Point>();
}
public void add(Point location)
{
myClick.Add(location);
}
public void paint(Graphics g, Point point)
{
g.FillEllipse(Brushes.Blue, point.X, point.Y, 20, 20);
}
}
}
This is form1 below.
namespace AmazingPaintball
{
public partial class Form1 : Form
{
Random positionX = new Random();
Random positionY = new Random();
Target einstein;
int count;
List<Point> ballList = new List<Point>();
Paintball gun;
public Form1()
{
InitializeComponent();
Point point = new Point(positionX.Next(0, 638), positionY.Next(0, 404));
einstein = new Target(point);
ptrEinstein.Location = point;
gun = new Paintball();
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
ptrEinstein.Location = einstein.Move(e.KeyData);
pictureBox1.Update();
pictureBox1.Refresh();
}
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
count++;
gun.add(e.Location);
pictureBox1.Refresh();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
foreach (var Paintball in ballList)
{
gun.paint(e.Graphics, this.PointToClient(Cursor.Position));
pictureBox1.Refresh();
}
}
private void Form1_Load(object sender, EventArgs e)
{
timer1.Start();
}
private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
pictureBox1.Refresh();
}
}
}
Please let me know if you know what has to be edited/created. Thank You
Your original code has many mistakes. Let's try to simplify what you are doing and tackle simply storing a list of points and drawing them to the picturebox.
public partial class Form1 : Form
{
List<Point> ballList = new List<Point>();
public Form1()
{
InitializeComponent();
}
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
ballList.Add(e.Location);
pictureBox1.Refresh();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
foreach (Point pBall in ballList)
{
e.Graphics.FillEllipse(Brushes.Blue, pBall.X, pBall.Y, 20, 20);
}
}
}
Here we have a list, we add the click points to it in the click handler and paint them in the paint handler. Once you get comfortable with this, perhaps move to the next task in your program and ask a new question if you get stuck with the next feature.
Ok, I've got a bit of time, so let's look at your paintball class. I've renamed it Paintballs since it contains many of them and this name is more appropriate. If you want to keep the list of points private that's ok. You are trying to implement a Paint method in the class, but it takes a Point as argument and does not operate on any of the class's instance state - this probably isn't what you want. Consider now :
public class Paintballs
{
private List<Point> myClick;
public Paintballs()
{
myClick = new List<Point>();
}
public void Add(Point location)
{
myClick.Add(location);
}
public void Paint(Graphics g)
{
foreach (Point p in myClick)
{
g.FillEllipse(Brushes.Blue, p.X, p.Y, 20, 20);
}
}
}
Here we have a public Paint method that will draw all of the paintballs in the class to any graphics instance you pass to it. Now your form code would look like :
public partial class Form1 : Form
{
Paintballs pBalls = new Paintballs();
public Form1()
{
InitializeComponent();
}
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
pBalls.Add(e.Location);
pictureBox1.Refresh();
}
private void pictureBox1_Paint(object sender, PaintEventArgs e)
{
pBalls.Paint(e.Graphics);
}
}
So we've simplified the form code by pushing the painting method into the paintballs class itself. This makes the class responsible for knowing what the paintballs look like, how many there are, where they are, and how to draw them to a Graphics object. This is step 1 in encapsulating responsibility.
You're drawing from a list of points stored in that ballList variable. However, you've never added any points to that list.
Make the myClick list in Paintball public and, in the pictureBox1_Paint method, iterate through that list instead of ballList.

Draw on a form by a separate thread

I'm trying to build a multithreaded game where I have a separate thread for painting on the form which is not the main thread. this brings us to thread-safe technics which I've read many articls about, but I'm not really sure I got it correctly.
my problem is that I have a structure where every data object is painting it self on the form so I didn't figure out how to implement it.
this is a snippet of my working mono-thread code:
public partial class Form1 : Form
{
GameEngine Engine;
public Form1()
{
InitializeComponent();
Engine = new GameEngine();
}
protected override void OnPaint(PaintEventArgs e)
{
Engine.Draw(e.Graphics);
}
}
class GameEngine
{
Maze Map;
List<Player> Players;
public void Draw(Graphics graphics)
{
Map.Draw(graphics);
foreach (var p in Players)
{
p.Draw(graphics);
}
}
}
so please can anyone give me a hint or a link to good article helping me to learn how to separate the drawing on an another thread?.
[Edit]
I managed to implement what I intended to do
and this is how I coded it
protected override void OnPaint(PaintEventArgs e)
{
formGraphics = e.Graphics;
DisplayThread = new Thread(new ThreadStart(Draw));
DisplayThread.Start();
}
private void Draw()
{
if (this.InvokeRequired)
{
this.Invoke(new DrawDelegate(this.Draw));
}
else
{
Engine.Draw(formGraphics);
}
}
but I got an ArgumentException : Parameter is not valid
would you please point to the error in that code
I think you will need to draw to a Bitmap, then in the OnPaint Method, draw that bitmap to the window. I will demonstrate in a moment.
As Hans pointed out, in the OnPaint method you are setting
formGraphics = e.Graphics;
but at the end of the method e.Graphics is disposed, so you can't use it anymore, if your code got to
Engine.Draw(formGraphics);
you would get an exception.
So basically you need to have a global
Bitmap buffer = new Bitmap(this.Width, this.Height)
in your asynced thread you would invoke your drawing to that Bitmap you can use
Graphics g=Graphics.FromBitmap(buffer);//
To get a graphics object, but remember you have to
g.Dispose()
it or wrap it in a
using (Graphics g=Graphics.FromBitmap(buffer))
{
//do something here
}
I am going to play with it for a few moments and see if I can get you a working sample
EDIT Here's your working sample.
I started a new form and tossed a button on it. I changed the mainform backgroundimagelayout to none.
I think you need to be using .net 4.0 or better, if not using this, let me know I can change it to match your version... I think.
//you need this line to use the tasks
using System.Threading.Tasks;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
public void Draw()
{
Bitmap buffer;
buffer = new Bitmap(this.Width, this.Height);
//start an async task
Task.Factory.StartNew( () =>
{
using (Graphics g =Graphics.FromImage(buffer))
{
g.DrawRectangle(Pens.Red, 0, 0, 200, 400);
//do your drawing routines here
}
//invoke an action against the main thread to draw the buffer to the background image of the main form.
this.Invoke( new Action(() =>
{
this.BackgroundImage = buffer;
}));
});
}
private void button1_Click(object sender, EventArgs e)
{
//clicking this button starts the async draw method
Draw();
}
}
}

Categories

Resources