ComboBox searching in string, not just the first letter - c#

I have problem making my combobox searching inside the strings in the items.
I want to narrow down a list of members. They are formatted in this way (unique member id) - First name - last name.
When i leave all the settings "as is", then it will only "allow" me to search at the first char in the string.
The DataSource is set from a list, what are made from looping through all the files in a folder. ​
The code i have been using is as follows(partial code)
private void searchForShooterComboBox_KeyUp(object sender, KeyEventArgs e)
{
//if(e => KeyCode == Keys::Down || e => KeyCode == Keys::Down)
//string comboBoxValue = searchForShooterComboBox.Text;
//searchForShooterComboBox.DataSource = null;
//searchForShooterComboBox.DataSource = fliterComboBox(searchForShooterComboBox, memberFileNames);
//searchForShooterComboBox.Text = comboBoxValue;
}
private void searchForShooterComboBox_TextChanged(object sender, EventArgs e)
{
searchForShooterComboBox.DataSource = null;
searchForShooterComboBox.DataSource = fliterComboBox(searchForShooterComboBox, memberFileNames);
}
private List<string> fliterComboBox(ComboBox cobx, List<string> stringList)
{
List<string> returnList = new List<string>();
if (cobx.Text != ""){
try
{
foreach (string s in stringList)
{
if (s.Contains(cobx.Text))
{
returnList.Add(s);
}
}
}catch{
}
}
return returnList;
}
some of the code i tried seemed to filter the list OK, but after the methods ran it fills what seems to be the first item in the new list into the "text field", so the user wont be able to continue typing a name ex.
Will it make any difference using ComboBox.Items.Add() and ComboBox.Items.Remove() instead of using DataSource?
edit: the comboBox DataSource is initially set in the form_load event handler. Where the following code regarding the combobox is:
searchForShooterComboBox.DropDownStyle = ComboBoxStyle.DropDown;
searchForShooterComboBox.AutoCompleteMode = AutoCompleteMode.Suggest;
searchForShooterComboBox.AutoCompleteSource = AutoCompleteSource.ListItems
Thanks for taking the time to look.

Okay seems i figured something out myself,don't know if its the best way, but seems to get the job done :)
firstly i added the string's into both the ComboBox.items and into a list<string>. The reason for adding them both ways is for the user to see all available options on load.
for (int i = 0; i < membersFiles.Length; i++)
{
searchForShooterComboBox.Items.Add(membersFiles[i].Replace(".txt", "").Replace(#"C:\Users\Nicolai\Desktop\skytter\", "").Replace("-", " "));
memberFileNames.Add(membersFiles[i].Replace(".txt", "").Replace(#"C:\Users\Nicolai\Desktop\skytter\", "").Replace("-", " "));
}
After that i added a combobox_keydown event from the property window.
private void searchForShooterComboBox_KeyDown(object sender, KeyEventArgs e)
{
try
{
//checking if the key pressed is RETURN, in that case try to fill the combobox with the selected item,
//and continuing with other method
if (e.KeyValue == 13)
{
searchForShooterComboBox.Text = (string)searchForShooterComboBox.SelectedItem;
fillInfoInForm();
}
//making sure the key pressed IS NOT DOWN, UP, LEFT, RIGHT arrow key.
else if (e.KeyValue > 40 || e.KeyValue < 37)
{
filterComboBox(searchForShooterComboBox, searchForShooterComboBox.Text);
searchForShooterComboBox.Select(searchForShooterComboBox.Text.Length, 0);
searchForShooterComboBox.DroppedDown = true;
}
}
catch (FileNotFoundException ex) {
MessageBox.Show("Der blev ikke fundet nogen fil med flg. sti " + ex.FileName + "\nHusk at vælge hele navnet i listen, eller skriv det nøjagtigt som det står!");
}
}
made this method to search through the list items, clear the items in the combobox, and add the ones that match.
private void filterComboBox(ComboBox cobx, string enteredSearch)
{
//clearing ComboBox items before adding the items from the LIST that meets the search
cobx.Items.Clear();
//looping over the items from the list, comparing them to the search from the combobox text field.
//if the item in the list does not contain the string searched it will return an index of -1.
for (int i = memberFileNames.Count-1; i >= 0; i--)
{
if (memberFileNames[i].IndexOf(enteredSearch, 0, StringComparison.CurrentCultureIgnoreCase) >= 0)
{
cobx.Items.Add(memberFileNames[i]);
}
}
}
if you are having trouble finding the right KeyValues, try looking at
https://msdn.microsoft.com/en-us/library/system.windows.forms.keyeventargs.keyvalue(v=vs.110).aspx
and copy paste the code from there, and add it to you key_down event handler, and it will show most info(if not all) in a message box.
That was my workaround, if you have a better way of doing it, i am all ears :)

Related

Multiple checking of checkboxes (for statement)

i recently got into coding i'm a first year IT student.
my prof give us a project where we should create any kind of management system and i pick the fast food type one.
i stumbled in some problem while making a checkout function.
I have this multiple picture boxes with individual check boxes i made a function that whenever the user clicked the picture box the checkbox will be checked.
my proj currently
the problem I'am having is i want to take all the data(text) of checked boxes with if statement but it would be so tedious because i have like 15 picture boxes with checkbox.
is there any simpler way to do this with for statement? or anything just to shorten my code.
this is my current code.
private void button1_Click(object sender, EventArgs e)
{
string food1 = "";
string food2 = "";
//...........................food15
if (checkBox1.Checked)
{
food1 = checkBox1.Text;
}
if (checkBox2.Checked)
{
food2 = checkBox2.Text;
}
//.............................................checkbox15
if (food1 != "" || food2 != "")
{
MessageBox.Show(food1 + food2);
}
else
{
MessageBox.Show("Pick something ");
}
}
something like this:
var foods = new List<string>();
foreach(var checkbox in Controls.OfType<CheckBox>())
{
if(checkbox.Checked)
{
foods.Add(c.Text);
}
}
if(foods.Count != 0){
//do popup
} else {
//pick something
}
the form provides a Form.Controls collection, which contains all controls on that form; OfType<CheckBox>() filters the list, so we're only looping though checkboxes.
i also used a list of foods rather than separate food1,food2 strings
you can then check the count, and use that.
also you could so something like String.Join(", ", foods.ToArray()); to make a comma separate list of your foods

File attribute selection error

I have this code where you are suppose to be able to pick specific file attributes, but for some reason it is acting really weird. Can anyone spot the error(s)?
This is in a form, I am triggering checkAttributes when a file is selected. (string)path is the path to the selected file.
private async void Dropdown_File_Attr_DropDownItemClicked(object sender, ToolStripItemClickedEventArgs e)
{
try
{
foreach (FileAttributes attr in Enum.GetValues(typeof(FileAttributes)))
if (e.ClickedItem.Text == attr.ToString() && !(bool)e.ClickedItem.Tag)
File.SetAttributes(path, File.GetAttributes(path) | attr);
else if (e.ClickedItem.Text == attr.ToString() && (bool)e.ClickedItem.Tag)
File.SetAttributes(path, File.GetAttributes(path) & ~attr);
checkAttributes(path);
await WaitX(5);
Dropdown_File.ShowDropDown();
Dropdown_File_Attr.ShowDropDown();
}
catch (Exception ex) { MessageBox.Show("An error occured:\n\n" + ex.ToString(), "Error"); }
}
public async Task WaitX(int miliseconds) { await Task.Delay(miliseconds); }
private List<string> getAttributes(string ppath)
{
List<string> result = new List<string>();
FileAttributes attrs = File.GetAttributes(ppath);
if ((attrs & FileAttributes.Archive) == FileAttributes.Archive) result.Add("Archive");
if ((attrs & FileAttributes.Compressed) == FileAttributes.Compressed) result.Add("Compressed");
// This goes on for every attribute
return result;
}
private void checkAttributes(string ppath)
{
foreach (string s in getAttributes(ppath))
foreach (ToolStripDropDownItem item in Dropdown_File_Attr.DropDownItems)
{
if (item.Text == s)
{
item.Image = Resources.check;
item.Tag = true; // isChecked
}
else
{
item.Image = Resources.cross;
item.Tag = false; // isChecked
}
}
}
Just an example:
If in the beginning only Normal is selected, and I select Hidden, Hidden is the only one with a cross. If then select ReadOnly, ReadOnly is the only one with a cross but if I check, the file is still Hidden in Windows Explorer.
I have been looking for the error hours. Can anyone please help me (I don't have a lot of experience with Enums and FileAttributes)?
you loop over all items of the dropdown, but act only on the clicked item. you have to check all of the items. and combine their values to construckt the final attribute value, or you'll never catch the automatically cleared item. or just set the one checked attribute, without combining it with the current flags of the file, id you only want to set the one anyway.
as an aside, your logic would work with multi selection, which i'd allow anyway. it makes sense to allow setting a file hidden and readonly, for example.

DataGridView: sort by one type of data first

I have a DataGridView that I populate with a file and folder list. I'd like to sort the DataGridView alphabetically, but with all the folders above the files. Here's the general idea:
.\folder1\
.\folder2\
.\folder3\
.\file1
.\file2
I have a column with icons for the different filetypes, so there's a folder icon and file icons. It's the only difference I have between the two columns. Here's a picture:
So you can see that files and folders have different icons. Here is my current sort method:
private void dgvFiles_SortCompare(object sender, DataGridViewSortCompareEventArgs e) {
if(e.Column.Index == 1) {
// ???
}
else if(e.Column.Index == 4) {
string cellValue1 = e.CellValue1.ToString(),
cellValue2 = e.CellValue2.ToString();
if(!string.IsNullOrWhiteSpace(cellValue1) && !string.IsNullOrWhiteSpace(cellValue2)) {
cellValue1 = Regex.Replace(cellValue1, "[^.0-9]", "");
cellValue2 = Regex.Replace(cellValue2, "[^.0-9]", "");
int a = int.Parse(cellValue1), b = int.Parse(cellValue2);
e.SortResult = a.CompareTo(b);
e.Handled = true;
}
}
}
Is it possible to sort the DataGridView this way using a custom SortCompare method? If so, how?
I depends on how you've set the image inside the column but instead of using e.CellValue1 and e.CellValue2 as you have done for the size sorting, use GridName.Rows[e.RowIndex1] and GridName.Rows[e.RowIndex2] to access the underlying data instead.
So what I did instead was I created a class for folder items named FolderItem. I then created a list of these FolderItem objects and populated the DataGridView using the list. It actually made it really easy--I just had to use this snippet of code:
List<FolderItem> items = new List<FolderItem>();
private void dgvFiles_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e) {
if(e.ColumnIndex == 1) {
items.OrderBy(i => i.type).ThenBy(i => i.oldName);
items.Reverse(); // to account for ascending/descending order
RefreshDataGridView();
}
}
public void RefreshDataGridView() {
dgvFiles.Rows.Clear();
foreach(FolderItem item in items) {
dgvFiles.Rows.Add(item.icon, item.oldName, item.newName, item.type, item.size, item.created, item.modified);
}
}
(type was null for folders so it occurred above the other items.)
You could also probably find some way to bind the datagridview to the list, but I didn't do that.

Get the index of Item selected in ListView

I've been searching for about an hour already and couldn't find a best solution.
I am migrating from VB.NET to C# Forms and to C# WPF.
Never mind that...
so I use this code for C# forms and it works, but not in C# WPF
if (ListView1.SelectedItems.Count > 0)
{
for (lcount = 0; lcount <= ListView1.Items.Count - 1; lcount++)
{
if (ListView1.Items[lcount].Selected == true)
{
var2 = lcount;
break;
}
}
}
this is the way I want to get the index of the item clicked in listbox.
I have the error in .SELECTED
please help.
You can get SelectedIndex from listView. No need to traverse over all items because as per your code you seems to be interested in index of any selected item.
var2 = ListView1.SelectedIndex;
OR
simply this will work if interested in only first index:
if (lst.SelectedItems.Count > 0)
{
var2 = lst.Items.IndexOf(lst.SelectedItems[0]);
}
If you are using the .NET Compact Framework, SelectedIndex is not supported. For a general solution, I prefer SelectedIndices:
ListView.SelectedIndexCollection indices = lst.SelectedIndices;
if (indices.Count > 0)
{
// Do something with indices[0]
}
For Visual Studio 2015, SelectedIndex does not seem to be available. Instead, you can use SelectedIndices[x] where x=0 will give you the first selected item:
listView.SelectedIndices[0]
You can also set the MultipleSelect property to false to only allow one item to be selected at a time.
It can return NULL. Also the SelectedIndexChanged event can be FIRED TWICE. And the first time, there nothing selected yet.
So the only safe way to find it is like this:
private void lv1_SelectedIndexChanged(object sender, EventArgs e)
{
if (lv1.FocusedItem == null) return;
int p = lv1.FocusedItem.Index;
... now int p has the correct value...
sColl.Clear();
string item = String.Empty;
if (listView1.SelectedItems.Count > 0) {
for (int i = 0; i < listView1.SelectedItems.Count; i++) {
if (listView1.SelectedItems[i].Selected) {
int i2 = listView1.SelectedItems[i].Index;
item = listView1.Items[i2].Text;
sColl.Add(item);
}
}
}
listView1.SelectedItems.Clear();
foreach (var itemS in sColl)
{
string items = itemS;
}
sColl.Clear();
listView1.SelectedItems.Clear();
Why don't bring back the SelectedIndex ? Add this extension after your current namespace.
public static class Extension
{
public static int SelectedIndex(this ListView listView)
{
if (listView.SelectedIndices.Count > 0)
return listView.SelectedIndices[0];
else
return 0;
}
}
Encapsulate this class in a namespace called Extensions and then add this inside your projects namespace to use the extension.
using Extensions;
Then simply use like this
private void ListView1_SelectedIndexChanged(object sender, EventArgs e)
{
int selectionindex = ListView1.SelectedIndex();
ListViewItem seletedItem = ListView1.Items[selectionindex];
}
P.S.
The extension method should have returned -1 on Else, but as long as you're using it from the SelectedIndexChanged event, you're fine as it won't get fired if there are no items.
This is by design, as the SelectedIndexChanged event gets fired twice. Once to deselect the initial item, then to select the new one.
Proper way is to return -1 and check for negative numer.
This is also why someone here got and ArgumentOutOfRange Exception.

Changing string colour

Okay, so this is a carry on from my last question, but I have the code:
private void btnTrans_Click(object sender, EventArgs e)
{
var abrvStr = inputBx.Text;
foreach (var kvp in d)
{
abrvStr = abrvStr.Replace(kvp.Key, kvp.Value);
}
outputBx.Text = abrvStr;
}
Basically it's part of a dictionary program, so that when you enter a line of text in textbox 1 it appears in textbox 2 with a word replaced from textbox 1 in the dicitonary.
So if black,white is in the dictionary and I enter The wall is black. The wall is white will appear in textbox 2. So all's good.
Right now the tricky part, how would I alter that to allow me have the changed word in textbox 2 as the colour red. So in my above example, the wall is white. White would be red in the output line of text.
Note, I am using RichTextBoxes
C# Language!
To build on Oliver Jacot-Descombes answer:
private void btnTrans_Click(object sender, EventArgs e)
{
var abrvStr = inputBx.Text;
foreach (var kvp in d)
{
abrvStr = abrvStr.Replace(kvp.Key, kvp.Value);
int start = abrvStr.IndexOf(kvp.Value);
if(start >= 0)
{
richTextBox1.Text = abrvStr;
richTextBox1.Select(start, kvp.Value.Length);
richTextBox1.SelectionColor = Color.Red;
}
}
}
You would use a switch statement on the value of the dictionary to get what color you want to change the selection. You will need to modify that to fit the values in your dictionary as well as what colors you want.
You can use the SelectionColor property of the RichTextBox. Start by selecting the word that you want to format
string word = "white";
int start = richTextBox1.Find(word);
if (start >= 0) {
richTextBox1.Select(start, word.Length);
richTextBox1.SelectionColor = Color.Red;
}
Add a the reference to the KVP in the dictionary to the Textbox's Tag property. When the user changes the color, get the KVP from the Tag property and change the value in the KVP. I provide insight to the Tag property on my blog C# Winforms and the Hidden Association Tag. WPF/Silverlight also use the Tag property on controls as well..
--- Edit per user request ---
I am not sure why you need to enumerate a dictionary. The whole point of a dictionary is to quickly get the key. My example uses that and does not do the for loop.
... The initialization location....
var myDictionary = new Dictionary<string, Tuple<string, System.Drawing.Color>>()
{
{ "Black", new Tuple<string,System.Drawing.Color>("White", Color.Green) },
{ "White", new Tuple<string,System.Drawing.Color>("Black", Color.Red) }
};
... (later in code)...
private void btnTrans_Click(object sender, EventArgs e)
{
var key = inputBx.Text; // Let us say "White"
if (myDictionary.ContainsKey(key))
{
var targetColor = myDictionary[key]; // Just get the value
outputBx.Select(0, targetColor.Item1.Length);
outputBx.SelectionColor = targetColor.Item2;
outputBx.Text = targetColor.Item1;
}
else
{
outputBx.Text = "Unknown";
}
}
Check out my blog article on dictionaries for more info C# Dictionary Tricks

Categories

Resources