The problem isn't that I don't know how to make a border-less form re-sizable, or to how to draw a border. The problem is what happens when you re-size the form with that custom border.
Here is a screenshot, because I don't know how to explain it:
Here is how I created the border (currently):
private void Form1_Paint(object sender, PaintEventArgs e)
{
int width = 1;
Rectangle rec = this.ClientRectangle;
ButtonBorderStyle bbs = ButtonBorderStyle.Solid;
Color clr = Color.Gray;
ControlPaint.DrawBorder(e.Graphics, rec, clr, width, bbs, clr, width, bbs, clr, width, bbs, clr, width, bbs);
}
As for re-sizing a border-less form; I created a repository for the project.
Resize Custom Border - Bitbucket
I don't have any idea as to why this happens, so I wouldn't know where to begin. I just need to draw a border without it doing this. I have tried other ways of drawing one, but the results were the same.
Hopefully this and the repository becomes useful for anyone trying to do the same.
Thank you for taking your time to read if you did.
Try to use Graphics.DrawRectangle instead of DrawBorder
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Single fWidth = 5.0f;
Rectangle r = new Rectangle(0,0,this.ClientRectangle.Width-1,this.ClientRectangle.Height-1);
e.Graphics.DrawRectangle(new Pen(Color.Gray, fWidth), r);
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
this.Invalidate();
}
Use Graphic library :
Step 1: Override the OnPaint handler for your main form
Step 2: Define a rectangle that covers your current form
Step 3: Draw the defined rectangle
protected override void OnPaint(PaintEventArgs e)
{
Rectangle r = new Rectangle(0,0,this.ClientRectangle.Width-1,this.ClientRectangle.Height-1);
e.Graphics.DrawRectangle(new Pen(Color.Gray, 1.0f), r);
}
You may also implement this using a condition statement like:
this.form.Resize += // some handler1
//in hadler1
{
this.form.Paint += // Your new paint handler2
}
//in handler2
{
Rectangle r = new Rectangle(0,0,this.ClientRectangle.Width-1,this.ClientRectangle.Height-1);
e.Graphics.DrawRectangle(new Pen(Color.Gray, 1.0f), r);
}
Related
I'm using an eye tracker to display eye movements on a form. The movements have been flickering a lot so I found out I can use BufferedGraphics which all works fine except when the eye movement starts it turns the form from the original colour to black. This is the code. Hopefully someone can help!
private void button2_Click(object sender, EventArgs e)
{
var host = new Host();
var gazeStream = host.Streams.CreateGazePointDataStream();
gazeStream.GazePoint((x, y, ts)
=> drawCircle(new PointF((float)x, (float)y)));
}
delegate void SetCallback(PointF point);
private void drawCircle(PointF point)
{
float x = point.X;
float y = point.Y;
if (this.InvokeRequired)
{
SetCallback d = new SetCallback(drawCircle);
this.Invoke(d, new object[] { point });
}
else
{
SolidBrush semiTransBrush = new SolidBrush(Color.Coral);
Pen pen = new Pen(Color.Aquamarine, 2);
BufferedGraphicsContext currentContext;
BufferedGraphics myBuffer;
// Gets a reference to the current BufferedGraphicsContext
currentContext = BufferedGraphicsManager.Current;
// Creates a BufferedGraphics instance associated with Form1, and with
// dimensions the same size as the drawing surface of Form1.
myBuffer = currentContext.Allocate(this.CreateGraphics(),this.DisplayRectangle);
myBuffer.Graphics.DrawEllipse(pen, x, y, 100, 100);
myBuffer.Graphics.FillEllipse(semiTransBrush, x, y, 100, 100);
// Renders the contents of the buffer to the specified drawing surface.
myBuffer.Render(this.CreateGraphics());
myBuffer.Dispose();
}
You can see in the image that the circle appears behind the controls which seems like the form is gone?
When you allocate the buffer, it creates a compatible rendering surface with the graphics you provided. But it will not copy it or anything so if you just paint a single circle, the remaining parts remain black.
BufferedGraphics really can help you to avoid flickering in special cases (eg. when system double buffering must be disabled for some reason), but here this is an overkill.
So the key is just enabling double buffering and do every paint in the Paint event (or OnPaint method). In your code you do immediate paint, which always flickers. Instead, you should just invalidate the form and let the system do a regular repaint session, which can use double buffering if you wish.
Into the constructor:
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
Then modify the click event:
private PointF lastGazePoint;
private void button2_Click(object sender, EventArgs e)
{
var host = new Host();
var gazeStream = host.Streams.CreateGazePointDataStream();
gazeStream.GazePoint((x, y, ts) =>
{
lastGazePoint = new PointF((float)x, (float)y);
Invalidate();
});
}
The painting itself:
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
DrawCircle(e.Graphics, lastGazePoint.X, lastGazePoint.Y);
}
And finally, modify DrawCircle to use the Graphics from PaintEventArgs:
private void DrawCircle(Graphics g, float x, float y)
{
using (Brush semiTransBrush = new SolidBrush(Color.Coral))
{
using (Pen pen = new Pen(Color.Aquamarine, 2))
{
g.DrawEllipse(pen, x, y, 100, 100);
g.FillEllipse(semiTransBrush, x, y, 100, 100);
}
}
}
I have a panel containing multiple picturebox.
I want to give users the option of selecting any part of any picturebox.
The user will select it by mouse.
I want to draw a semi-transparent rectangle on the picturebox while the mouse move as per the selection.
The code is working fine, but the rectangle is flickering. I want to stop the flickering.
I tried double buffer using how to stop flickering C# winforms
Also, added Invalide using How to force graphic to be redrawn with the invalidate method
But not working. Please help.
My Code:
private Brush selectionBrush = new SolidBrush(Color.FromArgb(70, 76, 255, 0));
private void Picture_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button != MouseButtons.Left) return;
PictureBox pb = (PictureBox)(sender);
Point tempEndPoint = e.Location;
Rect.Location = new Point(
Math.Min(RecStartpoint.X, tempEndPoint.X),
Math.Min(RecStartpoint.Y, tempEndPoint.Y));
Rect.Size = new Size(
Math.Abs(RecStartpoint.X - tempEndPoint.X),
Math.Abs(RecStartpoint.Y - tempEndPoint.Y));
pb.CreateGraphics().FillRectangle(selectionBrush, Rect);
pb.Invalidate(Rect);
}
This does not address the PictureBox, but maybe helps.
First I replaced the PictureBox with Panel which can also be used to draw pictures. Next I activated double buffering:
somewhere in the namespace add this class:
class DoubleBufferedPanel : Panel
{
public DoubleBufferedPanel() : base()
{
DoubleBuffered = true;
}
}
and then replace in
Form1.Designer.cs
all "System.Windows.Forms.Panel" with "DoubleBufferedPanel".
this works fine in many of my graphical applications.
You indicate the Double Buffering solution doesn't work for you, did you find out why? Because the following custom control draws a sizable rectangle on the picturebox without the flicker:
public class TryDoubleBufferAgain : PictureBox
{
public TryDoubleBufferAgain()
{
this.DoubleBuffered = true;
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
this.UpdateStyles();
}
protected override void OnMouseMove(MouseEventArgs e)
{
this.Refresh();
base.OnMouseMove(e);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
// Edit below to actually draw a usefull rectangle
e.Graphics.DrawRectangle(System.Drawing.Pens.Red, new System.Drawing.Rectangle(0, 0, Cursor.Position.X, Cursor.Position.Y));
}
}
I am trying to draw circles using Bitmap.
Each time I click the mouse, the circle I previously drew is moved to the new position.
What I want to happen is: Each time I click the mouse, a new circle is created/drawn at the position I clicked and all previously drawn circles remain without moving.
I am working with the following code:
using System;
using System.Drawing;
using System.Windows.Forms;
namespace multirectangle
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
}
Bitmap background;
Graphics scG;
Rectangle rectangleObj;
private Point clickCurrent = Point.Empty;
private Point clickPrev = Point.Empty;
private void Form1_Load(object sender, EventArgs e)
{
background = new Bitmap(Width, Height);
rectangleObj = new Rectangle(10, 10, 30, 30);
scG = Graphics.FromImage(background);
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
clickCurrent = PointToClient(Cursor.Position);
clickPrev = clickCurrent;
if (clickPrev == Point.Empty) return;
rectangleObj.X = clickPrev.X - rectangleObj.Height / 2;// +radius;
rectangleObj.Y = clickPrev.Y - rectangleObj.Width / 2;
Refresh();
}
protected override void OnPaint(PaintEventArgs pe)
{
pe.Graphics.DrawImage(Draw(), 0, 0);
}
public Bitmap Draw()
{
Graphics scG = Graphics.FromImage(background);
Pen myPen = new Pen(System.Drawing.Color.Red, 3);
scG.Clear(SystemColors.Control);
scG.DrawEllipse(myPen, rectangleObj);
return background;
}
}
}
Your English was a little confusing. If I'm understanding your problem correctly, right now the only thing that's being drawn is the new circle where the click was, and you want all the old ones to persist as well? In which case, there are two options:
Don't clear the bitmap before you draw. scG.Clear(SystemColors.Control); will clear the bitmap you just drew. If you remove that line and don't clear the bitmap, then the next time you click, it will then draw the new ellipse right on top of the last bitmap.
If you want a fresh drawing/bitmap everytime, you would need a list of your rectangleObj . Each time you click, you add that point to your rectangleObj collection. Then in your draw method, you would iterate through the collection and draw all of them.
I notice a few things. First, in Form1_MouseDown(), you have this:
clickCurrent = PointToClient(Cursor.Position);
clickPrev = clickCurrent;
You are overwriting the old position (clickPrev) before you even save it. If you want to keep both positions, you should put them in a simple structure, like a List. When you get a new point, just Add() it to the list. Then, in your Draw() routine, loop over all the elements in the list and draw them all.
If you just want two positions--and only two--just swap your statements like this:
clickPrev = clickCurrent;
clickCurrent = PointToClient(Cursor.Position);
And you'll have to allocate another rectangle object for the drawing, although it would make more sense to take care of this in the Draw() routine.
Swap the position of the following statements
clickCurrent = PointToClient(Cursor.Position);
clickPrev = clickCurrent;
I think you are assigning the clickCurrent to clickPrevious after you initialize clickCurrent. It needs to be the other way.
Please try this
Rectangle rectangleObj;
Bitmap background;
Graphics scG;
Pen myPen;
private void Form1_Load(object sender, EventArgs e)
{
rectangleObj = new Rectangle(10, 10, 30, 30);
background = new Bitmap(Width, Height);
scG = Graphics.FromImage(background);
myPen = new Pen(Color.Red, 3);
BackgroundImage = background;
}
private void Form1_MouseDown(object sender, MouseEventArgs e)
{
var point = PointToClient(Cursor.Position);
rectangleObj.X = point.X - rectangleObj.Height / 2;
rectangleObj.Y = point.Y - rectangleObj.Width / 2;
scG.DrawEllipse(myPen, rectangleObj);
Refresh();
}
OnPaint and Draw methods removed. As well as clickCurrent and clickPrev fields.
When you change the form size (for example, maximize it), Bitmap and Graphics remain the same, so you get this effect. To avoid this, you need to add the event handler
private void Form1_SizeChanged(object sender, EventArgs e)
{
background = new Bitmap(Width, Height);
scG = Graphics.FromImage(background);
BackgroundImage = background;
}
Note that each time you resize the form, all previously drawn is erased. If this is undesirable, a different approach is needed for drawing. Let me know, I will give another example.
I have found this question (as a few others), but this is the one I have implemented so far:
Crosshair cursor with additional lines in C#
As it states, I can use a stock cursor "cross" directly in the IDE. This is a really good way to do things. The answer specified in the answer above draws a cross on the screen at the given width / height. Eg:
private Cursor crossCursor(Pen pen, Brush brush, int x, int y)
{
var pic = new Bitmap(x, y);
Graphics gr = Graphics.FromImage(pic);
var pathX = new GraphicsPath();
var pathY = new GraphicsPath();
pathX.AddLine(0, y / 2, x, y / 2);
pathY.AddLine(x / 2, 0, x / 2, y);
gr.DrawPath(pen, pathX);
gr.DrawPath(pen, pathY);
IntPtr ptr = pic.GetHicon();
var c = new Cursor(ptr);
return c;
}
My issue is that I want my cross hairs to extend to the Bounds of the viewing area. To provide context here, I have:
//Form
//TableLayoutPanel
//UserControl (fills the TableLayoutPanel visible area)
So how can I adjust my cursor so that the lines extend (much like in CAD pacakages)?
Thanks.
Update: I have tried calling the method from here:
protected override void OnLoad(System.EventArgs e)
{
Cursor = crossCursor(Pens.WhiteSmoke, Brushes.WhiteSmoke, Bounds.Width, Bounds.Height);
}
But it is not Ok because at this point in time Bounds is returning a dimension of 150 by 150 which is not the size of the TableLayoutPanel.
Update: I have adjuted it to use the Resize handler instead and it does improve things:
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
Cursor = crossCursor(Pens.WhiteSmoke, Brushes.WhiteSmoke, Bounds.Width, Bounds.Height);
}
The only problem now (and it kind of makes sense I suppose) is that the cursor will only take the full width and height of the view when it is central to the view. As soon as I move about in the view that cursor does not adjust. I always want a horizontal/vertical line through the mouse position (not just the initial cross).
See:
The crosshairs need extending (the thicker red lines). Either I need to constantly create the cursor as the mouse moves or construct the two lines another way. What to do?
I came across this:
https://social.msdn.microsoft.com/Forums/vstudio/en-US/7bdbad6d-1f65-461b-8f0c-6ef4f243fa6b/crosshair-cursor-using-c?forum=csharpgeneral
So, instead of changing the cursor object I now draw lines in the controls MouseMove handler:
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.White, 0, e.Y, this.Width, e.Y);
g.DrawLine(Pens.White, e.X, 0, e.X, this.Height);
int intDiameter = 20;//the diameter of this circle
g.DrawEllipse(Pens.White, e.X - intDiameter / 2, e.Y - intDiameter / 2, 20, 20);
//to draw the circle
lastX = e.X;
lastY = e.Y;
It works, but I get noticiable screen flicker doing it this way.
You don't need to create a cursor. You can create a double buffered control and draw cross over control.
using System;
using System.Drawing;
using System.Windows.Forms;
public class DrawingSurface : Control
{
Pen crossPen;
Pen rectanglePen;
Brush rectangleBrush;
public DrawingSurface()
{
this.DoubleBuffered = true;
this.ResizeRedraw = true;
crossPen = new Pen(Color.Red, 2);
rectangleBrush = new SolidBrush(Color.FromArgb(50, Color.Blue));
rectanglePen = new Pen(Color.Blue, 1);
}
bool mouseDown = false;
Point startPoint = Point.Empty;
Point endPoint = Point.Empty;
protected override void OnMouseDown(MouseEventArgs e)
{
startPoint = e.Location;
mouseDown = true;
base.OnMouseDown(e);
}
protected override void OnMouseUp(MouseEventArgs e)
{
mouseDown = false;
base.OnMouseUp(e);
}
protected override void OnMouseMove(MouseEventArgs e)
{
endPoint = e.Location;
this.Invalidate();
base.OnMouseMove(e);
}
protected override void OnPaint(PaintEventArgs e)
{
var g = e.Graphics;
if (this.ClientRectangle.Contains(endPoint))
DrawCross(e.Graphics, endPoint);
if (mouseDown)
DrawRectangle(e.Graphics, startPoint, endPoint);
}
void DrawCross(Graphics g, Point point)
{
g.DrawLine(crossPen, new Point(0, point.Y), new Point(Width, point.Y));
g.DrawLine(crossPen, new Point(point.X, 0), new Point(point.X, Height));
}
void DrawRectangle(Graphics g, Point point1, Point point2)
{
var rectangle = new Rectangle(
Math.Min(point1.X, point2.X), Math.Min(point1.Y, point2.Y),
Math.Abs(point1.X - point2.X), Math.Abs(point1.Y - point2.Y));
g.FillRectangle(rectangleBrush, rectangle);
g.DrawRectangle(rectanglePen, rectangle);
}
protected override void Dispose(bool disposing)
{
crossPen.Dispose();
rectanglePen.Dispose();
rectangleBrush.Dispose();
base.Dispose(disposing);
}
}
I'm trying to make a basic soccer game in C#, and ive almost completed the field except for the various arcs and circles that are fairly important in the game, especially to set the bounds the computer's players cannot pass while their teammate/opponent is lining up for the kick.
So, all the methods I've tried havent worked because apparently I'm using fields like types, but I'm copying the code exactly. But I don't think its very important to show the buggy code, for a start I deleted it and I'd like the circles to be there permanently, not from when debugging starts.
So this is what I need: panels with round borders that stay round, and a way to put it in my code, which I'll post if necessary. Visual Studio C# Express 2010.
All help appreciated, thanks
One easy way to draw a circle on a panel is to inherit from Panel and override the OnPaint method. In this method you would call DrawEllipse on the Graphics object gotten from the event args. On point of interest is that the size is set to Width-1 and Height-1. This stops the right and bottom of the circle from dissapearing out of the Panel control.
One enhancement I have put in this code is to constrain the width & height in the OnResize method, this ensures your panel is always a circle, in oppose to an Ellipse (which can have different width and height). Simply drag this control onto a windows form and have a play in the designer.
public class CirclePanel : Panel
{
public CirclePanel()
{
}
protected override void OnPaint(PaintEventArgs e)
{
Graphics g = e.Graphics;
g.DrawEllipse(Pens.Black, 0,0,this.Width-1,this.Height-1);
}
protected override void OnResize(EventArgs e)
{
this.Width = this.Height;
base.OnResize(e);
}
}
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
Pen mypen;
Color mycolor=Color.Red;
Graphics mygraph;
int xloc = 50, yloc = 50;
public Form1()
{
InitializeComponent();
mygraph = CreateGraphics();
}
private void Form1_Load(object sender, EventArgs e)
{
}
float x = 270, y = 0.5f;
int xmover = 100, ymover = 48;
private void timer1_Tick(object sender, EventArgs e)
{
mygraph.DrawEllipse(new Pen(Color.Red), xloc, yloc, 102, 102);
mygraph.FillEllipse(new SolidBrush(Color.Red), xmover++, ymover++, 4, 4);
mycolor = this.BackColor;
mygraph.DrawPie(new Pen(mycolor), xloc+1, yloc+1, 100, 100, x-1, y);
mygraph.DrawPie(new Pen (Color.Red), xloc + 1, yloc + 1, 100, 100, x++, y);
mygraph.FillEllipse(new SolidBrush(mycolor), xmover-1, ymover - 1, 4, 4);
mygraph.DrawEllipse(new Pen(Color.Red), 100, 50, 5, 5);
}
}
}