How to add just 10 items - c#

I have undoredomanager.
And I need to view in listview only 10 entries.
already seething brain how to do it.
This code is added to the viewlist all records, but I only need the last 10.
lvUndoStack.Items.Clear();
var list = new List<object>();
foreach (var command in UndoRedoManager.UndoCommands)
{
list.Insert(0, command.ToString());
}
lvUndoStack.Items.AddRange(list.ToArray());
lvUndoStack.SelectedIndex = lvUndoStack.Items.Count - 1;
indexSeletedItemUndoStack = lvUndoStack.SelectedIndex;
list = new List<object>();
foreach (var command in UndoRedoManager.RedoCommands)
{
list.Insert(0, command.ToString());
}
lvUndoStack.Items.AddRange(list.ToArray());
importantly - not used linq
Update:
example:
undo1
undo2
undo3
undo4
undo5
undo6
undo7
redo1
redo2
redo3
redo4
redo5
I need obly 10. if start undo4 then you need to show everything in the last or a maximum of 10

If I understand your question, you want (at most) the last 10 undo and the last 10 redo commands. After you get the List of undo commands, use this to get up to the last 10:
if (list.Count <= 10)
{
lvUndoStack.Items.AddRange(list.ToArray());
}
else
{
for (int i = 0; i < 10; i++)
{
lvUndoStack.Items.Add(list[0]);
}
}
And do the same for the redo commands. That's not the best solution, IMO - really more of a kludge, but it should get you going in the right direction.
An even better solution would be to modify/enchance the UndoRedoManager class so that the ListView could call a method to get a list of the last n undo/redo commands. Something like:
public List<Object> GetUndoCommands(int numberOfCommands);
Then the ListView could simply call that method:
lvUndoStack.Items.AddRange(UndoRedoManager.GetUndoCommands(10).ToArray());
And something similar for the redo commands. It removes a bunch of code from your UI layer, and gives you the flexibility to easily switch the max number of items at a later date and just generally (again IMO) seems to be a better way to go.

Related

Optimizing inventory checking for scalability

EDIT: For clarity
I have multiple inventories (List<Item>s named items1, items2, etc) in a instance of an inventory (inventory). I also have multiple copies of the slot list below (slots1).
At the moment, I have to copy out the entire for loop given for each set of items/slots. I have been trying for a couple of days to condense it down in an intelligent manner so I only have a single bit of code that runs through all of the inventories.
I'm specifically trying to find a solution by which I can say something like:
items = inventory.items1
slots = slots1
and then run the loop once for each pair, so if I have items1/slots1, items2/slots2, items3/slots3 it would run three times.
My current code:
In the inventory instance:
public List<Item> items1 = new List<Item>();
In the UI script:
InventorySlot1[] slots1 = itemsParent1.GetComponentsInChildren<InventorySlot1>();
for (int i = 0; i < slots1.Length; i++)
{
if (i < inventory.items1.Count)
{
slots1[i].AddItem(inventory.items1[i]);
}
else
{
slots1[i].ClearSlot();
}
}
There is a copy of the above code for each items/slots pair! I would like to make it so I only need one copy of this loop.

c# collections and re-numbering not working as expected

Hi i'm trying to setup simple test data.
I simply want to take a collection which is smallish and make it bigger by add itself multiple times.
After I;ve added them together i want to re-number the property LineNumber
so that there are no duplicates and that it goes in order. 1,2,3,4....
no matter what i try it doesn't seem to work and i cant see the mistake.
var sampleTemplateLine = dataContext.TemplateFileLines.ToList();
*//tired this doesnt work either*
//List<TemplateFileLine> lineRange = new List<TemplateFileLine>();
//lineRange.AddRange(sampleTemplateLine);
//lineRange.AddRange(sampleTemplateLine);
//lineRange.AddRange(sampleTemplateLine);
//lineRange.AddRange(sampleTemplateLine);
var allProducts = sampleTemplateLine
.Concat(sampleTemplateLine)
.Concat(sampleTemplateLine)
.Concat(sampleTemplateLine)
.ToList();
int i = 1;
foreach (var item in allProducts)
{
item.LineNumber = i;
i++;
}
this doesnt seem to work either
//re-number the line number
var total = allProducts.Count();
for (int i =0; i < total; i++)
{
allProducts[i].LineNumber = i+1;
}
PROBLEM: below RETURN 4 when i'm expecting 1
var itemthing = allProducts.Where(x => x.LineNumber == 17312).ToList();
You are adding the same objects multiple times. You wold have to add new objects or clone the ones you have.
The problem is they are pointing the same object. So if you change a property it changes all the pointed objects at the same
You can use Clone method if it exist, if not you can create your own Clone method like in this question.

Iterating over Linq-to-Entities IEnumerable causes OutOfMemoryException

The part of the code I'm working on receives an
IEnumerable<T> items
where each item contains a class with properties reflecting a MSSQL database table.
The database table has a total count of 953664 rows.
The dataset in code is filtered down to a set of 284360 rows.
The following code throws an OutOfMemoryException when the process reaches about 1,5 GB memory allocation.
private static void Save<T>(IEnumerable<T> items, IList<IDataWriter> dataWriters, IEnumerable<PropertyColumn> columns) where T : MyTableClass
{
foreach (var item in items)
{
}
}
The variable items is of type
IQueryable<MyTableClass>
I can't find anyone with the same setup, and other's solutions that I've found doesn't apply here.
I've also tried paging, using Skip and Take with a page size of 500, but that just takes a long time and ends up with the same result. It seems like objects aren't being released after each iteration. How is that?
How can I rewrite this code to cope with a larger collection set?
Well, as Servy has already said you didn't provide your code so I'll try to make some predictions... (Sorry for my english)
If you have an exception in "foreach (var item in items)" when you are using paging then, I guess, something wrong with paging. I wrote a couple of examples to explain my idea.
if first example I suggest you (just for test) put your filter inside the Save function.
private static void Save<T>(IQueryable<T> items, IList<IDataWriter> dataWriters, IEnumerable<PropertyColumn> columns) where T : MyTableClass
{
int pageSize = 500; //Only 500 records will be loaded.
int currentStep = 0;
while (true)
{
//Here we create a new request into the database using our filter.
var tempList = items.Where(yourFilter).Skip(currentStep * pageSize).Take(pageSize);
foreach (var item in tempList)
{
//If you have an exception here maybe something wrong in your dataWriters or columns.
}
currentStep++;
if (tempList.Count() == 0) //No records have been loaded so we can leave.
break;
}
}
The second example show how to use paging without any changes in the Save function
int pageSize = 500;
int currentStep = 0;
while (true)
{
//Here we create a new request into the database using our filter.
var tempList = items.Where(yourFilter).Skip(currentStep * pageSize).Take(pageSize);
Save(tempList, dataWriters, columns); //Calling saving function.
currentStep++;
if (tempList.Count() == 0)
break;
}
Try both of them and you'll either resolve your problem or find another place where an exception is raised.
By the way, another potential place is your dataWriters. I guess there you store all data that your have been received from the database. Maybe you shouldn't save all data? Just calculate memory size that all objects are required.
P.S. And don't use while(true) in your code. It just an example:)

C# Auto Update List insted of .clear and refill the list

In my program I populate a list, but the next time I rerun my loop again I .clear the list to repopulate it again because some items in the list might update. How ever, is there somehow I can make it so it just auto updates the items in the list so I can't have to .clear and re add the items?
_players.Clear();
_weapons.Clear();
_entities.Clear();
var localPlayerPtr = Smurf.Memory.Read<IntPtr>(Smurf.ClientBase + Offsets.Misc.LocalPlayer);
LocalPlayer = new LocalPlayer(localPlayerPtr);
LocalPlayerWeapon = LocalPlayer.GetCurrentWeapon(localPlayerPtr);
for (var i = 0; i < _capacity; i++)
{
var entity = new BaseEntity(GetEntityPtr(i));
if (!entity.IsValid)
continue;
if (entity.IsPlayer())
_players.Add(new Player(GetEntityPtr(i)));
else if (entity.IsWeapon())
_weapons.Add(new LocalPlayerWeapon(GetEntityPtr(i)));
else
_entities.Add(new BaseEntity(GetEntityPtr(i)));
}
_lastUpdate = timeStamp;
Total repopulation of a list should be a fast enough way to deal with this. If you really need to go the other way, then your alternative is to when you run your loop:
Check your new element if it exists, and if yes, update it in the list (_players[index] = newPlayer; or something similar). Check by ID, or some other property that identifies your objects.
If it doesn't exist, add it as a new item to the list.
Store your new element in a separate list to keep track. (let's call it changesList)
(optional, if you need to prune) When you've finished going through all your new items, compare changesList with your other lists (you can probably use the Except(IEnumerable<T> list) method, and remove the objects that were not used.

Clearing Multi-Selected Items From Listbox C#

What i've tried:
try 1:
for(int x = listBox1.SelectedIndices.Count - 1; x>= 0; x--)
{
int idx = listBox1.SelectedIndices[x];
listBox2.Items.Add(listBox1.Items[idx]);
listBox1.Items.RemoveAt(idx);
}
try 2:
ArrayList tmpArr = new ArrayList();
foreach (object obj in listBox1.SelectedItems)
{
listBox2.Items.Add(obj);
tmpArr.Add(obj);
}
foreach (object obj in tmpArr.ToArray())
{
listBox1.Items.Remove(obj);
}
Also tried everything in the following post: How to remove multiple selected items in ListBox?
Still nothing worked. What am I doing wrong?
var selectedItems = new object[listBox1.SelectedItems.Count];
listBox1.SelectedItems.CopyTo(selectedItems, 0);
foreach (var item in selectedItems)
{
listBox1.Items.Remove(item);
}
or with a bit of LINQ to simplify the code:
foreach (var item in listBox1.SelectedItems.Cast<object>().ToArray())
{
listBox1.Items.Remove(item);
}
The reasoning here is that you get all the selected items and put them into another list first. The original issue is that any change you make to the ListBox will change things like SelectedItems and SelectedIndices. Once you've created an independent array and put the selected items into it, nothing you do to the ListBox will affect that array so you can just enumerate it normally.
listbox1.BeginUpdate();
for (int x = listBox1.SelectedIndices.Count - 1; x >= 0; x--)
{
int idx = listBox1.SelectedIndices[x];
listBox1.Items.RemoveAt(idx);
}
listbox1.EndUpdate();
If you cannot guarantee that every object in the list is unique, then this is the correct way to do it, to ensure that the correct selected items get removed.
If you have multiples of the same object in your listbox, you have to refer to them by "index", otherwise if you remove them by "item" it will remove the first instance of any matching items it finds.
I am in the process of writing a bus route planner which called for replication of the waypoint markers in the list. These were stored as strings, so for example I might have had "w1", "w2", "w3"... "w2" (think of a bus going down a high street, looping round a couple of blocks and then returning down the other side to understand why I have that... I only need waypoint markers in the centre of the road, not in each lane)
If I had selected the last "w2" marker as part of a range and used the selectedItem() method to to remove them, it would have removed the first "w2", not the second one. By using the SelectedIndex() method, it removes based on position, not value, so duplicate values are left safely intact.
I just wanted to add that as I have just been dealing with this very same problem, so saw first hand the problem removing by SelectedItem() caused.

Categories