For a school lab I have to make a linked list of messages and then sort those messages by priority, with "High" priority being pulled out first, then medium, then low. I've been trying to figure this out for days and I can't wrap my mind around the sorting. I've been trying to get it to sort without adding anything other than a head and a size field in my ListofMessages class but all I seem to do is add garbage code. I wanted to figure this out myself but right now I'm stumped.
Here's what I have so far. Hopefully you can make sense of it:
class ListOfMessages
{
private int m_nSize;
public Message m_cListStart;
//public Message m_cNextItem;
public Message m_cLastItem;
public ListOfMessages()
{
m_nSize = 0;
m_cListStart = null;
//m_cNextItem = null;
}
public int Count
{
get { return m_nSize; }
}
public string Display(Message cMessage)
{
return "Message: " + cMessage.m_strMessage + "\nPriority: " + cMessage.m_strPriority;
}
//list additions
public int Add(Message newMessage)
{
Message nextMessage = new Message();
//inserts objects at the end of the list
if (m_nSize == 0)
{
m_cListStart = newMessage;
//m_cLastItem = newMessage;
}
else
{
Message CurrentMessage = m_cListStart;
if (newMessage.m_strPriority == "High")
{
if (m_cListStart.m_strPriority != "High")
{
//Make the object at the start of the list point to itself
CurrentMessage.m_cNext = m_cListStart;
//Replace the object at the start of the list with the new message
m_cListStart = newMessage;
}
else
{
Message LastMessage = null;
for (int iii = 0; iii < m_nSize; iii++)//(newmessage.m_strpriority == iii.m_strpriority)
//&& (iii.m_cnext == null);)
{
if (m_cListStart.m_strPriority != "High")
{
nextMessage = newMessage;
nextMessage.m_cNext =
CurrentMessage = nextMessage;
//LastMessage.m_cNext = CurrentMessage;
}
LastMessage = CurrentMessage;
if (m_cListStart.m_cNext != null)
m_cListStart = m_cListStart.m_cNext;
}
}
//iii = iii.m_cnext;
}
// for (int iii = m_cListStart; ; iii = iii.m_cNext)//(newMessage.m_strPriority == iii.m_strPriority)
// //&& (iii.m_cNext == null);)
//{
// //Message lastMessage = iii;
// if (iii.m_strPriority != iii.m_strPriority)
// {
// //iii.m_cNext = newMessage;
// newMessage.m_cNext = iii.m_cNext;
// iii.m_cNext = newMessage;
// }
//m_cLastItem.m_cNext = newMessage;
}
//m_cLastItem = newMessage;
return m_nSize++;
}
public Message Pop()
{
//Message Current = m_cListStart;
//if the object at the start of the list has another object after it, make that object the start of the list
//and decrease the size by 1 after popping an object off if there is more than 1 object after the start of the list
if (m_cListStart.m_cNext != null)
{
m_cListStart = m_cListStart.m_cNext;
}
if (m_nSize > 0)
m_nSize--;
else
m_cListStart = null;
return m_cListStart;
//if (m_cListStart.m_cNext != null)
// m_cListStart = m_cListStart.m_cNext;
//if (m_nSize > 1)
// m_nSize--;
//return m_cListStart;
}
My pop function to retrieve the messages might need some refining but most of the work right now lies in the Add function. I'm really just stumbling through the dark there.
Does anyone know of a simple way of doing what I'm asking?
Why dont you write a custom linked list as follows:
class Node<T> : IComparable<T>
{
public int Priority {set;get;}
public T Data {set;get;}
public Node<T> Next {set;get;}
public Node<T> Previous {set;get;}
// you need to implement IComparable here for sorting.
}
This is your node definitions. Now We need to implement a LinkedList Class.
Your linked list class can be doubly linked list, since you dont have any specs on it. and it would be easier with doubly linked list.
Here is the definition:
class LinkedList<T> : IEnumerable<T> where T: IComparable
{
public Node<T> Head {set;get;}
public Node<T> Tail {set;get;}
// set of constructors
//.....
public void Insert(Node<T> node)
{
// you can do recursive or iterative impl. very easy.
}
// other public methods such as remove, insertAfter, insert before, insert last etc.
public void Sort()
{
// easiest solution is to use insertion sort based on priority.
}
}
If you can get away by creating extra memory, ie: another linked list. insertion sort would be fine. For this purpose you need to implement insert after functionality as well.
I have a LinkedList implementation, you can check it out. You just need to implement sorting based on priority, you can use bubble sort, insertion sort, or merge sort.
Also, you might want to look at Heap which you can use to implement a priority Queue, it serves the purpose. I have a Heap Data Structure Implementation, you can check it out.
The easiest solution would be to have three singly-linked lists, one for each priority.
When you add, you add to the end of the correct list. When you remove, you first try to remove from the highest priority list. If that is empty, try the middle one. If even that is empty, use the lowest list.
If you have constant number of priorities, the time complexities are O(1) in both cases.
Related
I have a background worker that streams data and saves it to a ConcurrentQueue<T> which is what I need since it is a thread safe First In First Out collection, but I also need to do tasks like perform simple calculations or to pull data from this collection and I'm not sure what I need to use at this point. Here is some example pseudo code:
public class ExampleData
{
public DateTime Date { get; set; }
public decimal Value { get; set; }
}
public ConcurrentQueue<ExampleData> QueueCol { get; set; } = new();
public void AddToQueue(DateTime date, decimal value)
{
QueueCol.Enqueue(new ExampleData() { Date = date, Value = value });
}
public void DisplayPastData()
{
var count = QueueCol.Count();
var prev1Data = count >= 2 ? QueueCol.ElementAt(count - 2) : null;
var prev2Data = count >= 3 ? QueueCol.ElementAt(count - 3) : null;
var prev3Data = count >= 4 ? QueueCol.ElementAt(count - 4) : null;
if (prev1Data != null)
{
Console.WriteLine($"Date: {prev1Data.Date} Value: {prev1Data.Value}");
}
if (prev2Data != null)
{
Console.WriteLine($"Date: {prev2Data.Date} Value: {prev2Data.Value}");
}
if (prev3Data != null)
{
Console.WriteLine($"Date: {prev3Data.Date} Value: {prev3Data.Value}");
}
}
This is a very rough example but even with displaying data most of it looks correct and then I will get dates completely out of left field like a date from the previous day in between dates from the current day and so because of ordering issues like that I know the data isn't correct so my question is how do I convert the concurrent queue to a new collection that will allow me to keep the order and to work with the data without giving incorrect results?
The usage pattern you describe in your question makes a ConcurrentQueue<T> not a suitable collection for your scenario. As far as I can understand the requirements are:
The producer(s) should be able to enqueue items in the collection without being blocked for any amount of time.
The consumer(s) should be able to perform calculations on a snapshot of the collection, without creating an expensive copy of the collection, and without interfering in any way with the producer(s).
The collection that seems more suitable for your scenario out of the box, is the ImmutableList<T>. This collection can be updated with lock-free Interlocked operations, and it is essentially a snapshot by itself (because it is immutable). Here is how you could use it in a multithreading scenario, with thread-safety and without blocking any thread:
private ImmutableList<ExampleData> _data = ImmutableList<ExampleData>.Empty;
public ImmutableList<ExampleData> Data => Volatile.Read(ref _data);
public void AddToQueue(DateTime date, decimal value)
{
var newData = new ExampleData() { Date = date, Value = value };
ImmutableInterlocked.Update(ref _data, (x, y) => x.Add(y), newData);
}
public void DisplayPastData()
{
ImmutableList<ExampleData> snapshot = Volatile.Read(ref _data);
int count = snapshot.Count;
var prev1Data = count >= 2 ? snapshot[count - 2] : null;
var prev2Data = count >= 3 ? snapshot[count - 3] : null;
var prev3Data = count >= 4 ? snapshot[count - 4] : null;
if (prev1Data != null)
{
Console.WriteLine($"Date: {prev1Data.Date} Value: {prev1Data.Value}");
}
if (prev2Data != null)
{
Console.WriteLine($"Date: {prev2Data.Date} Value: {prev2Data.Value}");
}
if (prev3Data != null)
{
Console.WriteLine($"Date: {prev3Data.Date} Value: {prev3Data.Value}");
}
}
The immutable collections are not without disadvantages. They are a lot slower in comparison with the normal collections, they require significantly more memory, and they create significantly more garbage every time they are updated.
An optimal solution to your specific scenario could be a combination of a ConcurrentQueue<ExampleData> (recent data) and a List<ExampleData> (historic data). The producer(s) would enqueue items in the ConcurrentQueue<T>, and the single consumer would dequeue all the items from the ConcurrentQueue<T> and then add them in the List<T>. Then it would use the List<T> to do the calculations.
I want the code to show how many wagons there are and which animals are in each wagon. This is my error:
System.InvalidOperationException: "The collection has been changed. The inventory processing may not be performed. "
This is the code:
public IEnumerable<Animal> GetAnimals()
{
return Animals.AsEnumerable();
}
public void Checker(List<Animal> listAnimals)
{
foreach (Animal animal in listAnimals)
{
foreach (Wagon wagon in Wagons)
{
foreach (Animal wagonAnimal in wagon.GetAnimals())
{
if (wagon.StartCapacity <= wagon.MaxCapacity &&
animal.Formaat + wagon.StartCapacity <= wagon.MaxCapacity &&
wagonAnimal.Eater == Eater.carnivoor &&
animal.Eater == Eater.herbivoor &&
animal.Formaat >= wagonAnimal.Formaat)
{
wagon.AddAnimal(animal);
Wagons.Add(wagon);
}
else
{
Wagon waggi = new Wagon();
waggi.AddAnimal(animal);
Wagons.Add(waggi);
}
}
}
Wagon wag = new Wagon();
wag.AddAnimal(animal);
Wagons.Add(wag);
}
}
Can anyone give me some hints on this issue?
If you are looking to modify the collection while looping I would use a List object instead of an IEnumerable.
Some sample code would be like this:
List<Wagons> Wagons = new List<Wagons>
Wagons.AddAnimal(animal1);
foreach(Animal animal in Wagons.GetAnimals(){
animal.Eater = Eater.herbivore;
}
Also looking at your code:
if (wagon.StartCapacity <= wagon.MaxCapacity &&
animal.Formaat + wagon.StartCapacity <=
wagon.MaxCapacity && wagonAnimal.Eater == Eater.carnivoor &&
animal.Eater == Eater.herbivoor && animal.Formaat >= wagonAnimal.Formaat)
{
wagon.AddAnimal(animal);
Wagons.Add(wagon);
} else {
wagon.AddAnimal(animal);
Wagons.Add(wagon);
}
This if/else statement does the exact same code, so you really don't need an if/else, you can just add the animal and add the wagon.
Lastly, shouldn't the parameter to your method accept a List or IEnumerable collection of wagons and not animals so you can loop through the wagons, and look through the animals in the wagons?
You cannot modify a list while iterating over it using foreach and in.
Example:
foreach (Wagon wagon in Wagons)
{
Wagon waggi = new Wagon();
Wagons.Add(waggi);
}
will not work.
If you use e.g.
// This is needed to not get an endless loop (Because the length of the list
// increases after each time the Add() method is called to Wagons.)
int wagonCount = Wagons.Count;
for (int i = 0; i < wagonCount ; i++)
{
Wagon waggi = new Wagon();
waggi.AddAnimal(animal);
Wagons.Add(waggi);
}
this will work.
My working example for your code (as far as I could get what you want to do is here:
https://dotnetfiddle.net/6HXYmI and here: https://gist.github.com/SeppPenner/a082062d3ce2d5b8196bbf4618319044.
I would also recommend to check your code style according to the definitions from Microsoft: https://learn.microsoft.com/en-US/dotnet/csharp/programming-guide/inside-a-program/coding-conventions.
Actually you can't modify the list while looping.
You need to create another object and add the wagon and animals respectively.
Try this and comment if you didn't understand yet
I have multiple objects(nodes) and each node has a getter and setter for a list named Calea which contains other nodes, also each node has neighbours and they are also nodes . The problem is that list is stacking up and I can't figure out why , it's like a static variable and also I am not using that getter and setter anywhere else.
Here is my code :
private int cost = 10000;
private LinkedList<GraphNode<string>> calea=new LinkedList<GraphNode<string>>() ;
public int Cost
{
get
{
return cost;
}
set
{
cost = value;
}
}
public LinkedList<GraphNode<string>> Calea
{
get
{
if (calea == null) return new LinkedList<GraphNode<string>>();
return calea;
}
set
{
calea = value;
}
}
Code above shows the method for Cost and Calea , Cost works fine but Calea is stacking up.The code below is a sample of code of how I am setting the value Calea for each node:
if (curr.Neighbors.ElementAt(i).Cost > curr.Costs.ElementAt(i) + curr.Cost)
{
curr.Neighbors.ElementAt(i).Cost = curr.Costs.ElementAt(i) + curr.Cost;
curr.Neighbors.ElementAt(i).Calea = curr.Calea;
curr.Neighbors.ElementAt(i).Calea.AddLast((GraphNode<string>)curr.Neighbors.ElementAt(i));
index = i;
}
++i;
The sample code where I change the current node below:
pathNodesToVisit.Remove(curr);
if (pathNodesToVisit.Count == 0) break;
if (curr.Neighbors.Count > index)
{
for (int j = 0; j < pathNodesToVisit.Count; j++)
{
if (pathNodesToVisit.ElementAt(j).Value == curr.Neighbors.ElementAt(index).Value)
{
indexx = j;
//MessageBox.Show(pathNodesToVisit.ElementAt(j).Value);
}
}
curr = pathNodesToVisit.ElementAt(indexx);
}
else
{
curr = pathNodesToVisit.ElementAt(0);
}
A few words : pathNodesToVisit are all the nods which I want to visit(Dijkstra algorithm) , in the code above I remove the curr node from the list and the new curr node is a node which had the Costs and Calea changed.
I have no idea what you mean by "stacking up," but:
public LinkedList<GraphNode<string>> Calea
{
get
{
if (calea == null) return new LinkedList<GraphNode<string>>();
return calea;
}
... creates a new list every time the property is read, not just the first time. calea will always be null with this approach.
Try
get
{
if (null == calea)
calea = new LinkedList<GraphNode<string>>();
return calea;
}
Update
The line
curr.Neighbors.ElementAt(i).Calea = curr.Calea;
Does not make a copy of the list. It copies a reference to the list. Any changes made to any node's calea afterward will affect every node, not just the one you're after.
Try
curr.Neighbors.ElementAt(i).Calea = new LinkedList<GraphNode<string>>(curr.Calea);
Though, you should make sure .Neighbors actually has an element i before doing this, among other things.
Note: In the case of an uninitialized node, this will actually create two lists - once when Calea is read (LH of the expression, which calls your .get), and another on the right.
There are many ways to copy a collection. I suggest googling c# deep copy LinkedList<T>.
I have a number of custom collection classes. Each serves to provide a collection of various custom types - one custom type to one custom collection. The custom collections inherit List<T> [where T in this case is the specific custom type, rather then a generic] and provide some additional functionality.
I previously did away with the custom collections and had custom methods elsewhere, but I found as I extended the code that I needed the collections with their own methods.
It all works, everything is happy. But it irritates me, because I know I am not doing it properly. The issue is that each class uses pretty much the same code, varying only the type and a parameter, so I feel that it could be implemented as an abstract class, or generic, or extension to List, or ... but I'm not really understanding enough of the differences or how to go about it to be able to sort out what I need.
Here are two of my several collections, so that you get the idea:
// JourneyPatterns
public class JourneyPatterns : List<JourneyPattern>
{
private Dictionary<string, JourneyPattern> jpHashes; // This is a hash table for quick lookup of a JP based on its values
/* Add a journey pattern to the JourneyPatterns collection. Three methods for adding:
1. "Insert Before" (=at) a particular point in the list. This is the method used by all three methods.
2. "Insert After" a particular point in the list. This is "before" shifted by 1 e.g. "after 6" is "before 7"
3. "Append" to the end of the list. This is "before" with a value equal to the list count, and is the same as inherited "Add", but with checks
*/
public JourneyPattern InsertBefore(JourneyPattern JP, int before)
{
// check for a pre-existing JP with the same parameters (ignore ID). Do this by constructing a "key" based on the values to check against
// and looking it up in the private hash dictionary
JourneyPattern existingJP;
if (jpHashes.TryGetValue(JP.hash, out existingJP)) { return existingJP; }
else
{
// construct a new ID for this JP
if (string.IsNullOrWhiteSpace(JP.id)) JP.id = "JP_" + (Count + 1).ToString();
// next check that the ID specified isn't already being used by a different JPS
if (Exists(a => a.id == JP.id)) JP.id = "JP_" + (Count + 1).ToString();
// now do the add/insert
if (before < 0) { Insert(0, JP); } else if (before >= Count) { Add(JP); } else { Insert(before, JP); }
// finally add to the hash table for fast compare / lookup
jpHashes.Add(JP.hash, JP);
return JP;
}
}
public JourneyPattern InsertAfter(JourneyPattern JP, int after) { return InsertBefore(JP, after + 1); }
public JourneyPattern Append(JourneyPattern JP) { return InsertBefore(JP, Count); }
}
// JourneyPatternSections
public class JourneyPatternSections : List<JourneyPatternSection>
{
private Dictionary<string, JourneyPatternSection> jpsHashes; // This is a hash table for quick lookup of a JPS based on its values
/* Add a journey pattern section to the journeyPatternSections collection. Three methods for adding:
1. "Insert Before" (=at) a particular point in the list. This is the method used by all three methods.
2. "Insert After" a particular point in the list. This is "before" shifted by 1 e.g. "after 6" is "before 7"
3. "Append" to the end of the list. This is "before" with a value equal to the list count, and is the same as inherited "Add", but with checks
*/
public JourneyPatternSection InsertBefore(JourneyPatternSection JPS, int before)
{
// check for a pre-existing JPS with the same parameters (ignore ID). Do this by constructing a "key" based on the values to check against
// and looking it up in the private hash dictionary
JourneyPatternSection existingJPS;
if (jpsHashes.TryGetValue(JPS.hash, out existingJPS)) { return existingJPS; }
else
{
// construct a new ID for this JPS
if (string.IsNullOrWhiteSpace(JPS.id)) JPS.id = "JPS_" + (Count + 1).ToString();
// next check that the ID specified isn't already being used by a different JPS
if (Exists(a => a.id == JPS.id)) JPS.id = "JPS_" + (Count + 1).ToString();
// now do the add/insert
if (before < 0) { Insert(0, JPS); } else if (before >= Count) { Add(JPS); } else { Insert(before, JPS); }
// finally add to the hash table for fast compare / lookup
jpsHashes.Add(JPS.hash, JPS);
return JPS;
}
}
public JourneyPatternSection InsertAfter(JourneyPatternSection JPS, int after) { return InsertBefore(JPS, after + 1); }
public JourneyPatternSection Append(JourneyPatternSection JPS) { return InsertBefore(JPS, Count); }
}
As you can see, what is differing is the type (JourneyPattern, or JourneyPatternSection), and the prefix that I am using for the "id" property of the type ("JP_" or "JPS_"). Everything else is common, since the method of determining "uniqueness" (the property "hash") is part of the custom type.
Some of my custom collections require more involved and different implementations of these methods, which is fine, but this is the most common one and I have implemented it about 6 times so far which seems a) pointless, and b) harder to maintain.
Your thoughts and help appreciated!
Assming tha both JourneyPattern and JourneyPatternSection implements a common interface like:
public interface IJourney
{
string hash { get; set; }
string id { get; set; }
}
You can implements a base class for your collections:
public abstract class SpecializedList<T> : List<T> where T : class, IJourney
{
private Dictionary<string, T> jpHashes; // This is a hash table for quick lookup of a JP based on its values
protected abstract string IdPrefix { get; }
/* Add a journey pattern to the JourneyPatterns collection. Three methods for adding:
1. "Insert Before" (=at) a particular point in the list. This is the method used by all three methods.
2. "Insert After" a particular point in the list. This is "before" shifted by 1 e.g. "after 6" is "before 7"
3. "Append" to the end of the list. This is "before" with a value equal to the list count, and is the same as inherited "Add", but with checks
*/
public T InsertBefore(T JP, int before)
{
// check for a pre-existing JP with the same parameters (ignore ID). Do this by constructing a "key" based on the values to check against
// and looking it up in the private hash dictionary
T existingJP;
if (jpHashes.TryGetValue(JP.hash, out existingJP)) { return existingJP; }
else
{
// construct a new ID for this JP
if (string.IsNullOrWhiteSpace(JP.id)) JP.id = "JP_" + (Count + 1).ToString();
// next check that the ID specified isn't already being used by a different JPS
if (Exists(a => a.id == JP.id)) JP.id = IdPrefix + (Count + 1).ToString();
// now do the add/insert
if (before < 0) { Insert(0, JP); } else if (before >= Count) { Add(JP); } else { Insert(before, JP); }
// finally add to the hash table for fast compare / lookup
jpHashes.Add(JP.hash, JP);
return JP;
}
}
public T InsertAfter(T JP, int after) { return InsertBefore(JP, after + 1); }
public T Append(T JP) { return InsertBefore(JP, Count); }
}
Then implement each collection:
public class JourneyPatterns : SpecializedList<JourneyPattern>
{
protected override string IdPrefix => "JP_";
}
public class JourneyPatternSections : SpecializedList<JourneyPatternSection>
{
protected override string IdPrefix => "JPS_";
}
I realize i have to sort the collection where the ListView gathers the items from:
ListView listCollection = new ListView();
But this doesn't seem to work unless the ListView is added as a GUI-control to the form, that in turn makes it very slow to add items to, hence why i have to use VirtualMode on my GUI-ListView in the first place.
Anyone know how to go about this or point me in the right direction?
basically, you will need to apply sort to the data pump itself.
I did a quick search on Google for listview sort virtualmode. First result was this page, where the above quote was taken from.
For example, if your datasource is a DataView, apply sorting on this instead of the ListView.
If it is just a question of performance when adding items, I would do as barism suggests; use BeginUpdate/EndUpdate instead of VirtualMode.
try {
listView1.BeginUpdate();
// add items
}
finally {
listView1.EndUpdate();
}
If you are using virtual mode, you have to sort your underlying data source. As you may have found, ListViewItemSorter does nothing for virtual lists.
If you are using a non-virtual listview, you can also use AddRange(), which is significantly faster than a series of Add() -- In addition to using BeginUpdate/EndUpdate that has already been described.
ObjectListView (an open source wrapper around .NET WinForms ListView) already uses all these techniques to make itself fast. It is a big improvement over a normal ListView. It supports both normal mode and virtual mode listviews, and make them both much easier to use. For example, sorting is handled completely automatically.
I had the same problem switching VirtualMode True with an existing project, but the solution was surprisingly easy:
First step I am populating a list of ListViewItem, instead of ListView.Items collection:
private List<ListViewItem> _ListViewItems;
Then I have implemented the RetrieveVirtualItem method
private void mLV_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
{
e.Item = _ListViewItems[e.ItemIndex];
}
Finally I am sorting my list of ListViewItem using the same class I was using before, I had just only to change the base class
_ListViewItems.Sort((System.Collections.Generic.IComparer<ListViewItem>)new ListViewItemComparer(new int[] { e.Column }, mLV.Sorting));
This is my IComparer class implementation:
class ListViewItemComparer : System.Collections.Generic.IComparer<ListViewItem>
{
int[] mColonne;
private System.Windows.Forms.SortOrder order;
public ListViewItemComparer(int[] mCols)
{
this.mColonne = mCols;
this.order = System.Windows.Forms.SortOrder.Ascending;
}
public ListViewItemComparer(int[] mCols, System.Windows.Forms.SortOrder order)
{
this.mColonne = mCols;
this.order = order;
}
public int Compare(ListViewItem x, ListViewItem y)
{
int returnVal = -1;
foreach (int mColonna in mColonne)
{
double mNum1;
double mNum2;
String mStr1 = "";
String mStr2 = "";
if ((x.SubItems[mColonna].Text == "NULL") && (x.SubItems[mColonna].ForeColor == Color.Red))
{
mStr1 = "-1";
}
else
{
mStr1 = x.SubItems[mColonna].Text;
}
if ((y.SubItems[mColonna].Text == "NULL") && (y.SubItems[mColonna].ForeColor == Color.Red))
{
mStr2 = "-1";
}
else
{
mStr2 = y.SubItems[mColonna].Text;
}
if ((double.TryParse(mStr1, out mNum1) == true) && (double.TryParse(mStr2, out mNum2) == true))
{
if (mNum1 == mNum2)
{
returnVal = 0;
}
else if (mNum1 > mNum2)
{
returnVal = 1;
}
else
{
returnVal = -1;
}
}
else if ((double.TryParse(mStr1, out mNum1) == true) && (double.TryParse(mStr2, out mNum2) == false))
{
returnVal = -1;
}
else if ((double.TryParse(mStr1, out mNum1) == false) && (double.TryParse(mStr2, out mNum2) == true))
{
returnVal = 1;
}
else
{
returnVal = String.Compare(mStr1, mStr2);
}
if (returnVal != 0)
{
break;
}
}
// Determine whether the sort order is descending.
if (order == System.Windows.Forms.SortOrder.Descending)
{
// Invert the value returned by String.Compare.
returnVal *= -1;
}
return returnVal;
}
}
Hope this will help you.
Did you try beginupdate() and endupdate()? Adding data is much faster when you use beginupdate/endupdate.(when you call beginupdate, listview doesn't draw until you call endupdate)
listView1.BeginUpdate();
for (int i = 0; i < 20000; i++)
{
listView1.Items.Add("abdc", 1);
}
listView1.EndUpdate();
For very large lists, the Virtual Mode ListView is the answer for certain. In non-virtual mode it seems to draw the entire list and then clip it to the view, while in Virtual mode it simply draws the ones in the view. In my case the list was 40K+ records. In non-virtual mode an update to the ListView could take minutes. In virtual mode it was instantaneous.
To sort the list, you must sort the underlying datasource, as has already been mentioned. That is the easy part. You will also need to force a refresh to the display, which is not done automatically. You can use ListView.TopItem.Index to find the index in the underlying data source that corresponds to the top row of the Virtual ListView before the sort. There is also an API call that returns the number of display rows in the ListView that you can implement as C# function, like this:
public const Int32 LVM_GETCOUNTPERPAGE = 0x1040;
public static int GetListViewRows( ListView xoView )
{
return (int)WindowsMessage.SendMessage( xoView.Handle, LVM_GETCOUNTPERPAGE, 0, 0 );
}
That will let you calculate the range you have to update. About the only remaining question is how you reconcile the existing display with what will appear after the data have been sorted. If you want to leave the same data element in the top row, you must have some mechanism in place that will find its new index in the newly sorted list, so you can replace it in the top position - something essentially equivalent to an SQL IDENTITY.