I am reading data from a file and would like to attach a progress bar to this operation. I found the following code on stackoverflow - this code is due to William Daniel, Sept 20, stackoverflow post titled, " How to Change Color of Progress Bar in C#.Net 3.5"
class CustomProgressBar : ProgressBar
{
public CustomProgressBar()
{
this.SetStyle(ControlStyles.UserPaint, true);
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
// None... Helps control the flicker.
}
protected override void OnPaint(PaintEventArgs e)
{
const int inset = 2;
using (Image offscreenImage = new Bitmap(this.Width, this.Height))
{
using (Graphics offscreen = Graphics.FromImage(offscreenImage))
{
Rectangle rect = new Rectangle(0, 0, this.Width, this.Height);
if (ProgressBarRenderer.IsSupported)
ProgressBarRenderer.DrawHorizontalBar(offscreen, rect);
rect.Inflate(new Size(-inset, -inset)); // Deflate inner rectangle
rect.Width = (int)(rect.Width * ((double)this.Value / this.Maximum));
if (rect.Width == 0) rect.Width = 1;
LinearGradientBrush brush = new LinearGradientBrush(rect,
this.BackColor, this.ForeColor, LinearGradientMode.Vertical);
offscreen.FillRectangle(brush, inset, inset, rect.Width, rect.Height);
e.Graphics.DrawImage(offscreenImage, 0, 0);
offscreenImage.Dispose();
}
}
}
}
The code works fine except the following:
The gradient does not seem to extend the entire width of the bar. It is there but is much more heavily concentrated near the bottom of the bar and thins out very quickly as we get to the top of the bar. Any suggestions as to how I can fix this?
If I place the progress bar on a form and open an Internet Explorer window over the portion of the form with the progress bar on it, some of the text from the internet window bleeds onto the progress bar in the form. I have no idea why, and how to fix this.
As an aside, how does one estimate the length of an operation that you wish to show via a progress bar?
Any help would be greatly appreciated. Thank you.
As for first part of your question - try using following constructor for your brush:
new LinearGradientBrush(new Point(0, 0), new Point(0, Height - inset * 2), BackColor, ForeColor);
Your current brush has top control point at Y=inset - that's why area from Y=0 to Y=inset is all painted in solid color.
You are not drawing at all in the non-filled portion of your progressbar. Try calling FillRectangle for area from rect.Right to this.Width with background color. It should prevent bleeding from overlapping windows.
try this one, you don't need on each paint to create an image...
class CustomProgressBar : ProgressBar
{
public CustomProgressBar()
{
this.SetStyle(ControlStyles.UserPaint|ControlStyles.OptimizedDoubleBuffer|ControlStyles.DoubleBuffer, true);
this.UpdateStyles();
}
protected override void OnPaint(PaintEventArgs e)
{
const int inset = 2;
Rectangle rect = this.ClientRectangle;
var offscreen = e.Graphics;
if (ProgressBarRenderer.IsSupported){
ProgressBarRenderer.DrawHorizontalBar(offscreen, rect);
}
rect.Inflate(new Size(-inset, -inset)); // Deflate inner rectangle
rect.Width = Convert.ToInt32(Math.Round((rect.Width * ((double)this.Value / this.Maximum))));
if (rect.Width == 0) rect.Width = 1;
LinearGradientBrush brush = new LinearGradientBrush(rect,this.BackColor, this.ForeColor, LinearGradientMode.Vertical);
offscreen.FillRectangle(brush, inset, inset, rect.Width, rect.Height);
offscreen.DrawString(Value.ToString(), this.Font,Brushes.Black,rect);
}
}
use
const int inset = 1;
for your other question
Related
I'm making some custom control for a small project. I have created a TP1CustomFlatButton like this:
It was easy for me if I added a label with text to bottom of my TP1CustomFlatButton. I didn't want to handle events for that label so I used event onPaint to draw the text. I followed the turtorial of Microsoft and I got the custom flat button like the picture I attached. What I'm trying to get is to make the text align center at the bottom of my TP1CustomFlatButton.
This is my code for TP1CustomFlatButton:
// constructor
public TP1CustomFlatButton()
{
this.FlatStyle = FlatStyle.Flat;
this.FlatAppearance.BorderSize = 0;
this.BackColor = Color.MediumSeaGreen;
this.ForeColor = Color.White;
this.Text = "TP1CustomButton";
}
protected override void OnPaint(PaintEventArgs pevent)
{
pevent.Graphics.FillRectangle(new SolidBrush(this.BackColor), new Rectangle(0, 0, this.Width, this.Height));
TextFormatFlags flags = TextFormatFlags.Bottom;
//render text
TextRenderer.DrawText(pevent.Graphics, this.Text, this.Font, new Point((int)(this.Width - Text.Length)/2,this.Height), this.ForeColor, flags);
//draw image
Image img = this.BackgroundImage;
//create rectangle to display image
Rectangle imgRec = new Rectangle(this.Width - 32 /3, this.Height - 32/ 3, 32, 32);
if(img!=null)
pevent.Graphics.DrawImage(img, imgRec);
}
I'm really confused with the coordinate X and Y. As you can see the code I tried to make the "SETTINGS" text string to align center at bottom of my TP1CustomFlatButton. I spent 5 hours to read more information about coordinate and location of controls in Windows Form. But now I'm really tired.
Hope someone can give me any advice or any solution for my custom control.
You need to use the MeasureString()method in order to calculate the middle.
also see the changes i have madein order to find the middle (calculated in drawPoint field).
see my example based on your code:
public TP1CustomFlatButton()
{
this.FlatStyle = FlatStyle.Flat;
this.FlatAppearance.BorderSize = 0;
this.BackColor = Color.MediumSeaGreen;
this.ForeColor = Color.White;
this.Text = "middle";
}
protected override void OnPaint(PaintEventArgs pevent)
{
pevent.Graphics.FillRectangle(new SolidBrush(this.BackColor), new Rectangle(0, 0, this.Width, this.Height));
TextFormatFlags flags = TextFormatFlags.Bottom;
//render text
String drawString = this.Text;
SizeF size = pevent.Graphics.MeasureString(drawString, this.Font);
Point drawPoint = new Point((int)this.Size.Width / 2 - (int)size.Width / 2,this.Height);
TextRenderer.DrawText(pevent.Graphics, this.Text, this.Font, drawPoint, this.ForeColor, flags);
//draw image
Image img = this.BackgroundImage;
//create rectangle to display image
Rectangle imgRec = new Rectangle(this.Width - 32 / 3, this.Height - 32 / 3, 32, 32);
if (img != null)
pevent.Graphics.DrawImage(img, imgRec);
}
Output:
Try Changing the TextFormatFlags as below:
TextFormatFlags flags = TextFormatFlags.Bottom | TextFormatFlags.VerticalCenter;
Also, checkout this link
I want to make a bitmap that has a linear opacity applied. (i.e. it is more opaque on the left side and get progressively less opaque as it approaches the right.)
Is this possible? I know its possible to have a constant opacity level.
I know its possible to have a constant opacity level
So don't make it constant, LinearGradientBrush has no trouble interpolating the alpha value. A simple demonstration of a form that has the BackgroundImage set:
protected override void OnPaint(PaintEventArgs e) {
base.OnPaint(e);
var rc = new Rectangle(20, 20, this.ClientSize.Width - 40, 50);
using (var brush = new System.Drawing.Drawing2D.LinearGradientBrush(
rc,
Color.FromArgb(255, Color.BlueViolet),
Color.FromArgb(0, Color.BlueViolet),
0f)) {
e.Graphics.PixelOffsetMode = System.Drawing.Drawing2D.PixelOffsetMode.Half;
e.Graphics.FillRectangle(brush, rc);
}
}
Produced:
Put the BitMap into a PictureBox control. Then you will have to use the PictureBox's Paint event handler to do the fading. Something like
private void pictureBox_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(pictureBox.Image, 0, 0,
pictureBox.ClientRectangle, GraphicsUnit.Pixel);
Color left = Color.FromArgb(128, Color.Blue);
Color right = Color.FromArgb(128, Color.Red);
LinearGradientMode direction = LinearGradientMode.Horizontal;
LinearGradientBrush brush = new LinearGradientBrush(
pictureBox.ClientRectangle, left, right, direction);
e.Graphics.FillRectangle(brush, pictureBox.ClientRectangle);
}
This example fades the image overlay from blue to red. I am sure you can play with this to get what you want working.
I hope this helps.
I have my custom button where I have overridden the OnPaint() and draw the text in it only. On runtime the text looks differently - spacing between chars is lacking. Here is the image of design & runtime of the button :
The paint methods is as:
protected override void OnPaint(PaintEventArgs pevent)
{
base.OnPaint(pevent);
if (base.ContainsFocus)
{
// Draw inner dotted rectangle when button is on focus
Pen pen = new Pen(Color.Gray, 3);
Point p = base.Location;
pen.DashStyle = System.Drawing.Drawing2D.DashStyle.Dot;
Rectangle rectangle = new Rectangle(4, 4, Size.Width - 8,
Size.Height - 8);
ControlPaint.DrawFocusRectangle(pevent.Graphics, rectangle);
}
// Draw the string to screen
SizeF sf = pevent.Graphics.MeasureString(displayText, this.Font,
this.Width);
Point ThePoint = new Point();
ThePoint.X = (int)((this.Width / 2) - (sf.Width / 2));
ThePoint.Y = (int)((this.Height / 2) - (sf.Height / 2));
pevent.Graphics.DrawString(displayText, Font,
new SolidBrush(Color.FromArgb(255, 255, 254, 255)), ThePoint);
this.Text = "";
}
Any idea where am I going wrong and how to take care of the same?
You need to set the correct smoothing mode like this:
Graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality
Then, the result should look OK.
Devils Child's answer will affect the quality of lines and circles, etc.
But for text rendering, you can use:
e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
I'm just starting to learn the GDI+ system for drawing lines, circles etc. I've created a component (scanner) that inherits a Panel to draw on (not sure if panel or picture box is best).
On the "Scanner" Im currently drawing a circle on it. the component can be added to a winform and using docking will resize when the winform resizes. At the moment I'm getting the size of the component to calculate the size of the circle but what I want to do is basically say no matter what size the component is the "canvas" is always 300 x 300 wide, so I can say the circle should be positioned at 25,25 with a size of 250x250.
As you might guess from the name "Scanner" I want to plot points on it, but these will be calculated from the center (150,150) location.
Below is the code I have that basically draws the circle.
Many thanks for any help on this.
public partial class Scanner : Panel
{
public Scanner() {
InitializeComponent();
this.DoubleBuffered = true;
}
protected override void OnPaint(PaintEventArgs e) {
Graphics g = e.Graphics;
Draw(g);
base.OnPaint(e);
}
protected override void OnResize(EventArgs e) {
Graphics g = this.CreateGraphics();
Draw(g);
base.OnResize(e);
}
private void Draw(Graphics g) {
g.Clear(Color.Black);
g.PageUnit = GraphicsUnit.Pixel;
Pen green = new Pen(Color.Green);
Font fnt = new Font("Arial", 10);
SolidBrush sb = new SolidBrush(Color.Red);
int pos = (this.Width < this.Height ? this.Width : this.Height) / 2;
int size = (int)(pos * 1.9);
pos -= ((int)size / 2);
g.DrawEllipse(green, pos, pos, size, size);
g.DrawString(this.Width.ToString(), fnt, sb, new Point(0, 0));
}
}
Based on your recent comment, I understand you want to do your drawing on a fixed-size canvas, and plot this canvas inside the control, as large as will fit in the control.
Try the code below:
public class Scanner : Panel
{
private Image _scanner;
public Scanner()
{
this.SetStyle(ControlStyles.ResizeRedraw, true);
CreateScanner();
}
private void CreateScanner()
{
Bitmap scanner = new Bitmap(300, 300);
Graphics g = Graphics.FromImage(scanner);
g.DrawEllipse(Pens.Green, 25, 25, 250, 250);
g.Dispose();
_scanner = scanner;
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
int shortestSide = Math.Min(this.Width, this.Height);
if (null != _scanner)
e.Graphics.DrawImage(_scanner, 0, 0, shortestSide, shortestSide);
}
}
Is it possible to rotate a button or any control at a particular angle in WinForms? If so, how?
If you really want to (I have no idea why one would..*) you could try to use a Button subclass, maybe like that:
public partial class TurnButton : Button
{
public TurnButton()
{
InitializeComponent();
}
int angle = 0; // current rotation
Point oMid; // original center
protected override void OnLayout(LayoutEventArgs levent)
{
base.OnLayout(levent);
if (oMid == Point.Empty) oMid = new Point(Left + Width / 2, Top + Height / 2);
}
protected override void OnPaint(PaintEventArgs pe)
{
int mx = this.Size.Width / 2;
int my = this.Size.Height / 2;
SizeF size = pe.Graphics.MeasureString(Text, Font);
string t_ = Text;
Text = "";
base.OnPaint(pe);
if (!this.DesignMode)
{
Text = t_; pe.Graphics.TranslateTransform(mx, my);
pe.Graphics.RotateTransform(angle);
pe.Graphics.TranslateTransform(-mx, -my);
pe.Graphics.DrawString(Text, Font, SystemBrushes.ControlText,
mx - (int)size.Width / 2, my - (int)size.Height / 2);
}
}
protected override void OnClick(EventArgs e)
{
this.Size = new Size(Height, Width);
this.Location = new Point(oMid.X - Width / 2, oMid.Y - Height / 2);
angle = (angle + 90) % 360;
Text = angle + "°";
base.OnClick(e);
}
}
(* I have no idea why I wrote that, either ;-)
You can't rotate controls. That's simply not supported by the native API controls that WinForms uses.
And one might wonder why it even should be supported. What could you possibly be trying to do that you'd need to rotate a button control? It would be much easier to draw it in a different place with a different shape in the first place, rather than trying to rotate an existing control. (Do note that you can also resize and reposition a control at run-time, if that would fit your needs. Investigate the Size and Location properties.)
The only workaround is to draw the control's image to a bitmap, hide the control, and draw the bitmap onto the form in the location you want it to appear. Of course, that won't result in a control that the user can interact with. They won't be able to click an image of a button, because it's not a real button. If that's acceptable to you, you should probably be using an image in the first place, rather than a button.
This is similar to the question asked here:
Rotating a .NET panel in Windows Forms
The quick summary of answers from that question is that while it may be possible to do it, it would be very, very complicated.
A possible workaround in some cases would be this:
Use a tabControl , and resize it so you only have the button left. Set the allignment to left/right, and you have your button rotated 90/270 degrees.
public class VerticalButton : Button
{
public string VirticalText { get; set; }
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
StringFormat stringFormat = new StringFormat();
stringFormat.FormatFlags = StringFormatFlags.DirectionVertical;
SolidBrush solidBrush = new SolidBrush(this.ForeColor);
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
pe.Graphics.DrawString(VirticalText, this.Font, solidBrush,
new Rectangle(0, 0, Width, Height), stringFormat);
}
}