Display caption in input field C# [duplicate] - c#

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Watermark TextBox in WinForms
I'm currently programming the settings dialog of a C# application.
The input fields should look like that:
→
What's the best way to realize that? I've thought of creating a background-image but I would like to know if there's a better way to do that (something dynamically)...

Use a panel with white set as the BackColor.
In the panel control, on the left insert a TextBox with BorderStyle set to None.
In the panel control, on the right insert a Label with BackColor set to Transparent and set Text to "Firstname".

I think this does pretty much what you need:
public class MyTextBox : TextBox
{
public const int WM_PAINT = 0x000F;
protected override void WndProc(ref Message m)
{
switch (m.Msg)
{
case WM_PAINT:
Invalidate();
base.WndProc(ref m);
if (!ContainsFocus && string.IsNullOrEmpty(Text))
{
Graphics gr = CreateGraphics();
StringFormat format = new StringFormat();
format.Alignment = StringAlignment.Far;
gr.DrawString("Enter your name", Font, new SolidBrush(Color.FromArgb(70, ForeColor)), ClientRectangle, format);
}
break;
default:
base.WndProc(ref m);
break;
}
}
}
Overriding OnPaint on a TextBox is usually not a good idea because the position of the caret will be calculated wrong.
Please notice that the label is shown only when the TextBox is empty and does not have the focus. But this is how most of such input boxes behave.
If the cue should be visible all the time you can just add it as a label:
public class MyTextBox : TextBox
{
private Label cueLabel;
public TextBoxWithLabel()
{
SuspendLayout();
cueLabel = new Label();
cueLabel.Anchor = AnchorStyles.Top | AnchorStyles.Right;
cueLabel.AutoSize = true;
cueLabel.Text = "Enter your name";
Controls.Add(cueLabel);
cueLabel.Location = new Point(Width - cueLabel.Width, 0);
ResumeLayout(false);
PerformLayout();
}
}

Just create a new textbox class
public class MyTextBox : TextBox
{
public MyTextBox()
{
SetStyle(ControlStyles.UserPaint, true);
}
protected override void OnTextChanged(EventArgs e)
{
base.OnTextChanged(e);
this.Invalidate();
}
protected override void OnPaint(PaintEventArgs e)
{
e.Graphics.DrawString(this.Text, this.Font, new SolidBrush(Color.Black), new System.Drawing.RectangleF(0, 0, this.Width , this.Height ), System.Drawing.StringFormat.GenericDefault);
e.Graphics.DrawString("Lloyd", this.Font, new SolidBrush(Color.Red), new System.Drawing.RectangleF(0, 0, 100, 100), System.Drawing.StringFormat.GenericTypographic);
base.OnPaint(e);
}
}
make appropriate changes to the Draw String params

Create a composition of a 3 controls, and put it in another UserControl. Three controls would be:
textbox, no borders
label, at the right of the textbox
panel, carrying the border for both of them.
Take the recipe from Hassan, then put that on the UserControl and use it as one new control.

Related

vertical navigation bar?

Sorry for this question, but I've been Googling "C# vertical menu bar" for a while, and I can't find one that looks something like this:
No, I'm not making a survey locker, but that's the only image I could find of what I'm looking for.
Could anyone tell me how to do this?
You can creat custom User Control that Inherit from Tab Control.
For Windows Forms, Follow Below Steps:
Right Click Project -> Add New Item -> User Control(C#)
Inherit from TabControl and write below code in Default Constructor
3 Override OnPaint Method to manually Design Tab Control
Save it
Add it to your Form from toolbox.
Set Dock Property to Fill and Alignment Property to Left of Control
Add more Tabs
Hope this Helps!
class CustomControl : TabControl
{
public CustomControl()
{
SetStyle(ControlStyles.AllPaintingInWmPaint , true);
SetStyle(ControlStyles.OptimizedDoubleBuffer , true);
SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.UserPaint, true);
DoubleBuffered = true;
SizeMode = TabSizeMode.Fixed;
ItemSize = new System.Drawing.Size(30, 120);
}
protected override void OnPaint(PaintEventArgs e)
{
var B = new Bitmap(Width, Height);
var G = (Graphics)Graphics.FromImage(B);
G.Clear(Color.Gainsboro);
for (int i = 0; i < TabCount -1; i++)
{
var TabRectangle = (Rectangle)GetTabRect(i);
if (i == SelectedIndex)
{
G.FillRectangle(Brushes.Navy, TabRectangle);
}
else
{
G.FillRectangle(Brushes.BlueViolet, TabRectangle);
}
G.DrawString(TabPages[i].Text, Font, Brushes.White, TabRectangle, new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
TabPages[i].Font = new Font(TabPages[i].Font, FontStyle.Strikeout);
}
e.Graphics.DrawImage((Image)B.Clone(),0,0);
G.Dispose();
B.Dispose();
base.OnPaint(e);
}
Happy Coding!
Assuming WPF, just plop a menu thats taller than its wider and add menu items

Change the borderColor of the TextBox

How can I change the BorderColor of the Textbox when a user Clicks on it or focuses on it?
You can handle WM_NCPAINT message of TextBox and draw a border on the non-client area of control if the control has focus. You can use any color to draw border:
using System;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class ExTextBox : TextBox
{
[DllImport("user32")]
private static extern IntPtr GetWindowDC(IntPtr hwnd);
private const int WM_NCPAINT = 0x85;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == WM_NCPAINT && this.Focused)
{
var dc = GetWindowDC(Handle);
using (Graphics g = Graphics.FromHdc(dc))
{
g.DrawRectangle(Pens.Red, 0, 0, Width - 1, Height - 1);
}
}
}
}
Result
The painting of borders while the control is focused is completely flicker-free:
BorderColor property for TextBox
In the current post I just change the border color on focus. You can also add a BorderColor property to the control. Then you can change border-color based on your requirement at design-time or run-time. I've posted a more completed version of TextBox which has BorderColor property:
in the following post:
BorderColor property for TextBox
try this
bool focus = false;
private void Form1_Paint(object sender, PaintEventArgs e)
{
if (focus)
{
textBox1.BorderStyle = BorderStyle.None;
Pen p = new Pen(Color.Red);
Graphics g = e.Graphics;
int variance = 3;
g.DrawRectangle(p, new Rectangle(textBox1.Location.X - variance, textBox1.Location.Y - variance, textBox1.Width + variance, textBox1.Height +variance ));
}
else
{
textBox1.BorderStyle = BorderStyle.FixedSingle;
}
}
private void textBox1_Enter(object sender, EventArgs e)
{
focus = true;
this.Refresh();
}
private void textBox1_Leave(object sender, EventArgs e)
{
focus = false;
this.Refresh();
}
This is an ultimate solution to set the border color of a TextBox:
public class BorderedTextBox : UserControl
{
TextBox textBox;
public BorderedTextBox()
{
textBox = new TextBox()
{
BorderStyle = BorderStyle.FixedSingle,
Location = new Point(-1, -1),
Anchor = AnchorStyles.Top | AnchorStyles.Bottom |
AnchorStyles.Left | AnchorStyles.Right
};
Control container = new ContainerControl()
{
Dock = DockStyle.Fill,
Padding = new Padding(-1)
};
container.Controls.Add(textBox);
this.Controls.Add(container);
DefaultBorderColor = SystemColors.ControlDark;
FocusedBorderColor = Color.Red;
BackColor = DefaultBorderColor;
Padding = new Padding(1);
Size = textBox.Size;
}
public Color DefaultBorderColor { get; set; }
public Color FocusedBorderColor { get; set; }
public override string Text
{
get { return textBox.Text; }
set { textBox.Text = value; }
}
protected override void OnEnter(EventArgs e)
{
BackColor = FocusedBorderColor;
base.OnEnter(e);
}
protected override void OnLeave(EventArgs e)
{
BackColor = DefaultBorderColor;
base.OnLeave(e);
}
protected override void SetBoundsCore(int x, int y,
int width, int height, BoundsSpecified specified)
{
base.SetBoundsCore(x, y, width, textBox.PreferredHeight, specified);
}
}
WinForms was never good at this and it's a bit of a pain.
One way you can try is by embedding a TextBox in a Panel and then manage the drawing based on focus from there:
public class BorderTextBox : Panel {
private Color _NormalBorderColor = Color.Gray;
private Color _FocusBorderColor = Color.Blue;
public TextBox EditBox;
public BorderTextBox() {
this.DoubleBuffered = true;
this.Padding = new Padding(2);
EditBox = new TextBox();
EditBox.AutoSize = false;
EditBox.BorderStyle = BorderStyle.None;
EditBox.Dock = DockStyle.Fill;
EditBox.Enter += new EventHandler(EditBox_Refresh);
EditBox.Leave += new EventHandler(EditBox_Refresh);
EditBox.Resize += new EventHandler(EditBox_Refresh);
this.Controls.Add(EditBox);
}
private void EditBox_Refresh(object sender, EventArgs e) {
this.Invalidate();
}
protected override void OnPaint(PaintEventArgs e) {
e.Graphics.Clear(SystemColors.Window);
using (Pen borderPen = new Pen(this.EditBox.Focused ? _FocusBorderColor : _NormalBorderColor)) {
e.Graphics.DrawRectangle(borderPen, new Rectangle(0, 0, this.ClientSize.Width - 1, this.ClientSize.Height - 1));
}
base.OnPaint(e);
}
}
Using OnPaint to draw a custom border on your controls is fine. But know how to use OnPaint to keep efficiency up, and render time to a minimum. Read this if you are experiencing a laggy GUI while using custom paint routines: What is the right way to use OnPaint in .Net applications?
Because the accepted answer of PraVn may seem simple, but is actually inefficient. Using a custom control, like the ones posted in the answers above is way better.
Maybe the performance is not an issue in your application, because it is small, but for larger applications with a lot of custom OnPaint routines it is a wrong approach to use the way PraVn showed.
set Text box Border style to None
then write this code to container form "paint" event
private void Form1_Paint(object sender, PaintEventArgs e)
{
System.Drawing.Rectangle rect = new Rectangle(TextBox1.Location.X,
TextBox1.Location.Y, TextBox1.ClientSize.Width, TextBox1.ClientSize.Height);
rect.Inflate(1, 1); // border thickness
System.Windows.Forms.ControlPaint.DrawBorder(e.Graphics, rect,
Color.DeepSkyBlue, ButtonBorderStyle.Solid);
}
With PictureBox1
.Visible = False
.Width = TextBox1.Width + 4
.Height = TextBox1.Height + 4
.Left = TextBox1.Left - 2
.Top = TextBox1.Top - 2
.SendToBack()
.Visible = True
End With
Here is my complete Flat TextBox control that supports themes including custom border colors in normal and focused states.
The control uses the same concept mentioned by Reza Aghaei https://stackoverflow.com/a/38405319/5514131 ,however the FlatTextBox control is more customizable and flicker-free.
The control handles the WM_NCPAINT window message in a better way to help eliminate flicker.
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = WindowMessage.WM_NCPAINT AndAlso _drawBorder AndAlso Not DesignMode Then 'Draw the control border
Dim w As Integer
Dim h As Integer
Dim clip As Rectangle
Dim hdc As IntPtr
Dim clientRect As RECT = Nothing
GetClientRect(Handle, clientRect)
Dim windowRect As RECT = Nothing
GetWindowRect(Handle, windowRect)
w = windowRect.Right - windowRect.Left
h = windowRect.Bottom - windowRect.Top
clip = New Rectangle(CInt((w - clientRect.Right) / 2), CInt((h - clientRect.Bottom) / 2), clientRect.Right, clientRect.Bottom)
hdc = GetWindowDC(Handle)
Using g As Graphics = Graphics.FromHdc(hdc)
g.SetClip(clip, CombineMode.Exclude)
Using sb = New SolidBrush(BackColor)
g.FillRectangle(sb, 0, 0, w, h)
End Using
Using p = New Pen(If(Focused, _borderActiveColor, _borderNormalColor), BORDER_WIDTH)
g.DrawRectangle(p, 0, 0, w - 1, h - 1)
End Using
End Using
ReleaseDC(Handle, hdc)
Return
End If
MyBase.WndProc(m)
End Sub
I have removed the default BorderStyle property and replaced it with a simple boolean DrawBorder property that controls whether to draw a border around the control or not.
Use the BorderNormalColor property to specify the border color when the TextBox has no focus, and the BorderActiveColor property to specify the border color when the control receives focus.
The FlatTextBox comes with two themes VS2019 Dark and VS2019 Light, use the Theme property to switch between them.
Complete FlatTextBox control code written in VB.NET
https://gist.github.com/ahmedosama007/37fe2004183a51a4ea0b4a6dcb554176

C# Win Forms tab Control tab width error

I have custom tab control where OnPaint method is override.
Then strange growth of tabs occurs. Tabs getting bigger (padding getting bigger) and they width depends on length of the text.
When I use default Tab Control - padding is OK. How to avoid this situation when I use UserPaint?
partial class Tab : TabControl
{
public Tab()
{
InitializeComponent();
Init();
}
private void Init()
{
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.SetStyle(ControlStyles.DoubleBuffer, true);
this.SetStyle(ControlStyles.ResizeRedraw, true);
}
protected override void OnPaint(PaintEventArgs e)
{
DrawTabPane(e.Graphics);
}
private void DrawTabPane(Graphics g)
{
if (!Visible)
return;
// here we draw our tabs
for (int i = 0; i < this.TabCount; i++)
DrawTab(g, this.TabPages[i], i);
}
internal void DrawTab(Graphics g, TabPage tabPage, int nIndex)
{
Rectangle recBounds = this.GetTabRect(nIndex);
RectangleF tabTextArea = recBounds;
Point[] pt = new Point[4];
pt[0] = new Point(recBounds.Left + 1, recBounds.Bottom);
pt[1] = new Point(recBounds.Left + 1, recBounds.Top + 1);
pt[2] = new Point(recBounds.Right - 1, recBounds.Top + 1);
pt[3] = new Point(recBounds.Right - 1, recBounds.Bottom);
Brush br = new SolidBrush(clr_tab_norm);
g.FillPolygon(br, pt);
br.Dispose();
StringFormat stringFormat = new StringFormat();
stringFormat.Alignment = StringAlignment.Center;
stringFormat.LineAlignment = StringAlignment.Center;
br = new SolidBrush(clr_txt);
g.DrawString(tabPage.Text, Font, br, tabTextArea, stringFormat);
}
}
Turning on ControlStyles.UserPaint for controls that are built into Windows, like TabControl, is not the proper thing to do. I assume the bug is in GetTabRect(), it isn't visible in the snippet.
Instead, you should use the TabControl.DrawMode property and implement the DrawItem event. There's a good example in the MSDN Library.
It would appear from the image that your code is setting the size of the tabs to be wider than they need to be. The extra padding is present in all your tabs but it is just more visible in the tabs with longer text.
I can't be sure why this is but I'd guess that the code to calculate the size of the tabs (based on font metrics) is using a different font from that used to draw the tabs.

Transparent controls on painted form

Using C# WinForms I've got a form that I'm painting a gradient background on it from say light blue to dark blue. I've got some buttons and a couple of labels on it which I've already tried setting the backcolor to transparent as I want the background to show through - especially for the labels, but that doesn't seem to work.
But I've noticed that the painting is also paining the labels, even though the code is only on the form_paint event.
How can I make the labels not contain the same grade shading, but to be transparent? e.g. the labels at the top of the form will be light blue, but the ones at the bottom will be dark blue.
private void frmOptions_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
Rectangle rectangle = e.ClipRectangle;
using (Brush aBrush = new LinearGradientBrush(rectangle, Color.LightBlue, Color.DarkBlue, LinearGradientMode.Vertical))
{
e.Graphics.FillRectangle(aBrush, rectangle);
}
}
Many thanks for any help on this.
EDIT:
I've just been stepping through the lines of code to debug it and noticed that the paint event is being called for EACH object on the form - the labels & buttons, the rectangle on line 2 above has the size of each time. So I'm now really confused why it's getting called for each object on the form.
EDIT 2:
Just been stepping through the code of my quick test project (see comment below) and also noticed that the paint event is beening called for each object. the differenece is that the test project doesn't have the line "Rectangle rectangle = e.ClipRectangle;" - it basically uses the width & height of the form. where my code above sets rectangle to ClipRectangle - which is the size of each label & button... Is this Normal behaviour ? If so, then it looks like I need to folow the answer below by Reniuz.
EDIT 3
I've change the cliprectangle line to "Rectangle rectangle = new Rectangle(0, 0, this.Width, this.Height);" This seems to make the transparent look how it should, but the paint event is still being called for every object on the form - good job there is only 7 of the, so it's drawing 8 times :( Think I'll still look at the "TransparentLabel" code to test that.
Take a look at this article.
Edit:
Also you can use this code:
public class TransparentLabel : Control
{
public TransparentLabel(Label label)
{
//setting default properties
this.Text = label.Text;
this.Font = label.Font;
this.Location = label.Location;
this.Size = label.Size;
this.Parent = label.Parent;
this.BringToFront();
label.Dispose();
TabStop = false;
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
protected override void OnPaintBackground(PaintEventArgs e)
{
// do nothing
}
protected override void OnPaint(PaintEventArgs e)
{
DrawText();
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if(m.Msg == 0x000F)
{
DrawText();
}
}
private void DrawText()
{
using(Graphics graphics = CreateGraphics())
using(SolidBrush brush = new SolidBrush(ForeColor))
{
SizeF size = graphics.MeasureString(Text, Font);
// first figure out the top
float top = 0;
switch(textAlign)
{
case ContentAlignment.MiddleLeft:
case ContentAlignment.MiddleCenter:
case ContentAlignment.MiddleRight:
top = (Height - size.Height) / 2;
break;
case ContentAlignment.BottomLeft:
case ContentAlignment.BottomCenter:
case ContentAlignment.BottomRight:
top = Height - size.Height;
break;
}
float left = -1;
switch(textAlign)
{
case ContentAlignment.TopLeft:
case ContentAlignment.MiddleLeft:
case ContentAlignment.BottomLeft:
if(RightToLeft == RightToLeft.Yes)
left = Width - size.Width;
else
left = -1;
break;
case ContentAlignment.TopCenter:
case ContentAlignment.MiddleCenter:
case ContentAlignment.BottomCenter:
left = (Width - size.Width) / 2;
break;
case ContentAlignment.TopRight:
case ContentAlignment.MiddleRight:
case ContentAlignment.BottomRight:
if(RightToLeft == RightToLeft.Yes)
left = -1;
else
left = Width - size.Width;
break;
}
graphics.DrawString(Text, Font, brush, left, top);
}
}
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
RecreateHandle();
}
}
public override RightToLeft RightToLeft
{
get
{
return base.RightToLeft;
}
set
{
base.RightToLeft = value;
RecreateHandle();
}
}
public override Font Font
{
get
{
return base.Font;
}
set
{
base.Font = value;
RecreateHandle();
}
}
private ContentAlignment textAlign = ContentAlignment.TopLeft;
public ContentAlignment TextAlign
{
get { return textAlign; }
set
{
textAlign = value;
RecreateHandle();
}
}
}
Transparent label will replace your existing label so you can use it like this:
TransparentLabel transparentLabel1 = new TransparentLabel(label1);
TransparentLabel transparentLabel2 = new TransparentLabel(label2);
and then you should see the result(design time on left, run time on right):

C#: Overriding OnPaint on ProgressBar not working?

Was thinking it should be pretty easy to create a ProgressBar that drew some text upon itself. However, I am not quite sure what is happening here...
I added the following two overrides:
protected override void OnPaintBackground(PaintEventArgs pevent)
{
base.OnPaintBackground(pevent);
var flags = TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine | TextFormatFlags.WordEllipsis;
TextRenderer.DrawText(pevent.Graphics, "Hello", Font, Bounds, Color.Black, flags);
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
var flags = TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine | TextFormatFlags.WordEllipsis;
TextRenderer.DrawText(e.Graphics, "Hello", Font, Bounds, Color.Black, flags);
}
However, I get no text, and the methods doesn't even seem to be called. What is going on here?
Update: Thanks to the two answers so far, I have gotten it to actually call the OnPaint by using SetStyle(ControlStyles.UserPaint, true), and I have gotten it to draw the text in the right place by sending in new Rectangle(0, 0, Width, Height) instead of Bounds.
I do get text now, but the ProgressBar is gone... and the point was kind of to have the text on top of the ProgressBar. Any idea how I can solve this?
You could override WndProc and catch the WmPaint message.
The example below paints the Text property of the progressbar in its center.
public class StatusProgressBar : ProgressBar
{
const int WmPaint = 15;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
switch (m.Msg)
{
case WmPaint:
using (var graphics = Graphics.FromHwnd(Handle))
{
var textSize = graphics.MeasureString(Text, Font);
using(var textBrush = new SolidBrush(ForeColor))
graphics.DrawString(Text, Font, textBrush, (Width / 2) - (textSize.Width / 2), (Height / 2) - (textSize.Height / 2));
}
break;
}
}
}
I needed to do this myself and I thought that I would post a simplified example of my solution since I could not find any examples. It is actually pretty simple if you use the ProgressBarRenderer class:
class MyProgressBar : ProgressBar
{
public MyProgressBar()
{
this.SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
}
protected override void OnPaint(PaintEventArgs e)
{
Rectangle rect = this.ClientRectangle;
Graphics g = e.Graphics;
ProgressBarRenderer.DrawHorizontalBar( g, rect );
rect.Inflate(-3, -3);
if ( this.Value > 0 )
{
Rectangle clip = new Rectangle( rect.X, rect.Y, ( int )Math.Round( ( ( float )this.Value / this.Maximum ) * rect.Width ), rect.Height );
ProgressBarRenderer.DrawHorizontalChunks(g, clip);
}
// assumes this.Maximum == 100
string text = this.Value.ToString( ) + '%';
using ( Font f = new Font( FontFamily.GenericMonospace, 10 ) )
{
SizeF strLen = g.MeasureString( text, f );
Point location = new Point( ( int )( ( rect.Width / 2 ) - ( strLen.Width / 2 ) ), ( int )( ( rect.Height / 2 ) - ( strLen.Height / 2 ) ) );
g.DrawString( text, f, Brushes.Black, location );
}
}
}
Your problem is that you're passing in Bounds as your Rectangle parameter. Bounds contains the Height and Width of your control, which is what you want, but it also contains the Top and Left properties of your control, relative to the parent form, so your "Hello" is being offset on the control by however much your control is offset on its parent form.
Replace Bounds with new Rectangle(0, 0, this.Width, this.Height) and you should see your "Hello".
It seems that if you call 'SetStyle(ControlStyles.UserPaint, true)' the standard OnPaint method implemented for ProgressBar could not be invoked (using base.OnPaint(e) does not work at all). The strangest thing is that even if you actually create a UserControl, and try to draw draw some text upon the progress bar... it doesn't seem to work too... Of course you may place a Label on top of it... but I suppose it is not actually what you wanted to achieve.
Ok, it seems that I have managed to solve this problem. It is although a little complicated. First you need to create a transparent Label control. Code below:
public class TransparentLabel : System.Windows.Forms.Label
{
public TransparentLabel()
{
this.SetStyle(ControlStyles.Opaque, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20;
return cp;
}
}
}
Second thing is to create UserControl, place a ProgressBar on it (Dock=Fill) - this will be the control that we will use instead of standard ProgressBar. Code:
public partial class UserControl2 : UserControl
{
public UserControl2()
{
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs e)
{
this.progressBar1.SendToBack();
this.transparentLabel1.BringToFront();
this.transparentLabel1.Text = this.progressBar1.Value.ToString();
this.transparentLabel1.Invalidate();
}
public int Value
{
get { return this.progressBar1.Value; }
set
{
this.progressBar1.Value = value;
}
}
}
The strange thing with ProgressBar is that it 'overdraws' the controls that are being placed upon it, so it is needed to send progressbar to back, and bring the label control to front. I haven't found more elegant solution at the moment.
This works, the label is being displayed on the progressbar, the background of the label control is transparent, so I think it looks like you wanted it to look :)
I may share my sample code if you wish...
Oh, btw. this strange behaviour of ProgressBar control that I have mentioned, is responsible for that it is not possible to use Graphics object to draw anything on a control that derives from ProgressBar. The text (or whatever you draw using Graphics object) is actually being drawn but... behind the ProgressBar control (if you take a closer look, you may see this user drawn things flickering when the Value of the ProgressBar changes and it need to repaint itself).
Here's another solution along with other people's suggestions. I subclassed the progressbar control to make this work. I mixed and matched codes from various places for this. The paint event could be cleaner, but that's for you to do ;)
public class LabeledProgressBar: ProgressBar
{
private string labelText;
public string LabelText
{
get { return labelText; }
set { labelText = value; }
}
public LabeledProgressBar() : base()
{
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
this.Paint += OnLabelPaint;
}
public void OnLabelPaint(object sender, PaintEventArgs e)
{
using(Graphics gr = this.CreateGraphics())
{
string str = LabelText + string.Format(": {0}%", this.Value);
LinearGradientBrush brBG = new LinearGradientBrush(e.ClipRectangle,
Color.GreenYellow, Color.Green, LinearGradientMode.Horizontal);
e.Graphics.FillRectangle(brBG, e.ClipRectangle.X, e.ClipRectangle.Y,
e.ClipRectangle.Width * this.Value / this.Maximum, e.ClipRectangle.Height);
e.Graphics.DrawString(str, SystemFonts.DefaultFont,Brushes.Black,
new PointF(this.Width / 2 - (gr.MeasureString(str, SystemFonts.DefaultFont).Width / 2.0F),
this.Height / 2 - (gr.MeasureString(str, SystemFonts.DefaultFont).Height / 2.0F)));
}
}
}

Categories