how to change an item index in listview? - c#

I have a listView and two buttons (UP , DOWN) and i want to move the selected item up or down.
I've thought about swapping between the selected item and the upper one.. but the code i tried .. doesn't make sense because index is readonly.
also mines or sum doesn't owrk .. i can't mess with index at all.
private void btnDown_Click(object sender, EventArgs e)
{
listView1.SelectedItems[0].Index--; // It's ReadOnly.
}
So .. how do i let the user the ability to change a ListViewItem index like how VB let us to change these item index [like in the pic]
thanks in advance ...

You have to remove the selected item first, then re-add it at the new position.
E.g to move the item up one position:
var currentIndex = listView1.SelectedItems[0].Index;
var item = listView1.Items[index];
if (currentIndex > 0)
{
listView1.Items.RemoveAt(currentIndex);
listView1.Items.Insert(currentIndex-1, item);
}

Following is an improvement to M4N answer to handle the re order of an item in the top of the list and make it in the bottom of the list
int currentIndex = listView1.SelectedItems[0].Index;
ListViewItem item = listView1.Items[currentIndex];
if (currentIndex > 0)
{
listView1.Items.RemoveAt(currentIndex);
listView1.Items.Insert(currentIndex - 1, item);
}
else
{
/*If the item is the top item make it the last*/
listView1.Items.RemoveAt(currentIndex);
listView1.Items.Insert(listView1.Items.Count, item);
}

In case of observable collections you are also able to call: .Move(currentindex, newindex);
MSDN

Related

removing multiple list items from a listview

So I have created a button that when pressed it deletes the multiple items in the listview. However, I can only seem to make it delete one list item at a time. I know this is badly worded so my code should help explain what I am doing:
private void button2_Click(object sender, EventArgs e) //remove
{
try
{
foreach ( ListViewItem eachItem in listView1.SelectedItems)
{
listView1.Items.Remove(eachItem);
}
task.RemoveAt(listView1.SelectedItems[0].Index); // Remove task from the list "task"
I am trying to make it delete all selected items from my listview as well as my list named "task" However, the line of code above only allows me to delete one selected item from the list at a time.
Any help? Thanks
Try This,
for (int i = task.SelectedItems.Count - 1; i >= 0; i++)
{
if (task.Items[i].Selected)
{
task.Items.Remove(listView2.Items[i]);
}
}
I'm going to assume that your list of tasks is equal, in item count, to your ListView, and that the object in task[0] is the same as ListView.Items[0]. If that is true, remove the item from the task first then remove the item from the ListView
// Removing items from the bottom of the selected items and working your way up
for (int i = listView1.SelectedIndices.Count - 1; i >= 0; i--)
{
task.RemoveAt(listView1.SelectedIndices[i]);
listView1.Items.RemoveAt(listView1.SelectedIndices[i];
}
foreach (ListViewItem eachItem in listView1.Items)
{
listView1.Items.Remove(eachItem);
}
you can only remove one item per line,
you can use the ".ToArray" extension method, if you are enumerating over a changing enumerable.
you should also use the BeginUpdate / EndUpdate function, to speed it up
https://msdn.microsoft.com/library/system.windows.forms.listview.beginupdate%28v=vs.110%29.aspx

Get single listView SelectedItem

I have the MultiSelect property of the listView set to false and I'm trying to get a single listViewItem. But the available property is SelectedItems. I've been using the following code...
foreach (ListViewItem item in listView1.SelectedItems)
{
//do something with item.text or whatever
}
Because I know there will only be one item selected. What is the correct way of doing this?
Usually SelectedItems returns either a collection, an array or an IQueryable.
Either way you can access items via the index as with an array:
String text = listView1.SelectedItems[0].Text;
By the way, you can save an item you want to look at into a variable, and check its structure in the locals after setting a breakpoint.
I do this like that:
if (listView1.SelectedItems.Count > 0)
{
var item = listView1.SelectedItems[0];
//rest of your logic
}
Sometimes using only the line below throws me an Exception,
String text = listView1.SelectedItems[0].Text;
so I use this code below:
private void listView1_SelectedIndexChanged(object sender, EventArgs e)
{
if (listView1.SelectedIndices.Count <= 0)
{
return;
}
int intselectedindex = listView1.SelectedIndices[0];
if (intselectedindex >= 0)
{
String text = listView1.Items[intselectedindex].Text;
//do something
//MessageBox.Show(listView1.Items[intselectedindex].Text);
}
}
If its just a natty little app with one or two ListViews I normally just create a little helper property:
private ListViewItem SelectedItem { get { return (listView1.SelectedItems.Count > 0 ? listView1.SelectedItems[0] : null); } }
If I have loads, then move it out to a helper class:
internal static class ListViewEx
{
internal static ListViewItem GetSelectedItem(this ListView listView1)
{
return (listView1.SelectedItems.Count > 0 ? listView1.SelectedItems[0] : null);
}
}
so:
ListViewItem item = lstFixtures.GetSelectedItem();
The ListView interface is a bit rubbish so I normally find the helper class grows quite quickly.
For a shopping cart situation here's what I recommend. I'm gonna break it down into it's simplest form.
Assuming we start with this(a list view with 2 colums, 2 buttons, and a label):
First things first, removing the items, to do that we'll enter our remove button:
private void button2_Click(object sender, EventArgs e)
{
listView1.Items.Remove(listView1.SelectedItems[0]);
label1.Text = updateCartTotal().ToString();
}
Now the second line is updating our labels total using the next function i'll post to addup all the total of column 2 in the listview:
private decimal updateCartTotal()
{
decimal runningTotal = 0;
foreach(ListViewItem l in listView1.Items)
{
runningTotal += Convert.ToDecimal(l.SubItems[1].Text);
}
return runningTotal;
}
You don't have to use decimal like I did, you can use float or int if you don't have decimals. So let's break it down. We use a for loop to total all the items in the column 2(SubItems[1].Text). Add that to a decimal we declared prior to the foreach loop to keep a total. If you want to do tax you can do something like:
return runningTotal * 1.15;
or whatever your tax rate is.
Long and short of it, using this function you can retotal your listview by just calling the function. You can change the labels text like I demo'd prior if that's what you're after.
None of the answers above, at least to me, show how to actually handle determining whether you have 1 item or multiple, and how to actually get the values out of your items in a generic way that doesn't depend on there actually only being one item, or multiple, so I'm throwing my hat in the ring.
This is quite easily and generically done by checking your count to see that you have at least one item, then doing a foreach loop on the .SelectedItems, casting each item as a DataRowView:
if (listView1.SelectedItems.Count > 0)
{
foreach (DataRowView drv in listView1.SelectedItems)
{
string firstColumn = drv.Row[0] != null ? drv.Row[0].ToString() : String.Empty;
string secondColumn = drv.Row[1] != null ? drv.Row[1].ToString() : String.Empty;
// ... do something with these values before they are replaced
// by the next run of the loop that will get the next row
}
}
This will work, whether you have 1 item or many. It's funny that MSDN says to use ListView.SelectedListViewItemCollection to capture listView1.SelectedItems and iterate through that, but I found that this gave an error in my WPF app: The type name 'SelectedListViewItemCollection' does not exist in type 'ListView'.
foreach (ListViewItem itemRow in taskShowListView.Items)
{
if (itemRow.Items[0].Checked == true)
{
int taskId = Convert.ToInt32(itemRow.SubItems[0].Text);
string taskDate = itemRow.SubItems[1].ToString();
string taskDescription = itemRow.SubItems[2].ToString();
}
}
If you want to select single listview item no mouse click over it try this.
private void timeTable_listView_MouseUp(object sender, MouseEventArgs e)
{
Point mousePos = timeTable_listView.PointToClient(Control.MousePosition);
ListViewHitTestInfo hitTest = timeTable_listView.HitTest(mousePos);
try
{
int columnIndex = hitTest.Item.SubItems.IndexOf(hitTest.SubItem);
edit_textBox.Text = timeTable_listView.SelectedItems[0].SubItems[columnIndex].Text;
}
catch(Exception)
{
}
}
This works for single as well as multi selection list:
foreach (ListViewItem item in listView1.SelectedItems)
{
int index = item.Index;
//index is now zero based index of selected item
}
On mouse click, I would do it like this:
public static string GetSelectedItem(ListView list)
{
foreach (ListViewItem item in list.Items)
{
if (item.Selected)
return item.Text;
}
return null;
}

How to delete duplicate items in listview?

Well, I need to check if there exists duplicate items inside listview on my app, but... I don't know how.
The way to detect this is by checking the field "Tag", if they are the same, then delete the item.
A good way to find duplicates is to use a temporary hashset. This gives you an O(n) O(n log n) algorithm (see Rick Sladkeys comments) to detect duplicates. Example:
var tags = new HashSet<string>();
var duplicates = new List<Item>();
foreach(Item item in listView.Items)
{
// HashSet.Add() returns false if it already contains the key.
if(!tags.Add(item.Tag)
duplicates.Add(item);
}
[Remove duplicates here]
Used nested for loops to go through and check each item against each other item.
//tag duplicates for removal
List<Item> toRemove = new List<Item>();
foreach(Item item1 in listView.Items)
{
foreach(Item item2 in listView.Items)
{
//compare the two items
if(item1.Tag == item2.Tag)
toRemove.Add(item2);
}
}
//remove duplicates
foreach(Item item in toRemove)
{
listView.Items.Remove(item);
}
You'll have to tweak the syntax for your code, but that's the basic idea behind it. Also, there are optimizations that could probably be made to it, but don't worry about those yet.
None of the above helped me, so I thought I'd post what I came up with in case someone else is having this issue.
myListView is a ListView, sorted alphabetically (so duplicates in this case are adjacent). You could sort it programatically beforehand if you wanted.
myListView.Sorting = SortOrder.Ascending;
Anyway this is fairly simplistic but I hope it helps someone!
for (int i = 0; i < myListView.Items.Count - 1; i++)
{
if (myListView.Items[i].Tag == myListVIew.Items[i + 1].Tag)
{
myListView.Items[i + 1].Remove();
}
}
If you have a lot of items then you can use a HashSet to keep the performance acceptable.
Loop over the items starting with the HashSet empty. For each item check if the tag is in the HashSet. If so this is a duplicate. If not add the tag to the HashSet.
This approach avoids an N^2 algorithm which is what you get with a nested loop. The HashSet makes the algorithm linear complexity providing the removal phase is carefully implemented. Of course this may not matter to you depending on how many items you have. If the list is small use nested loops and keep it simple.
If you are comparing duplicated Items based on specific columns you should simply use:
if(listview.Items.Find(SearchQuery).Count > 0)
{
//remove duplicates from list...
}
but you should name every item in the list when you fill up the listview..
for example if I want to search for duplicate items in column 1+2+5 I'd name the whole row like:
myItemName.Name = column1.Text + "_" + column2.Text + "_" + column5.Text;
and then perform the above "if statement"..
I would like to offer my solution using a Dictionary for future reference:
(you will edit for your own "Colums" (subitems), Simply read the code to understand.
I used the below to remove "Dups" in a listview on a button click, i am searching subitem you can edit code for your own use...
uses dictionary and a little easy "update" class i wrote.
private void removeDupBtn_Click(object sender, EventArgs e)
{
Dictionary<string, string> dict = new Dictionary<string, string>();
int num = 0;
while (num <= listView1.Items.Count)
{
if (num == listView1.Items.Count)
{
break;
}
if (dict.ContainsKey(listView1.Items[num].SubItems[1].Text).Equals(false))
{
dict.Add(listView1.Items[num].SubItems[1].Text, ListView1.Items[num].SubItems[0].Text);
}
num++;
}
updateList(dict, listView1);
}
and using a little updateList() class...
private void updateList(Dictionary<string, string> dict, ListView list)
{
#region Sort
list.Items.Clear();
string[] arrays = dict.Keys.ToArray();
int num = 0;
while (num <= dict.Count)
{
if (num == dict.Count)
{
break;
}
ListViewItem lvi;
ListViewItem.ListViewSubItem lvsi;
lvi = new ListViewItem();
lvi.Text = dict[arrays[num]].ToString();
lvi.ImageIndex = 0;
lvi.Tag = dict[arrays[num]].ToString();
lvsi = new ListViewItem.ListViewSubItem();
lvsi.Text = arrays[num];
lvi.SubItems.Add(lvsi);
list.Items.Add(lvi);
list.EndUpdate();
num++;
}
#endregion
}
Good luck!
int J = 0;
In this simple code we will remove duplicates from a loop which will take the first item and compare it with the items bellow, so if it exists you can remove the above one or the second one
foreach (ListViewItem item1 in listView1.Items)
{
//J+1 to do not repeat the list from first and remove itself just take a look on next items
for (int i=J+1;i<listView1.Items.Count-1;i++)
{
//i compare two subitems that must be unique in my list
if (listView1.Items[J].SubItems[1].ToString() == listView1.Items[i].SubItems[1].ToString())
// listView1.Items.RemoveAt(i); // remove the second one
listView1.Items.RemoveAt(J); // remove the first one and keep the second
}
J++;
}

Cloning items in a listbox c#

I have 2 list boxes and want to be able to copy selected items from one to the other how ever many times I want. Ive managed to do this but I have buttons on the 2nd list box that allow me to go up and down..Now when theres to items in the second list box that are the same (e.g "gills" and "gills") it doesnt behave normally and crashes.
Is there a way in which I can get them to act as seperate items in the 2nd listbox?
code
private void buttonUp_Click(object sender, EventArgs e)
{
object selected = listBox2.SelectedItem;
int index = list2.Items.IndexOf(selected);
listBox2.Items.Remove(selected);
listBox2.Items.Insert(index - 1, selected);
listBox2.SetSelected(index - 1, true);
}
private void buttonAdd_Click(object sender, EventArgs e)
{
DataRowView selected = (DataRowView)listBox1.SelectedItem;
string item = selected["title"].ToString();
listBox2.Items.Add(item);
}
It works fine when i havnt got duplicates but when i do they just jump around randomly when i press up/down.
(ive not included down as its pretty much the same as up)
It seems like you're travelling around the world to do something simple. I would approach this using List and databinding the list.
// Add code
DataRowView selected = listBox1.SelectedItem as DataRowView;
if (selected != null)
{
_myList.Add(selected); // Adds at end
BindList2();
}
// Move up code
int selectedIndex = listBox2.SelectedIndex;
if(selectedIndex > 0)
{
var temp = _myList[selectedIndex];
_myList.Remove(temp);
_myList.InsertAt(selectedIndex - 1, temp);
BindList2();
}
// BindList2
public void BindList2()
{
listBox2.DataSource = _myList;
listBox2.DataBind();
}
You can use SelectedIndex instead of SelectedItem when you have multiple items that are all equal. I also recommend checking that it's not -1.
The problem for the up case is the following set of code.
object selected = listBox2.SelectedItem;
int index = list2.Items.IndexOf(selected);
This code will only function correctly if you have unique items in the list. Once you have duplicate items the value index will be the index of the first instance of say gills in the list and not necessarily the index of the selected value.
It seems like you mirror the items in listBox2 and list2. If that is the case then you can just use the SelectedIndex property directly on listBox2 since the index will be equal in both liss.
int index = listBox2.SelectedIndex;
If you are trying to use an list of objects, try implementing the Iclonnable. This will make copies of the same item over & over. Also note to move an item to the top or bottom you don't have to remove the item in the list & reinsert them back. But you can change the index of the item. Hope this helps.
Just the code, because the rest of the answers cover it anyways:
private void buttonAdd_Click(object sender, EventArgs e)
{
DataRowView selected = listBox1.SelectedItem as DataRowView;
if (selected != null)
{
string item = selected["title"].ToString();
listBox2.Items.Add(item);
}
}
private void buttonUp_Click(object sender, EventArgs e)
{
string selected = listBox2.SelectedItem as string;
int oldIndex = listBox2.SelectedIndex;
int newIndex = oldIndex;
if (!string.IsNullOrEmpty(selected) && listBox2.Items.Count > 1 && oldIndex > 0)
{
listBox2.SuspendLayout();
listBox2.Items.RemoveAt(oldIndex);
newIndex = oldIndex - 1;
listBox2.Items.Insert(newIndex, selected);
listBox2.SelectedIndex = newIndex;
listBox2.ResumeLayout();
}
}

Remove selected rows from multi-column listView

I have a listview with two columns and I'm using a context menu to allow users to remove selected rows. To remove the selected rows, I've tried with the following code but it doesn't work:
private void toolStripMenuItem1_Click(object sender, EventArgs e)
{
listView1.SelectedItems.Clear();
}
I suspect this is because the listview has two columns, but I can't figure out a solution to remove selected rows. Removing all rows works with: listView1.Items.Clear();.
The latest example of BeefTurkey looks correct, but he should decrement the variable i after removing a selected item:
for (int i = 0; i < listView1.Items.Count; i++ )
{
if (listView1.Items[i].Selected)
{
listView1.Items[i].Remove();
i--;
}
}
The index of items larger as i is decremented by 1 after the removal. So you should reposition i to match the next not tested item.
while (listBox1.SelectedItems.Count > 0)
{
listBox1.Items.Remove(listBox1.SelectedItem);
}
foreach(ListViewItem lvItem in lvDocument.SelectedItems)
{
lvDocument.Items.Remove(lvItem);
}
This seems to work:
for (int i = 0; i < listView1.Items.Count; i++ )
{
if (listView1.Items[i].Selected)
{
listView1.Items[i].SubItems.Clear();
}
}
Is there any way to remove items and re-order the listView so that there are no empty rows in the middle of other rows?
This seems to be a better solution:
for (int i = 0; i < listView1.Items.Count; i++ )
{
if (listView1.Items[i].Selected)
{
listView1.Items[i].Remove();
}
}
What you can do:
foreach (ListViewItem Item in LstvClients.Items)
{
if (item.Selected)
{
LstvClients.Items.Remove(Item);
}
}
(Yours is better, item.Remove())
I have been using something slightly different then the others to remove all the selected items from a ListView control:
foreach (ListViewItem listViewItem in listView1.SelectedItems)
{
listView1.Items.Remove(listViewItem);
}
I'm not sure how this would match up performance-wise to the other posted methods on large lists, but I think it is a little cleaner looking in cases where that isn't an issue.
This is the correct way to remove all selected items. The method is to always access fist selected item with an index 0 and loop until no more selected items left. You cannot refer to other items inside collection with an absolute index safely since indexes will change as soon as you delete one of the items.
while( listView1.SelectedItems.Count > 0)
{
listView1.Items.Remove(lvFiles.SelectedItems[0]);
}
do
{
this.listView1.CheckedItems[0].Remove();
} while (this.listView1.CheckedItems.Count > 0);
This works better

Categories