In C# Winforms, I have a textbox with AutoCompleteMode. When the user types some letters the suggestion list populates Correctly. However, if an item in list is selected using (Keyboard) UP and Down key it could not navigate through the list of items. it just picks up the first item shown in the list.
On the other hand selection using mouse click works fine. here is my code
private void txtQryName_TextChanged(object sender, EventArgs e)
{
List<string> fullName = _customerBll.NameSuggestor(txtQryName.Text);
AutoCompleteStringCollection source = new AutoCompleteStringCollection();
source.AddRange(fullName.ToArray());
txtQryName.AutoCompleteMode = AutoCompleteMode.Suggest;
txtQryName.AutoCompleteSource = AutoCompleteSource.CustomSource;
txtQryName.AutoCompleteCustomSource = source;
}
The problem with having a key based selection is that it is very sensitive to the number of items it the stack contains. A possible solution could to be to set that KEY_UP and KEY_DOWN only increase/decreases or selects a specific type within the list.
Additionally I believe you can set the textbox to have a specific responds when you press it, you should consider that if for example the user has not choose an item within ( BOOLEAN: false or true) than no items are selected
An example
private void textBox1_KeyPress
(object sender,System.Windows.Forms.KeyPressEventArgs e)
{
// Check for the flag being set in the KeyDown event.
if (nonNumberEntered == true)
{
e.Handled = true;
}
}
https://msdn.microsoft.com/en-us/library/system.windows.forms.control.keydown(v=vs.110).aspx
Here is some useful stuff for you if you want to expand upon this idea. Hope my answer helps
Related
I have a combobox in which I can type in text and it suggests results based on list of items containing that text. Everything seems to be working fine, except when I type in the first letter, the combobox automatically selects the first item in the results list as can be seen in the screenshot (http://prntscr.com/pple6f). I need to press the key again to clear the text field and type in the proper name. Only then the results are correct (http://prntscr.com/pplekc)
I have the following method to check for combobox text update
private void comboBox1_TextUpdate(object sender, EventArgs e)
{
comboBox1.Items.Clear();
listNew.Clear();
var source = new AutoCompleteStringCollection();
foreach (var item in listOnit)
{
if (item.ToLower().Contains(this.comboBox1.Text.ToLower()))
{
listNew.Add(item);
}
}
comboBox1.Items.AddRange(listNew.ToArray());
comboBox1.SelectionStart = this.comboBox1.Text.Length;
Cursor = Cursors.Default;
comboBox1.DroppedDown = true;
}
AutoCompleteMode is set to None, same with AutoCompleteSource.
I need the combobox to start filtering the results as soon as I start typing, not having to press the same letter twice. Are there any other options/properties that I need to check?
My program generates ListView full of information. I type into a text box a name that might match one of the item names in the ListView. I want this typed name to weed out the names from the ListView that don't match.
For example, if I type in "abc", names like "uvw" and "xyz" wouldn't show up anymore, but "abc" and "abcde" would still show up in the list view.
The end goal is to be able to check the checkboxes next to the names I want, and search for more names, eventually selecting several, without resetting the checkboxes.
Right now I click a button and the ListView is populated:
private void button1_Click(object sender, EventArgs e)
{
List<string> myList = getList();
foreach(string s in myList)
{
listView1.Items.Add(s);
}
}
getList() just returns a List<string> of all the names I want.
I can't figure out how to make the ListView update in real time when I type in my text box. I'm able to update it with a button click via repopulating the ListView based on looping through the List, and checking each name, but that's not what I want. It also doesn't retain checked check boxes, as it's a newly generated list each time.
I read about a "text change listener", but I'm not sure that's what I should be using here...
With filtering you need some way of remembering which ListViewItems are selected, so instead of inserting all your ListViewItems into your listview you want to instantiate them in a master list. Then attach a TextChanged event handler to your text box and when the text changes you display the items.
List<ListViewItem> masterlist;
public Form1()
{
InitializeComponent();
masterlist = new List<ListViewItem>();
}
private void button1_Click(object sender, EventArgs e)
{
// Populate the masterlist
masterlist.Clear();
foreach(string s in getList())
{
masterlist.Items.Add(new ListViewItem(s));
}
// Display the items in the listview
DisplayItems();
}
private void DisplayItems()
{
listView1.Items.Clear();
// This filters and adds your filtered items to listView1
foreach(ListViewItem item in masterlist.Where(lvi => lvi.Text.ToLower().Contains(textBox1.Text.ToLower().Trim())))
{
listView1.Items.Add(item);
}
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
// Re-display the items when the filter changes
DisplayItems();
}
As you're dealing directly with ListViewItems in your masterlist they will retain their checked state when swapped in and out of listView1.
I have assumed that your filter textbox is called textBox1.
If you want to go for a full C# solution (rather than using any Javascript), as much as it pains me, I would suggest using an UpdatePanel.
Put your ListBox inside they the <ContentTemplate> section of the <UpdatePanel> then add an <asp:AsyncPostBackTrigger> with the ControlID set to that of your textbox. Make sure that the UpdateMode property of the UpdatePanel is set to "Conditional".
On your TextBox you will also have to set the AutoPostBack property to true. On the TextBox itself you will have to create a TextChanged event handler, then in your code behind (.cs file) you will have the logic for your TextChanged handler which will filter the list then set the new value for your ListBox.
UpdatePanels are ok for simply scenarios, but you can very easily get yourself into trouble by mis-using them.
I have a winform with a group of comboboxes, all of the comboboxes with the same list items in them.
I need a way to confirm that when the user is done selecting a value in each box that they only selected each list value once.
Ex:
cbox1 cbox2 cbox 3
Item A Item B Item A (this needs to flag an error since Item A is already selected in cbox1)
I was thinking trying to use the selectedvaluecommited action (as after i populate the list I change the selected index to -1 so they all show "empty" to start) but the loop to make it work seems to be eluding me.
background: this is choosing fields to build a spreadsheet and the user needs to choose the field order.
You can do it like this (quick and dirty):
Add SelectedIndexChanged handler for all three comboboxes (in Form_Load in example)
comboBox1.SelectedIndexChanged += CheckComboBoxes;
comboBox2.SelectedIndexChanged += CheckComboBoxes;
comboBox3.SelectedIndexChanged += CheckComboBoxes;
in CheckComboBoxes method do your checking:
private void CheckComboBoxes(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex == comboBox2.SelectedIndex ||
comboBox1.SelectedIndex == comboBox3.SelectedIndex ||
comboBox2.SelectedIndex == comboBox3.SelectedIndex)
MessageBox.Show("comboboxes are not unique");
}
EDIT:
this is approach when having n comboboxes. Put all items into list, select distinct values and compare that distinct count with items count... Something like this:
private void CheckComboBoxes(object sender, EventArgs e)
{
List<string> comboValues = new List<string>();
foreach (Control c in this.Controls)
{
if (c is ComboBox && !string.IsNullOrEmpty((c as ComboBox).SelectedItem.ToString()))
comboValues.Add((c as ComboBox).SelectedItem.ToString());
}
if (comboValues.Distinct().ToList().Count < comboValues.Count)
MessageBox.Show("not all combos are unique");
}
Here's an approach you can take.
To make the affected comboboxes easy to distinguish, put them all in a GroupBox container.
Write a validation method for your group box.
Subscribe to the group box Validating event by attaching it to your validation method.
In your validation method, loop through all the ComboBox controls in the group box and check if there are any duplicates, and issue an error if so.
For example, assuming the group box is called groupBox1:
private void GroupBox1_Validating(object sender, CancelEventArgs e)
{
base.OnValidating(e);
var selectedIndices = groupBox1.Controls.OfType<ComboBox>().Select(item => item.SelectedIndex);
var anyDuplicates = selectedIndices.GroupBy(x => x).Any(x => x.Count() > 1);
if (!anyDuplicates)
return;
MessageBox.Show("There are duplicates!");
e.Cancel = true;
}
And subscribe to the group box Validating event in the Form1 constructor:
public Form1()
{
InitializeComponent();
groupBox1.Validating += GroupBox1_Validating;
}
Sometimes when validating like this, you need to prevent the validation logic from executing if the user clicks the Cancel button. You're supposed to be able to set the CausesValidation property of the Cancel button to false to prevent this, but I find that it doesn't work for me.
Instead, I just use a bool cancelling field which I set to true in the Cancel button handler:
private void cancelButton_Click(object sender, EventArgs e)
{
cancelling = true;
this.Close();
}
bool cancelling;
And then add the following to the start of GroupBox1_Validating():
if (cancelling)
return;
If it is possible to have different UI design then my suggestion goes as under:
Alternative UI Design - A
Create One ListBox ListFieldsOriginal and populate
Create Second ListBox ListUserSelection, keep it empty initially
Provide buttons as under:
Button '>' means add currently selected item from ListFieldsOrginial to ListUserSelection at end; and remove that item from ListFieldsOriginal
Button '<' means remove currenly selected item from lstUserSelection; and add that item back to ListFieldsOriginal (of course at end)
NOTE: If adding item back to ListFieldsOriginal is your requirement then extra coding is required to find its appropriate index in the ListFieldsOriginal.
Alternative UI Design - B
Create One CheckedListBox ListFieldsOriginal and populate
Create one ListBox ListUserSelection, keep it empty initially
Define ItemCheck event handler for ListFieldsOriginal to add/remove items to/from ListUserSelected.
if (e.CurrentValue==CheckState.Unchecked)
{
string item = ListFieldsOriginal.Items[item];
ListUserSelection.Items.Add(item);
}
else
{
string item = ListFieldsOriginal.Items[item];
ListUserSelection.Items.Remove(item);
}
I have a combo box (winform). This combo box has some items (eg. 1,2,3,4).
Now, when I change the selection within this combo, I wish to know the old index and the new index.
How do I get this?
Possible approaches that I wish to AVOID.
Add an enter event, cache the current index and then on selection index change get the new index.
Using the selected text/selected item property received by the sender of the event.
What I ideally want:
In the event args that are received, I want something like:
e.OldIndex;
e.newIndex;
Right now the event args which are received in the SelectionIndex Change event are totally useless.
I don't want to use more than one event.
If C#, does not offer this, can I have my event which passes the old index and new index as event args?
Seems like this is a possible duplicate
ComboBox SelectedIndexChanged event: how to get the previously selected index?
There is nothing built in, you will need to listen for this event and keep track in a class variable.
But this answer seems to suggest a sensible way of extending the combobox to keep track of the previous index
https://stackoverflow.com/a/425323/81053
1-Make a List of integers
2-Bind a Button to switch to previous Screen (button Name "prevB")
3-change the ComboBox Index as Per described in the code
//initilize List and put current selected index in it
List<int> previousScreen = new List<int>();
previousScreen.Add(RegionComboBox.SelectedIndex);
//Button Event
private void prevB_Click(object sender, EventArgs e)
{
if (previousScreen.Count >= 2)
{
RegionComboBox.SelectedIndex = previousScreen[previousScreen.Count - 2];
}
}
You will need to replace the ComboBox with the following control:
public class AdvancedComboBox : ComboBox
{
private int myPreviouslySelectedIndex = -1;
private int myLocalSelectedIndex = -1;
public int PreviouslySelectedIndex { get { return myPreviouslySelectedIndex; } }
protected override void OnSelectedIndexChanged(EventArgs e)
{
myPreviouslySelectedIndex = myLocalSelectedIndex;
myLocalSelectedIndex = SelectedIndex;
base.OnSelectedIndexChanged(e);
}
}
Now you can get the PreviouslySelectedIndex property.
You can use YourComboBox.Tag (or other unused string/int property) to store old selected index...
I use such pair
comboBox.SelectedItem new item
comboBox.SelectionBoxItem old item
I have a TextBox in a winform and have set the AutoCompleteSource of the TextBox as CustomSource. Now the problem is to set the other fields in the form accordingly a user selects an option from the auto complete list.
For example my auto complete list contains "foo", "food", "foomatic". When a user types 'f' all the three options are shown. User selects "foo". And the next text box in the form changes accordingly. How to accomplish this.
Thanks in advance.
The textbox fires key events for "Down" arrow key when you travel down the auto complete list; it also sets the selected item text to the textbox. You can track the down key to set the other fields.
Alternatively, you can capture the key events for "Enter" key, which is raised if the user selects an item in the list pressing the enter key or mouse click
private void textBox1_KeyUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
//Check if the Text has changed and set the other fields. Reset the textchanged flag
Console.WriteLine("Enter Key:" + textBox1.Text);
}
else if (e.KeyCode == Keys.Down)
{
//Check if the Text has changed and set the other fields. Reset the textchanged flag
Console.WriteLine("Key Down:" + textBox1.Text);
}
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
//this event is fired first. Set a flag to record if the text changed.
Console.WriteLine("Text changed:" + textBox1.Text);
}
I've used ComboBox to gain this option:
// Create datasource
List<string> lstAutoCompleteData = new List<string>() { "first name", "second name", "third name"};
// Bind datasource to combobox
cmb1.DataSource = lstAutoCompleteData;
// Make sure NOT to use DropDownList (!)
cmb1.DropDownStyle = ComboBoxStyle.DropDown;
// Display the autocomplete using the binded datasource
cmb1.AutoCompleteSource = AutoCompleteSource.ListItems;
// Only suggest, do not complete the name
cmb1.AutoCompleteMode = AutoCompleteMode.Suggest;
// Choose none of the items
cmb1.SelectedIndex = -1;
// Every selection (mouse or keyboard) will fire this event. :-)
cmb1.SelectedValueChanged += new EventHandler(cmbClientOwner_SelectedValueChanged);
now, the event is fired on value selected even if it's only from the Autocomplete popup window. (doesn't matter if the selection done with mouse or keyboard)