I have a ListView control that I'm filtering results from with a TextBox. The code works for highlighting the backcolors of the matching results, but I want to get the total found results/highlighted objects as an int. The int that is populating now is incorrect and not in line with the found/highlighted results.
How can I get the total number of found/highlighted results?
private void textBox1_TextChanged(object sender, EventArgs e)
{
foreach (ListViewItem lvi in this.browserlistview.Items)
{
if (textBox1.TextLength > 0)
{
if (lvi.Text.IndexOf(textBox1.Text, StringComparison.CurrentCultureIgnoreCase) >= 0)
{
Color b = Color.Cyan;
lvi.BackColor = b;
foundobjlbl.Text = "Objects found: " + lvi.Text.IndexOf(textBox1.Text, StringComparison.InvariantCultureIgnoreCase).ToString();
//this is turning up incorrect integers
}
else
{
Color w = Color.White;
lvi.BackColor = w;
}
}
else if (textBox1.TextLength == 0)
{
Color w = Color.White;
lvi.BackColor = w;
foundobjlbl.Text = "Objects found : 0";
}
}
}
Does anyone see where I'm going wrong?
You're setting your count to the index of the text you're looking for in a specific item. So you're setting it once for each item you've found, so what you'll get is the index of your search term in the last item found.
What you want is to track the count of found items and set it once the search is finished, so something like this (I've left out most of the other implementation details):
var foundCount = 0;
foreach (var item in items)
{
if (IsMatch(item))
{
// set colour
foundCount++;
}
else
{
// set white
}
}
foundobjlbl.Text = $"Objects found {foundCount}";
Related
I have a TextBox that searches whatever I have in my ListView. I would like to have a ComboBox that will allow the user to “Show All”, “Show Match” and “Show Non Match” within the ListView depending on search criteria.
private void SearchBtn_Click(object sender, EventArgs e)
{
int count = 0, searchStartIndex = selectedIndexPos = 0;
// Clear previously selected indices
listView.SelectedIndices.Clear();
string target = searchTextBox.Text;
// Search for item with text from the search text box, including subItems, from searchStartIndex, not a prefixSearch
ListViewItem item = listView.FindItemWithText(target, true, searchStartIndex, false);
/*----------------------------------------------------------------------------------------------------*
* While the search results in an item found continue searching. *
*----------------------------------------------------------------------------------------------------*/
while (item != null)
{
count++;
// Update progressBar
progressBar.Value = (int)((float)searchStartIndex / listView.VirtualListSize * 100);
ListView.SelectedIndexCollection indexes = listView.SelectedIndices;
if (!indexes.Contains(item.Index))
{
listView.SelectedIndices.Add(item.Index);
}
/*----------------------------------------------------------------------------------------------------*
* Set the start index to the index after the last found, if valid start index search for next item.*
*----------------------------------------------------------------------------------------------------*/
if ((searchStartIndex = item.Index + 1) < listView.VirtualListSize)
{
item = listView.FindItemWithText(searchTextBox.Text, true, searchStartIndex, false);
// count++;
}
else
{
item = null;
}
}
if (listView.SelectedIndices.Count == 0)
{
MessageBox.Show("Find item with text \"" + searchTextBox.Text + "\" has no result.");
}
else
{
RefilterListView();
listView.EnsureVisible(listView.SelectedIndices[0]);
}
}
I would like to have my items in the 'ComboBox' to help filter my 'ListView'. The "Show All" should display all contents of 'ListView' along with item that was searched, the "Show Match" should show only the searched item removing everything else that doesn't match the search and "Show Non Match" should show all of the contents from 'ListView' that doesn't match the searched item.
I can't do it exactly in your case. But I hope I realize it. I mean this is just a solution. You can customize it for your case.
First of all, put a BindingSource on your Form and bind it to your data:
bindingSource1.DataSource = data;
Then bind your ListView(actually DataGridView) to the BindingSource:
dataGridView1.DataSource = bindingSource1;
And then define an enum like this:
public enum Something
{
ShowMatch = 1,
ShowNonMatch = 2
}
Now, put a ComboBox on your Form and add your options to it:
comboBox1.Items.Add("Show All");
comboBox1.Items.Add("ShowMatch");
comboBox1.Items.Add("ShowNonMatch");
comboBox1.SelectedIndex = 0;
After that, you can catch the selected item in SelectedIndexChanged:
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.SelectedIndex == 0)
bindingSource1.Filter = null;
else
bindingSource1.Filter = $"Name = '{comboBox1.SelectedItem.ToString()}'";
}
So I have a button, where if you click it, it adds "Candy" to a listbox, how do I make it so, if another item with the same name is being added, instead of adding it in a new line, update the first line to show x2, 3, 4, etc. Is that possible or would I have to make another Listbox and match the index? I've tried the following with another listbox and an int variable.
private void btnCandy_Click(object sender, EventArgs e)
{
lstProducts.Items.Add("Candy");
foreach (var item in lstProducts.Items)
{
if (item.ToString() == "Candy")
{
++Productcount;
lstQuantity.Items.Add(Productcount);
if (Productcount > 1)
{
lstQuantity.Items.Insert(lstProducts.Items.IndexOf("Candy"), Productcount);
}
}
}
}
using System.Text.RegularExpressions;
Use:
private void btnCandy_Click(object sender, EventArgs e)
{
string query = "Candy";
bool isExist = false;
for (int i = 0; i < lstProducts.Items.Count; i++)
{
var s = lstProducts.Items[i].ToString();
if (s.StartsWith(query))
{
if (s == query)
{
lstProducts.Items[i] = query + "x2";
isExist = true;
break;
}
else
{
// Escape your plain text before use with regex
var pattern = Regex.Escape(query);
// Check if s has this formnat: queryx2, queryx3, queryx4, ...
Match m = Regex.Match(s, "^" + pattern + #"x(\d+)$");
if (m.Success)
{
lstProducts.Items[i] = query + "x" + (Int32.Parse(m.Groups[1].Value) + 1);
isExist = true;
break;
}
}
}
}
if (!isExist) lstProducts.Items.Add(query);
}
Note:
\d mean any digit (0 - 9)
I'd try to iterate through listbox items and if I find "Candy" then take that index and update title.
private void btnCandy_Click(object sender, EventArgs e)
{
bool found = false;
foreach (var item in lstProducts.Items)
{
if (item.ToString().StartsWith("Candy"))
{
// update item title
found = true;
break; // no need to continue
}
}
if(!found)
{
lstProducts.Items.Add("Candy");
}
}
this way you are not going to add duplicates
Here is some pseudo-code to help you. Add this to your button click event:
int i = 0;
foreach (string item in listbox1.Items)
{
If (item == textbox1.text) //textbox1.text contains the string such as 'candy'
{
i++;
listbox1.Items.Remove(item);
listbox1.Items.Add(textbox1.text + " " + i.ToString());
}
}
You may have to reset the counter as needed.
I am trying to change the 2nd Column of a ListView on a Win-Form.
I know it's a "Sub-Item", but when I am trying to change the Sub-Item (key = 0) it changes the Text of the very first column.
int number = 1;
foreach (ListViewItem existingItem in this.ListViewDokumente.Items) {
if (existingItem.Group.Name == "ListViewGroupSEE") {
existingItem.SubItems(0).Text = number.ToString;
number = number + 1;
}
}
When I change the 0 to 1 in SubItems(0), there's anI get an ArgumentOutOfRangeException.
How can I change the 2nd Column?
Okay, my mistake, and here is what was missing:
ListViewDokumente.Items.Add(item).SubItems.Add("");
The ".Subitems.Add("")" was missing. It needed some "empty spot".
And then, the For Each Loop:
int number = 1;
foreach (ListViewItem existingItem in this.ListViewDokumente.Items) {
if (existingItem.Group.Name == "ListViewGroupSEE") {
existingItem.SubItems(1).Text = number.ToString;
number = number + 1;
}
}
It's "SubItems(1)" this time.
Am trying to delete textboxes and labels while their names which have equal value from the selected items from the listbox. If i run this code only the first if statement is executed and removing only the label controls inside the table.
I must also mention that controls of the table are dynamically created.
private void pictureBox2_Click(object sender, EventArgs e)
{
for (int i = 0; i < listBox2.SelectedItems.Count; i++)
{
foreach (Control t in table2.Controls)
{
if (t is Label && t.Text==listBox2.SelectedItem.ToString())
{
table2.Controls.Remove(t);
continue;
}
if (t is TextBox && t.Name.Contains(listBox2.SelectedItem.ToString()))
{
table2.Controls.Remove(t); continue;
}
}
listBox2.Items.Remove(listBox2.SelectedItems[i]); i--;
}
}
This is how controls are created indide the table.
private void pictureBox1_Click(object sender, EventArgs e)
{
listBox2.Items.Clear();
this.table2.Controls.Clear();
foreach (var item in listBox1.SelectedItems)
{
table2.Controls.Add(new Label() { Name = item.ToString(), Text = item.ToString(), AutoSize = true });
table2.Controls.Add(new TextBox() { Name = item.ToString(), AutoSize = true });
}
}
}
When you remove an item from a collection (suppose the item at position 0), the item at the next position (postion 1) shifts in position zero. But your for loop execute the next iteration and your indexer becomes 1 and so it terminate the loop.
The first approach to avoid this is to loop in reverse order, from the end of the collection toward the begin of it
But you could also simplify a lot your code with
private void pictureBox2_Click(object sender, EventArgs e)
{
for (int i = listBox2.SelectedItems.Count - 1 ; i >= 0 ; i--)
{
// This is our search term...
string curItem = listBox2.SelectedItems[i].ToString();
// Get only the controls of type Label with Text property equal to the current item
var labels = table2.Controls
.OfType<Label>()
.Where (c => c.Text == curItem)
.ToList();
if(labels != null)
{
for(int x = labels.Count()-1; x >= 0; x--)
table2.Remove(labels[x]);
}
// Get only the controls of type TextBox with Name property containing the current item
var boxes = table2.Controls
.OfType<TextBox>()
.Where (c => c.Name.Contains(curItem)
.ToList();
if(boxes != null)
{
for(int x = boxes.Count()-1; x >= 0; x--)
table2.Remove(boxes[x]);
}
listBox2.Items.Remove(curItem);
}
}
Why are you decrementing your iterator at the end of your for loop? It looks like you're stuck in the loop, buddy.
I have a list box with a bunch of values in it. I also have an UP button and a DOWN button.
With these buttons, I would like to move the selected item in the list box up/down. I am having trouble doing this.
Here is my code so far:
private void btnDataUp_Click(object sender, RoutedEventArgs e)
{
int selectedIndex = listBoxDatasetValues.SelectedIndex; //get the selected item in the data list
if (selectedIndex != -1 && selectedIndex != 0) //if the selected item is selected and not at the top of the list
{
//swap items here
listBoxDatasetValues.SelectedIndex = selectedIndex - 1; //keep the item selected
}
}
I do not know how to swap the values! Any help would be GREATLY appreciated!
Since you have populated the listbox by binding to a ObservableCollection using ItemsSource, you cant modify the Items property of the listbox.
ItemsSource can be set only when the Items collection is empty, and Items can be modified only when ItemsSource is null.
Otherwise you will get the error "Operation is not valid while ItemsSource is in use..."
What you have to do, is modify the underlying collection, and because it's an ObservableCollection, the ListBox will reflect the changes.
The following code shows how you can move an item up and down by swapping the item in the collection.
The corresponding XAML just contains a listbox called lbItems and 2 buttons which hook up the eventhandlers.
public partial class MainWindow : Window
{
private ObservableCollection<string> ListItems = new ObservableCollection<string>
{
"Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6"
};
public MainWindow()
{
InitializeComponent();
lbItems.ItemsSource = this.ListItems;
}
private void up_click(object sender, RoutedEventArgs e)
{
var selectedIndex = this.lbItems.SelectedIndex;
if (selectedIndex > 0)
{
var itemToMoveUp = this.ListItems[selectedIndex];
this.ListItems.RemoveAt(selectedIndex);
this.ListItems.Insert(selectedIndex - 1, itemToMoveUp);
this.lbItems.SelectedIndex = selectedIndex - 1;
}
}
private void down_click(object sender, RoutedEventArgs e)
{
var selectedIndex = this.lbItems.SelectedIndex;
if (selectedIndex + 1 < this.ListItems.Count)
{
var itemToMoveDown = this.ListItems[selectedIndex];
this.ListItems.RemoveAt(selectedIndex);
this.ListItems.Insert(selectedIndex + 1, itemToMoveDown);
this.lbItems.SelectedIndex = selectedIndex + 1;
}
}
}
I make some extension methods for this:
public static void MoveItemUp<T>(this ObservableCollection<T> baseCollection, int selectedIndex)
{
//# Check if move is possible
if (selectedIndex <= 0)
return;
//# Move-Item
baseCollection.Move(selectedIndex - 1, selectedIndex);
}
public static void MoveItemDown<T>(this ObservableCollection<T> baseCollection, int selectedIndex)
{
//# Check if move is possible
if (selectedIndex < 0 || selectedIndex + 1 >= baseCollection.Count)
return;
//# Move-Item
baseCollection.Move(selectedIndex + 1, selectedIndex);
}
public static void MoveItemDown<T>(this ObservableCollection<T> baseCollection, T selectedItem)
{
//# MoveDown based on Item
baseCollection.MoveItemDown(baseCollection.IndexOf(selectedItem));
}
public static void MoveItemUp<T>(this ObservableCollection<T> baseCollection, T selectedItem)
{
//# MoveUp based on Item
baseCollection.MoveItemUp(baseCollection.IndexOf(selectedItem));
}
There is no need to know the ListBox for this.
This is the easiest way to do this and it fires all the right events so you don't have to worry about XAML. ObservableCollection has a nice method called
MoveItem(previousIndex, newIndex)
Given that you have a ObservableCollection named DataItemList
public void MoveUp()
{
var currentIndex = DataItemList.SelectedIndex;
//Index of the selected item
if (currentIndex > 0)
{
int upIndex = currentIndex - 1;
//move the items
DataItemList.MoveItem(upIndex,currentIndex);
}
}
For Down you get the index of the preceding item.
Simple as that!
I would have added a comment, but I can't since I only have 3 reputation :/
Peter Hansen's solution is great, but if there isn't a selected element, down_click throws an ArgumentOutOfRange Exception. This is because if there is no element selected, the Index is equal to -1.
I'd edit down_click like this:
private void down_click(object sender, RoutedEventArgs e)
{
if (this.lbItems.SelectedIndex != -1) //Added condition
{
var selectedIndex = this.lbItems.SelectedIndex;
if (selectedIndex + 1 < this.ListItems.Count)
{
var itemToMoveDown = this.ListItems[selectedIndex];
this.ListItems.RemoveAt(selectedIndex);
this.ListItems.Insert(selectedIndex + 1, itemToMoveDown);
this.lbItems.SelectedIndex = selectedIndex + 1;
}
}
}
try this:
if (listBoxDatasetValues.SelectedItems.Count > 0)
{
object selected = listBoxDatasetValues.SelectedItem;
int indx = listBoxDatasetValues.Items.IndexOf(selected);
int totl = listBoxDatasetValues.Items.Count;
if (indx == 0)
{
listBoxDatasetValues.Items.Remove(selected);
listBoxDatasetValues.Items.Insert(totl - 1, selected);
listBoxDatasetValues.SetSelected(totl - 1, true);
}
else{
listBoxDatasetValues.Items.Remove(selected);
listBoxDatasetValues.Items.Insert(indx - 1, selected);
listBoxDatasetValues.SetSelected(indx - 1, true);
}
}
if(listBoxDatasetValues.ListIndex > 0)
{
// add a duplicate item up in the listbox
listBoxDatasetValues.AddItem(listBoxDatasetValues.Text, listBoxDatasetValues.ListIndex - 1);
// make it the current item
listBoxDatasetValues.ListIndex = (listBoxDatasetValues.ListIndex - 2);
// delete the old occurrence of this item
listBoxDatasetValues.RemoveItem(listBoxDatasetValues.ListIndex + 2);
}
You may try something like this:
For moving UP:
if (listboxName.SelectedIndex == -1 || listboxName.SelectedIndex == 0)
return;
Object select, previous, temp;
select = listboxName.Items[listboxName.SelectedIndex];
previous = listboxName.Items[listboxName.SelectedIndex-1];
temp = select;
select = previous;
previous = temp;
listboxName.Items[listboxName.SelectedIndex] = select;
listboxName.Items[listboxName.SelectedIndex-1] = previous;
listboxName.SelectedIndex--;
For moving Down:
if (listboxName.SelectedIndex == -1 || listboxName.SelectedIndex == listboxName.Items.Count-1)
return;
Object select, next, temp;
select = listboxName.Items[listboxName.SelectedIndex];
next = listboxName.Items[listboxName.SelectedIndex+1];
temp = select;
select = next;
next = temp;
listboxName.Items[listboxName.SelectedIndex] = select;
listboxName.Items[listboxName.SelectedIndex+1] = next;
listboxName.SelectedIndex++;
No need to use observable collection you can do it simpler just by relying on the collection object inside the ListBox, here is the optimized version of #Peter Hansen`s answer:
private void up_click(object sender, RoutedEventArgs e)
{
var selectedIndex = this.lbItems.SelectedIndex;
if (selectedIndex > 0)
{
var itemToMoveUp = this.lbItems.Items[selectedIndex];
this.lbItems.Items.RemoveAt(selectedIndex);
this.lbItems.Items.Insert(selectedIndex - 1, itemToMoveUp);
this.lbItems.SelectedIndex = selectedIndex - 1;
}
}
private void down_click(object sender, RoutedEventArgs e)
{
var selectedIndex = this.lbItems.SelectedIndex;
if (selectedIndex + 1 < this.lbItems.Items.Count)
{
var itemToMoveDown = this.lbItems.Items[selectedIndex];
this.lbItems.Items.RemoveAt(selectedIndex);
this.lbItems.Items.Insert(selectedIndex + 1, itemToMoveDown);
this.lbItems.SelectedIndex = selectedIndex + 1;
}
}