Replace text in ComboBox upon selecting an item - c#

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;
//..
}
`

Related

How can i store a textboxt text for an array and restore it later?

What i want to do is: I have a "+" button, that creates a new tab every click, and one textbox. I want to do every tab create a text array for the same textbox (1 textbox, having different values according to the selected tabcontrol tab.) i have a maximum value of 5 tabs that i set, how can i do that?
i searched alot on how to create it, but didnt found a specific one for what i need
code of tabcontrols
private void label1_Click(object sender, EventArgs e)
{
if(tabControl1.TabPages.Count != 5)
{
page++;
string title = "Script " + page.ToString();
TabPage tabipage = new TabPage(title);
tabControl1.TabPages.Add(tabipage);
}
else
{
MessageBox.Show("You cant add more tabs!");
}
}
also, doesnt need to be exactly an array, just need to save and restore values between tabs
This is a pretty open ended question, and you'll have to forgive me if I misunderstood. What I'm guessing is you have a tab control, and OUTSIDE the tab control you have a single textbox that should change based on which tab is selected? If so you could do something similar to the below:
class tabWithScript : TabPage
{
public tabWithScript(string title) : base(title)
{ }
public string myscript { get; set; }
}
private void button1_Click(object sender, EventArgs e)
{
if (tabControl1.TabPages.Count < 5)
{
string title = "Script " + (tabControl1.TabPages.Count +1 ).ToString();
tabWithScript tabipage = new tabWithScript(title);
tabipage.myscript = $"Oh man, this is my script {tabControl1.TabPages.Count +1}";
tabControl1.TabPages.Add(tabipage);
}
else
{
MessageBox.Show("You cant add more tabs!");
}
}
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
try
{
tabWithScript myTab = (tabWithScript)tabControl1.SelectedTab;
textBox1.Text = myTab.myscript;
}
catch (Exception ex) { }
}
}
Instead of using the default TabPage object you can create your own class that inherits TabPage, then store whatever information you want in there. (You'd likely have to delete the default TabPages the tabcontrol creates).
Alternatively, you can just create a string array and index it appropriately e.g.
string[] myList = new string[5];
private void button1_Click(object sender, EventArgs e)
{
if (tabControl1.TabPages.Count < 5)
{
string title = "Script " + (tabControl1.TabPages.Count +1 ).ToString();
TabPage tabipage = new TabPage(title);
myList[tabControl1.TabPages.Count] = $"Oh man, this is my script {tabControl1.TabPages.Count +1}";
tabControl1.TabPages.Add(tabipage);
}
else
{
MessageBox.Show("You cant add more tabs!");
}
}
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
textBox1.Text = myList[tabControl1.SelectedIndex];
}
`
An alternative of #Devon Page's solution is using the Tag property, existing in almost all controls, to store value.
private void label1_Click(object sender, EventArgs e)
{
if (tabControl1.TabPages.Count < 5)
{
page++;
string title = "Script " + page.ToString();
TabPage tabipage = new TabPage(title);
tabipage.Tag = title; //Store whatever you want.
tabControl1.TabPages.Add(tabipage);
}
else
{
MessageBox.Show("You cant add more tabs!");
}
}
private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
{
textBox1.Text = tabControl1.SelectedTab.Tag.ToString(); // Cast to restore it
}

Create a procedure to reduce redundant code in C#

When I click a check box on a Windows Form, it enables a text box and sets the cursor in it ready for input. Code is relatively simple:
private void chkLatte_CheckedChanged(object sender, EventArgs e)
{
if(chkLatte.Checked)
{
txtLatte.Enabled = true;
txtLatte.Focus();
}
else
{
txtLatte.Enabled = false;
txtLatte.Text = "0";
}
}
Now, here's the rub. I have lots of these check boxes so what I want is something like this:
public void setCheckBox(string chkName, string txtName)
{
if (chkName.Checked)
{
txtName.Enabled = true;
txtName.Focus();
}
else
{
txtName.Enabled = false;
txtName.Text = "0";
}
}
Now, I can just call the method and pass the appropriate parameters like this:
private void chkMocha_CheckedChanged(object sender, EventArgs e)
{
setCheckBox(chkMocha,txtMocha);
}
Of course, this won't work: .Checked .Enabled .Focus() etc only work with a check box object and I define chkName as a string
How should I re-write the procedure setCheckBox to overcome this problem?
And why don't you pass the object sender as it is?
I mean something like this:
public void setCheckBox(CheckBox chk, TextBox txt)
{
if (chk.Checked)
{
txt.Enabled = true;
txt.Focus();
}
else
{
txt.Enabled = false;
txt.Text = "0";
}
}
And casting of course:
In the designer you have something like:
private System.Windows.Forms.TextBox txtMocha;
And by this reason you will solve a lot problems.
private void chkMocha_CheckedChanged(object sender, EventArgs e)
{
setCheckBox((CheckBox)sender, txtMocha);
}
Also, I have to say, that the code you give doesn't work... You have supposed it.
If you want pass the parameters as strings, use this:
Get a Windows Forms control by name in C#
One way to solve this is to assign the same handler to all checkboxes even
checkbox1.Check += chk_CheckedChanged;
checkbox2.Check += chk_CheckedChanged;
private void chk_CheckedChanged(object sender, EventArgs e)
{
// do your logic here
}

Non-invocable member 'System.Windows.Forms.Control.Text' cannot be used like a method

noob programmer here so sorry if I'm missing something really simple, here's my code:
namespace Programming_Assignment_2
{
public partial class Form1 : Form
{
string combination;
const int MinLength = 6;
bool CombinationCheck(string combination)
{
if (combination.Length > 5)
return true;
else
return false;
}
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
}
private void btn_SetComb_Click(object sender, EventArgs e)
{
combination = My_Dialogs.InputBox("Please enter a password longer than 5 characters: ");
if (combination.Length < MinLength)
My_Dialogs.InputBox("Error! Please make sure password is longer than 5 characters: ");
else
{
MessageBox.Show("Thank you, password saved.");
}
}
private void label1_Click(object sender, EventArgs e)
{
}
private void text_SafeStatus_TextChanged(object sender, EventArgs e)
{
bool CombinationCheck;
if (
text_SafeStatus.Text("Combination Set"));
else (
text_SafeStatus.Text("Combination Not Set"));
}
}
}
So the two .Text's at the bottom return the error in the Title plus I get a few more mini-errors here and there such as
Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement
at the bracket after the else..
Again sorry for what is probably a noob question but I can't seem to find answers that specifically help me
edit 1:
So the text error was fixed for me thanks but I have another problem which is trying to get the true/false value from a bool and using it to determine what the text box displays..
if bool CombinationCheck;
if CombinationCheck true;
text_SafeStatus.Text = "Combination Set";
else
text_SafeStatus.Text = "Combination Not Set";
that's what I currently have, I have no idea how to make this work.
You need to assign TextBox text property, as there's no such method called Text(), so use assignment operator = to assign the value.
text_SafeStatus.Text = "Combination Set";
And if you need to check the value of a Textbox, use == comparison operator, don't use single = for comparison, as per above example = is assignment operator and only used to assign a value.
if (text_SafeStatus.Text == "Combination Set")
// do something //
EDIT
CombinationCheck returns true or false depending on the length of string you pass into the method.
Move this code snippet from text_SafeStatus_TextChanged to btn_SetComb_Click event. Paste it below the existing codes.
private void btn_SetComb_Click(object sender, EventArgs e)
{
// .. existing codes .. //
if (CombinationCheck(combination))
text_SafeStatus.Text = "Combination set";
else
text_SafeStatus.Text = "Combination not set";
}

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.

How to show only a substring of an item in a ComboBox in C#

I have some Company names as Items in a ComboBox like this:
123 SomeCompany
Always a number between 100 and 999 plus the company name.
Now if the user selects an Item I want the TextBox to only show the number, not the company name. he company name should only be visible when he drops down the ComboBox...
I tried to set ComboBox1.Text = ComboBox.Text.Substring(0, 3) in the SelectedIndexChanged-Event and the TextChanged-Event, but it didn't do anything, there was always everything in the ComboBox...
AutocompleteMode is set to none.
What did I do wrong?
To always format the value, you could use the Format event (with FormattingEnabled = true)
private void comboBox1_Format(object sender, ListControlConvertEventArgs e)
{
e.Value = e.Value.ToString().Substring(0, 3);
}
But if you want the full value to be displayed when the dropdown is shown, you can temporarily disable the formatting:
private void comboBox1_DropDown(object sender, EventArgs e)
{
comboBox1.FormattingEnabled = false;
}
private void comboBox1_DropDownClosed(object sender, EventArgs e)
{
comboBox1.FormattingEnabled = true;
}
Try this
textbox1.Text = ComboBox1.SelectedItem.ToString().SubString(0, 3);
Try something like this:
private void combox1_ SelectedIndexChanged(object sender,EventArgs e)
{
string value = (combox1.selectedItem != null) ?
combox1.selectedItem.ToString().Substring(0, 3) : string.Empty;
}

Categories