I am making a custom progress bar in C#, and I want to show the percent on top of the bar. I need it so that when the bar reaches the text, it changes color. Take for example the image I made below:
Pretend that the orange rectangle on the left is the progress bar, and the black rectangle is blank space.
Is there anyway I can recreate this using GDI?
Thanks in advance,
Pat
You can do it by overriding paint on the control of your choice,
First draw the Black background and orange text
e.Graphics.FillRectangle(Brushes.Black, panel1.ClientRectangle);
e.Graphics.DrawString("StackOverflow", Font, Brushes.Orange, panel1.ClientRectangle);
Then Draw the overlay and clip to the size of the progress value
var clipRect = new Rectangle(0, 0, (panel1.Width / 100) * _progress, panel1.Height);
e.Graphics.SetClip(clipRect);
e.Graphics.FillRectangle(Brushes.Orange, clipRect);
e.Graphics.DrawString("StackOverflow", Font, Brushes.Black, 0, 0);
This is a working example using Panel as the control to override paint on (Just add a panel to a Form)
Example:
public partial class Form1 : Form
{
private Timer _progresstimer = new Timer();
private int _progress = 0;
public Form1()
{
InitializeComponent();
panel1.Paint += new PaintEventHandler(panel1_Paint);
_progresstimer.Interval = 250;
_progresstimer.Tick += (s, e) =>
{
if (_progress < 100)
{
_progress++;
panel1.Invalidate();
return;
}
_progress = 0;
panel1.Invalidate();
};
_progresstimer.Start();
}
void panel1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.FillRectangle(Brushes.Black, panel1.ClientRectangle);
e.Graphics.DrawString("StackOverflow", Font, Brushes.Orange, panel1.ClientRectangle);
var clipRect = new Rectangle(0, 0, (panel1.Width / 100) * _progress, panel1.Height);
e.Graphics.SetClip(clipRect);
e.Graphics.FillRectangle(Brushes.Orange, clipRect);
e.Graphics.DrawString("StackOverflow", Font, Brushes.Black, 0, 0);
}
}
You will want to set DoubleBuffering etc as this will flicker without, but this should be a good starting example.
Result:
Related
I just need to show scrollbars on forms if my shape height is greater than the form height. That way, when the user scrolls down, it can show the end of the shape.
This is my code:
public partial class Form1: Form {
public Form1() {
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e) {
}
private void Form1_Paint(object sender, PaintEventArgs e) {
e.Graphics.DrawLine(new Pen(Color.Black, 2), 100, 50, 100, 1000);
//if line height > form height then show scroll bars
}
}
You need to enable the AutoScroll property of the form, use the AutoScrollPosition coordinates in drawings, and set the AutoScrollMinSize property to contain your shapes:
In the constructor add:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
AutoScroll = true;
}
}
and the painting routine:
private void Form1_Paint(object sender, PaintEventArgs e)
{
using (Matrix m = new Matrix(1, 0, 0, 1, AutoScrollPosition.X, AutoScrollPosition.Y))
{
var sY = VerticalScroll.Value;
var sH = ClientRectangle.Y;
var w = ClientRectangle.Width - 2 - (VerticalScroll.Visible ? SystemInformation.VerticalScrollBarWidth : 0);
var h = ClientRectangle.Height;
var paintRect = new Rectangle(0, sY, w, h); //This will be your painting rectangle.
var g = e.Graphics;
g.SmoothingMode = SmoothingMode.AntiAlias;
g.Transform = m;
g.Clear(BackColor);
using (Pen pn = new Pen(Color.Black, 2))
e.Graphics.DrawLine(pn, 100, 50, 100, 1000);
sH += 1050; //Your line.y + line.height
//Likewise, you can increase the w to show the HorizontalScroll if you need that.
AutoScrollMinSize = new Size(w, sH);
}
}
Good Luck.
I created two Rectangles. I want to add events on them. For example when mouse hover on one, the hovered one will change color, can do resize or drag them(rectangles) to other place...
I was just wondering if I could control the drawn graphic, or it will like Microsoft Paint that after you painted, the object can not be operate unless you clear canvas and do redraw.
Is it possible to control a drawn graphic, thanks for any suggestion.
Simple code of my drawn graphics
private void Form1_Paint(object sender, PaintEventArgs e)
{
// Create pen.
Pen blackPen = new Pen(Color.Black, 3);
// Create rectangle.
Rectangle rect1 = new Rectangle(20, 20, 250, 250);
Rectangle rect2 = new Rectangle(70, 70, 150, 150);
// Draw rectangle to screen.
e.Graphics.FillRectangle(Brushes.DeepSkyBlue, rect1);
e.Graphics.FillRectangle(Brushes.LightBlue, rect2);
}
Also, you can create your own control like:
class RectangleControl : Control
{
public void FillRectangle(Color color)
{
this.BackColor = color;
}
}
Then :
private void Form1_Paint(object sender, PaintEventArgs e)
{
RectangleControl rect1 = new RectangleControl() { Parent = this, Left = 20, Top = 20, Width = 250, Height = 250 };
rect1.FillRectangle(Color.DeepSkyBlue);
RectangleControl rect2 = new RectangleControl() { Parent = rect1, Left = 50, Top = 50, Width = 150, Height = 150 };
rect2.FillRectangle(Color.LightBlue);
rect1.MouseHover += Rect1_MouseHover;
rect2.MouseLeave += Rect2_MouseLeave;
}
private void Rect2_MouseLeave(object sender, EventArgs e)
{
(sender as RectangleControl).BackColor = Color.Yellow;
}
private void Rect1_MouseHover(object sender, EventArgs e)
{
(sender as RectangleControl).BackColor = Color.LightBlue;
}
You can use Panel control instead.
Just add 2 panel controls as you would like them to be arranged and add 2 event handlers:
private void panel1_MouseHover(object sender, EventArgs e)
{
panel1.BackColor = Color.Yellow;
}
private void panel1_MouseLeave(object sender, EventArgs e)
{
panel1.BackColor = Color.LightBlue;
}
You can monitor the MouseMove.
Here's code using MouseMove and some brush values used by the rectangles.
using System.Drawing;
using System.Windows.Forms;
namespace Question_Answer_WinForms_App
{
public partial class Form1 : Form
{
public Brush outerRectangleBrush = Brushes.DeepSkyBlue;
public Brush innerRectangleBrush = Brushes.LightBlue;
public Form1()
{
InitializeComponent();
Paint += Form1_Paint;
MouseMove += Form1_MouseMove;
}
private void Form1_MouseMove(object sender, MouseEventArgs e)
{
var isWithinOuterRectangle = e.Location.X >= 20
&& e.Location.X <= 250 + 20
&& e.Location.Y >= 20
&& e.Location.Y <= 250 + 20;
var isWithinInnerRectangle = e.Location.X >= 70
&& e.Location.X <= 150 + 70
&& e.Location.Y >= 70
&& e.Location.Y <= 150 + 70;
if (isWithinOuterRectangle)
{
if (isWithinInnerRectangle)
{
outerRectangleBrush = Brushes.DeepSkyBlue;
innerRectangleBrush = Brushes.Red;
Refresh();
}
else
{
outerRectangleBrush = Brushes.Red;
innerRectangleBrush = Brushes.LightBlue;
Refresh();
}
}
else
{
outerRectangleBrush = Brushes.DeepSkyBlue;
innerRectangleBrush = Brushes.LightBlue;
Refresh();
}
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
// Create pen.
Pen blackPen = new Pen(Color.Black, 3);
// Create rectangle.
Rectangle rect1 = new Rectangle(20, 20, 250, 250);
Rectangle rect2 = new Rectangle(70, 70, 150, 150);
// Draw rectangle to screen.
e.Graphics.FillRectangle(outerRectangleBrush, rect1);
e.Graphics.FillRectangle(innerRectangleBrush, rect2);
}
}
}
#FJF, writing a ms-paint like application is not a complicated task. Windows Forms applications are using GDI+ to render graphics. so you can write a simple paint application using WindowsForms.
#nexolini uses a panel to do some draws. actually Ms-Paint does the same somehow. Ms-Paint is a Single-Layer Editor. So you can't resize all objects anytime you wanted (but as I said before you can assume that you have a panel for each newly drawn Shapes; Something like what Ms-Paint does).
So what is the problem?
Ms-Paint doesn't tracks your mouse movements and it doesn't needed (as it's a single layer). You can do all of it's tasks using these Answers.
e.g: for adding a Fill Color tool you can use a getpixel and putpixel to do a recursive algorithm on you image. and you are not needed to know which shape you are working on.
all the other tasks could be implemented easy.
for multi-layer Editors I will prefer to use a more powerful framework (but it's also could be implemented in Windows forms in a bad way), Like WPF. WPF uses DirectX to render your graphics and you are able to write an smooth Editor. WPF is designed to handle your graphical request so it does better on graphics.
#Reza_Aghaei 's comments are useful for Windows Forms.
I was wondering if it's possible to fill a certain percentage of a Rectangle object that's been draw on-screen so that it behaves like a Progress Bar, where you can watch its "level" go up and down. Specifically, I'd like to fill it from top to bottom rather than left to right.
You can try this.
private void button1_Click(object sender, EventArgs e)
{
panel1.Width = 400;
panel1.Height = 50;
using (Graphics g = this.panel1.CreateGraphics())
{
g.Clear(Color.Black);
Pen pen = new Pen(Color.Red, 2);
for (int i = 0; i <= 50;i++ )
{
g.DrawRectangle(pen, 0, 0, 400, i);
Thread.Sleep(100);
}
pen.Dispose();
}
}
I've made a custom control in C# and anytime that the user's cursor is hovering over the custom control I want the cursor to be displayed as the 'Hand'. Where do i place the code to do such a thing?
????.Cursor = Cursors.Hand;
in order to make it so the Hand Cursor is being displayed when hovering over this custom control?
namespace CustomRangeBar
{
public partial class RangeBar : UserControl
{
public RangeBar()
{
InitializeComponent();
label1.ForeColor = Color.Black;
this.ForeColor = SystemColors.Highlight; // set the default color the rangeBar
this.Click += new EventHandler(RangeBar_Click);
}
protected float percent = 0.0f; // Protected because we don't want this to be accessed from the outside
// Create a Value property for the rangeBar
public float Value
{
get
{
return percent;
}
set
{
// Maintain the value between 0 and 100
if (value < 0) value = 0;
else if (value > 100) value = 100;
percent = value;
label1.Text = value.ToString();
//redraw the rangeBar every time the value changes
this.Invalidate();
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
Brush b = new SolidBrush(this.ForeColor); //create brush that will draw the background of the range bar
// create a linear gradient that will be drawn over the background. FromArgb means you can use the Alpha value which is the transparency
LinearGradientBrush lb = new LinearGradientBrush(new Rectangle(0, 0, this.Width, this.Height), Color.FromArgb(255, Color.White), Color.FromArgb(50, Color.White), LinearGradientMode.Vertical);
// calculate how much has the rangeBar to be filled for 'x' %
int width = (int)((percent / 100) * this.Width);
e.Graphics.FillRectangle(b, 0, 0, width, this.Height);
e.Graphics.FillRectangle(lb, 0, 0, width, this.Height);
b.Dispose(); lb.Dispose();
}
private void RangeBar_SizeChanged(object sender, EventArgs e)
{
// maintain the label in the center of the rangeBar
label1.Location = new Point(this.Width / 2 - 21 / 2 - 4, this.Height / 2 - 15 / 2);
}
}
}
public void RangeBar_Click(object obj, EventArgs ea)
{
// This get executed if the pictureBox gets clicked
label1.text = "Increment 1";
}
UserControl derives from Control and therefore should already have a Cursor property inherited from that class. Do you not see a Cursor property in code/Properties?
I have overridden the OnPaint method of my Label control in VS2008:
void Label_OnPaint(object sender, PaintEventArgs e) {
base.OnPaint(e);
dim lbl = sender as Label;
if (lbl != null) {
string Text = lbl.Text;
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
if (myShowShadow) { // draw the shadow first!
e.Graphics.DrawString(Text, lbl.Font, new SolidBrush(myShadowColor), myShadowOffset, StringFormat.GenericDefault);
}
e.Graphics.DrawString(Text, lbl.Font, new SolidBrush(lbl.ForeColor), 0, 0, StringFormat.GenericDefault);
}
}
This works, but I really want to find out how to center the text both vertically and horizontally. I've heard of the MeasureString() method, but my "Text" complicates matters because it could include page breaks.
Could someone guide me with how to do this?
Alternatively you can create your own StringFormat object and pass it in using an overload of DrawString that supports a RectangleF:
StringFormat formatter = new StringFormat();
formatter.LineAlignment = StringAlignment.Center;
formatter.Alignment = StringAlignment.Center;
RectangleF rectangle = new RectangleF(0, 0, lbl.Width, lbl.Height);
e.Graphics.DrawString(Text, lbl.Font, new SolidBrush(lbl.ForeColor), rectangle, formatter);
You can call TextRenderer.DrawText with the HorizontalCenter and VerticalCenter flags.
Here is the code i'm using at the moment,
SizeF size;
string text = "Text goes here";
size = e.Graphics.MeasureString(text, font);
x = (lineWidth / 2) - (size.Width / 2);
y = top;
e.Graphics.DrawString(text, font, Brushes.Black, x, y);
I just wanted to add (a year later) a tool I created because StringAlignment turned out to be not very dependable. It turns out to be very similar to Neo's version.
The code below does an excellent job of centering the text both vertically and horizontally. Also, I wrote it with various overloads so that different options could be supplied to make this control behave exactly like I want.
Here are my overloads:
private static void DrawCenter(Label label, Graphics graphics) {
DrawCenter(label.Text, label, label.Location, label.ForeColor, graphics);
}
private void DrawCenter(string text, Label label, Graphics graphics) {
DrawCenter(text, label, label.Location, label.ForeColor, graphics);
}
private static void DrawCenter(string text, Label label, Point location, Graphics graphics) {
DrawCenter(text, label, location, label.ForeColor, graphics);
}
private static void DrawCenter(string text, Label label, Point location, Color fontColor, Graphics graphics) {
Rectangle rect = new Rectangle(location, label.Size);
SizeF lSize = graphics.MeasureString(text, label.Font, rect.Width);
PointF lPoint = new PointF(rect.X + (rect.Width - lSize.Width) / 2, rect.Y + (rect.Height - lSize.Height) / 2);
graphics.DrawString(text, label.Font, new SolidBrush(fontColor), lPoint);
}
To use these for the Label's OnPaint event, simply modify my original code in the question to following:
private void Label_OnPaint(object sender, PaintEventArgs e) {
base.OnPaint(e);
Label lbl = sender as Label;
if (lbl != null) {
string txt = lbl.Text;
e.Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
if (myShowShadow) { // draw the shadow first!
Point offset = new Point(lbl.Location.X - 1, lbl.Location.Y - 1)
DrawCenter(txt, lbl, offset, myShadowColor, e.Graphics);
}
DrawCenter(lbl, e.Graphics);
}
}
For a Print_Document event, I have a version that will also print a box around the label if there is already a box around it in the designer:
private static void DrawCenter(string text, Label label, Point location, Color fontColor, Graphics graphics) {
Rectangle rect = new Rectangle(location, label.Size);
SizeF lSize = graphics.MeasureString(text, label.Font, rect.Width);
PointF lPoint = new PointF((rect.Width - lSize.Width) / 2, (rect.Height - lSize.Height) / 2);
graphics.DrawString(text, label.Font, new SolidBrush(fontColor), lPoint);
if (label.BorderStyle != BorderStyle.None) {
using (Pen p = new Pen(Color.Black)) {
graphics.DrawRectangle(p, rect);
}
}
}
If you find this at all useful, give me a +1.
~Joe