Problems with user controls and Tab order - c#

I've created a project with multiple user controls to support transparency, gradient and themes for winforms.
I was looking for a way to create a replacement for textbox, since winforms textboxes are not using the regular OnPaint and OnPaintBackground that other winforms control use, and sure enough, I've found something I can work with right here on stackoverflow. Brian's comment gave me the solution - Wrapping a transparent RichTextBox inside my own control.
However, this posed a new problem that I can't figure out how to solve - The TabIndex property dosn't operate as expected.
With normal textboxes, when you have multiple textboxes and each one have a different tab index, the focus goes from one textbox to the other in the order specified by the tab index. In my case, it doesn't. Instead, it's unpredictable.
I've tried multiple forms with different layouts and controls on them, but I can't seem to find any predictable pattern of behavior that would suggest the problem.
Here is the relevant control's code (the parent, ZControl inherits UserControl if that matters):
/// <summary>
/// A stylable textbox.
/// <Remarks>
/// The solution for writing a stylable textbox was inspired by this SO post and Brian's comment:
/// https://stackoverflow.com/a/4360341/3094533
/// </Remarks>
/// </summary>
[DefaultEvent("TextChanged")]
public partial class ZTextBox : ZControl
{
#region ctor
public ZTextBox()
{
TextBox = new TransparentRichTextBox();
TextBox.BackColor = Color.Transparent;
TextBox.BorderStyle = BorderStyle.None;
TextBox.Multiline = false;
TextBox.TextChanged += TextBox_TextChanged;
TextBox.TabStop = true;
TextBox.AcceptsTab = false;
InitializeComponent();
AdjustTextBoxRectangle();
this.Controls.Add(TextBox);
this.RoundedCorners.PropertyChanged += RoundedCorners_PropertyChanged;
}
#endregion ctor
#region properties
private TransparentRichTextBox TextBox { get; }
public override string Text
{
get
{
return TextBox.Text;
}
set
{
TextBox.Text = value;
}
}
[DefaultValue(false)]
public bool Multiline
{
get
{
return this.TextBox.Multiline;
}
set
{
this.TextBox.Multiline = value;
}
}
public override Font Font
{
get
{
return base.Font;
}
set
{
if (base.Font != value)
{
base.Font = value;
if (TextBox != null)
{
TextBox.Font = value;
}
}
}
}
public new int TabIndex
{
get
{
return this.TextBox.TabIndex;
}
set
{
this.TextBox.TabIndex = value;
}
}
#region hidden properties
[
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never)
]
public override Color ForeColor
{
get
{
return TextBox.ForeColor;
}
set
{
TextBox.ForeColor = value;
}
}
[
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never)
]
public override ContentAlignment TextAlign
{
get
{
return base.TextAlign;
}
set
{
base.TextAlign = value;
}
}
[
DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden),
Browsable(false),
EditorBrowsable(EditorBrowsableState.Never)
]
public override Point TextLocationOffset
{
get
{
return base.TextLocationOffset;
}
set
{
base.TextLocationOffset = value;
}
}
#endregion hidden properties
#endregion properties
#region methods
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
TextBox.Focus();
}
protected override void DrawText(Graphics graphics, string text, ContentAlignment textAlign, Point locationOffset, Size stringSize)
{
// Do nothing - The transparent rich textbox is responsible for drawing the text...
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
AdjustTextBoxRectangle();
}
private void AdjustTextBoxRectangle()
{
var corners = this.RoundedCorners.Corners;
var leftAdjustment = ((corners & RoundedEdges.TopLeft) == RoundedEdges.TopLeft || (corners & RoundedEdges.BottomLeft) == RoundedEdges.BottomLeft) ? this.RoundedCorners.ArcSize / 2 : 0;
var rightAdjustment = ((corners & RoundedEdges.TopRight) == RoundedEdges.TopRight || (corners & RoundedEdges.BottomRight) == RoundedEdges.BottomRight) ? this.RoundedCorners.ArcSize / 2 : 0;
TextBox.Top = 0;
TextBox.Left = leftAdjustment;
TextBox.Width = this.Width - leftAdjustment - rightAdjustment;
TextBox.Height = this.Height;
}
#endregion methods
#region event handlers
private void RoundedCorners_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
AdjustTextBoxRectangle();
}
private void TextBox_TextChanged(object sender, EventArgs e)
{
OnTextChanged(e);
}
#endregion event handlers
#region private classes
private class TransparentRichTextBox : RichTextBox
{
public TransparentRichTextBox()
{
this.SetStyle(ControlStyles.SupportsTransparentBackColor, true);
this.SetStyle(ControlStyles.Opaque, true);
this.SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
}
protected override CreateParams CreateParams
{
get
{
CreateParams parms = base.CreateParams;
parms.ExStyle |= 0x20; // Turn on WS_EX_TRANSPARENT
return parms;
}
}
}
#endregion private classes
}
And the designer code, if that's relevant:
partial class ZTextBox
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.SuspendLayout();
//
// ZTextBox
//
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None;
this.Name = "ZTextBox";
this.RoundedCorners.ArcSize = 50;
this.RoundedCorners.Corners = Zohar.UserControls.RoundedEdges.None;
this.Size = new System.Drawing.Size(100, 20);
this.Style.DisabledStyle.BackColor = System.Drawing.Color.Empty;
this.Style.DisabledStyle.BackgroundImage = null;
this.Style.DisabledStyle.BorderColor = System.Drawing.Color.Empty;
this.Style.DisabledStyle.ForeColor = System.Drawing.Color.Empty;
this.Style.DisabledStyle.Gradient.Angle = 0F;
this.Style.DisabledStyle.Gradient.BackColor = System.Drawing.Color.Empty;
this.Style.DisabledStyle.Image = null;
this.Style.DisabledStyle.Name = null;
this.Style.EnabledStyle.BackColor = System.Drawing.Color.Empty;
this.Style.EnabledStyle.BackgroundImage = null;
this.Style.EnabledStyle.BorderColor = System.Drawing.Color.Empty;
this.Style.EnabledStyle.ForeColor = System.Drawing.Color.Empty;
this.Style.EnabledStyle.Gradient.Angle = 0F;
this.Style.EnabledStyle.Gradient.BackColor = System.Drawing.Color.Empty;
this.Style.EnabledStyle.Image = null;
this.Style.EnabledStyle.Name = null;
this.Style.HoverStyle.BackColor = System.Drawing.Color.Empty;
this.Style.HoverStyle.BackgroundImage = null;
this.Style.HoverStyle.BorderColor = System.Drawing.Color.Empty;
this.Style.HoverStyle.ForeColor = System.Drawing.Color.Empty;
this.Style.HoverStyle.Gradient.Angle = 0F;
this.Style.HoverStyle.Gradient.BackColor = System.Drawing.Color.Empty;
this.Style.HoverStyle.Image = null;
this.Style.HoverStyle.Name = null;
this.ResumeLayout(false);
}
#endregion
}

The issue is caused by the following code:
public new int TabIndex
{
get
{
return this.TextBox.TabIndex;
}
set
{
this.TextBox.TabIndex = value;
}
}
You should never do this for UserControl (actually for any control). The documentation for Control.TabIndex property states:
Gets or sets the tab order of the control within its container.
In other words, the control TabIndex property is not global for the form, but scoped to the control container (parent).
The effect is that the form designer code where your control resides will call the shadow TabOrder setter, but the tab navigation handling will simply call the base Control property, leading to undetermined behavior.
Also note that setting the TabIndex of the inner TextBox makes no any sense since it's the only control inside the container (your control). While what you really need is to set the TabIndex of your control inside its container.
With that being said, simply remove the above code and everything will work as expected.

that's probably because you have not added your textbox's in order or maybe you deleted some of them while adding them, anyway you can choose the order on the properties of the controls => TabIndex

Related

WinForms BindingComplete is triggered by another property

I have a TextBox whose Text property is bound to a ViewModel property called Name and a Button whose Enable property is bound to a ViewModel property called IsBusy.
And the TexBox Binding implements BindingComplete that changes the TextBox's background color if an exception is thrown by the property.
The problem is that BindingComplete is also raised when IsBusy property changes which the IsBusy property binding is not subscribing, and the BindingComplete event argument has the BindingField and BindingMember of "Name" and the associated control of this binding is TextBox.
Why IsBusy property is raising BindingComplete that is bound to the Name property?
Because of this, after I force TextBox binding to call WriteValue() to validate the property which sets the background color as expected when there is an error, and assign a true value to IsBusy property to indicate it is busy, it sets the background color back to White, because IsBusy raises TextBox's BindingComplete without an exception.
FYI, I'm intentionally raising PropertyChanged events and throwing exceptions because I want the incorrect values to be entered for users to review.
This is just a demonstration of the problem I'm having. I have more complex business requirements when errors occur. So, it would be greatly appreciated if you could provide an explanation of why this is happening and how to prevent it or fix it.
public partial class BindingTestForm : Form
{
private TestViewModel viewModel = new TestViewModel();
public BindingTestForm()
{
InitializeComponent();
var textBoxTextBinding = new Binding(nameof(TextBox.Text), viewModel, nameof(TestViewModel.Name), true, DataSourceUpdateMode.OnPropertyChanged);
textBoxTextBinding.BindingComplete += TextBoxTextBinding_BindingComplete;
NameTextBox.DataBindings.Add(textBoxTextBinding);
var enableBinding = new Binding(nameof(Control.Enabled), viewModel, nameof(TestViewModel.IsBusy));
enableBinding.Format += EnableBinding_Format;
SaveButton.DataBindings.Add(enableBinding);
Load += BindingTestForm_Load;
}
private void EnableBinding_Format(object sender, ConvertEventArgs e)
{
e.Value = !(bool)e.Value;
}
private void TextBoxTextBinding_BindingComplete(object sender, BindingCompleteEventArgs e)
{
if (e.Exception != null)
NameTextBox.BackColor = Color.Red;
else
NameTextBox.BackColor = Color.White;
}
private async void BindingTestForm_Load(object sender, EventArgs e)
{
viewModel.IsBusy = true;
await Task.Run(async () =>
{
await Task.Delay(2000);
});
viewModel.IsBusy = false;
}
public class TestViewModel: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string _name;
public string Name
{
get => _name;
set
{
_name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TestViewModel.Name)));
if (string.IsNullOrWhiteSpace(_name))
throw new Exception("Please enter package name.");
if (_name.Length > 5)
throw new Exception("Max length 5.");
}
}
public bool _isBusy = false;
public bool IsBusy
{
get => _isBusy;
set
{
_isBusy = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TestViewModel.IsBusy)));
}
}
}
private async void SaveButton_Click(object sender, EventArgs e)
{
NameTextBox.DataBindings[nameof(TextBox.Text)].WriteValue();
viewModel.IsBusy = true;
await Task.Run(async () =>
{
await Task.Delay(2000);
});
viewModel.IsBusy = false;
}
}
partial class BindingTestForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.NameTextBox = new System.Windows.Forms.TextBox();
this.SaveButton = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// NameTextBox
//
this.NameTextBox.Location = new System.Drawing.Point(13, 13);
this.NameTextBox.Name = "NameTextBox";
this.NameTextBox.Size = new System.Drawing.Size(100, 20);
this.NameTextBox.TabIndex = 0;
//
// SaveButton
//
this.SaveButton.Location = new System.Drawing.Point(13, 75);
this.SaveButton.Name = "SaveButton";
this.SaveButton.Size = new System.Drawing.Size(75, 23);
this.SaveButton.TabIndex = 1;
this.SaveButton.Text = "Save";
this.SaveButton.UseVisualStyleBackColor = true;
this.SaveButton.Click += new System.EventHandler(this.SaveButton_Click);
//
// BindingTestForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.SaveButton);
this.Controls.Add(this.NameTextBox);
this.Name = "BindingTestForm";
this.Text = "FlowLayoutTestForm";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.TextBox NameTextBox;
private System.Windows.Forms.Button SaveButton;
}
I must confess that I don't know why this happens.
As an alternative solution, I suggest you to create a text box control being able to display a red border as well as a tooltip.
public class TextBoxEx : TextBox
{
[DllImport("user32")]
private static extern IntPtr GetWindowDC(IntPtr hwnd);
private const int WM_NCPAINT = 0x85;
private readonly ToolTip _toolTip = new ToolTip();
private bool _hasError;
private string _toolTipText;
public string ToolTipText
{
get {
return _toolTipText;
}
set {
if (value != _toolTipText) {
_toolTipText = value;
if (String.IsNullOrEmpty(_toolTipText)) {
_toolTip.Hide(this);
_hasError = false;
} else {
_toolTip.Show(_toolTipText, this, 3000);
_hasError = true;
}
// This is required to update the border color immediately.
var m = Message.Create(Handle, WM_NCPAINT, IntPtr.Zero, IntPtr.Zero);
WndProc(ref m);
}
}
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (_hasError && m.Msg == WM_NCPAINT) {
var dc = GetWindowDC(Handle);
using (Graphics g = Graphics.FromHdc(dc)) {
g.DrawRectangle(Pens.Red, 0, 0, Width - 1, Height - 1);
}
}
}
}
Setting the tooltip text to something different than null or empty show the tooltip and shows a red border.
The data model (view model in a MVVM context) must then provide an error message and implement INotifyPropertyChanged. Example:
public class Model : INotifyPropertyChanged
{
private string _text;
public string Text
{
get { return _text; }
set {
if (value != _text) {
_text = value;
OnPropertyChanged(nameof(Text));
OnPropertyChanged(nameof(TextErrorMessage));
}
}
}
public string TextErrorMessage
{
get {
return _text?.Length == 4
? null
: "Please enter a text of length 4";
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
Now bind the Text property of the TextBoxEx to the Text property of the model and bind the ToolTipText property of the TextBoxEx to the TextErrorMessage property of the model. Set both bindings to OnPropertyChanged and assign a model object to the DataSource property of the BindingSource component on your form.
Now you have a full functional MVVM pattern requiring no code in the form other than assigning the model to the binding source.

Custom control: Adding custom properties from another class [duplicate]

Im trying to create a custom control that inherits NumericUpDown to show a settable unit.
This is (visually) what I've got so far:
My Code: Looks a bit long, but isnt doing that much
class NumericUpDownUnit : NumericUpDown
{
public event EventHandler ValueChanged;
/// <summary>
/// Constructor creates a label
/// </summary>
public NumericUpDownUnit()
{
this.TextChanged += new EventHandler(TextChanged_Base);
this.Maximum = 100000000000000000;
this.DecimalPlaces = 5;
this.Controls.Add(lblUnit);
lblUnit.BringToFront();
UpdateUnit();
}
public void TextChanged_Base(object sender, EventArgs e)
{
if(ValueChanged != null)
{
this.ValueChanged(sender, e);
}
}
/// <summary>
/// My designer property
/// </summary>
private Label lblUnit = new Label();
[Description("The text to show as the unit.")]
public string Unit
{
get
{
return this.lblUnit.Text;
}
set
{
this.lblUnit.Text = value;
UpdateUnit();
}
}
/// <summary>
/// When unit has changed, calculate new label-size
/// </summary>
public void UpdateUnit()
{
System.Drawing.Size size = TextRenderer.MeasureText(lblUnit.Text, lblUnit.Font);
lblUnit.Padding = new Padding(0, 0, 0, 3);
lblUnit.Size = new System.Drawing.Size(size.Width, this.Height);
lblUnit.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
lblUnit.BackColor = System.Drawing.Color.Transparent;
lblUnit.Location = new System.Drawing.Point(this.Width-lblUnit.Width-17, 0);
}
/// <summary>
/// If text ends with seperator, skip updating text as it would parse without decimal palces
/// </summary>
protected override void UpdateEditText()
{
if (!this.Text.EndsWith(".") && !this.Text.EndsWith(","))
Text = Value.ToString("0." + new string('#', DecimalPlaces));
}
/// <summary>
/// Culture fix
/// </summary>
protected override void OnKeyPress(KeyPressEventArgs e)
{
if (e.KeyChar.Equals('.') || e.KeyChar.Equals(','))
{
e.KeyChar = System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator.ToCharArray()[0];
}
base.OnKeyPress(e);
}
/// <summary>
/// When size changes, call UpdateUnit() to recalculate the lable-size
/// </summary>
protected override void OnResize(EventArgs e)
{
UpdateUnit();
base.OnResize(e);
}
/// <summary>
/// Usability | On enter select everything
/// </summary>
protected override void OnEnter(EventArgs e)
{
this.Select(0, this.Text.Length);
base.OnMouseEnter(e);
}
/// <summary>
/// If, when leaving, text ends with a seperator, cut it out
/// </summary>
protected override void OnLeave(EventArgs e)
{
if(this.Text.EndsWith(System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat.NumberDecimalSeparator))
{
this.Text = this.Text.Substring(0, this.Text.Length - 1);
}
base.OnLeave(e);
}
}
My problem:
The lable is currently covering the end of the box. So if a big value comes in (or the size is low) it gets covered by the label as seen in here:
I know that the NumericUpDown has something like a scroll-function when a typed in value is longer than the size of the inputbox. This is triggered at the end of the box.
Is there in any way the possibility of setting up something like padding for the text inside the box? For example setting the padding on the right to the size of my label?
I like this custom control pretty much but this one last thing is annoying.
Unfortunately I dont know how to lookup the properties of an existing control as for example there is a method called UpdateEditText(). Maybe someone can tell me how to lookup this base functions/properties.
Thanks a lot!
NumericUpDown is a control which inherits from UpDownBase composite control. It contains an UpDownEdit and an UpDownButtons control. The UpDownEdit is a TextBox. You can change appearance of the control and its children. For example, you can add a Label to the textbox control and dock it to the right of TextBox, then set text margins of textbox by sending an EM_SETMARGINS message to get such result:
Code
using System;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Windows.Forms;
public class ExNumericUpDown : NumericUpDown
{
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hwnd, int msg, int wParam, int lParam);
private const int EM_SETMARGINS = 0xd3;
private const int EC_RIGHTMARGIN = 2;
private Label label;
public ExNumericUpDown() : base()
{
var textBox = Controls[1];
label = new Label() { Text = "MHz", Dock = DockStyle.Right, AutoSize = true };
textBox.Controls.Add(label);
}
public string Label
{
get { return label.Text; }
set { label.Text = value; if (IsHandleCreated) SetMargin(); }
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
SetMargin();
}
private void SetMargin()
{
SendMessage(Controls[1].Handle, EM_SETMARGINS, EC_RIGHTMARGIN, label.Width << 16);
}
}

Setting default size/text of custom control in c#

I am creating a custom control in my C# application in order to add a new property (MyProperty below). It is inheriting from Label. One thing I would like it to do, is display at a particular size when I drag it on to my form (200x132). I'd also like it to display no text. However, no matter how I try to do this, it doesn't seem to work. I am able to set BackColor and BorderStyle with no problem, however. I'm fairly new to C#, so maybe I'm missing something obvious.
Here is my code:
using System.Drawing;
using System.Windows.Forms;
namespace MyProgram
{
public enum MyEnum
{
Value1, Value2, Value3
}
public partial class MyControl : Label
{
public MyControl()
{
BackColor = Color.LightCoral;
BorderStyle = BorderStyle.FixedSingle;
AutoSize = false;
Size = new Size(200, 132);
Text = "";
InitializeComponent();
}
protected override void OnPaint(PaintEventArgs pe)
{
base.OnPaint(pe);
}
private MyEnum myProperty;
public MyEnum MyProperty
{
get { return myProperty; }
set { myPropery = value; }
}
}
}
The answer provided via Dispersia's link has a bug, in my opinion. The text reset should happen once and then whatever a user does after that shouldn't matter. In Dispersia's link you can't actually set the text back to the control name because it will keep blanking it out.
The answer provided by cramopy doesn't technically answer your question, it is a way to do it by using the defaults on a UserControl though. You'll also need to bind the Text property of the UserControl to the label's.
The following should work while inheriting from a Label and will only reset the Text property once.
public partial class MyControl : Label
{
#region fields
private IComponentChangeService _changeService;
private bool canResetText = false;
#endregion
#region properties
protected override Size DefaultSize
{
get { return new Size(200, 132); }
}
[Browsable(false)]
public override bool AutoSize
{
get { return false; }
set { base.AutoSize = false; }
}
public override ISite Site
{
get { return base.Site; }
set
{
base.Site = value;
if (!base.DesignMode)
return;
this._changeService = (IComponentChangeService)base.GetService(typeof(IComponentChangeService));
if (this._changeService != null)
this._changeService.ComponentChanged += new ComponentChangedEventHandler(this.OnComponentChanged);
}
}
#endregion
#region constructors
public MyControl()
{
base.BackColor = Color.LightCoral;
base.BorderStyle = BorderStyle.FixedSingle;
}
#endregion
#region methods
protected override void InitLayout()
{
base.InitLayout();
this.canResetText = true;
}
private void OnComponentChanged(object sender, ComponentChangedEventArgs ce)
{
if (ce.Component != null &&
ce.Component == this &&
ce.Member.Name == "Text" &&
base.DesignMode &&
this.canResetText)
{
((MyControl)ce.Component).Text = string.Empty;
this.canResetText = false;
if (this._changeService != null)
this._changeService.ComponentChanged -= new ComponentChangedEventHandler(this.OnComponentChanged);
}
}
#endregion
}
#Dispersia reply only answers the myControl1 thing. (deleted meanwhile)
Here comes a full guide for solving your problem:
Add a new UserControl named MyLabel
Change the following within Designer Mode:
BorderStyle:= FixedSingle
Size:= 200; 132
Now Drag&Drop a new Label onto the control
Edit those Label values (also within Designer Mode):
AutoSize:= false
BackColor:= LightCoral
Dock:= Fill
Text:= clear/empty this box!! (don't write this inside the box, you really have to clear it!)
TextAlign:= MiddleCenter
Just recompile your project && add a MyLabel control from the Toolbar.
Now it show up as you wanted!!

Host TreeView in Windows Forms DataGridView Cells

I want to create a TreeView column for a DataGridView. I have followed the example in here by extending the TreeView as below.
public class TreeViewEditingControl : TreeView, IDataGridViewEditingControl
public class TreeViewCell : DataGridViewComboBoxCell // Not sure whether this should be DataGridViewTextBoxCell
This is my issue. I can see the Treeview in cells, but I don't know how to increase the height of the Cell/TreeView when user click on a cell (as ComboBox expands). Does anyone have any idea on this?
I would spawn a new borderless form with a TreeCtrl Docked inside, I've done this with a CalendarControl and it works well. The user will not know the difference if you set the upper left hand corner of the form to the upper left hand corner of the cell that is being edited. Hope this is what you are looking for.
Edit:
Here is an implementation I did for a File Selection Cell. It has a Browse button that appears in the cell when you click it for editing and it opens a FileOpenDialog. The code is lengthy, but I think you can pick out the parts you need to implement.
public class DataGridViewFileColumn : DataGridViewColumn
{
public DataGridViewFileColumn() : base(new DataGridViewFileCell())
{
BrowseLabel = "...";
SaveFullPath = false;
}
public override DataGridViewCell CellTemplate
{
get
{
return base.CellTemplate;
}
set
{
// Ensure that the cell used for the template is a DataGridViewFileCell.
if (value != null &&
!value.GetType().IsAssignableFrom(typeof(DataGridViewFileCell)))
{
throw new InvalidCastException("Must be a DataGridViewFileCell");
}
base.CellTemplate = value;
}
}
[Description("Label to place on Browse button"),Category("Appearance")]
[DefaultValue("...")]
public string BrowseLabel
{
get;
set;
}
[Description("Save full path name"), Category("Behavior")]
[DefaultValue(true)]
public bool SaveFullPath
{
get;
set;
}
}
public class DataGridViewFileCell : DataGridViewTextBoxCell
{
public DataGridViewFileCell() : base()
{
}
public override void InitializeEditingControl(int rowIndex, object
initialFormattedValue, DataGridViewCellStyle dataGridViewCellStyle)
{
// Set the value of the editing control to the current cell value.
base.InitializeEditingControl(rowIndex, initialFormattedValue,
dataGridViewCellStyle);
FileEditingControl ctl = (FileEditingControl)DataGridView.EditingControl;
// Use the default row value when Value property is null.
if (this.Value == null)
{
ctl.Filename = this.DefaultNewRowValue.ToString();
}
else
{
ctl.Filename = this.Value.ToString();
}
}
public override Type EditType
{
get
{
// Return the type of the editing control that DataGridViewFileCell uses.
return typeof(FileEditingControl);
}
}
public override Type ValueType
{
get
{
// Return the type of the value that DataGridViewFileCell contains.
return typeof(string);
}
}
}
class FileEditingControl : FileTextBox, IDataGridViewEditingControl
{
DataGridView dataGridView;
private bool valueChanged = false;
int rowIndex;
public FileEditingControl()
{
}
#region IDataGridViewEditingControl implementations
public object EditingControlFormattedValue
{
get
{
return Filename;
}
set
{
if (value is String)
{
try
{
Filename = (String)value;
}
catch
{
Filename = value.ToString();
}
}
}
}
public object GetEditingControlFormattedValue(
DataGridViewDataErrorContexts context)
{
return EditingControlFormattedValue;
}
public void ApplyCellStyleToEditingControl(
DataGridViewCellStyle dataGridViewCellStyle)
{
this.Font = dataGridViewCellStyle.Font;
}
public int EditingControlRowIndex
{
get
{
return rowIndex;
}
set
{
rowIndex = value;
}
}
public bool EditingControlWantsInputKey(
Keys key, bool dataGridViewWantsInputKey)
{
switch (key & Keys.KeyCode)
{
case Keys.Left:
case Keys.Up:
case Keys.Down:
case Keys.Right:
case Keys.Home:
case Keys.End:
case Keys.PageDown:
case Keys.PageUp:
return true;
default:
return !dataGridViewWantsInputKey;
}
}
public void PrepareEditingControlForEdit(bool selectAll)
{
}
public bool RepositionEditingControlOnValueChange
{
get
{
return false;
}
}
public DataGridView EditingControlDataGridView
{
get
{
return dataGridView;
}
set
{
dataGridView = value;
}
}
public bool EditingControlValueChanged
{
get
{
return valueChanged;
}
set
{
valueChanged = value;
}
}
public Cursor EditingPanelCursor
{
get
{
return base.Cursor;
}
}
#endregion
protected override void OnValueChanged(FileEventArgs eventargs)
{
// Notify the DataGridView that the contents of the cell
// have changed.
valueChanged = true;
this.EditingControlDataGridView.NotifyCurrentCellDirty(true);
base.OnValueChanged(eventargs);
}
}
public partial class FileTextBox : UserControl
{
#region Constructors
public FileTextBox()
{
InitializeComponent();
Tooltip = new ToolTip();
SaveFullPath = false;
AllowMultipleFiles = false;
BrowseLabel = "...";
}
#endregion Constructors
#region Properties
/// <summary>
/// Tooltip object used to show full path name
/// </summary>
private ToolTip Tooltip;
/// <summary>
/// Return the full path or just the filename?
/// </summary>
[Description("Save Full Path"), Category("Behavior")]
[DefaultValue(false)]
public bool SaveFullPath
{
get;
set;
}
/// <summary>
/// String representing the filename for this control
/// </summary>
public override string Text
{
get
{
return base.Text;
}
set
{
if (base.Text != value)
{
base.Text = value;
Tooltip.SetToolTip(this, base.Text);
Invalidate();
OnValueChanged(new FileEventArgs(base.Text));
}
}
}
[Description("Browse Label"), Category("Appearance")]
[DefaultValue("...")]
public string BrowseLabel
{
get
{
return Browse.Text;
}
set
{
Browse.Text = value;
Browse.Width = TextRenderer.MeasureText(Browse.Text, Browse.Font).Width + 8;
Browse.Location = new Point(this.Width - Browse.Width, Browse.Location.Y);
}
}
[Description("Allow Multiple Files"), Category("Behavior")]
[DefaultValue(false)]
public bool AllowMultipleFiles
{
get;
set;
}
/// <summary>
/// Selected filename (same as Text property)
/// </summary>
[Description("Filename"), Category("Data")]
public string Filename
{
get { return Text; }
set { Text = value; }
}
#endregion Properties
#region Event Handlers
/// <summary>
/// Event raised when
/// </summary>
public event EventHandler ValueChanged;
protected virtual void OnValueChanged(FileEventArgs eventargs)
{
eventargs.Filename = Filename;
if (this.ValueChanged != null)
this.ValueChanged(this, eventargs);
}
private void Browse_Click(object sender, EventArgs e)
{
OpenFileDialog dlg = new OpenFileDialog();
dlg.FileName = Text;
dlg.Multiselect = AllowMultipleFiles;
if (dlg.ShowDialog() == DialogResult.OK)
{
if (SaveFullPath)
Text = dlg.FileName;
else
Text = dlg.SafeFileName;
}
}
protected override void OnPaint(PaintEventArgs e)
{
// Draw the client window
Rectangle r = new Rectangle(new Point(0, 0), new Size(Size.Width-1, Size.Height-1));
Graphics g = e.Graphics;
g.FillRectangle(new SolidBrush(SystemColors.Window), r);
g.DrawRectangle(new Pen(VisualStyleInformation.TextControlBorder), r);
r.Y += Margin.Top;
r.Width -= Browse.Width;
// Fill with Text
TextRenderer.DrawText(g, Text, Font, r, ForeColor, TextFormatFlags.PathEllipsis);
base.OnPaint(e);
}
private void FileTextBox_DragDrop(object sender, DragEventArgs e)
{
DataObject data = (DataObject)e.Data;
StringCollection filenames = data.GetFileDropList();
if ( filenames.Count == 1)
Text = filenames[0];
}
private void FileTextBox_DragEnter(object sender, DragEventArgs e)
{
DataObject data = (DataObject)e.Data;
StringCollection filenames = data.GetFileDropList();
if (/*!AllowMultipleFiles &&*/ filenames.Count == 1)
e.Effect = DragDropEffects.Link;
}
#endregion Event Handlers
}
public class FileEventArgs : EventArgs
{
public FileEventArgs(string Text)
{
Filename = Text;
}
/// <summary>
/// Name of the file in the control
/// </summary>
public String Filename { get; set; }
}

How do I put text on ProgressBar?

I have used ProgressBar Control in my c# desktop application.I have used it in a thread other then the thread in which control has been declared.Its working Fine.
Now I am wondering how i can show some text inside progress bar control like "Initiating Registration" etc.Also I want to use it as Marquee progress bar.Please help me.
You will have to override the OnPaint method, call the base implementation and the paint your own text.
You will need to create your own CustomProgressBar and then override OnPaint to draw what ever text you want.
Custom Progress Bar Class
namespace ProgressBarSample
{
public enum ProgressBarDisplayText
{
Percentage,
CustomText
}
class CustomProgressBar: ProgressBar
{
//Property to set to decide whether to print a % or Text
public ProgressBarDisplayText DisplayStyle { get; set; }
//Property to hold the custom text
public String CustomText { get; set; }
public CustomProgressBar()
{
// Modify the ControlStyles flags
//http://msdn.microsoft.com/en-us/library/system.windows.forms.controlstyles.aspx
SetStyle(ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, true);
}
protected override void OnPaint(PaintEventArgs e)
{
Rectangle rect = ClientRectangle;
Graphics g = e.Graphics;
ProgressBarRenderer.DrawHorizontalBar(g, rect);
rect.Inflate(-3, -3);
if (Value > 0)
{
// As we doing this ourselves we need to draw the chunks on the progress bar
Rectangle clip = new Rectangle(rect.X, rect.Y, (int)Math.Round(((float)Value / Maximum) * rect.Width), rect.Height);
ProgressBarRenderer.DrawHorizontalChunks(g, clip);
}
// Set the Display text (Either a % amount or our custom text
int percent = (int)(((double)this.Value / (double)this.Maximum) * 100);
string text = DisplayStyle == ProgressBarDisplayText.Percentage ? percent.ToString() + '%' : CustomText;
using (Font f = new Font(FontFamily.GenericSerif, 10))
{
SizeF len = g.MeasureString(text, f);
// Calculate the location of the text (the middle of progress bar)
// Point location = new Point(Convert.ToInt32((rect.Width / 2) - (len.Width / 2)), Convert.ToInt32((rect.Height / 2) - (len.Height / 2)));
Point location = new Point(Convert.ToInt32((Width / 2) - len.Width / 2), Convert.ToInt32((Height / 2) - len.Height / 2));
// The commented-out code will centre the text into the highlighted area only. This will centre the text regardless of the highlighted area.
// Draw the custom text
g.DrawString(text, f, Brushes.Red, location);
}
}
}
}
Sample WinForms Application
using System;
using System.Linq;
using System.Windows.Forms;
using System.Collections.Generic;
namespace ProgressBarSample
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
// Set our custom Style (% or text)
customProgressBar1.DisplayStyle = ProgressBarDisplayText.CustomText;
customProgressBar1.CustomText = "Initialising";
}
private void btnReset_Click(object sender, EventArgs e)
{
customProgressBar1.Value = 0;
btnStart.Enabled = true;
}
private void btnStart_Click(object sender, EventArgs e)
{
btnReset.Enabled = false;
btnStart.Enabled = false;
for (int i = 0; i < 101; i++)
{
customProgressBar1.Value = i;
// Demo purposes only
System.Threading.Thread.Sleep(100);
// Set the custom text at different intervals for demo purposes
if (i > 30 && i < 50)
{
customProgressBar1.CustomText = "Registering Account";
}
if (i > 80)
{
customProgressBar1.CustomText = "Processing almost complete!";
}
if (i >= 99)
{
customProgressBar1.CustomText = "Complete";
}
}
btnReset.Enabled = true;
}
}
}
I have written a no blinking/flickering TextProgressBar
You can find the source code here: https://github.com/ukushu/TextProgressBar
WARNING: It's a little bit buggy! But still, I think it's better than another answers here. As I have no time for fixes, if you will do sth with them, please send me update by some way:) Thanks.
Samples:
AVOID FLICKERING TEXT
The solution provided by Barry above is excellent, but there's is the "flicker-problem".
As soon as the Value is above zero the OnPaint will be envoked repeatedly and the text will flicker.
There is a solution to this. We do not need VisualStyles for the object since we will be drawing it with our own code.
Add the following code to the custom object Barry wrote and you will avoid the flicker:
[DllImportAttribute("uxtheme.dll")]
private static extern int SetWindowTheme(IntPtr hWnd, string appname, string idlist);
protected override void OnHandleCreated(EventArgs e)
{
SetWindowTheme(this.Handle, "", "");
base.OnHandleCreated(e);
}
I did not write this myself. It found it here: https://stackoverflow.com/a/299983/1163954
I've testet it and it works.
I wold create a control named for example InfoProgresBar, that provide this functionality with a label or two (Main Job, Current Job) and ProgressBar and use it instead of that ProgressBar.
I have used this simple code, and it works!
for (int i = 0; i < N * N; i++)
{
Thread.Sleep(50);
progressBar1.BeginInvoke(new Action(() => progressBar1.Value = i));
progressBar1.CreateGraphics().DrawString(i.ToString() + "%", new Font("Arial",
(float)10.25, FontStyle.Bold),
Brushes.Red, new PointF(progressBar1.Width / 2 - 10, progressBar1.Height / 2 - 7));
}
It just has one simple problem and this is it: when progress bar start to rising, percentage some times hide, and then appear again.
I did't write it myself.I found it here:
text on progressbar in c#
I used this code, and it does work.
I tried placing a label with transparent background over a progress bar but never got it to work properly. So I found Barry's solution here very useful, although I missed the beautiful Vista style progress bar. So I merged Barry's solution with http://www.dreamincode.net/forums/topic/243621-percent-into-progress-bar/ and managed to keep the native progress bar, while displaying text percentage or custom text over it. I don't see any flickering in this solution either. Sorry to dig up and old thread but I needed this today and so others may need it too.
public enum ProgressBarDisplayText
{
Percentage,
CustomText
}
class ProgressBarWithCaption : ProgressBar
{
//Property to set to decide whether to print a % or Text
private ProgressBarDisplayText m_DisplayStyle;
public ProgressBarDisplayText DisplayStyle {
get { return m_DisplayStyle; }
set { m_DisplayStyle = value; }
}
//Property to hold the custom text
private string m_CustomText;
public string CustomText {
get { return m_CustomText; }
set {
m_CustomText = value;
this.Invalidate();
}
}
private const int WM_PAINT = 0x000F;
protected override void WndProc(ref Message m)
{
base.WndProc(m);
switch (m.Msg) {
case WM_PAINT:
int m_Percent = Convert.ToInt32((Convert.ToDouble(Value) / Convert.ToDouble(Maximum)) * 100);
dynamic flags = TextFormatFlags.VerticalCenter | TextFormatFlags.HorizontalCenter | TextFormatFlags.SingleLine | TextFormatFlags.WordEllipsis;
using (Graphics g = Graphics.FromHwnd(Handle)) {
using (Brush textBrush = new SolidBrush(ForeColor)) {
switch (DisplayStyle) {
case ProgressBarDisplayText.CustomText:
TextRenderer.DrawText(g, CustomText, new Font("Arial", Convert.ToSingle(8.25), FontStyle.Regular), new Rectangle(0, 0, this.Width, this.Height), Color.Black, flags);
break;
case ProgressBarDisplayText.Percentage:
TextRenderer.DrawText(g, string.Format("{0}%", m_Percent), new Font("Arial", Convert.ToSingle(8.25), FontStyle.Regular), new Rectangle(0, 0, this.Width, this.Height), Color.Black, flags);
break;
}
}
}
break;
}
}
}
Just want to point out something on #codingbadger answer. When using "ProgressBarRenderer" you should always check for "ProgressBarRenderer.IsSupported" before using the class. For me, this has been a nightmare with Visual Styles errors in Win7 that I couldn't fix. So, a better approach and workaround for the solution would be:
Rectangle clip = new Rectangle(rect.X, rect.Y, (int)Math.Round(((float)Value / Maximum) * rect.Width), rect.Height);
if (ProgressBarRenderer.IsSupported)
ProgressBarRenderer.DrawHorizontalChunks(g, clip);
else
g.FillRectangle(new SolidBrush(this.ForeColor), clip);
Notice that the fill will be a simple rectangle and not chunks. Chunks will be used only if ProgressBarRenderer is supported
I have created a InfoProgressBar control which uses a TransparentLabel control. Testing on a form with a Timer, I get some slight glitches displaying the text every 30-40 value changes if using a timer interval of less than 250 milliseconds (probably because of the time required to update the screen is greater than the timer interval).
It would be possible to modify UpdateText method to insert all the calculated values into CustomText but it isn't something that I have needed yet. This would remove the need for the DisplayType property and enumerate.
The TransparentLabel class was created by adding a new UserControl and changing it to inheriting from Label with the following implementation:
using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace Utils.GUI
{
public partial class TransparentLabel : Label
{
// hide the BackColor attribute as much as possible.
// setting the base value has no effect as drawing the
// background is disabled
[Browsable(false)]
[EditorBrowsable(EditorBrowsableState.Never)]
public override Color BackColor
{
get
{
return Color.Transparent;
}
set
{
}
}
protected override CreateParams CreateParams
{
get
{
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x20; // WS_EX_TRANSPARENT
return cp;
}
}
public override string Text
{
get
{
return base.Text;
}
set
{
base.Text = value;
if(Parent != null) Parent.Invalidate(Bounds, false);
}
}
public override ContentAlignment TextAlign
{
get
{
return base.TextAlign;
}
set
{
base.TextAlign = value;
if(Parent != null) Parent.Invalidate(Bounds, false);
}
}
public TransparentLabel()
{
InitializeComponent();
SetStyle(ControlStyles.Opaque, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, false);
base.BackColor = Color.Transparent;
}
protected override void OnMove(EventArgs e)
{
base.OnMove(e);
RecreateHandle();
}
protected override void OnPaintBackground(PaintEventArgs pevent)
{
// do nothing
}
}
}
I did not make any changes to the related designer code but here it is for completeness.
namespace Utils.GUI
{
partial class TransparentLabel
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
components = new System.ComponentModel.Container();
}
#endregion
}
}
I then created another new UserControl and changed it to derive from ProgressBar with the following implementation:
using System.Collections;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Windows.Forms;
using System.Windows.Forms.Design;
using System.Windows.Forms.Design.Behavior;
namespace Utils.GUI
{
[Designer(typeof(InfoProgressBarDesigner))]
public partial class InfoProgressBar : ProgressBar
{
// designer class to add font baseline snapline by copying it from the label
private class InfoProgressBarDesigner : ControlDesigner
{
public override IList SnapLines
{
get
{
IList snapLines = base.SnapLines;
InfoProgressBar control = Control as InfoProgressBar;
if(control != null)
{
using(IDesigner designer = TypeDescriptor.CreateDesigner(control.lblText, typeof(IDesigner)))
{
if(designer != null)
{
designer.Initialize(control.lblText);
ControlDesigner boxDesigner = designer as ControlDesigner;
if(boxDesigner != null)
{
foreach(SnapLine line in boxDesigner.SnapLines)
{
if(line.SnapLineType == SnapLineType.Baseline)
{
snapLines.Add(new SnapLine(SnapLineType.Baseline, line.Offset, line.Filter, line.Priority));
break;
}
}
}
}
}
}
return snapLines;
}
}
}
// enum to select the type of displayed value
public enum ProgressBarDisplayType
{
Custom = 0,
Percent = 1,
Progress = 2,
Remain = 3,
Value = 4,
}
private string _customText;
private ProgressBarDisplayType _displayType;
private int _range;
[Bindable(false)]
[Browsable(true)]
[DefaultValue("{0}")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
// {0} is replaced with the result of the selected calculation
public string CustomText
{
get
{
return _customText;
}
set
{
_customText = value;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(ProgressBarDisplayType.Percent)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public ProgressBarDisplayType DisplayType
{
get
{
return _displayType;
}
set
{
_displayType = value;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
// don't use the lblText font as if it is null, it checks the parent font (i.e. this property) and gives an infinite loop
public override Font Font
{
get
{
return base.Font;
}
set
{
base.Font = value;
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(100)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new int Maximum
{
get
{
return base.Maximum;
}
set
{
base.Maximum = value;
_range = base.Maximum - base.Minimum;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(0)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new int Minimum
{
get
{
return base.Minimum;
}
set
{
base.Minimum = value;
_range = base.Maximum - base.Minimum;
UpdateText();
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(ContentAlignment.MiddleLeft)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public ContentAlignment TextAlign
{
get
{
return lblText.TextAlign;
}
set
{
lblText.TextAlign = value;
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(typeof(Color), "0x000000")]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public Color TextColor
{
get
{
return lblText.ForeColor;
}
set
{
lblText.ForeColor = value;
}
}
[Bindable(false)]
[Browsable(true)]
[DefaultValue(0)]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
[EditorBrowsable(EditorBrowsableState.Always)]
public new int Value
{
get
{
return base.Value;
}
set
{
base.Value = value;
UpdateText();
}
}
public InfoProgressBar()
{
InitializeComponent();
CustomText = "{0}";
DisplayType = ProgressBarDisplayType.Percent;
Maximum = 100;
Minimum = 0;
TextAlign = ContentAlignment.MiddleLeft;
TextColor = Color.Black;
Value = 0;
// means the label gets drawn in front of the progress bar
lblText.Parent = this;
_range = base.Maximum - base.Minimum;
}
protected void UpdateText()
{
switch(DisplayType)
{
case ProgressBarDisplayType.Custom:
{
lblText.Text = _customText;
break;
}
case ProgressBarDisplayType.Percent:
{
if(_range > 0)
{
lblText.Text = string.Format(_customText, string.Format("{0}%", (int)((Value * 100) / _range)));
}
else
{
lblText.Text = "100%";
}
break;
}
case ProgressBarDisplayType.Progress:
{
lblText.Text = string.Format(_customText, (Value - Minimum));
break;
}
case ProgressBarDisplayType.Remain:
{
lblText.Text = string.Format(_customText, (Maximum - Value));
break;
}
case ProgressBarDisplayType.Value:
{
lblText.Text = string.Format(_customText, Value);
break;
}
}
}
public new void Increment(int value)
{
base.Increment(value);
UpdateText();
}
public new void PerformStep()
{
base.PerformStep();
UpdateText();
}
}
}
And the designer code:
namespace Utils.GUI
{
partial class InfoProgressBar
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if(disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.lblText = new Utils.GUI.TransparentLabel();
this.SuspendLayout();
//
// lblText
//
this.lblText.BackColor = System.Drawing.Color.Transparent;
this.lblText.Dock = System.Windows.Forms.DockStyle.Fill;
this.lblText.Location = new System.Drawing.Point(0, 0);
this.lblText.Name = "lblText";
this.lblText.Padding = new System.Windows.Forms.Padding(3, 0, 3, 0);
this.lblText.Size = new System.Drawing.Size(100, 23);
this.lblText.TabIndex = 0;
this.lblText.Text = "transparentLabel1";
this.ResumeLayout(false);
}
#endregion
private TransparentLabel lblText;
}
}
Alliteratively you can try placing a Label control and placing it on top of the progress bar control. Then you can set whatever the text you want to the label. I haven't done this my self. If it works it should be a simpler solution than overriding onpaint.

Categories