I'm trying to have a rectangle that changes size based on a value, But I cannot get it to update. If I draw the rectangle with set values it shows up, But if I then add a "*" operator to it, it does not show.
I've never used winform graphics before, this is based on other posts i've come across.
The code:
private void Send()
{
int l = 25, r = 20; // Testing values
using (Graphics g = this.MainPanel.CreateGraphics())
{
Brush brush = new SolidBrush(Color.LimeGreen);
g.FillRectangle(brush, 59, 74, 16, 56 * (l / 100));
g.FillRectangle(brush, 81, 74, 16, 56 * (r / 100));
brush.Dispose();
this.Invalidate();
}
string start = l + ":" + r + ".";
char[] end = start.ToCharArray();
port.Write(new string(end));
}
This bit of code runs every 15ms if that matters.
Use RectangleF instead. The height of your calculated rectangle is 0, because the int value is rounded to 0 when you're dividing. Better use float and RectangleF.
Also, in my opinion is better to calculate the rectangle you need to draw in the method Send(), and then invalidate the Form. You paint the rectangles in the method OnPaint() of the form. For example:
private void Send()
{
float l = 25, r = 20; // Testing values
mRectangle1 = new RectangleF(59, 74, 16, 56 * (l / 100));
mRectangle2 = new RectangleF(81, 74, 16, 56 * (r / 100));
Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
using (Brush brush = new SolidBrush(Color.LimeGreen))
{
e.Graphics.FillRectangle(brush, mRectangle1);
e.Graphics.FillRectangle(brush, mRectangle2);
}
}
private RectangleF mRectangle1;
private RectangleF mRectangle2;
And this is the result:
Hope it helps.
Related
I can not find any information on how to draw a few dots (for Split separator slider) as shown in the first picture.
I am trying to draw this:
Right now, my custom control code is drawing a straight line with a shadow
public class CustomPaintSplitter : SplitContainer {
...
protected override void OnPaint(PaintEventArgs pe)
{
Graphics g = pe.Graphics;
Rectangle r = ClientRectangle;
g.FillRectangle(new SolidBrush(BackColor), r);
if (Orientation == Orientation.Horizontal)
{
SplitterWidth = 9;
int recWidth = SplitterRectangle.Width / 3;
Rectangle split_rect = new(SplitterRectangle.X + recWidth, SplitterRectangle.Y, SplitterRectangle.Width - recWidth, SplitterRectangle.Height);
int x = split_rect.X;
int y = split_rect.Y + 3;
g.DrawLine(new Pen(SystemColors.ControlLightLight), x, y, x, y + 2);
g.DrawLine(new Pen(SystemColors.ControlLightLight), x, y, x + recWidth, y);
g.DrawLine(new Pen(SystemColors.ControlDark), x, y + 2, x + recWidth, y + 2);
g.DrawLine(new Pen(SystemColors.ControlDark), x + recWidth, y, x + recWidth, y + 2);
}
// Calling the base class OnPaint
base.OnPaint(pe);
}
}
How can this be achieved? I tried different Point[] methods on the internet, but none of them draw anything near what I am trying to achieve.
Even a parameter to specify the number of "dots" drawn would be a plus.
Any help is appreciated!
I just draw it on a panel but you can use it the same way.
private void panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
Brush b = new SolidBrush(Color.Gray);
e.Graphics.FillEllipse(b, new Rectangle(10, 5, 5, 5));
e.Graphics.FillEllipse(b, new Rectangle(20, 5, 5, 5));
e.Graphics.FillEllipse(b, new Rectangle(30, 5, 5, 5));
}
I have a Form which contains:
a TrackBar (minimum = 1, maximum = 200, represents zoom percent);
a UserControl with BorderStyle = BorderStyle.None.
Relevant code
Form1
From designer code
trackBar1.Value = 100;
BackColor = Color.Gray;
From code-behind
private void trackBar1_Scroll(object sender, EventArgs e)
{
userControl11.SetZoomFactor(trackBar1.Value / 100F);
}
UserControl1
internal float MyBaseWidth;
public UserControl1()
{
InitializeComponent();
MyBaseWidth = Width;
SetZoomFactor(1);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
e.Graphics.SmoothingMode = SmoothingMode.HighQuality;
e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
e.Graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
Pen p = new Pen(Color.Yellow);
e.Graphics.DrawPath(p, GraphicsPathWithBorder);
}
internal GraphicsPath GraphicsPathWithBorder;
internal void SetZoomFactor(float z)
{
Width = (int)(MyBaseWidth * z);
GraphicsPathWithBorder = RoundedCornerRectangle(ClientRectangle);
Region = new Region(GraphicsPathWithBorder);
}
internal static GraphicsPath RoundedCornerRectangle(Rectangle r)
{
GraphicsPath path = new GraphicsPath();
float size = 10 * 2F;
path.StartFigure();
path.AddArc(r.X, r.Y,
size, size, 180, 90);
path.AddArc((r.X + (r.Width - size)), r.Y,
size, size, 270, 90);
path.AddArc((r.X + (r.Width - size)), (r.Y + (r.Height - size)),
size, size, 0, 90);
path.AddArc(r.X, (r.Y + (r.Height - size)),
size, size, 90, 90);
path.CloseFigure();
return path;
}
Initial screenshot
Screenshot after using the trackbar
The right side of the yellow border becomes invisible after zooming out, and when zooming in there are multiple yellow borders on the right side.
Update:
The answer Works, but there is a part of the control that goes beyond the border. Screenshot for top-right corner, for curveSize = 20:
and for curveSize = 24:
I suggest a slightly different method to draw the Border and the content of the User Control that should also cure the artifacts generated when the control is redrawn.
When you create a Region for a Control and then you paint the Region as it is, the outer borders of the painting are not anti-aliased: the aliased pixels fall outside the Region. The same effect of course is applied when a border is painted around the bounds of the Region.
Here, I apply a Scale Matrix and a Translate Matrix that scale and move the bounds of the Region on the inside of the outer Region that defines the control's bounds.
The size of the scale and the translate transformations are determined by the Pen size.
More information on the Matrix usage here: Flip the GraphicsPath
In this case, when the borders are painted, the outer, anti-aliased, section of the border is inside the Region bounds and the anti-aliasing is preserved.
The background color of the Control is set to Color.Transparent (a User Control supports color transparency on its own).
I've also added a couple of (non decorated) properties that allow to define the inner Color (the Control's BackColor) and Size and Color of the Border. The rest is more or less what it was before.
Sample results:
using System.Drawing;
using System.Drawing.Drawing2D;
public partial class RoundControl : UserControl
{
private GraphicsPath GraphicsPathWithBorder;
private float MyBaseWidth;
private float m_PenSize = 2f;
private Color m_BorderColor = Color.Yellow;
private Color m_FillColor = Color.Green;
public RoundControl()
{
ResizeRedraw = true;
InitializeComponent();
MyBaseWidth = Width;
}
public float BorderSize
{
get => m_PenSize;
set {
m_PenSize = value;
Invalidate();
}
}
public Color BorderColor
{
get => m_BorderColor;
set {
m_BorderColor = value;
Invalidate();
}
}
public Color FillColor
{
get => m_FillColor;
set {
m_FillColor = value;
Invalidate();
}
}
protected override void OnLayout(LayoutEventArgs e) {
UpdateRegion();
base.OnLayout(e);
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
RectangleF rect = GraphicsPathWithBorder.GetBounds();
float scaleX = 1 - ((m_PenSize + 1) / rect.Width);
float scaleY = 1 - ((m_PenSize + 1) / rect.Height);
using (Pen pen = new Pen(m_BorderColor, m_PenSize))
using (Brush brush = new SolidBrush(m_FillColor))
using (Matrix mx = new Matrix(scaleX, 0, 0, scaleY, pen.Width / 2, pen.Width / 2))
{
e.Graphics.Transform = mx;
e.Graphics.FillPath(brush, GraphicsPathWithBorder);
e.Graphics.DrawPath(pen, GraphicsPathWithBorder);
}
base.OnPaint(e);
}
internal void SetZoomFactor(float z) {
int newWidth = (int)(MyBaseWidth * z);
if (newWidth <= (30 + m_PenSize * 2)) return;
Width = newWidth;
UpdateRegion();
}
private void UpdateRegion() {
GraphicsPathWithBorder = RoundedCornerRectangle(ClientRectangle);
Region = new Region(GraphicsPathWithBorder);
Invalidate();
}
private GraphicsPath RoundedCornerRectangle(Rectangle r)
{
GraphicsPath path = new GraphicsPath();
// Fixed curve size since we only scale on X-dimension
// Otherwise, adjust also considering the height
float curveSize = 10 * 2.4F;
path.StartFigure();
path.AddArc(r.X, r.Y, curveSize, curveSize, 180, 90);
path.AddArc(r.Right - curveSize, r.Y, curveSize, curveSize, 270, 90);
path.AddArc(r.Right - curveSize, r.Bottom - curveSize, curveSize, curveSize, 0, 90);
path.AddArc(r.X, r.Bottom - curveSize, curveSize, curveSize, 90, 90);
path.CloseFigure();
return path;
}
}
I'm working on project and I have to do kind of color picker using C#.
So I've decided that it will be a Panel with this background in Win Forms App.
Background should have gradient with three colors in rgb: red (0 - 255), blue (0 - 255) and green = 0.
But I can't find any information about what I should use for this.
I tried to write some code and here is what I've done.
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
panel1.Paint += new PaintEventHandler(panel1_Paint);
panel1.Refresh();
}
private void panel1_Paint(object sender, PaintEventArgs e)
{
Point startPoint = new Point(0, 0);
Point endPoint = new Point(150, 150);
LinearGradientBrush lgb =
new LinearGradientBrush(startPoint, endPoint, Color.FromArgb(255, 255, 0, 0), Color.FromArgb(255, 255, 255, 0));
Graphics g = e.Graphics;
g.FillRectangle(lgb, 0, 0, 150, 150);
// g.DrawLine(new Pen(Color.Yellow, 1.5f), startPoint, endPoint);
}
}
}
And now I have panel with this gradient
What I should use to get gradient at first picture?
And second question: What should I do to get the pixel color after clicking on this background?
Here is an example for using a multicolor LinearGradientBrush in the Paint event:
LinearGradientBrush linearGradientBrush =
new LinearGradientBrush(panel4.ClientRectangle, Color.Red, Color.Yellow, 45);
ColorBlend cblend = new ColorBlend(3);
cblend.Colors = new Color[3] { Color.Red, Color.Yellow, Color.Green };
cblend.Positions = new float[3] { 0f, 0.5f, 1f };
linearGradientBrush.InterpolationColors = cblend;
e.Graphics.FillRectangle(linearGradientBrush, panel4.ClientRectangle);
You can freely vary the number of colors, the angle or the spread of the stop points. Just make sure you always have the same number of colors and stop points and let them start at 0 and end at 1.
The colors in the constructor are ignored, btw..
To get a clicked color you can code the MouseClick:
Color clickedColor = Color.Empty;
private void panel_MouseClick(object sender, MouseEventArgs e)
{
using (Bitmap bmp = new Bitmap( panel.ClientSize.Width, panel4.ClientSize.Height))
{
panel.DrawToBitmap(bmp,panel.ClientRectangle);
clickedColor = bmp.GetPixel(e.X, e.Y);
}
}
If you want to catch many clicks it may be better to keep the Bitmap in a class level variable instead of recreating it all the time.. Setting it as the Panel's BackgroundImage, as Kala's answer assumes may also be a good option..
This should answer the question in the title. However your first image doesn't show a gradient with three colors. It shows a 2D gradient with four colors. For such a more expensive coloring method you should put the colors in a Bitmap and set it as the Panel's BackgroundImage..
Update1 Here is a piece of code that creates a 2D Gradient:
Bitmap Gradient2D(Rectangle r, Color c1, Color c2, Color c3, Color c4)
{
Bitmap bmp = new Bitmap(r.Width, r.Height);
float delta12R = 1f * (c2.R - c1.R) / r.Height;
float delta12G = 1f * (c2.G - c1.G) / r.Height;
float delta12B = 1f * (c2.B - c1.B) / r.Height;
float delta34R = 1f * (c4.R - c3.R) / r.Height;
float delta34G = 1f * (c4.G - c3.G) / r.Height;
float delta34B = 1f * (c4.B - c3.B) / r.Height;
using (Graphics G = Graphics.FromImage(bmp) )
for (int y = 0; y < r.Height; y++)
{
Color c12 = Color.FromArgb(255, c1.R + (int)(y * delta12R),
c1.G + (int)(y * delta12G), c1.B + (int)(y * delta12B));
Color c34 = Color.FromArgb(255, c3.R + (int)(y * delta34R),
c3.G + (int)(y * delta34G), c3.B + (int)(y * delta34B));
using ( LinearGradientBrush lgBrush = new LinearGradientBrush(
new Rectangle(0,y,r.Width,1), c12, c34, 0f) )
{ G.FillRectangle(lgBrush, 0, y, r.Width, 1); }
}
return bmp;
}
Here is how you use it:
public Form1()
{
InitializeComponent();
panel.BackgroundImage = Gradient2D(panel.ClientRectangle,
Color.Black, Color.FromArgb(255, 0, 255, 0), Color.Red, Color.Yellow);
}
This uses simple LinearGradientBrushes without an extra colors list going down over the height of the Panel.
Note that Color.Green is a rather dark hue, so I used FromRgb for a brighter green. If your Panel is greater than 256 pixels you may want to optimze by filling larger stripes; if it is vertical you may want to change the loop to go over x instead of y..
Here is the result:
To pick with a click you now simply read out the color from the BackgroundImage:
private void panel_MouseClick(object sender, MouseEventArgs e)
{
clickedColor = ((Bitmap)panel.BackgroundImage).GetPixel(e.X, e.Y);
}
Update 2:
When looking over this MSDN page we can find that there actually is a built-in tool to create 2D gradients.
It is the PathGradientBrush
Here is an example..:
.. and the code:
Bitmap Gradient2D(Rectangle r, Color c1, Color c2, Color c3, Color c4)
{
List<Color> colors = new List<Color> { c1, c3, c4, c2 };
Bitmap bmp = new Bitmap(r.Width, r.Height);
using (Graphics g = Graphics.FromImage(bmp))
for (int y = 0; y < r.Height; y++)
{
using (PathGradientBrush pgb = new PathGradientBrush(getCorners(r).ToArray()))
{
pgb.CenterColor = medianColor(colors);
pgb.SurroundColors = colors.ToArray();
g.FillRectangle(pgb, 0, y, r.Width, 1);
}
}
return bmp;
}
This uses two simple helper functions. One returns the corner points of a rectangle:
public List<PointF> getCorners(RectangleF r)
{
return new List<PointF>() { r.Location, new PointF(r.Right, r.Top),
new PointF(r.Right, r.Bottom), new PointF(r.Left, r.Bottom)};
}
The other calculates a median color from a List<Color>. This is used as the CenterColor..:
public static Color medianColor(List<Color> cols)
{
int c = cols.Count;
return Color.FromArgb(cols.Sum(x => x.A) / c, cols.Sum(x => x.R) / c,
cols.Sum(x => x.G) / c, cols.Sum(x => x.B) / c);
}
The result pretty much identical to the one from using stripes of LinearGradientBrushes. It is simpler and should perform a bit better; it is what I would recommend obviously..
Note the changed order of the colors (or corners)! The SurroundColors apply to opposing corners of the rectangle..
Note:
When studying that page one can find that there actually are four different uses for that brush.
They differ in how to set it up (GraphicsPath or Point[]), which color collections to fill (SurroundColors or InterpolationColors.Colors) and how to call it (with a shape or a path). And the results also differ a lot.
Also note that only three results of the four ways are shown, although code for all four is provided!..
From the mouse click event argument e, you can get the Point with the exact co-ordinates of the click:
Point clickPoint = e.GetPosition(backgroundControlWithImg);
Then get the colour of the image at that position using something like:
System.Drawing.Image image = backgroundControl.BackgroundImage;
Bitmap _bitmap = new Bitmap(image);
Color _color = bitmap.GetPixel(Point.x, Point.y);
Something like that. What are you using for the Color Picker, WPF or?
I'm looking into a way of altering the frame around a windows form box. I want to make it either transparent, or get rid of it entirely. I managed to get rid of the icon, and get rid of the maximize button, as well as restrict the user's ability to resize the window. The bar clashes with the visual theme I'm trying to implement.
So question - Is there any good way customizing the form border & the top bar in a Windows Form application?
Select the correct value for FormBorderStyle, like None.
This might be overkill for what you're asking, but here goes... This was a quick application I created with a circular border. It's not the entire code, but maybe you'll get the idea from it.
First, I remove the FormBorderStyle (Set it equal to None) and add a mouse handler to allow movement of the form. Code below...
public static extern int SendMessage(IntPtr hWnd, int Msg, int wParam, int lParam);
[DllImportAttribute("user32.dll")]
public static extern bool ReleaseCapture();
public const int WM_NCLBUTTONDOWN = 0xA1;
public const int HT_CAPTION = 0x2;
private void Form1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
ReleaseCapture();
SendMessage(Handle, WM_NCLBUTTONDOWN, HT_CAPTION, 0);
}
}
Then I created a pink mesh to display as the form background. and set the TransparencyKey property to the RGB value of the background color I wanted to be transparent (in this case 255,15,255).
Then I drew the literal controls with a graphics module.
private void Form1_Paint(object sender, PaintEventArgs e)
{
// Set the starting coordinants for our graphics drawing
int y = 0;
int x = 0;
// Set the end coordinants for our graphics drawing
int width = this.Size.Width;
int height = this.Size.Height;
// Set our graphics options
e.Graphics.InterpolationMode = XGraphics.xInterpolation;
e.Graphics.SmoothingMode = XGraphics.xSmoothingMode;
e.Graphics.CompositingMode = XGraphics.xComposingMode;
e.Graphics.CompositingQuality = XGraphics.xComposingQuality;
e.Graphics.PixelOffsetMode = XGraphics.xPixelOffsetMode;
// Set the colors, positions, and gradient directions then draw our background
using (LinearGradientBrush gpxBrush = XGraphics.GradientBrushOrientation(0, x, y, width, height))
{
// Create our color blender object
ColorBlend gpxBlend = null;
gpxBlend = new ColorBlend(4);
gpxBlend.Colors = new Color[] { Color.FromArgb(255, 0, 100, 255), Color.FromArgb(205, 255, 240, 240),
Color.FromArgb(255, 20, 100, 230), Color.FromArgb(105, 105, 100, 255) };
gpxBlend.Positions = new float[] { 0.0F, .45F, .55F, 1.0F };
gpxBrush.InterpolationColors = gpxBlend;
// Draw our background
e.Graphics.FillEllipse(gpxBrush, x, y, width, height);
}
// Set the end coordinants for our graphics drawing
width = this.Size.Width-5;
height = this.Size.Height-5;
using (LinearGradientBrush gpxBrush = XGraphics.GradientBrushOrientation(0, x, y, width, height))
{
// Create our color blender object
ColorBlend gpxBlend = null;
gpxBlend = new ColorBlend(2);
gpxBlend.Colors = new Color[] { Color.White, Color.Gray };
gpxBlend.Positions = new float[] { 0.0F, 1.0F };
gpxBrush.InterpolationColors = gpxBlend;
// Draw our background
e.Graphics.FillEllipse(gpxBrush, x+5, y+5, width-5, height-5);
}
width = this.Size.Width / 2;
height = this.Size.Height / 2;
using (LinearGradientBrush gpxBrush = XGraphics.GradientBrushOrientation(0, x, y, width, height))
{
// Create our color blender object
ColorBlend gpxBlend = null;
gpxBlend = new ColorBlend(2);
gpxBlend.Colors = new Color[] { Color.White, Color.Gray };
gpxBlend.Positions = new float[] { 0.0F, 1.0F };
gpxBrush.InterpolationColors = gpxBlend;
// Draw our background
e.Graphics.FillEllipse(gpxBrush, x + width / 2, y + height / 2, width, height);
}
width = (this.Size.Width / 2) - 5;
height = (this.Size.Height / 2) - 5;
using (LinearGradientBrush gpxBrush = XGraphics.GradientBrushOrientation(0, x, y, width-5, height-5))
{
// Create our color blender object
ColorBlend gpxBlend = null;
gpxBlend = new ColorBlend(4);
gpxBlend.Colors = new Color[] { Color.FromArgb(255, 0, 100, 255), Color.FromArgb(205, 255, 240, 240),
Color.FromArgb(255, 20, 100, 230), Color.FromArgb(105, 105, 100, 255) };
gpxBlend.Positions = new float[] { 0.0F, .45F, .55F, 1.0F };
gpxBrush.InterpolationColors = gpxBlend;
// Draw our background
e.Graphics.FillEllipse(gpxBrush, (x + width / 2) +7, (y + height / 2) + 7, width - 5, height - 5);
}
}
As you can see, there is no border now and the edges are transparent. Just make sure to add a button to exit the application since there isn't an 'X' option anymore! ;)
Hope this helps you somewhat!
I'm writing an extended progress bar control at present, 100% open source and I've created some basic styles with gradients and solid colours.
One of the options I wanted to add was animation to the bar, prety much like the windows 7 and vista green progress bar. So I need to add a moving "Glow" to the % bar, however my attempt at this looks terrible.
My method is to draw an ellipse with set size and move it's x position until it reaches the end were the animation starts again.
First of does anyone have nay links or code to help me achieve the current windows 7 glow effect using GDI or some similar method?
I have several other animations that will also be added the the bar hence the GDI.
private void renderAnimation(PaintEventArgs e)
{
if (this.AnimType == animoptions.Halo)
{
Rectangle rec = e.ClipRectangle;
Rectangle glow = new Rectangle();
//SolidBrush brush = new SolidBrush(Color.FromArgb(100, Color.White));
//int offset = (int)(rec.Width * ((double)Value / Maximum)) - 4;
int offset = (int)(rec.Width / Maximum) * Value;
if (this.animxoffset > offset)
{
this.animxoffset = 0;
}
glow.Height = rec.Height - 4;
if (this.animxoffset + glow.X > offset)
{
glow.Width = offset - (this.animxoffset + 50);
}
else
{
glow.Width = 50;
}
glow.X = this.animxoffset;
LinearGradientBrush brush = new LinearGradientBrush(glow, Color.FromArgb(0, Color.White), Color.FromArgb(100, Color.White), LinearGradientMode.Horizontal);
e.Graphics.FillEllipse(brush, this.animxoffset, 2, glow.Width, glow.Height);
brush.Dispose();
string temp = offset.ToString();
e.Graphics.DrawString(temp + " : " + glow.X.ToString(), DefaultFont, Brushes.Black, 2, 2);
animTimer = new System.Timers.Timer();
animTimer.Interval = 10;
animTimer.Elapsed += new System.Timers.ElapsedEventHandler(t_Elapsed);
animTimer.Start();
}
}
void t_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
this.animTimer.Stop();
this.animxoffset += 2;
Invalidate();
}
This is just an example glow iterating through a pen array.
You could also use a transparent image (although it could have a performance impact).
Pen[] gradient = { new Pen(Color.FromArgb(255, 200, 200, 255)), new Pen(Color.FromArgb(150, 200, 200, 255)), new Pen(Color.FromArgb(100, 200, 200, 255)) };
int x = 20;
int y = 20;
int sizex = 200;
int sizey = 10;
int value = 25;
//draw progress bar basic outline (position - 1 to compensate for the outline)
e.Graphics.DrawRectangle(Pens.Black, new Rectangle(x-1, y-1, sizex, sizey));
//draw the percentage done
e.Graphics.FillRectangle(Brushes.AliceBlue, new Rectangle(x, y, (sizex/100)*value, sizey));
//to add the glow effect just add lines around the area you want to glow.
for (int i = 0; i < gradient.Length; i++)
{
e.Graphics.DrawRectangle(gradient[i], new Rectangle(x - (i + 1), y - (i + 1), (sizex / 100) * value + (2 * i), sizey + (2 * i)));
}