ListView.FindItemWithText inside WHILE loop fails after first ListView line - c#

I want change the content of 2nd column of each line of ListView with diferents data according with is found via FindItemWith.
My trouble is that from of 2nd line is be overriding the previous columns, for example when i want change the content searching a text that stays on first line works fine, see:
Already when i want change the content searching a text that stays on second line this happens:
This is the code:
public void fillData(string search, string data, ListView haystack)
{
if (haystack.Items.Count > 0)
{
int idx = 0;
ListViewItem found;
while (idx < haystack.Items.Count)
{
found = haystack.FindItemWithText(search, true, idx);
if (found != null)
{
haystack.Items[idx].SubItems[1].Text = data.ToString();
}
idx++;
}
}
}
private void button3_Click(object sender, EventArgs e)
{
int i = 0;
while (i < 3)
{
ListViewItem item = new ListViewItem();
item.Text = i.ToString();
item.SubItems.Add("192.168.0." + i.ToString());
listView1.Items.Add(item);
i++;
}
}
private void button1_Click(object sender, EventArgs e)
{
fillData("192.168.0.0", "AAA", listView1);
}
private void button2_Click(object sender, EventArgs e)
{
fillData("192.168.0.1", "BBB", listView1);
}

This is because the overload function you used for FindItemWithText, keeps searching all the items from the index you passed in.
When the loop has idx = 0 then FindItemWithText will try to search all three items 0,1,2.
When the loop has idx = 1 then FindItemWithText will try to search two items 1,2.
When the loop has idx = 2 then FindItemWithText will try to search only one item 2.
So now in the first case, As you are searching for first item, your loop found it only once. But where as in second case you are searching for second item, it was found twice both (idx = 0 ---- 0,1,2) and (idx = 1 ---- 1,2) iterations. So you are updating two values both for idx=0 and idx = 1.
Here is the documentation link
https://learn.microsoft.com/en-us/dotnet/api/system.windows.forms.listview.finditemwithtext?view=netframework-4.7.2#System_Windows_Forms_ListView_FindItemWithText_System_String_System_Boolean_System_Int32_
Any how FindItemWithText returns the System.Windows.Forms.ListViewItem. Just search once from zero. Use that item to update.

Related

How can i take data from Listbox and split into 2 different listboxes?

I am currently trying to make a random team generator as a project.
i have a Win form which have a textbox and an add button to add the text into a listbox.
I also have a Generate button, which are suppose to take the names from Listbox3 and split them randomly to Listbox1 and Listbox2.
I am stuck on the last part of getting the names from Listbox3 and split into randomly to the listbox1 and listbox2.
this is my code so far:
private void listBox2_SelectedIndexChanged(object sender, EventArgs e)
{
}
private void button3_Click(object sender, EventArgs e)
{
Application.Exit();
}
private void button1_Click(object sender, EventArgs e)
{
textBox1.Select();
listBox3.Items.Add(textBox1.Text);
textBox1.Text = "";
}
private void button2_Click(object sender, EventArgs e)
{
//*Here i need some code to take listbox3 and randomly split the list into 2 teams (Listboxt1 & Listbox2).
}
}
Here is how the form looks with information of what is what:
https://i.stack.imgur.com/gveAB.jpg
The following first off uses decent control names. Code splits a list into two and clears the available members from the available listbox so the end result is two team list boxes are populated.
// get available members, assumes there are enough to work with
// consider adding proper assertion
var membersList = AvailableTeamMembersListBox.Items.OfType<string>().ToList();
var random = new Random();
var numberRequired = membersList.Count / 2;
var team1 = new List<string>();
var team2 = new List<string>();
// add a member to first team, remove from current items in ListBox
for (var index = 0; index < numberRequired; index++)
{
var nextIndex = random.Next(membersList.Count);
team1.Add(membersList[nextIndex]);
membersList.RemoveAt(nextIndex);
}
// remaining items in ListBox go into second list
team2.AddRange(membersList);
// clear because they have been used in the two list
membersList.Clear();
Team1ListBox.DataSource = team1;
Team2ListBox.DataSource = team2;
You can try this:
var listBoxIndex = 0;
var listBoxes = new[] { this.listBox1, this.listBox2 };
var random = new Random();
while (listBox3.Items.Count > 0)
{
var index = random.Next(0, listBox3.Items.Count);
var item = listBox3.Items[index];
listBox3.Items.RemoveAt(index);
var listBox = listBoxes[listBoxIndex++ % 2];
listBox.Items.Add(item);
}
You save into a list (listBoxes) your 2 teams listboxes and use listBoxIndex to choose one and other in each iteration.
In each iteration, you get a random item in your source listbox, remove from the list and put in the selected team listbox.

Winforms DataGridView cant select right Id and IndexOutOfRangeException

I am trying to get id on click from my datagrid the first 3 work fine but the last one I get an error saying :
System.IndexOutOfRangeException: 'There is no line at position 1003.
The first 3 elements are from 1-3 the 4th element has it's id as 1003 I don't know why, but shouldn't it work anyway?
Load and Button to select element :
private void EditarComputador_Load(object sender, EventArgs e)
{
opBD.ListaComputadoresPreFeitos("SELECT * FROM ComputadoresPreFeitos");
dataGridView_PreFeitos.Visible = true;
List<ComputadoresPrefeitos> lista = new List<ComputadoresPrefeitos>();
lista = opBD.ListaComputadoresPreFeitos();
dataGridView_PreFeitos.DataSource = lista;
}
private void button1_Click(object sender, EventArgs e)
{
id = Convert.ToInt32(dataGridView_PreFeitos.Rows[dataGridView_PreFeitos.CurrentRow.Index].Cells[0].Value); // Pegar ID
if (opBD.dtTabelaComputadoresPreFeitos != null && opBD.dtTabelaComputadoresPreFeitos.Rows.Count > 0)
{
DataRow linha = opBD.dtTabelaComputadoresPreFeitos.Rows[id];
textBox1.Text = linha["Id_Prefeitos"].ToString();
textBox2.Text = linha["Nome"].ToString();
textBox3.Text = linha["Marca"].ToString();
textBox4.Text = linha["Preco"].ToString();
}
}
opBD.dtTabelaComputadoresPreFeitos.Rows[id-1] picks the right elements but still gives me the same error
opBD.dtTabelaComputadoresPreFeitos.Rows[id-1]
////////////////
opBD.dtTabelaComputadoresPreFeitos.Rows[id] it picks one element above the other, lets say i click on id 1 it picks id 2 and still gives me the same error
opBD.dtTabelaComputadoresPreFeitos.Rows[id]
Your problem is, your trying to take the Cell's value, 1003 which is your record id, and use it as the row index, which should be 3 for the record in question. This will not work, you should never rely on the id's matching the row index.
The fix below removes the line that set's the id to the cell value, and uses the dataGridView_PreFeitos.CurrentRow.Index property instead.
private void button1_Click(object sender, EventArgs e)
{
if (opBD.dtTabelaComputadoresPreFeitos != null && opBD.dtTabelaComputadoresPreFeitos.Rows.Count > 0)
{
// use the dataGridView_PreFeitos.CurrentRow.Index property to get your row
DataRow linha = opBD.dtTabelaComputadoresPreFeitos.Rows[dataGridView_PreFeitos.CurrentRow.Index];
textBox1.Text = linha["Id_Prefeitos"].ToString();
textBox2.Text = linha["Nome"].ToString();
textBox3.Text = linha["Marca"].ToString();
textBox4.Text = linha["Preco"].ToString();
}
}

How to filter ListView by using a ComboBox?

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()}'";
}

dynamically delete controls from table

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.

How to get multiple selected items from ListPicker and display them in MessageBox

Here is my code for the list of the items in a ListPicker. What I am trying to do is select one or more options and then after I press submit button I would like to display a MessageBox with the selected Items separated by commas. I also would like to store this value of the items selected to the database but, the first I am trying to do is populate the data into MessageBox.
lstPickerType.Items.Add("Aircrafts");
lstPickerType.Items.Add("Boats");
lstPickerType.Items.Add("Cars");
lstPickerType.Items.Add("Helicopters");
lstPickerType.Items.Add("Electric Powered");
lstPickerType.Items.Add("Gas Powered");
Here is the code that I have got to create a string from the list that is then displayed when the ListPicker is collapsed.
private void lstPickerType_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
lstPickerType.SummaryForSelectedItemsDelegate = SummarizeItems;
}
private string SummarizeItems(IList items)
{
if (items != null && items.Count > 0)
{
string summarizedString = "";
for (int i = 0; i < items.Count; i++)
{
summarizedString += (string)items[i];
// if the item is not last coma is added
if (i != items.Count - 1)
summarizedString += ", ";
}
return summarizedString;
}
else
return "Nothing selected";
}
Finally for the button to display the MessageBox i have following code.
private void btnAddLocation_Click(object sender, RoutedEventArgs e)
{
foreach (var item in this.lstPickerType.SelectedItems)
{
var items = new List<object>();
MessageBox.Show(items.ToString());
}
I would really appreciate if anyone could help me to solve this issue. Thank You.
I'm a bit rusty of C#, and i don't have visual studio in this pc, but I would achieve your result without the selectionchanged event in the listpicker.
Trythis code in the button click:
Edit: a cast was missing.
private void btnAddLocation_Click(object sender, RoutedEventArgs e)
{
string r = "";
for (int i=0; i<this.lstPickerType.SelectedItems.Count; i++)
{
r += ((ListPickerItem)this.lstPickerType.SelectedItems[i]).Content;
if (i != this.lstPickerType.SelectedItems.Count - 1)
r += ", ";
}
MessageBox.Show(r);
}

Categories