TextBox.Text property not being set when I set it? - c#

This is the weirdest thing as it works for me in some code in not other. The following code is in a class that subclasses TextBox (NOTE: I don't know if it matters but I subclass the Text property to set/get from a private field _realText)
In the below code, the first base.Text = this.RealText works fine!!! We also set it inside that method MaskData() as well and it works!!!! so why in the world does it not work in the if(!field.isSecure) section? (look at the logs for what I mean). I tried adding Invalidate(), Update() after base.Text=temp but that didn't help.
Code:
private void SetupTextInBox()
{
if (isInManualMode)
{
this.ReadOnly = false;
base.Text = this.RealText;
}
else
{
this.ReadOnly = true;
if (!field.IsSecure)
{
string temp = this.RealText;
log.Info("This field is not secure so show it. field=" + field.Variable + " real='" + temp+"'");
base.Text = temp;
log.Info("text value='" + base.Text+"'");
return;
}
else
{
MaskData();
}
}
}
Logs
2012-06-30 07:15:51,468 [1] INFO AlpineAccess.Plugins.SecureTalkPlugin.SecureTextControl (null) - This field is not secure so show it. field=1.acc real='2222'
2012-06-30 07:15:51,468 [1] INFO AlpineAccess.Plugins.SecureTalkPlugin.SecureTextControl (null) - text value=''
EDIT: Note that this method is ALWAYS called from the same thread. It comes from server notifications telling us a tone on a phone somewhere else has been pressed, AND then that thread calls a BeginInvoke to put it in the GUI/controls thread or whatnot.
code just upstream from the above method is
public void AppendDTMFDigit(string digit)
{
log.Info("specified="+field.MaxSpecified+" someone appending dtmf digit on field=" + field.Variable+" fieldMax="+field.Max+" len="+RealText.Length);
if (field.MaxSpecified && this.RealText.Length >= field.Max)
return; //shortcut out since we can't exceed max digits
BeginInvoke(new MethodInvoker(delegate()
{
this.RealText = this.RealText + digit;
log.Info("new realtext=" + this.RealText);
SetupTextInBox();
}
));
}
MORE INFO: If I change ALL my client code to stop using Text property and use RealText property, and then stop overriding the Text property, it then works fine. (Obviously I don't want that though as now I can't just change from my control to TextBox and back easily without changing lots of client code referring to the RealText property....ugh, may have to live with that though....seems like some kind of odd bug.
MORE INFO: got debugger stepping into it and this is very odd.
2 very odd things.
It steps into the getter, not the setter???
It steps into MY Text property instead of TextBox's Text Property.
grrrrr, why would that be...sounds like a major bug, right? I mean, base.Text should refer to superclass's base, correct? – Dean Hiller just now edit
Adding Text method property code
public override string Text
{
get
{
return RealText;
}
set
{
if (value == null)
throw new ArgumentException("Not allowed to set RealText to null. Set to empty string instead");
RealText = value;
}
}

Full source for this user control that doesn't work unless you comment out the Text property is here (You would have to use a RealText property and expose that one to clients instead because base.Text does not appear to work properly).
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using log4net;
using IntraNext.Win32.Config;
namespace AlpineAccess.Plugins.SecureTalkPlugin
{
public partial class SecureTextBox : TextBox
{
private static readonly ILog log = LogManager.GetLogger(typeof(SecureTextControl));
private static readonly Color COLOR_FOCUSED = Color.Yellow;
private static readonly Color COLOR_FOCUSED_MANUALMODE = Color.PaleGreen;
private FocusEvent focusListener;
private Field field;
private bool isInManualMode;
private EventHandler textChanged;
private string RealText;
public SecureTextBox()
{
InitializeComponent();
RealText = "";
}
internal void Initialize(Field field, FocusEvent focusList, EventHandler textChanged)
{
this.focusListener = focusList;
this.textChanged = textChanged;
this.field = field;
this.Enter += new EventHandler(SecureTextBox_Enter);
this.Leave += new EventHandler(SecureTextBox_Leave);
this.TextChanged += new EventHandler(SecureTextBox_TextChanged);
MenuItem mnuItemNew = new MenuItem();
mnuItemNew.Text = "&Clear";
//THIS textbox HAS a context menu ALREADY BUT here we assign a new context menu to
//our text box to replace the old one which had cut, paste etc. that we don't want
//the agent using...
this.ContextMenu = new ContextMenu();
this.ContextMenu.MenuItems.Add(mnuItemNew);
mnuItemNew.Click += new EventHandler(clear_Click);
SwitchModes();
}
void SecureTextBox_TextChanged(object sender, EventArgs e)
{
if(isInManualMode) //make sure if in manual mode, we keep changes up to date in realText field
RealText = Text;
textChanged(sender, e);
}
void clear_Click(object sender, EventArgs e)
{
ClearAll();
}
internal void SetManualMode(bool inManual)
{
if (isInManualMode == inManual)
return; //we don't care if there is no change so return;
isInManualMode = inManual;
SwitchModes();
}
void SecureTextBox_Leave(object sender, EventArgs e)
{
log.Info("exiting=" + field.Variable);
focusListener(field.Variable, false, this.RealText);
BeginInvoke(new MethodInvoker(delegate()
{
ChangeBackground();
}
));
}
void SecureTextBox_Enter(object sender, EventArgs e)
{
log.Info("entering=" + field.Variable );
focusListener(field.Variable, true, this.RealText);
BeginInvoke(new MethodInvoker(delegate()
{
ChangeBackground();
}
));
}
private void SwitchModes()
{
SetupTextInBox();
ChangeBackground();
}
private void SetupTextInBox()
{
if (isInManualMode)
{
this.ReadOnly = false;
base.Text = RealText;
}
else if (!field.IsSecure)
{
this.ReadOnly = true;
string temp = RealText;
base.Text = temp;
Invalidate();
log.Info("txt=" + base.Text + " temp=" + temp);
}
else //not manual mode and IsSecure so mask it and make it readonly
{
this.ReadOnly = true;
MaskData();
}
}
private void MaskData()
{
log.Debug("mask=" + this.field.NumBeginDigitsToMaskSpecified + " num=" + field.NumBeginDigitsToMask + " txtLen=" + RealText.Length);
int numDigitsToMask = RealText.Length;
if (this.field.NumBeginDigitsToMaskSpecified && this.field.NumBeginDigitsToMask < RealText.Length)
{
int numDigits = this.field.NumBeginDigitsToMask;
string maskedPart = "".PadLeft(numDigits, '●');
string unmasked = RealText.Substring(numDigits);
string full = maskedPart + unmasked;
base.Text = full;
}
else
{
log.Debug("masking all digits");
base.Text = "".PadLeft(RealText.Length, '●');
}
}
private void ChangeBackground()
{
if (isInManualMode)
SetManualModeColor();
else
SetNonManualModeColor();
}
private void SetNonManualModeColor()
{
if (this.Focused)
this.BackColor = COLOR_FOCUSED;
else
this.BackColor = Control.DefaultBackColor;
}
private void SetManualModeColor()
{
if (this.Focused)
this.BackColor = COLOR_FOCUSED_MANUALMODE;
else
this.BackColor = Control.DefaultBackColor;
}
public void AppendDTMFDigit(string digit)
{
log.Info("manualmode="+isInManualMode+" specified=" + field.MaxSpecified + " someone appending dtmf digit on field=" + field.Variable + " fieldMax=" + field.Max + " len=" + RealText.Length);
if (isInManualMode)
return;
else if (field.MaxSpecified && RealText.Length >= field.Max)
return; //shortcut out since we can't exceed max digits
BeginInvoke(new MethodInvoker(delegate()
{
RealText = RealText + digit;
SetupTextInBox();
}
));
}
internal void ClearAll()
{
log.Info("Cleared textbox for =" + field.Variable);
base.Text = "";
RealText = "";
SetError("");
}
public override string Text
{
get
{
return RealText;
}
set
{
if (value == null)
throw new ArgumentException("Not allowed to set RealText to null. Set to empty string instead");
RealText = value;
}
}
/**
* Set to "" to clear the error or set anything to make valid
*/
public void SetError(string error)
{
if (!this.IsHandleCreated)
return;
SecureTextBox box = this;
//set to "" to clear the error
BeginInvoke(new MethodInvoker(delegate()
{
errorProvider1.SetError(box, error);
}));
}
}
}

Ok you've overridden Text - and you have THIS on one of your lines:
RealText = Text;
This will call the most derived 'Text' property (since you didn't specify base) and will call your overridden 'Text' on the current control
Which means you are setting RealText to itself
e.g.
void SecureTextBox_TextChanged(object sender, EventArgs e)
{
if (isInManualMode) //make sure if in manual mode, we keep changes up to date in realText field
RealText = Text; <---- **THIS CALLS THE OVERIDDEN 'Text' BELOW**
... snip
calls
public override string Text
{
get { return RealText; }
}
Which is a bit...nuts!
Did you mean to do:
void SecureTextBox_TextChanged(object sender, EventArgs e)
{
if (isInManualMode) //make sure if in manual mode, we keep changes up to date in realText field
RealText = base.Text; <--- THIS?
Check your code carefully, I bet this is one of your issues
Also it might be useful to change your naming conventions a little... e.g. I tend to use an underscore for private/protected fields
private string _realText;
instead of caps which is for properties
private string RealText;
Don't usually expose public fields, usually use properties but when I do I tend to use the same casing as I do for properties
This is so that it's easier to differentiate between properties and fields in your code and makes debugging a bit easier

Related

Same custom set property for some class field

I want every time that TextBox filed is set the back color change according to the sign.
public TextBox Current_value_text1
{
get { return Current_value_text; }
set {
Current_value_text = value;
if (Current_value_text.Text[0] == '+')
Current_value_text.BackColor = Color.Green;
else
Current_value_text.BackColor = Color.Red;
}
}
but I want to do this for all my textbox fields - any suggestions?
Thanks
You are probably looking for TextChanged event: whenever TextBox(es) change its Text, analyze it and update BackColor when necessary.
If it's your case, can select all the Textboxes (say, with a mouse), open "Properties" window and implement event hander for the TextChanged:
private void myTextBox_TextChanged(object sender, EventArgs e) {
TextBox box = sender as TextBox;
if (null == box)
return;
box.BackColor = box.Text.Trim().StartsWith("-")
? Color.Red
: Color.Green;
}
EDIT: The question in the comment is a quite different case; here we don't have events, but we can try extracting a method (NumSet):
private int m_Num1;
private int m_Num2;
private void NumSet(ref int num, int value) {
if (value == 1)
num = value + 1;
else
num = value - 1;
}
public int Num1 {
get => m_Num1;
set => NumSet(ref m_Num1, value);
}
public int Num2 {
get => m_Num2;
set => NumSet(ref m_Num2, value);
}

How to color different words with different colors in a RichTextBox while a user is writing and raise an event when that colored text is clicked

When a user writes some words in a rich text box, if that word matches some specific word, than the color of the that word should automatically change.
When the user clicks on that particular colored text, it should raise an event.
Given the requirements:
1) A User inserts some text in a RichTextBox Control.
2) If the word entered is part of a pre-defined list of words, that word should change color (so, define a relation between a word and a color).
3) When a mouse Click event is generated on a colored word, an event is raised, to notify which word was clicked.
Possible result (to replicate what's in the visual example):
Define a custom EventHandler with custom EventArgs:
public class WordsEventArgs : EventArgs
{
private string m_word;
public WordsEventArgs(string word) { m_word = word; }
public string Word { get { return m_word; } set { m_word = value; } }
}
public delegate void WordsEventHandler(object sender, WordsEventArgs e);
public event WordsEventHandler WordClicked;
protected void OnWordClicked(WordsEventArgs e) => WordClicked?.Invoke(this, e);
Subscribe to the event:
this.WordClicked += new WordsEventHandler(this.Word_Click);
Simple Class for the list of words:
public class ColoredWord
{
public string Word { get; set; }
public Color WordColor { get; set; }
}
public List<ColoredWord> ColoredWords = new List<ColoredWord>();
Fill the list with some words an related color, then bind it to a ListBox, calling the FillColoredWords() method (in other words, handle a collection of objects that relate pieces of text with Color values):
public void FillColoredWords()
{
ColoredWords.Add(new ColoredWord { Word = "SIMPLE", WordColor = Color.Goldenrod });
ColoredWords.Add(new ColoredWord { Word = "COLORED", WordColor = Color.Salmon });
ColoredWords.Add(new ColoredWord { Word = "TEXT", WordColor = Color.DarkCyan });
this.listBox1.DisplayMember = "Word";
this.listBox1.DataSource = ColoredWords;
}
In the KeyPress event, evaluate whether the last entered word is part of the list of words to color:
private void richTextBox1_KeyPress(object sender, KeyPressEventArgs e)
{
int currentPosition = richTextBox1.SelectionStart;
if (e.KeyChar == (char)Keys.Space && currentPosition > 0 && richTextBox1.Text.Length > 1) {
int lastSpacePos = richTextBox1.Text.LastIndexOf((char)Keys.Space, currentPosition - 1);
lastSpacePos = lastSpacePos > -1 ? lastSpacePos + 1 : 0;
string lastWord = richTextBox1.Text.Substring(lastSpacePos, currentPosition - (lastSpacePos));
ColoredWord result = ColoredWords.FirstOrDefault(s => s.Word == lastWord.ToUpper());
richTextBox1.Select(lastSpacePos, currentPosition - lastSpacePos);
if (result != null) {
if (richTextBox1.SelectionColor != result.WordColor) {
richTextBox1.SelectionColor = result.WordColor;
}
}
else {
if (richTextBox1.SelectionColor != richTextBox1.ForeColor) {
richTextBox1.SelectionColor = richTextBox1.ForeColor;
}
}
richTextBox1.SelectionStart = currentPosition;
richTextBox1.SelectionLength = 0;
richTextBox1.SelectionColor = richTextBox1.ForeColor;
}
}
In the MouseClick event, verify whether the event is generated on a colored word.
In that case, raise the custom OnWordClicked() event:
private void richTextBox1_MouseClick(object sender, MouseEventArgs e)
{
if (richTextBox1.SelectionColor.ToArgb() != richTextBox1.ForeColor.ToArgb()) {
try {
int wordInit = richTextBox1.Text.LastIndexOf((char)32, richTextBox1.SelectionStart);
wordInit = wordInit > -1 ? wordInit : 0;
int wordEnd = richTextBox1.Text.IndexOf((char)32, richTextBox1.SelectionStart);
string wordClicked = richTextBox1.Text.Substring(wordInit, wordEnd - wordInit) + Environment.NewLine;
OnWordClicked(new WordsEventArgs(wordClicked));
}
catch (Exception) {
//Handle a fast DoubleClick: RTB is a bit dumb.
//Handle a word auto-selection that changes the `.SelectionStart` value
}
}
}
In the custom event, you can append the clicked word to a TextBox (or do whatever else you want to do with it):
private void Word_Click(object sender, WordsEventArgs e)
{
textBox1.AppendText(e.Word);
}
first of all add an event to your rich box text changed,then
you need to change the color of text if that is an specific word like for example : Munis , Ali
private void Rchtxt_TextChanged(object sender, EventArgs e)
{
this.CheckKeyword("Munis", Color.Purple, 0);
this.CheckKeyword("Ali", Color.Green, 0);
}

How to disable button if textbox is empty

First of all sorry for my bad english.
I'm beginner at C# and i made a Windows forms application but i can't disable one button if a textbox is empty.
I tried some of the Enabled methods but they didn't work. Hope someone can help me fix this. Thank you very much
public partial class ModulusForm : Form
{
public double nje;
public double dy;
public double pergjigja;
public double rezultati;
public ModulusForm()
{
InitializeComponent();
Button btn = new Button();
btn.Click += new EventHandler(butoniGjenero_Click);
}
private void butoniPerfundo_Click(object sender, EventArgs e)
{
this.Close();
}
private void butoniGjenero_Click(object sender, EventArgs e)
{
Random random = new Random();
nje = random.Next(1, 100);
dy = random.Next(1, 100);
if (nje > dy)
{ textboxPyetja.Text = "X = " + nje + " " + "dhe" + " " + "Y = " + dy; }
else if (nje > dy)
{
nje = random.Next(1, 100);
dy = random.Next(1, 100);
}
rezultati = nje / dy;
}
private void butoniPastro_Click(object sender, EventArgs e)
{
textboxPyetja.Clear();
textboxPergjigja.Clear();
textboxPergjigjaSakt.Clear();
}
private void butoniVerteto_Click(object sender, EventArgs e)
{
try
{
pergjigja = double.Parse(textboxPergjigja.Text);
}
catch
{
var informim = MessageBox.Show("Rishiko fushat!", "Verejtje", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
if (textboxPergjigja.Text == "")
{
//nothin' baby
}
else
{
if (textboxPyetja.Text == "")
{
var informim = MessageBox.Show("Fusha e pyetjes eshte null!", "Verejtje", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
else
{
if (pergjigja == rezultati)
{
textboxPergjigjaSakt.Text = "Pergjigja eshte e sakte";
}
else
{
textboxPergjigjaSakt.Text = "Gabim." + " " + "Pergjigja e sakte eshte: " + "" + rezultati;
}
comboboxVargu.Items.Add(nje + " / " + dy + " = " + rezultati);
}
}
}
}
}
Credit to #Cody Gray for already suggesting this; I have just expanded it, so you can see how to implement and how it works
Overview
You can wire up an event handler for when your textboxPergjigja.Text's text has changed.
In the handler you create, you can then evaluate whether your button should be Enabled or not - using the string.IsNullOrWhiteSpace() check to set this.
First:
In your constructor for the form, subscribe to the textboxPergjigja.Text text box's TextChanged event.
Like this:
public ModulusForm()
{
InitializeComponent();
Button btn = new Button();
btn.Click += new EventHandler(butoniGjenero_Click);
// Add the subscription to the event:
textboxPergjigja.TextChanged += textboxPergjigja_TextChanged;
}
Next:
Add a handler that matches the correct delegate signature for that event.
Like this:
public textboxPergjigja_TextChanged(object sender, TextChangedEventArgs e)
{
// If the text box is not "empty", it will be enabled;
// If the text is "empty", it will be disabled.
butoniVerteto.Enabled = !string.IsNullOrWhiteSpace(textBoxPergjigja.Text);
}
This way, whenever the text in the textBoxPergjigja text box is changed; the evaluation is run and your button will always be enabled/disabled correctly.
Hope this helps! :)
Additional Info
You can also use textBox.Text.IsNullOrEmpty(), and it will still work - as suggested by #Cody
I have used string.IsNullOrWhiteSpace(), as opposed to textBox.Text.IsNullOrEmpty() for the following reasons:
The .IsNullOrEmpty() method only checks if the textBox.Text is either null or the total amount of characters is equal to 0.
The problem this might pose is, if the user only enters a space in the textbox, it is no longer Empty or null; thus this check will return true. If the logic of the program requires that an actual value be entered into the textbox, this logic can be flawed.
On the other hand: The string.IsNullOrWhiteSpace() check will check on 3 conditions - if the input string is null, Empty and contains only whitespace characters (space, newline etc.), also.
I hope this adds a little bit of extra fluff to give you an informed decision for future.
Hope it work!
private void YourTextBox_TextChanged(object sender, EventArgs e)
{
if (String.IsNullOrEmpty(YourTextBox.Text))
YourButton.Enabled = false;
else
YourButton.Enabled = true;
}
Handle it on TextChanged event of your TextBox. (double click on your textbox control when in designed, which will automatically create the text changed event for you).
private void textboxPyetja_OnTextChanged(..blah blah)
{
if(String.IsNullOrWhitespace(txtTargetTextbox.Text)
{
//disable your control
}
else
{
//enable your control
}
}
Edit after 4 years - for some reason:
And here's a one-liner version, some people just love them...
private void textboxPyetja_OnTextChanged(..blah blah)
{
btnILoveButtons.Enabled = string.IsNullOrWhitespace(txtTargetTextbox.Text);
{
Try this:
if (String.IsNullOrEmpty(textboxPergjigja.Text))
butoniVerteto.Enabled = false;
else
butoniVerteto.Enabled = true;
add an event for edit text in the txtbox. when the text changes, enable the button
Change textBox1 for your textbox name then change button1 name for you button name
if (textBox1.Text == "")
{
button1.Enabled = false;
}
else
button1.Enabled = true;

Real Hint in TextBox Visual Studio C# [duplicate]

This question already has answers here:
Watermark TextBox in WinForms
(11 answers)
Closed 4 years ago.
I'm currently making a Windows Forms Application on Visual Studio in C# and I'm trying to find a way to have a real hint.
I've found a lot of answers online on how to have some text preset there, Some examples even show how to grey out the text to look like a placeholder, but that's not what I'm looking for.
I want a grayed out text that you don't have to backspace to type something there. So I want it to behave like an HTML placeholder like the "Search Q&A" search bar on stack Overflow.
Is there an easy way to do this, like configuring a property of the textbox in the designer on Visual Studio?
This might be the ugliest code but I think you can improve it.
This following class is merely an extension of the standard TextBox
class PHTextBox : System.Windows.Forms.TextBox
{
System.Drawing.Color DefaultColor;
public string PlaceHolderText {get;set;}
public PHTextBox(string placeholdertext)
{
// get default color of text
DefaultColor = this.ForeColor;
// Add event handler for when the control gets focus
this.GotFocus += (object sender, EventArgs e) =>
{
this.Text = String.Empty;
this.ForeColor = DefaultColor;
};
// add event handling when focus is lost
this.LostFocus += (Object sender, EventArgs e) => {
if (String.IsNullOrEmpty(this.Text) || this.Text == PlaceHolderText)
{
this.ForeColor = System.Drawing.Color.Gray;
this.Text = PlaceHolderText;
}
else
{
this.ForeColor = DefaultColor;
}
};
if (!string.IsNullOrEmpty(placeholdertext))
{
// change style
this.ForeColor = System.Drawing.Color.Gray;
// Add text
PlaceHolderText = placeholdertext;
this.Text = placeholdertext;
}
}
}
Copy/paste to new cs file entitled PHTextBox.cs.
Go to your graphic designer and add a TextBox.
Go to the designer and change the instiantion line for the textbox as follow:
Now compile but before you do, just make sure the textbox is not the first element to get the focus. Add button for that matter.
Have you tried overlapping a label on the textbox?
On the textbox keypress event you can check the length of the textbox.text and set the label.
On the keypress event..
MyLabel.Visible = String.IsNullOrEmpty(MyTextBox.Text);
Of course you might want to set the default text of the label as well as grey it out too.
Issue with this is if your form is re sizable.
What you want to achieve is not native to windows forms.
I know that this is an old question; However I was searching for a way and I found my answer to be best answer to display a prompt text in a TextBox:
1) Create a class .cs file called for example MyExtensions.cs having a namespace called for example 'Extensions'.
2) Create a method in the TextBox called Init(string prompt) that takes the prompt text you want to display inside the TextBox.
3) Let me stop talking and give you the rest of the code for MyExtensions.cs (The entire code):
MyExtensions.cs
using System.Drawing;
using System.Windows.Forms;
namespace Extensions
{
public static class MyExtensions
{
public static void Init(this TextBox textBox, string prompt)
{
textBox.Text = prompt;
bool wma = true;
textBox.ForeColor = Color.Gray;
textBox.GotFocus += (source, ex) =>
{
if (((TextBox)source).ForeColor == Color.Black)
return;
if (wma)
{
wma = false;
textBox.Text = "";
textBox.ForeColor = Color.Black;
}
};
textBox.LostFocus += (source, ex) =>
{
TextBox t = ((TextBox)source);
if (t.Text.Length == 0)
{
t.Text = prompt;
t.ForeColor = Color.Gray;
return;
}
if (!wma && string.IsNullOrEmpty(textBox.Text))
{
wma = true;
textBox.Text = prompt;
textBox.ForeColor = Color.Gray;
}
};
textBox.TextChanged += (source, ex) =>
{
if (((TextBox)source).Text.Length > 0)
{
textBox.ForeColor = Color.Black;
}
};
}
}
}
Now Assume that you have three TextBox's : tbUsername, tbPassword, tbConfirm:
In your Form_Load(object sender, EventArgs e) method initialize your three TextBox's to have their appropriate Prompt Text Messages:
using Extensions;
namespace MyApp{
public partial class Form1 : Form{
private void Form1_Load(object sender,
EventArgs e){
tbUsername.Init("Type a username");
tbPassword.Init("Type a password");
tbConfirm.Init("Confirm your password");
}
}
}
Enjoy! :)
What about this
private bool hasTextBeenTyped;
private void Form1_Load(object sender, EventArgs e)
{
this.ActiveControl = label1;
textBox1.ForeColor = Color.LightGray;
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
hasTextBeenTyped = !String.IsNullOrEmpty(textBox1.Text);
if (hasTextBeenTyped)
{
textBox1.ForeColor = Color.Black;
}
}
private void textBox1_Click(object sender, EventArgs e)
{
if (!hasTextBeenTyped)
{
textBox1.Text = "";
}
}
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
hasTextBeenTyped = true;
}
the this.ActiveControl = label1; is just to take focus away from the text box initially. If something else already does, don't worry about that line.
Please, take a look at my ControlHintManager class, ControlHintInfo type and ControlHintType enumeration. They are written in Vb.Net, but you can get the idea analyzing the source-code, or compile the library to use it in your C# project without any more effort.
My solution has even better behavior than the StackOverflows hint that you mentioned, taking into account that when the control leave focus, the hint-text should be restored if the string remains empty.
Usage is so friendlly:
ControlHintInfo hint1 =
new ControlHintInfo("I'm a hint text.", font (or nul), Color.Gray,
ControlHintType.Persistent);
ControlHintManager.SetHint(TextBox1, hint1);
To acchieve this by your own, one way is calling the Win32 SendMessage function with the EM_SETCUEBANNER message, however, that will produce a too basic hint with poor behavior, not recommendable,
so a proper way to acchieve this is by taking control of the edit-control text by yourself, handling the Control.HandleCreated, Control.Enter, Control.Leave, Control.MouseDown, Control.KeyDown and Control.Disposed events (as you can see in my linked source-code).
Just use a object to keep track of the control's state (forecolor, text, and optionally the font), then use properlly the event-handlers mentioned to set or restore the text and color.
This is a online C# translation of the most-important code of the linked urls if this help you understand it better:
private static void Control_HandleCreated(object sender, EventArgs e) {
InstanceControlHintFields();
Control ctrl = (Control)sender;
ControlHintInfo hintInfo = controlHintsB(ctrl);
SetProperties(ctrl, hintInfo);
}
private static void Control_Enter(object sender, EventArgs e) {
InstanceControlHintFields();
Control ctrl = (Control)sender;
string ctrlText = ctrl.Text;
ControlHintInfo ctrlDefaults = controlHintsDefaults(ctrl);
ControlHintInfo hintInfo = controlHintsB(ctrl);
switch (hintInfo.HintType) {
case ControlHintType.Normal:
if ((ctrlText.Equals(hintInfo.Text, StringComparison.OrdinalIgnoreCase))) {
RestoreProperties(ctrl, ctrlDefaults);
}
break;
}
}
private static void Control_Leave(object sender, EventArgs e) {
InstanceControlHintFields();
Control ctrl = (Control)sender;
string ctrlText = ctrl.Text;
ControlHintInfo ctrlDefaults = controlHintsDefaults(ctrl);
ControlHintInfo hintInfo = controlHintsB(ctrl);
switch (hintInfo.HintType) {
case ControlHintType.Normal:
if ((ctrlText.Equals(hintInfo.Text, StringComparison.OrdinalIgnoreCase))) {
RestoreProperties(ctrl, ctrlDefaults);
} else if (string.IsNullOrEmpty(ctrlText)) {
SetProperties(ctrl, hintInfo);
}
break;
case ControlHintType.Persistent:
if (string.IsNullOrEmpty(ctrlText)) {
SetProperties(ctrl, hintInfo);
}
break;
}
}
private static void Control_MouseDown(object sender, MouseEventArgs e) {
InstanceControlHintFields();
Control ctrl = (Control)sender;
string ctrlText = ctrl.Text;
ControlHintInfo hintInfo = controlHintsB(ctrl);
switch (hintInfo.HintType) {
case ControlHintType.Persistent:
if ((ctrlText.Equals(hintInfo.Text, StringComparison.OrdinalIgnoreCase))) {
// Get the 'Select' control's method (if exist).
MethodInfo method = sender.GetType.GetMethod("Select", BindingFlags.Public | BindingFlags.Instance | BindingFlags.InvokeMethod, null, {
typeof(int),
typeof(int)
}, null);
if ((method != null)) {
// Select the zero length.
method.Invoke(ctrl, new object[] {
0,
0
});
}
}
break;
}
}
private static void Control_KeyDown(object sender, KeyEventArgs e) {
Control ctrl = (Control)sender;
string ctrlText = ctrl.Text;
ControlHintInfo ctrlDefaults = controlHintsDefaults(ctrl);
ControlHintInfo hintInfo = controlHintsB(ctrl);
switch (hintInfo.HintType) {
case ControlHintType.Persistent:
if ((ctrlText.Equals(hintInfo.Text, StringComparison.OrdinalIgnoreCase))) {
RestoreProperties(ctrl, ctrlDefaults);
} else if (string.IsNullOrEmpty(ctrlText)) {
RestoreProperties(ctrl, ctrlDefaults, skipProperties: { "Text" });
}
break;
case ControlHintType.Normal:
if (string.IsNullOrEmpty(ctrlText)) {
RestoreProperties(ctrl, ctrlDefaults);
}
break;
}
}
private static void Control_Disposed(object sender, EventArgs e) {
RemoveHint((Control)sender);
}
PS: It is Reflection based to support more variety of controls.

Replace text in ComboBox upon selecting an item

I have an editable ComboBox that should contain a path. The user can select several default paths (or enter his own) from a dropdown list, such as %ProgramData%\\Microsoft\\Windows\\Start Menu\\Programs\\ (All Users). The items in the dropdown list contain a short explanation, like the (All Users) part in the former example. Upon selection of such an item, I want to remove this explanation, so that a valid path is displayed in the ComboBox.
I currently strip the explanation out of the string and try to change the text via setting the Text property of the ComboBox. But this doesn't work, the string is parsed correctly, but the displayed text won't update (it stays the same as in the dropdown list, with the explanation).
private void combobox_TextChanged(object sender, EventArgs e) {
//..
string destPath = combobox.GetItemText(combobox.SelectedItem);
destPath = destPath.Replace("(All Users)", "");
destPath.Trim();
combobox.Text = destPath;
//..
}
I found the solution in a similar question, by using BeginInvoke()
Using Nikolay's solution, my method now looks like this:
private void combobox_SelectedIndexChanged(object sender, EventArgs e) {
if (combobox.SelectedIndex != -1) {
//Workaround (see below)
var x = this.Handle;
this.BeginInvoke((MethodInvoker)delegate { combobox.Text = combobox.SelectedValue.ToString(); });
}
}
The workaround is required, since BeginInvoke requires the control to be loaded or shown, which isn't necessarily the case if the program just started. Got that from here.
I suggest you to create PathEntry class to store both Path and its Description.
public sealed class PathEntry
{
public string Path { get; private set; }
public string Description { get; private set; }
public PathEntry(string path)
: this(path, path)
{
}
public PathEntry(string path, string description)
{
this.Path = path;
this.Description = description;
}
}
Then create an instance of BindingList<PathEntry> to store all the known paths and descriptions. Later you can add user-defined paths to it.
private readonly BindingList<PathEntry> m_knownPaths =
new BindingList<PathEntry>();
And update your Form's constructor as follows:
public YourForm()
{
InitializeComponent();
m_knownPaths.Add(new PathEntry("%ProgramData%\\Microsoft\\Windows\\Start Menu\\Programs",
"(All Users)"));
// TODO: add other known paths here
combobox.ValueMember = "Path";
combobox.DisplayMember = "Description";
combobox.DataSource = m_knownPaths;
}
private void combobox_DropDown(object sender, EventArgs e)
{
combobox.DisplayMember = "Description";
}
private void combobox_DropDownClosed(object sender, EventArgs e)
{
combobox.DisplayMember = "Path";
}
You might want to learn more abount DataSource, DisplayMember and ValueMember from MSDN.
this is probably not the most elegant solution, but a simple one for beginners - like me - who don't want to use those InvokeMethods before they understand them a little better.
The boolean is required because when assign a new string(object) to the position in the Items array, the handler is fired again - recursively.
I figured this out just now, since i was working on the exact same problem.
Just sharing :)
bool preventDoubleChange = false;
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (preventDoubleChange){
preventDoubleChange = false;
return;
}
preventDoubleChange = true;
switch (comboBox1.SelectedIndex)
{
case 0:
comboBox1.Items[0] = "ChangeToThisText";
break;
case 1:
comboBox1.Items[1] = "ChangeToThisText";
break;
default:
preventDoubleChange = false;
break;
}
}
...or if you are comfortable using the "Tag" field, you can avoid the whole mess with the boolean. This second variation is also cleaner in my opinion.
public Form1()
{
InitializeComponent();
comboBox1.Items.Add("Item One"); //Index 0
comboBox1.Items.Add("Item Two"); //Index 1
comboBox1.Items.Add("Item Three"); //Index 2
comboBox1.Items.Add("Item Four"); //Index 3
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.Tag != null && (int)comboBox1.Tag == comboBox1.SelectedIndex)
return;
switch (comboBox1.SelectedIndex)
{
case 0:
break;
case 1:
comboBox1.Tag = comboBox1.SelectedIndex;
comboBox1.Items[comboBox1.SelectedIndex] = "changed item 2";
break;
case 2:
comboBox1.Tag = comboBox1.SelectedIndex;
comboBox1.Items[comboBox1.SelectedIndex] = "changed item 3";
break;
case 3:
break;
default:
break;
}
}
redfalcon,
You can't change the text this way. What you need to do is get the selectedindex value and then set the Text property. Something like that:
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{
var index = DropDownList1.SelectedIndex;
DropDownList1.Items[index].Text = "changed";
}
`
private void combobox_TextChanged(object sender, EventArgs e)
{
//..
string destPath = combobox.SelectedItem.Text;
destPath = destPath.Replace("(All Users)", "");
destPath.Trim();
combobox.SelectedItem.Text = destPath;
//..
}
`

Categories