Can't find System.Windows.Forms.Textbox Background property [duplicate] - c#

Can anyone point me to a good implementation of a basic Windows Forms TextBox that will initially show watermark text that disappears when the cursor enters it? I think I can create my own with some creative use of Enter and Leave events, but I'm sure there's a perfectly usable implementation sitting around somewhere. I saw the WPF implementation and if necessary I could nest it, but a native WinForms TextBox derivative would be better.
I have this so far; haven't tried it yet but does anyone see any glaring problems?
public class WatermarkTextBox:TextBox
{
public string WatermarkText { get; set; }
public Color WatermarkColor { get; set; }
private Color TextColor { get; set; }
private bool isInTransition;
public WatermarkTextBox()
{
WatermarkColor = SystemColors.GrayText;
}
private bool HasText { get { return Text.IsNotNullOrBlankOr(WatermarkText); }}
protected override void OnEnter(EventArgs e)
{
base.OnEnter(e);
if (HasText) return;
isInTransition = true;
ForeColor = TextColor;
Text = String.Empty;
isInTransition = false;
}
protected override void OnForeColorChanged(EventArgs e)
{
base.OnForeColorChanged(e);
if (!isInTransition) //the change came from outside
TextColor = ForeColor;
}
protected override void OnLeave(EventArgs e)
{
base.OnLeave(e);
if (HasText) return;
isInTransition = true;
ForeColor = WatermarkColor;
Text = WatermarkText.EmptyIfNull();
isInTransition = false;
}
}
EDIT: The above would have eventually worked with some finessing, but the CueProvider worked much better. Here's my final implementation:
public class WatermarkTextBox:TextBox
{
private string watermarkText;
public string WatermarkText
{
get { return watermarkText; }
set
{
watermarkText = value;
if (watermarkText.IsNullOrBlank())
CueProvider.ClearCue(this);
else
CueProvider.SetCue(this, watermarkText);
}
}
}
I could have integrated the CueProvider functionality completely, but this works beautifully.

The official term is "cue banner". Here's another way to do it, just inheriting TextBox gets the job done too. Add a new class to your project and paste the code shown below. Compile. Drop the new control from the top of the toolbox and set the Cue property.
You get a live preview of the Cue value in the designer, localized to the form's Language property. Lots of bang for very little buck, an excellent demonstration of the good parts of Winforms.
using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class CueTextBox : TextBox {
[Localizable(true)]
public string Cue {
get { return mCue; }
set { mCue = value; updateCue(); }
}
private void updateCue() {
if (this.IsHandleCreated && mCue != null) {
SendMessage(this.Handle, 0x1501, (IntPtr)1, mCue);
}
}
protected override void OnHandleCreated(EventArgs e) {
base.OnHandleCreated(e);
updateCue();
}
private string mCue;
// PInvoke
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, string lp);
}

I've updated the answer given by #Hans Passant above to introduce constants, make it consistent with pinvoke.net definitions and to let the code pass FxCop validation.
class CueTextBox : TextBox
{
private static class NativeMethods
{
private const uint ECM_FIRST = 0x1500;
internal const uint EM_SETCUEBANNER = ECM_FIRST + 1;
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, string lParam);
}
private string _cue;
public string Cue
{
get
{
return _cue;
}
set
{
_cue = value;
UpdateCue();
}
}
private void UpdateCue()
{
if (IsHandleCreated && _cue != null)
{
NativeMethods.SendMessage(Handle, NativeMethods.EM_SETCUEBANNER, (IntPtr)1, _cue);
}
}
protected override void OnHandleCreated(EventArgs e)
{
base.OnHandleCreated(e);
UpdateCue();
}
}
Edit: update the PInvoke call to set CharSet attribute, to err on the safe side. For more info see the SendMessage page at pinvoke.net.

.NET 5.0+, .NET Core 3.0+
You can use PlaceholderText property. It supports both multi-line and single-line text-box.
textBox1.PlaceholderText = "Something";
If you look into the .NET CORE implementation of TextBox you see it's been implemented by handling paint message.
.NET Framework
Here is an implementation of a TextBox which supports showing hint (or watermark or cue):
It also shows the hint when MultiLine is true.
It's based on handling WM_PAINT message and drawing the hint. So you can simply customize the hint and add some properties like hint color, or you can draw it right to left or control when to show the hint.
using System.Drawing;
using System.Windows.Forms;
public class ExTextBox : TextBox
{
string hint;
public string Hint
{
get { return hint; }
set { hint = value; this.Invalidate(); }
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == 0xf)
{
if (!this.Focused && string.IsNullOrEmpty(this.Text)
&& !string.IsNullOrEmpty(this.Hint))
{
using (var g = this.CreateGraphics())
{
TextRenderer.DrawText(g, this.Hint, this.Font,
this.ClientRectangle, SystemColors.GrayText , this.BackColor,
TextFormatFlags.Top | TextFormatFlags.Left);
}
}
}
}
}
If you use EM_SETCUEBANNER, then there will be 2 issues. The text always will be shown in a system default color. Also the text will not be shown when the TextBox is MultiLine.
Using the painting solution, you can show the text with any color that you want. You also can show the watermark when the control is multi-line:
Download
You can clone or download the working example:
Download Zip
Github repository

[DllImport("user32.dll", CharSet = CharSet.Unicode)]
private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, string lParam);
And the message constants:
private const uint EM_SETCUEBANNER = 0x1501;
private const uint CB_SETCUEBANNER = 0x1703; // minimum supported client Windows Vista, minimum supported server Windows Server 2008
And imho the best way to implement it is as an extension method.
So for the TextBox control the syntax would be:
MyTextBox.CueBanner(false, "Password");
From the code:
public static void CueBanner(this TextBox textbox, bool showcuewhenfocus, string cuetext)
{
uint BOOL = 0;
if (showcuewhenfocus == true) { BOOL = 1; }
SendMessage(textbox.Handle, EM_SETCUEBANNER, (IntPtr)BOOL, cuetext); ;
}

Using WinForms on .NET Core:
This has been greatly simplified in .NET Core. You can directly add placeholder text by modifying the new PlaceholderText Property of the TextBox.
public virtual string PlaceholderText { get; set; }
Note that you would probably still have to edit the ForeColor if you want to get a colored Placeholder Text. The PlaceholderText field is visible when the Text field is null or empty.

You can add a watermark to a Textbox (multiline or not) that works pretty well by drawing it in different controls Paint event. For Example:
Private Sub Panel1_Paint(sender As Object, e As PaintEventArgs) Handles Panel1.Paint
If TextBox1.Text = "" Then
TextBox1.CreateGraphics.DrawString("Enter Text Here", Me.Font, New SolidBrush(Color.LightGray), 0, 0)
End If
End Sub
-OO-

I wrote a reusable custom control class for my project.
maybe it can help someone that need to implement multiple placeholder textboxes in his project.
here is the C# and vb.net versions:
C#:
namespace reusebleplaceholdertextbox
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
// implementation
CustomPlaceHolderTextbox myCustomTxt = new CustomPlaceHolderTextbox(
"Please Write Text Here...", Color.Gray, new Font("ARIAL", 11, FontStyle.Italic)
, Color.Black, new Font("ARIAL", 11, FontStyle.Regular)
);
myCustomTxt.Multiline = true;
myCustomTxt.Size = new Size(200, 50);
myCustomTxt.Location = new Point(10, 10);
this.Controls.Add(myCustomTxt);
}
}
class CustomPlaceHolderTextbox : System.Windows.Forms.TextBox
{
public string PlaceholderText { get; private set; }
public Color PlaceholderForeColor { get; private set; }
public Font PlaceholderFont { get; private set; }
public Color TextForeColor { get; private set; }
public Font TextFont { get; private set; }
public CustomPlaceHolderTextbox(string placeholdertext, Color placeholderforecolor,
Font placeholderfont, Color textforecolor, Font textfont)
{
this.PlaceholderText = placeholdertext;
this.PlaceholderFont = placeholderfont;
this.PlaceholderForeColor = placeholderforecolor;
this.PlaceholderFont = placeholderfont;
this.TextForeColor = textforecolor;
this.TextFont = textfont;
if (!string.IsNullOrEmpty(this.PlaceholderText))
{
SetPlaceHolder(true);
this.Update();
}
}
private void SetPlaceHolder(bool addEvents)
{
if (addEvents)
{
this.LostFocus += txt_lostfocus;
this.Click += txt_click;
}
this.Text = PlaceholderText;
this.ForeColor = PlaceholderForeColor;
this.Font = PlaceholderFont;
}
private void txt_click(object sender, EventArgs e)
{
// IsNotFirstClickOnThis:
// if there is no other control in the form
// we will have a problem after the first load
// because we dont other focusable control to move the focus to
// and we dont want to remove the place holder
// only on first time the place holder will be removed by click event
RemovePlaceHolder();
this.GotFocus += txt_focus;
// no need for this event listener now
this.Click -= txt_click;
}
private void RemovePlaceHolder()
{
this.Text = "";
this.ForeColor = TextForeColor;
this.Font = TextFont;
}
private void txt_lostfocus(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(this.Text))
{
// set placeholder again
SetPlaceHolder(false);
}
}
private void txt_focus(object sender, EventArgs e)
{
if (this.Text == PlaceholderText)
{
// IsNotFirstClickOnThis:
// if there is no other control in the form
// we will have a problem after the first load
// because we dont other focusable control to move the focus to
// and we dont want to remove the place holder
RemovePlaceHolder();
}
}
}
}
VB.NET:
Namespace CustomControls
Public Class PlaceHolderTextBox
Inherits System.Windows.Forms.TextBox
Public Property PlaceholderText As String
Public Property PlaceholderForeColor As Color
Public Property PlaceholderFont As Font
Public Property TextForeColor As Color
Public Property TextFont As Font
Public Sub New(ByVal placeholdertext As String, ByVal placeholderforecolor As Color, ByVal placeholderfont As Font, ByVal txtboxbackcolor As Color, ByVal textforecolor As Color, ByVal textfont As Font)
Me.PlaceholderText = placeholdertext
Me.PlaceholderFont = placeholderfont
Me.PlaceholderForeColor = placeholderforecolor
Me.PlaceholderFont = placeholderfont
Me.TextForeColor = textforecolor
Me.TextFont = textfont
Me.BackColor = txtboxbackcolor
If Not String.IsNullOrEmpty(Me.PlaceholderText) Then
SetPlaceHolder(True)
Me.Update()
End If
End Sub
Private Sub SetPlaceHolder(ByVal addEvents As Boolean)
If addEvents Then
AddHandler Me.LostFocus, AddressOf txt_lostfocus
AddHandler Me.Click, AddressOf txt_click
End If
Me.Text = PlaceholderText
Me.ForeColor = PlaceholderForeColor
Me.Font = PlaceholderFont
End Sub
Private Sub txt_click(ByVal sender As Object, ByVal e As EventArgs)
RemovePlaceHolder()
AddHandler Me.GotFocus, AddressOf txt_focus
RemoveHandler Me.Click, AddressOf txt_click
End Sub
Private Sub RemovePlaceHolder()
Me.Text = ""
Me.ForeColor = TextForeColor
Me.Font = TextFont
End Sub
Private Sub txt_lostfocus(ByVal sender As Object, ByVal e As EventArgs)
If String.IsNullOrEmpty(Me.Text) Then
SetPlaceHolder(False)
End If
End Sub
Private Sub txt_focus(ByVal sender As Object, ByVal e As EventArgs)
If Me.Text = PlaceholderText Then
RemovePlaceHolder()
End If
End Sub
End Class
End Namespace

using System;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace PlaceHolderTextBoxCSharp
{
public class CTextBox : TextBox
{
private Panel contenedor;
protected string texto = "PlaceHolderText";
protected Color colorTextoDefault = Color.Gray;
public Color colorTexto = Color.Gray;
protected Color colorTextoObligatorio = Color.Red;
private Font fuente;
private SolidBrush establecerColorTexto;
private bool obligatoriedad = false;
private bool colorConFoco = false;
private int vuelta = 0;
public CTextBox()
{
Inicializar();
}
private void Inicializar()
{
fuente = Font;
CharacterCasing = CharacterCasing.Upper;
contenedor = null;
MuestraPlaceHolder();
Leave += new EventHandler(PierdeFoco);
TextChanged += new EventHandler(CambiaTexto);
}
private void EliminaPlaceHolder()
{
if (contenedor != null)
{
Controls.Remove(contenedor);
contenedor = null;
}
}
private void MuestraPlaceHolder()
{
if (contenedor == null && TextLength <= 0)
{
contenedor = new Panel();
contenedor.Paint += new PaintEventHandler(contenedorPaint);
contenedor.Invalidate();
contenedor.Click += new EventHandler(contenedorClick);
Controls.Add(contenedor);
}
}
private void contenedorClick(object sender, EventArgs e)
{
Focus();
}
private void contenedorPaint(object sender, PaintEventArgs e)
{
contenedor.Location = new Point(2, 0);
contenedor.Height = Height;
contenedor.Width = Width;
contenedor.Anchor = AnchorStyles.Left | AnchorStyles.Right;
establecerColorTexto = new SolidBrush(colorTexto);
Graphics g = e.Graphics;
g.DrawString(texto, fuente, establecerColorTexto, new PointF(-1f, 1f));
}
private void PierdeFoco(object sender, EventArgs e)
{
if (TextLength > 0)
{
EliminaPlaceHolder();
}
else
{
if (obligatoriedad == true)
{
colorTexto = colorTextoObligatorio;
}
else
{
colorTexto = colorTextoDefault;
}
Invalidate();
}
}
private void CambiaTexto(object sender, EventArgs e)
{
if (TextLength > 0)
{
EliminaPlaceHolder();
}
else
{
MuestraPlaceHolder();
vuelta += 1;
if (vuelta >= 1 && obligatoriedad == true)
{
colorTexto = colorTextoObligatorio;
}
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
MuestraPlaceHolder();
}
protected override void OnInvalidated(InvalidateEventArgs e)
{
base.OnInvalidated(e);
if (contenedor != null)
{
contenedor.Invalidate();
}
}
[Category("Atributos PlaceHolder")]
[Description("Establece el texto a mostrar.")]
public string PlaceHolderText
{
get
{
return texto;
}
set
{
texto = value;
Invalidate();
}
}
[Category("Atributos PlaceHolder")]
[Description("Establece el estilo de fuente del PlaceHolder.")]
public Font PlaceHolderFont
{
get
{
return fuente;
}
set
{
fuente = value;
Invalidate();
}
}
[Category("Atributos PlaceHolder")]
[Description("Indica si el campo es obligatorio.")]
public bool PlaceHolderFieldRequired
{
get
{
return obligatoriedad;
}
set
{
obligatoriedad = value;
Invalidate();
}
}
}
}

With .Net Core 3 a property was introduced into TextBox: PlaceHolderText
If one is going to need this in a FrameWork application the required code parts can be taken from the official open source code and placed in a TextBox descendant (see license).
This supports multiline TextBox and also RTL text.
public class PlaceHolderTextBox : TextBox
{
private const int WM_KILLFOCUS = 0x0008;
private const int WM_PAINT = 0x000F;
private string _placeholderText;
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (this.ShouldRenderPlaceHolderText(m))
{
using Graphics g = this.CreateGraphics();
this.DrawPlaceholderText(g);
}
}
#region PlaceHolder
/// <summary>
/// Gets or sets the text that is displayed when the control has no text and does not have the focus.
/// </summary>
/// <value>The text that is displayed when the control has no text and does not have the focus.</value>
[Localizable(true), DefaultValue("")]
public virtual string PlaceholderText
{
get => _placeholderText;
set
{
if (value == null)
{
value = string.Empty;
}
if (_placeholderText != value)
{
_placeholderText = value;
if (this.IsHandleCreated)
{
this.Invalidate();
}
}
}
}
//-------------------------------------------------------------------------------------------------
/// <summary>
/// Draws the <see cref="PlaceholderText"/> in the client area of the <see cref="TextBox"/> using the default font and color.
/// </summary>
private void DrawPlaceholderText(Graphics graphics)
{
TextFormatFlags flags = TextFormatFlags.NoPadding | TextFormatFlags.Top |
TextFormatFlags.EndEllipsis;
Rectangle rectangle = this.ClientRectangle;
if (this.RightToLeft == RightToLeft.Yes)
{
flags |= TextFormatFlags.RightToLeft;
switch (this.TextAlign)
{
case HorizontalAlignment.Center:
flags |= TextFormatFlags.HorizontalCenter;
rectangle.Offset(0, 1);
break;
case HorizontalAlignment.Left:
flags |= TextFormatFlags.Right;
rectangle.Offset(1, 1);
break;
case HorizontalAlignment.Right:
flags |= TextFormatFlags.Left;
rectangle.Offset(0, 1);
break;
}
}
else
{
flags &= ~TextFormatFlags.RightToLeft;
switch (this.TextAlign)
{
case HorizontalAlignment.Center:
flags |= TextFormatFlags.HorizontalCenter;
rectangle.Offset(0, 1);
break;
case HorizontalAlignment.Left:
flags |= TextFormatFlags.Left;
rectangle.Offset(1, 1);
break;
case HorizontalAlignment.Right:
flags |= TextFormatFlags.Right;
rectangle.Offset(0, 1);
break;
}
}
TextRenderer.DrawText(graphics, this.PlaceholderText, this.Font, rectangle, SystemColors.GrayText, this.BackColor, flags);
}
private bool ShouldRenderPlaceHolderText(in Message m) =>
!string.IsNullOrEmpty(this.PlaceholderText) &&
(m.Msg == WM_PAINT || m.Msg == WM_KILLFOCUS) &&
!this.GetStyle(ControlStyles.UserPaint) &&
!this.Focused && this.TextLength == 0;
#endregion
}

Update 30/12/2022
With .NET6 (or maybe lower version have this option, I'm not sure)
Just right-click on textbox > properties > PlaceHolderText > and set it as anything you want
click here to see that property in picture

Private Sub randomSubName() Handles txtWatermark.Click
txtWatermark.text = ""
End Sub
Make the default text of the textbox whatever you want the watermark to be, I assume in this example you name the textbox txtWatermark
Hey, I'm new. So sorry if I terribly screwed up the post... I also have no idea if this works...

Related

WinForms : Not able to Maximize Blurred Window

I have a window on which I have some blur effects running. I want this window to be maximized so I set the Window State field to be Maximized in the designer.But the Window is not maximized and leaves some uncovered area in the top left corner. Ive tried multiple Start Position settings but none of them solve the problem.
The Settings
The Window
The code for the blurry Window
using System.Runtime.InteropServices;
namespace WF4
{
public partial class Form1 : Form
{
public Form1()
{
this.EnableBlur();
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
SetStyle(ControlStyles.SupportsTransparentBackColor, true);
BackColor = Color.LimeGreen;
TransparencyKey = Color.LimeGreen;
InitializeComponent();
FormBorderStyle = FormBorderStyle.None;
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void Form1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
MessageBox.Show("Hllo");
}
}
}
public static class WindowExtension
{
[DllImport("user32.dll")]
static internal extern int SetWindowCompositionAttribute(IntPtr hwnd, ref WindowCompositionAttributeData data);
public static void EnableBlur(this Form #this)
{
var accent = new AccentPolicy();
accent.AccentState = AccentState.ACCENT_ENABLE_BLURBEHIND;
var accentStructSize = Marshal.SizeOf(accent);
var accentPtr = Marshal.AllocHGlobal(accentStructSize);
Marshal.StructureToPtr(accent, accentPtr, false);
var Data = new WindowCompositionAttributeData();
Data.Attribute = WindowCompositionAttribute.WCA_ACCENT_POLICY;
Data.SizeOfData = accentStructSize;
Data.Data = accentPtr;
SetWindowCompositionAttribute(#this.Handle, ref Data);
Marshal.FreeHGlobal(accentPtr);
}
}
enum AccentState
{
ACCENT_DISABLED = 0,
ACCENT_ENABLE_GRADIENT = 1,
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
ACCENT_ENABLE_BLURBEHIND = 3,
ACCENT_INVALID_STATE = 4
}
struct AccentPolicy
{
public AccentState AccentState;
public int AccentFlags;
public int GradientColor;
public int AnimationId;
}
struct WindowCompositionAttributeData
{
public WindowCompositionAttribute Attribute;
public IntPtr Data;
public int SizeOfData;
}
enum WindowCompositionAttribute
{
WCA_ACCENT_POLICY = 19
}
}
I tested Hans's suggestion and it worked well.
The solution is to set the FormBorderStyle property to None.
Make sure the background color is Control.
Calling EnableBlur() after InitializeComponent():
InitializeComponent();
this.EnableBlur();
Output:

How to initialize collapsed GroupBox in c#?

I have customized the GroupBox to make it collapsable/expandable.
Code is working fine. But I want to initialize this GroupBox in collapsed form instead of expendable. I tried to set values (m_collapsed = true/false and m_bResizingFromCollapse = true/false)and override OnPaint, but nothing is working.
namespace ABC
{
/// <summary>
/// GroupBox control that provides functionality to
/// allow it to be collapsed.
/// </summary>
[ToolboxBitmap(typeof(CollapsibleGroupBox))]
public partial class CollapsibleGroupBox : GroupBox
{
#region Fields
private Rectangle m_toggleRect = new Rectangle(8, 2, 11, 11);
private Boolean m_collapsed = false;
private Boolean m_bResizingFromCollapse = false;
private const int m_collapsedHeight = 20;
private Size m_FullSize = Size.Empty;
#endregion
#region Events & Delegates
/// <summary>Fired when the Collapse Toggle button is pressed</summary>
public delegate void CollapseBoxClickedEventHandler(object sender);
public event CollapseBoxClickedEventHandler CollapseBoxClickedEvent;
#endregion
#region Constructor
public CollapsibleGroupBox()
{
InitializeComponent();
}
#endregion
#region Public Properties
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int FullHeight
{
get { return m_FullSize.Height; }
}
[DefaultValue(false), Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public bool IsCollapsed
{
get { return m_collapsed; }
set
{
if (value != m_collapsed)
{
m_collapsed = value;
if (!value)
// Expand
this.Size = m_FullSize;
else
{
// Collapse
m_bResizingFromCollapse = true;
this.Height = m_collapsedHeight;
m_bResizingFromCollapse = false;
}
foreach (Control c in Controls)
c.Visible = !value;
Invalidate();
}
}
}
[Browsable(false), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
public int CollapsedHeight
{
get { return m_collapsedHeight; }
}
#endregion
#region Overrides
protected override void OnMouseUp(MouseEventArgs e)
{
if (m_toggleRect.Contains(e.Location))
ToggleCollapsed();
else
base.OnMouseUp(e);
}
protected override void OnPaint(PaintEventArgs e)
{
HandleResize();
DrawGroupBox(e.Graphics);
DrawToggleButton(e.Graphics);
}
#endregion
#region Implimentation
void DrawGroupBox(Graphics g)
{
// Get windows to draw the GroupBox
Rectangle bounds = new Rectangle(ClientRectangle.X, ClientRectangle.Y + 6, ClientRectangle.Width, ClientRectangle.Height - 6);
GroupBoxRenderer.DrawGroupBox(g, bounds, Enabled ? GroupBoxState.Normal : GroupBoxState.Disabled);
// Text Formating positioning & Size
StringFormat sf = new StringFormat();
int i_textPos = (bounds.X + 8) + m_toggleRect.Width + 2;
int i_textSize = (int)g.MeasureString(Text, this.Font).Width;
i_textSize = i_textSize < 1 ? 1 : i_textSize;
int i_endPos = i_textPos + i_textSize + 1;
// Draw a line to cover the GroupBox border where the text will sit
g.DrawLine(SystemPens.Control, i_textPos, bounds.Y, i_endPos, bounds.Y);
// Draw the GroupBox text
using (SolidBrush drawBrush = new SolidBrush(Color.FromArgb(0, 70, 213)))
g.DrawString(Text, this.Font, drawBrush, i_textPos, 0);
}
void DrawToggleButton(Graphics g)
{
if (IsCollapsed)
{
Image plusImage = Image.FromFile(Path.Combine(GameManager.sAssetsDir, "plus.bmp"));
g.DrawImage(plusImage, m_toggleRect);
}
else
{
Image minusImage = Image.FromFile(Path.Combine(GameManager.sAssetsDir, "minus.bmp"));
g.DrawImage(minusImage, m_toggleRect);
}
}
void ToggleCollapsed()
{
IsCollapsed = !IsCollapsed;
if (CollapseBoxClickedEvent != null)
CollapseBoxClickedEvent(this);
}
void HandleResize()
{
if (!m_bResizingFromCollapse && !m_collapsed)
m_FullSize = this.Size;
}
#endregion
}
}

Show percentage or some text in ProgressBar [duplicate]

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.

Show ToolTip with ComboBox (dropdownmenu)

I am showing a little tooltip, but if I change the selecteditem/text in the dropdownmenu, tooltip shows the old text and the new text. I want it to show only the new text.
private void optionsvalueComboBox_MouseHover(object sender, EventArgs e)
{
ToolTip buttonToolTip = new ToolTip();
buttonToolTip.ToolTipTitle = "Value";
buttonToolTip.UseFading = true;
buttonToolTip.UseAnimation = true;
buttonToolTip.IsBalloon = true;
buttonToolTip.ShowAlways = true;
buttonToolTip.AutoPopDelay = 5000;
buttonToolTip.InitialDelay = 1000;
buttonToolTip.ReshowDelay = 0;
buttonToolTip.SetToolTip(optionsvalueComboBox, optionsvalueComboBox.Text);
}
Assuming what you don't like is the tooltip text changing from the old text to the new text...
The reason it's doing that is because you are creating a new tooltip instance on every hover event. Every time the hover event is fired, the old tooltip instance is replaced with a new one which is why you see both. To fix this, put the declaration outside the event, like this:
ToolTip buttonToolTip = new ToolTip();
private void optionsvalueComboBox_MouseHover(object sender, EventArgs e)
{
buttonToolTip.ToolTipTitle = "Value";
buttonToolTip.UseFading = true;
buttonToolTip.UseAnimation = true;
buttonToolTip.IsBalloon = true;
buttonToolTip.ShowAlways = true;
buttonToolTip.AutoPopDelay = 5000;
buttonToolTip.InitialDelay = 1000;
buttonToolTip.ReshowDelay = 0;
buttonToolTip.SetToolTip(optionsvalueComboBox, optionsvalueComboBox.Text);
}
Now the same tooltip is being used with the wording simply being replaced. Let me know if this works for you!
I've tried digging in the MouseHover event of a ComboBox and looks like it doesn't work normally as we expect. The MouseHover is in fact fired only when you move the mouse over the drop down button if your ComboBox has style of dropdown. The simplest solution for this is change your combobox style to dropdownlist like this:
comboBox1.DropDownStyle = ComboBoxStyle.DropDownList;
However that kind of style will make the ComboBox readonly. If that's not what you want, there is a work-around for you is to use the event MouseMove with a Timer to mimic the MouseHover, here is the code for you:
public partial class Form1 : Form {
public Form1(){
InitializeComponent();
t.Interval = 600;
t.Tick += (se, ev) => {
buttonToolTip.SetToolTip(comboBox1, (string)comboBox1.SelectedItem);
t.Stop();
};
//init the buttonToolTip
buttonToolTip.ToolTipTitle = "Value";
buttonToolTip.UseFading = true;
buttonToolTip.UseAnimation = true;
buttonToolTip.IsBalloon = true;
buttonToolTip.ShowAlways = true;
buttonToolTip.AutoPopDelay = 5000;
buttonToolTip.InitialDelay = 1000;
buttonToolTip.ReshowDelay = 0;
//register MouseMove event handler for your comboBox1
comboBox1.MouseMove += (se, ev) => {
//Restart the timer every time the mouse is moving
t.Stop();
t.Start();
};
}
Timer t = new Timer();
ToolTip buttonToolTip = new ToolTip();
}
A complete working example:
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public RECT(Rectangle rect)
{
Left = rect.Left;
Top = rect.Top;
Right = rect.Right;
Bottom = rect.Bottom;
}
public Rectangle Rect
{
get
{
return new Rectangle(Left, Top, Right - Left, Bottom - Top);
}
}
public Point Location
{
get
{
return new Point(Left, Top);
}
}
public int Left;
public int Top;
public int Right;
public int Bottom;
}
public class ToolTipComboBox: ComboBox
{
#region Fields
private ToolTip toolTip;
private bool _tooltipVisible;
private bool _dropDownOpen;
#endregion
#region Types
[StructLayout(LayoutKind.Sequential)]
// ReSharper disable once InconsistentNaming
public struct COMBOBOXINFO
{
public Int32 cbSize;
public RECT rcItem;
public RECT rcButton;
public ComboBoxButtonState buttonState;
public IntPtr hwndCombo;
public IntPtr hwndEdit;
public IntPtr hwndList;
}
public enum ComboBoxButtonState
{
// ReSharper disable once UnusedMember.Global
StateSystemNone = 0,
// ReSharper disable once UnusedMember.Global
StateSystemInvisible = 0x00008000,
// ReSharper disable once UnusedMember.Global
StateSystemPressed = 0x00000008
}
[DllImport("user32.dll")]
public static extern bool GetComboBoxInfo(IntPtr hWnd, ref COMBOBOXINFO pcbi);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool GetWindowRect(IntPtr hwnd, out RECT lpRect);
#endregion
#region Properties
private IntPtr HwndCombo
{
get
{
COMBOBOXINFO pcbi = new COMBOBOXINFO();
pcbi.cbSize = Marshal.SizeOf(pcbi);
GetComboBoxInfo(Handle, ref pcbi);
return pcbi.hwndCombo;
}
}
private IntPtr HwndDropDown
{
get
{
COMBOBOXINFO pcbi = new COMBOBOXINFO();
pcbi.cbSize = Marshal.SizeOf(pcbi);
GetComboBoxInfo(Handle, ref pcbi);
return pcbi.hwndList;
}
}
[Browsable(false)]
public new DrawMode DrawMode
{
get { return base.DrawMode; }
set { base.DrawMode = value; }
}
#endregion
#region ctor
public ToolTipComboBox()
{
toolTip = new ToolTip
{
UseAnimation = false,
UseFading = false
};
base.DrawMode = DrawMode.OwnerDrawFixed;
DrawItem += OnDrawItem;
DropDownClosed += OnDropDownClosed;
DropDown += OnDropDown;
MouseLeave += OnMouseLeave;
}
#endregion
#region Methods
private void OnDropDown(object sender, EventArgs e)
{
_dropDownOpen = true;
}
private void OnMouseLeave(object sender, EventArgs e)
{
ResetToolTip();
}
private void ShowToolTip(string text, int x, int y)
{
toolTip.Show(text, this, x, y);
_tooltipVisible = true;
}
private void OnDrawItem(object sender, DrawItemEventArgs e)
{
ComboBox cbo = sender as ComboBox;
if (e.Index == -1) return;
// ReSharper disable once PossibleNullReferenceException
string text = cbo.GetItemText(cbo.Items[e.Index]);
e.DrawBackground();
if ((e.State & DrawItemState.Selected) == DrawItemState.Selected)
{
TextRenderer.DrawText(e.Graphics, text, e.Font, e.Bounds.Location, SystemColors.Window);
if (_dropDownOpen)
{
Size szText = TextRenderer.MeasureText(text, cbo.Font);
if (szText.Width > cbo.Width - SystemInformation.VerticalScrollBarWidth && !_tooltipVisible)
{
RECT rcDropDown;
GetWindowRect(HwndDropDown, out rcDropDown);
RECT rcCombo;
GetWindowRect(HwndCombo, out rcCombo);
if (rcCombo.Top > rcDropDown.Top)
{
ShowToolTip(text, e.Bounds.X, e.Bounds.Y - rcDropDown.Rect.Height - cbo.ItemHeight - 5);
}
else
{
ShowToolTip(text, e.Bounds.X, e.Bounds.Y + cbo.ItemHeight - cbo.ItemHeight);
}
}
}
}
else
{
ResetToolTip();
TextRenderer.DrawText(e.Graphics, text, e.Font, e.Bounds.Location, cbo.ForeColor);
}
e.DrawFocusRectangle();
}
private void OnDropDownClosed(object sender, EventArgs e)
{
_dropDownOpen = false;
ResetToolTip();
}
private void ResetToolTip()
{
if (_tooltipVisible)
{
// ReSharper disable once AssignNullToNotNullAttribute
toolTip.SetToolTip(this, null);
_tooltipVisible = false;
}
}
#endregion
}

How do I implement a TextBox that displays "Type here"?

Displaying "Type here to ..." until the user enters text into a TextBox is a well-known usability feature nowadays. How would one implement this feature in C#?
My idea is to override OnTextChanged, but the logic to handle the changes of text from and to "Type here" is a bit tricky...
Displaying "Type here" on initialization and removing it on first input is easy, but I want to display the message every time the entered text becomes empty.
Something that has worked for me:
this.waterMarkActive = true;
this.textBox.ForeColor = Color.Gray;
this.textBox.Text = "Type here";
this.textBox.GotFocus += (source, e) =>
{
if (this.waterMarkActive)
{
this.waterMarkActive = false;
this.textBox.Text = "";
this.textBox.ForeColor = Color.Black;
}
};
this.textBox.LostFocus += (source, e) =>
{
if (!this.waterMarkActive && string.IsNullOrEmpty(this.textBox.Text))
{
this.waterMarkActive = true;
this.textBox.Text = "Type here";
this.textBox.ForeColor = Color.Gray;
}
};
Where bool waterMarkActive is a class member variable and textBox is the TextBox. This probably should be encapsulated though :) There might be some issues with this approach, but I'm not currently aware of any.
I recently discovered that Windows support water marks in text boxes; they are called cue banners (see here). It's very easy to implement:
// Within your class or scoped in a more appropriate location:
[DllImport("user32.dll")]
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)] string lParam);
// In your constructor or somewhere more suitable:
SendMessage(textBox.Handle, 0x1501, 1, "Please type here.");
Where textBox is an instance of TextBox, 0x1501 is the code for the windows message EM_SETCUEBANNER, the wParam may either be TRUE (non-zero) or FALSE (zero), and lParam is the water mark you'd like to display. wParam indicates when the cue banner should be displayed; if set to TRUE then the cue banner will be displayed even when the control has focus.
What you're looking for is a TextBox with a "watermark".
There's a sample implementation for C# here, all credits to Wael Alghool.
The relevant part of his code is:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
namespace wmgCMS
{
class WaterMarkTextBox : TextBox
{
private Font oldFont = null;
private Boolean waterMarkTextEnabled = false;
#region Attributes
private Color _waterMarkColor = Color.Gray;
public Color WaterMarkColor
{
get { return _waterMarkColor; }
set { _waterMarkColor = value; Invalidate();/*thanks to Bernhard Elbl
for Invalidate()*/ }
}
private string _waterMarkText = "Water Mark";
public string WaterMarkText
{
get { return _waterMarkText; }
set { _waterMarkText = value; Invalidate(); }
}
#endregion
//Default constructor
public WaterMarkTextBox()
{
JoinEvents(true);
}
//Override OnCreateControl ... thanks to "lpgray .. codeproject guy"
protected override void OnCreateControl()
{
base.OnCreateControl();
WaterMark_Toggel(null, null);
}
//Override OnPaint
protected override void OnPaint(PaintEventArgs args)
{
// Use the same font that was defined in base class
System.Drawing.Font drawFont = new System.Drawing.Font(Font.FontFamily,
Font.Size, Font.Style, Font.Unit);
//Create new brush with gray color or
SolidBrush drawBrush = new SolidBrush(WaterMarkColor);//use Water mark color
//Draw Text or WaterMark
args.Graphics.DrawString((waterMarkTextEnabled ? WaterMarkText : Text),
drawFont, drawBrush, new PointF(0.0F, 0.0F));
base.OnPaint(args);
}
private void JoinEvents(Boolean join)
{
if (join)
{
this.TextChanged += new System.EventHandler(this.WaterMark_Toggel);
this.LostFocus += new System.EventHandler(this.WaterMark_Toggel);
this.FontChanged += new System.EventHandler(this.WaterMark_FontChanged);
//No one of the above events will start immeddiatlly
//TextBox control still in constructing, so,
//Font object (for example) couldn't be catched from within
//WaterMark_Toggle
//So, call WaterMark_Toggel through OnCreateControl after TextBox
//is totally created
//No doupt, it will be only one time call
//Old solution uses Timer.Tick event to check Create property
}
}
private void WaterMark_Toggel(object sender, EventArgs args )
{
if (this.Text.Length <= 0)
EnableWaterMark();
else
DisbaleWaterMark();
}
private void EnableWaterMark()
{
//Save current font until returning the UserPaint style to false (NOTE:
//It is a try and error advice)
oldFont = new System.Drawing.Font(Font.FontFamily, Font.Size, Font.Style,
Font.Unit);
//Enable OnPaint event handler
this.SetStyle(ControlStyles.UserPaint, true);
this.waterMarkTextEnabled = true;
//Triger OnPaint immediatly
Refresh();
}
private void DisbaleWaterMark()
{
//Disbale OnPaint event handler
this.waterMarkTextEnabled = false;
this.SetStyle(ControlStyles.UserPaint, false);
//Return back oldFont if existed
if(oldFont != null)
this.Font = new System.Drawing.Font(oldFont.FontFamily, oldFont.Size,
oldFont.Style, oldFont.Unit);
}
private void WaterMark_FontChanged(object sender, EventArgs args)
{
if (waterMarkTextEnabled)
{
oldFont = new System.Drawing.Font(Font.FontFamily,Font.Size,Font.Style,
Font.Unit);
Refresh();
}
}
}
}
Based on #Pooven's answer (thank you!), I created this class. Works for me.
/// <summary>
/// A textbox that supports a watermak hint.
/// </summary>
public class WatermarkTextBox : TextBox
{
/// <summary>
/// The text that will be presented as the watermak hint
/// </summary>
private string _watermarkText = "Type here";
/// <summary>
/// Gets or Sets the text that will be presented as the watermak hint
/// </summary>
public string WatermarkText
{
get { return _watermarkText; }
set { _watermarkText = value; }
}
/// <summary>
/// Whether watermark effect is enabled or not
/// </summary>
private bool _watermarkActive = true;
/// <summary>
/// Gets or Sets whether watermark effect is enabled or not
/// </summary>
public bool WatermarkActive
{
get { return _watermarkActive; }
set { _watermarkActive = value; }
}
/// <summary>
/// Create a new TextBox that supports watermak hint
/// </summary>
public WatermarkTextBox()
{
this._watermarkActive = true;
this.Text = _watermarkText;
this.ForeColor = Color.Gray;
GotFocus += (source, e) =>
{
RemoveWatermak();
};
LostFocus += (source, e) =>
{
ApplyWatermark();
};
}
/// <summary>
/// Remove watermark from the textbox
/// </summary>
public void RemoveWatermak()
{
if (this._watermarkActive)
{
this._watermarkActive = false;
this.Text = "";
this.ForeColor = Color.Black;
}
}
/// <summary>
/// Applywatermak immediately
/// </summary>
public void ApplyWatermark()
{
if (!this._watermarkActive && string.IsNullOrEmpty(this.Text)
|| ForeColor == Color.Gray )
{
this._watermarkActive = true;
this.Text = _watermarkText;
this.ForeColor = Color.Gray;
}
}
/// <summary>
/// Apply watermak to the textbox.
/// </summary>
/// <param name="newText">Text to apply</param>
public void ApplyWatermark(string newText)
{
WatermarkText = newText;
ApplyWatermark();
}
}
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern Int32 SendMessage(IntPtr hWnd, int msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)]string lParam);
const int EM_SETCUEBANNER = 0x1501;
public Form1()
{
InitializeComponent();
SendMessage(textBox1.Handle, EM_SETCUEBANNER, 1, "Username");
SendMessage(textBox2.Handle, EM_SETCUEBANNER, 1, "Password");
}
I'm just starting to learn C# this semester so I'm not an expert, but this worked for me:
(This is using windows forms)
private void Form1_Load(object sender, EventArgs e)
{
textBox1.SelectionStart = 0; //This keeps the text
textBox1.SelectionLength = 0; //from being highlighted
textBox1.ForeColor = Color.Gray;
}
private void textBox_MouseMove(object sender, MouseEventArgs e)
{
Cursor.Current = Cursors.IBeam; //Without this the mouse pointer shows busy
}
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
if (textBox1.Text.Equals("Type here...") == true)
{
textBox1.Text = "";
textBox1.ForeColor = Color.Black;
}
}
private void textBox1_KeyUp(object sender, KeyEventArgs e)
{
if (textBox1.Text.Equals(null) == true || textBox1.Text.Equals("") == true)
{
textBox1.Text = "Type here...";
textBox1.ForeColor = Color.Gray;
}
}
PRODUCES SIMILAR OUTPUT TO HTML WATERMARK
Here is my code for textbox "watermark" or "preview" text - works great! Using Windows Forms Application.
NOTE: This example has 3 text boxes, each has the below method for the "mouse leave" event, and "mouse enter" event respectively.
private void textBoxFav_Leave(object sender, EventArgs e) {
TextBox textbox = (TextBox)sender;
if (String.IsNullOrWhiteSpace(textbox.Text)) {
textbox.ForeColor = Color.Gray;
if (textbox.Name == "textBoxFavFood") {
textbox.Text = "Favorite Food";
}
else if (textbox.Name == "textBoxFavDrink") {
textbox.Text = "Favorite Drink";
}
else if (textbox.Name == "textBoxFavDesert") {
textbox.Text = "Favorite Desert";
}
}
else {
textbox.ForeColor = Color.Black;
}
}
private void textBoxFav_Enter(object sender, EventArgs e) {
TextBox textbox = (TextBox)sender;
if (textbox.Text == "Favorite Food" || textbox.Text == "Favorite Drink" || textbox.Text == "Favorite Desert") {
textbox.Text = "";
textbox.ForeColor = Color.Black;
}
}
Based on answer of Ahmed Soliman Flasha use following class:
public class TextBoxHint : TextBox
{
string _hint;
[Localizable(true)]
public string Hint
{
get { return _hint; }
set { _hint = value; OnHintChanged(); }
}
protected virtual void OnHintChanged()
{
SendMessage(this.Handle, EM_SETCUEBANNER, 1, _hint);
}
const int EM_SETCUEBANNER = 0x1501;
[DllImport("user32.dll", CharSet = CharSet.Auto)]
private static extern Int32 SendMessage(IntPtr hWnd, int msg, int wParam, [MarshalAs(UnmanagedType.LPWStr)]string lParam);
}
Handle the lost focus event and if the property Text is empty, fill it with your default string.
If this is ASP.NET (as opposed to winforms), you could do this:
If you are using jQuery, add this to your document ready (or however you initialize your page):
var $textbox = $("textbox selector"); // assumes you select a single text box
if ($textbox.val() == "") {
$textbox.val("Type here to...");
$textbox.one('focus', function() {
$(this).attr('value', '');
});
}
You'll need to do some small refactoring if you are selecting more than one text box (put the if statement inside of an each on the element).
In the last version of C# the TextBox has the property PlaceholderText, which does all work. So you only need to set "Type here..." as value of this property.
You can draw string "Type here" to the textbox background until it empty
If this is for ASP.NET then you can try TextBoxWatermark.
If this is for Windows Forms, this is already answered here in SO.
Why using OnTextChanged?
I would suggest to remove the text "Type here" when the TextBox gets focus.
When the control loses focus and no text is entered, you can display the text again.
Same result and no need for tricky logic.
If you want to avoid control resizing problems and data binding problems and make the code simpler (ok, it is questionable), you can just use a label and toggle it's visibility. Then
private void FilterComboBox_GotFocus(object sender, EventArgs e)
{
FilterWatermarkLabel.Visible = false;
}
private void FilterComboBox_LostFocus(object sender, EventArgs e)
{
if (!FilterWatermarkLabel.Visible && string.IsNullOrEmpty(FilterComboBox.Text))
{
FilterWatermarkLabel.Visible = true;
}
}
Another approach for images and also avoiding data binding problems is here
https://msdn.microsoft.com/en-us/library/bb613590(v=vs.100).aspx
Based on #Joel's answer. I fix his class (thanks for the base!)
/// <summary>
/// A textbox that supports a watermak hint.
/// Based on: https://stackoverflow.com/a/15232752
/// </summary>
public class WatermarkTextBox : TextBox
{
/// <summary>
/// The text that will be presented as the watermak hint
/// </summary>
private string _watermarkText;
/// <summary>
/// Gets or Sets the text that will be presented as the watermak hint
/// </summary>
public string WatermarkText
{
get { return _watermarkText; }
set { _watermarkText = value; }
}
/// <summary>
/// Whether watermark effect is enabled or not
/// </summary>
private bool _watermarkActive;
/// <summary>
/// Gets or Sets whether watermark effect is enabled or not
/// </summary>
public bool WatermarkActive
{
get { return _watermarkActive; }
set { _watermarkActive = value; }
}
/// <summary>
/// Create a new TextBox that supports watermak hint
/// </summary>
public WatermarkTextBox()
{
this.WatermarkActive = _watermarkActive;
this.Text = _watermarkText;
}
protected override void OnCreateControl()
{
base.OnCreateControl();
if (this.WatermarkActive)
CheckWatermark();
}
protected override void OnGotFocus(EventArgs e)
{
base.OnGotFocus(e);
CheckWatermark();
}
protected override void OnLostFocus(EventArgs e)
{
base.OnLostFocus(e);
CheckWatermark();
}
public void CheckWatermark()
{
if ((this.WatermarkActive) && String.IsNullOrWhiteSpace(this.Text))
{
ForeColor = Color.Gray;
this.Text = _watermarkText;
}
else if ((this.WatermarkActive) && (!String.IsNullOrWhiteSpace(this.Text)))
{
if (this.Text == _watermarkText)
this.Text = "";
ForeColor = Color.Black;
}
else
ForeColor = Color.Black;
}
}
Displaying "Type here to ..." until the user enters text into a TextBox is a well-known usability feature nowadays. How would one implement this feature in C#?
Set textbox.text as "Type here to ..."
create an event, say box_click()
-->Put this code in your method
private void box_Click(object sender, EventArgs e)
{
Textbox b = (Textbox)sender;
b.Text = null;
}
now assign this method to the "Enter" event of your textbox(maybe one or many)

Categories

Resources