ListView moving items - c#

I have another problem with ListView :( Now I need to move items in group (up, down, to the beginning, to the end), but ListView is displaying moved items always at the end.
Here is sample code for moving item to the beginning:
if (1 == listView1.SelectedItems.Count)
{
ListViewItem item = listView1.SelectedItems[0];
ListViewGroup gp = item.Group;
int index;
index = item.Index;
if (index < listView1.Items.Count)
{
index = 0;
listView1.Items.Remove(item);
item.Group = gp;
listView1.Items.Insert(index, item);
}
}
I tried google to find some solution, and I found someone else (http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/838f90cd-33d8-4c81-9ed9-85220b511afe) who had same problem like me, but his solution is not working :(
I considered using ObjectListView but I have modified ListView witch now supports drag & drop with WinAmp effect, onScroll events, scrolling synchronization etc.. and I don't want to lose this stuff :(

Try this:
/// <summary>
/// Move the given item to the given index in the given group
/// </summary>
/// <remarks>The item and group must belong to the same ListView</remarks>
public void MoveToGroup(ListViewItem lvi, ListViewGroup group, int indexInGroup) {
group.ListView.BeginUpdate();
ListViewItem[] items = new ListViewItem[group.Items.Count + 1];
group.Items.CopyTo(items, 0);
Array.Copy(items, indexInGroup, items, indexInGroup + 1, group.Items.Count - indexInGroup);
items[indexInGroup] = lvi;
for (int i = 0; i < items.Length; i++)
items[i].Group = null;
for (int i = 0; i < items.Length; i++)
group.Items.Add(items[i]);
group.ListView.EndUpdate();
}

My automatic response to this question would be to check and see if it works if you say
listView1.Items.Count - 1
because the list is zero-indexed so the count is 1 greater than the last index)

Looking at the code, it has a few problems which make me curious. It looks like what may be the problem is the intial if statement. If there is only 1 item in the itemlist, then removing it and re-adding it will not do anything regardless of where you stick it. I believe you want <=
Also, you're setting the index to 0 right after getting it from the item you're interested in. If the code is supposed to move it to the top, Index=0 should be moved inside the if statement.
Not sure if those will solve the problem though...

Related

Print the last 3 elements added to a string list

I have a string list and I am continuously adding elements to this list.
I do not want this list to grow beyond 6 elements(index 0 to 5). So once it reaches index[5], I do not want to grow the list, but instead add elements at the start of the list or do something similar to it. At any point, I would be printing the last 3 items added to this list ordered by item last added to this list. I have tried it below but i think it is crappy piece of code. After this piece of code, I would get the list count and print UrlList[UrlList.Count - 1],UrlList[UrlList.Count - 2];,UrlList[UrlList.Count - 3]
Pls help me with it
if (UrlList.Count == 5)
{
var move = UrlList[5];
UrlList.RemoveAt(5);
UrlList.Insert(0, move);
move = UrlList[4];
UrlList.RemoveAt(4);
UrlList.Insert(1, move);
move = UrlList[3];
UrlList.RemoveAt(3);
UrlList.Insert(2, move);
UrlList.Add(uri.ToString());
}
else
{
UrlList.Add(uri.ToString());
}
If you want the latest items to be the last ones in the list, you can use this code:
const int Max_Capacity = 6;
if (UrlList.Count >= Max_Capacity)
UrlList.RemoveAt(0); // <- oldest (first) item should be removed
UrlList.Add(uri.ToString());
...
// Printing out the lastest 3 items:
int start = UrlList.Count <= 3 ? 0 : UrlList.Count - 3;
for (int i = start; i < UrlList.Count; ++i)
Console.Out.WriteLine(UrlList[i]);
I think you better be using % Operator-.
In your case you can use:
UrlList.Insert((UrlList.count%6),move);

Number increments too much

I've got a DataGrid, which is bound to a List: _bhaList.
Now, I've written a method which will allow the user to select an item on the grid, and move it up. And as it moves up, the No. column is updated to show it's current position. However, in this particular case, where I have 3 items, if I choose No. 2, and move it up, it does switch places with No. 1, and those numbers update, but No. 3 will change to 4. If I repeat the process, the new No. 4 will change to 5.
Here's the code that I've attempted:
var oldIndex = grdBha.SelectedIndex;
var newIndex = oldIndex - 1;
var bha = _bhaList[oldIndex];
_bhaList.RemoveAt(oldIndex);
bha.Number = oldIndex;
_bhaList.Insert(newIndex, bha);
for (var i = newIndex + 1; i <= _bhaList.Count; i++)
{
if (i != _bhaList.Count)
{
_bhaList[i].Number += 1;
}
}
I've tried different variations in the for loop, with no success.
I have the feeling that this is going to be something really simple, but my mind just isn't seeing it.
Your problem is that only two items in this case had their "order changed", yet you incremented the number for everybody below the item moved. In this case Bit X/O Sub was 3, but gets incremented to 4.
You know the only two indices involved:
// swap the two
var temp = _bhaList[newIndex];
_bhaList[newIndex] = _bhaList[oldIndex];
_bhaList[oldIndex] = temp;
_bhaList[newIndex].Number = newIndex + 1;
_bhaList[oldIndex].Number = oldIndex + 1;
If your items can only move one spot at a time the loop is unnecessary and can be replaced with the following:
_bhaList[oldIndex].Number++;
You only need to reshuffle/ swap the locations,
When you _bhaList.Insert(newIndex, bha); it will increment the count by 1.
Try only by replacing those two selected indexes.
Your for loop doesn't make sence, you don't need to increase the number of each items. You only need to do this if you add a new item in the list.
var oldIndex = grdBha.SelectedIndex;
var newIndex = oldIndex - 1;
var oldBha = _bhaList[oldIndex];
var newBha = _bhaList[newIndex];
oldBha.Number--;
newBha.Number++;
_bhaList.RemoveAt(oldIndex);
_bhaList.Insert(newIndex, oldBha);
or
_bhaList[oldIndex] = newBha;
_bhaList[newIndex] = oldBha;
by using
_bhaList.RemoveAt(oldIndex);
bha.Number = oldIndex;
_bhaList.Insert(newIndex, bha);
you have moved the item to the new index. .NET will push down all the other elements as required with out the need for you to do so. As such there is no need for the for loop.
To resolve your issue, simply remove the for loop.

Looping and changing a list - remove doesn't always work

I'm trying to go through a loop 40 times and changing a list in the process.
This is the code:
for (int i = 0; i < 40; i++)
{
location = rand.Next(rows.Count);
rank = rand2.Next(pondRanks.Count);
ComputerPonds[rows[location]].Rank = (PondRank)pondRanks[rank];
rows.Remove(location);
pondRanks.Remove(rank);
}
For some reason the remove doesn't happen all the time, and only sometimes. Anyone has a suggestion?
Both of the list are List , they have 40 elements, and I want to remove the element itself.
Even when debugging I can see that the list count isn't the same (they both have the same initial numbers and they both need to do remove at this loop). If it matters, I'm working on windows phone platform..
I'm pretty sure you should be using List.RemoveAt not List.Remove. RemoveAt will remove the item at the specified index, whereas Remove will look for that object you passed in and remove it from the List if it's in there. But I'm pretty sure that looking at your code that location and rank represent the index, not the objects themselves.
for (int i = 0; i < 39; i++)
{
location = rand.Next(rows.Count);
rank = rand2.Next(pondRanks.Count);
ComputerPonds[location].Rank = (PondRank)pondRanks[rank];
rows.RemoveAt(location);
pondRanks.RemoveAt(rank);
}
EDIT: You may also want to consider making sure that your rows and pondRanks have enough elements (39) before starting the loop (or altering the i < 39 to max out at the upper limit of their length)

Clearing a list using a for loop

I'm making a Black Jack game, and at the start of every new round I need to clear the list of cards that represents the Player's and the Dealer's hands. I used this to do so:
public void ClearPlayerHand()
{
for (int i = 0; i < PlayerHand.Count; ++i)
{
PlayerHand.Remove(PlayerHand[i]);
}
}
Problem is I always seem to be left with one card left in the list, or I receive an out of bounds error, no matter how I change the value of i, what is the best method of removing all the elements from the PlayerHand?
If your collection PlayerHand implements ICollection<T> you can just call the .Clear() method.
A common implementation of this interface is List<T>.
If you do want to clear a List<T> via a for loop, you should use a reverse for loop. The reason for this is that as you remove an item from the list, it will shift all the index's down one, and you could easy run into index out of bounds exceptions.
An example of this would be:
for (int i = PlayerHand.Count - 1; i >= 0; i--)
{
PlayerHand.RemoveAt(i);
}
The other answers are right: use Clear.
But, if you wanted to do this with a loop and Remove calls, here's how you would do it:
for(int i = PlayerHand.Count - 1; i >= 0; i--)
{
PlayerHand.RemoveAt(i);
}
Reversing the direction of the iteration is the real trick.
This is the best/easiest way to do it.
PlayerHand.Clear();
Reason for out of bounds
As for why you are receiving the out of bounds exception, it's happening because you're removing elements from the list but continually counting up. You would want the last operation to remove i = 0 but it keeps counting.
Say PlayerHand has 3 items in it, the following occurs:
i = 0
remove PlayerHand[0] (it now contains 2 elements)
i = 1
remove PlayerHand[1] (it now contains 1 element)
i = 2
remove PlayerHand[2] (this throws an exception as only PlayerHand[0] exists)
Normally you would count backwards in this case:
for (int i = PlayerHand.Count - 1; i >= 0; i--)
Alternatively, you can consider using data binding and then you should update the ItemSource, instead of directly manipulating the listbox or listview items.
List<T> SomeSource=...
PlayHand.ItemSource=SomeSource;
SomeSource.Clear();
Another suggested approach beside Clear method, you can also use RemoveAll to either remove all or part of list
// Remove all items
PlayerHand.RemoveAll(x => true);
// Remove part of list
PlayerHand.RemoveAll(x => ConditionMethod(x));

Visible listview items in winforms listview?

How can I get the visible items from a winforms listview? There doesn't seem to be a straightforward way, and I am reluctant to query items by control.clientrectangle or other hacks similar to the following:
https://stackoverflow.com/questions/372011/how-do-i-get-the-start-index-and-number-of-visible-items-in-a-listview)
When I say visible I mean the items are visible on the screen.
You can iterate from ListView.TopItem and check ListViewItem.Bounds property of each item whether it is located within the client area.
Better ListView Express is a freeware component that have also BottomItem property, so you can easily go through the visible items with a for loop (if both TopItem and BottomItem are not null):
for (int i = betterListView.TopItem.Index; i < betterListView.BottomItem.Index; i++)
{
// your code here
}
You can try this - it have the same interface as ListView and have many improvements over .NET ListView.
Sample code using GetItemAt
Looking at #Hans Passants comment I made a stab at actually creating code.
This code gets the top/bottom items. To get a Collection of visible items should be easy by out from Items where index is between index of top/bottom.
For me this worked much better than using Bounds, the bounds of the ListView appeared to have been higher then the visible part.
/// <summary>
/// Finds top/bottom visible items
/// </summary>
public static (ListViewItem, ListViewItem) GetTopBottomVisible(ListView listView)
{
ListViewItem topItem = listView.TopItem;
int lstTop = listView.Top;
int lstHeight = lstTop + listView.Height;
int lstBottom = lstHeight;
int step = lstHeight/2;
int x = listView.Left + listView.Width/2;
int y = lstTop + step;
ListViewItem bottomCandidate=null;
// iterate by interval halving
while ( step > 0 )
{
step /= 2; // halv interval
ListViewItem itm = listView.GetItemAt(x, y);
if ( itm == null )
{
// below last, move up
y -= step;
}
else if ( itm == bottomCandidate )
{
// Moving still in same item, stop here
break;
}
else
{
// above last, move down, storing candidate
bottomCandidate = itm;
y += step;
}
}
return (topItem, bottomCandidate);
}
If you are looking for a function that gives you only the visible item list, there is no such thing. You can go foreach item and check if its visible or not.
(If i understood your question right? Please give much clear explanation)

Categories