|
Hi,
I have a ListView with alternate row colors! After sorting a column, the rows colors appear mixed
Is there a way to exented this nice class to handle this problem?
Eddie, thanks for yor nice class!
Max Glaser
Email: MaxGlaser@gmx.net
|
|
|
|
|
GlaserSoft wrote:
I have a ListView with alternate row colors! After sorting a column, the rows colors appear mixed... Is there a way to exented this nice class to handle this problem?
I had the same issue and extended the class to add an event to be raised after each sort is complete. Then I could just add a listener to the event to call a method which sets the background colors on this ListView each time a sort occurs.
The only change necessary to Eddie's code is to mark as virtual the method public void Sort(Int32 column, SortOrder order)
Here's my code:
using System;
using System.Windows.Forms;
namespace MyApplication
{
public class EventRaisingSortManager : EV.Windows.Forms.ListViewSortManager
{
public EventRaisingSortManager(ListView list, Type[] comparers) :
base(list, comparers)
{
}
public override void Sort(Int32 column, SortOrder order)
{
base.Sort(column,order);
OnSorted(column,order);
}
private void OnSorted(Int32 column, SortOrder order)
{
if (Sorted != null)
Sorted(this,new SortedEventArgs(column,order));
}
public event SortedEvent Sorted;
}
public delegate void SortedEvent(object sender, SortedEventArgs e);
public class SortedEventArgs : System.EventArgs
{
int _column;
SortOrder _order;
public SortedEventArgs(Int32 column, SortOrder order) : base()
{
this._column = column;
this._order = order;
}
public Int32 Column
{
get { return _column; }
set { _column = value; }
}
public SortOrder Order
{
get { return _order; }
set { _order = value; }
}
}
}
|
|
|
|
|
Hi Eddie
I like your code, but I would like to send you a change I made:
I created a date column, and the sorting crashed the program because my dates were not in a proper date-time format.
So I modified the OnCompare of each class like so:
private bool m_SortError=false;
protected override Int32 OnCompare(String lhs, String rhs)
{ //Error-handling added by Willem Semmelink
if (m_SortError) return 0; // dont show error message for each row
else {
try { // insert here Eddie's original code - int/date/string compare as the case may be
return DateTime.Parse(lhs).CompareTo(DateTime.Parse(rhs));
} catch(Exception E) {
m_SortError=true;
MessageBox.Show(E.Message);
}
return 0;
}
}
Perhaps, in stead of repeating this code in each OnCompare method, I should have followed a true O-O design and modified the base class, but this works for me.
Now, instead of crashing the application, I show the error to the user, and let him/her continue.
Just thought you may find it useful, too.
Greetings,
Willem Semmelink
|
|
|
|
|
WillemSe wrote:
and the sorting crashed the program because my dates were not in a proper date-time format. So I modified the OnCompare of each class like so:
I thought about adding something similar but dropped the idea because I think that ill-formed data shouldn't be allowed to be inserted in the list in the first place. Besides, all the checks would impose a large overhead to the sort process.
WillemSe wrote:
Perhaps, in stead of repeating this code in each OnCompare method, I should have followed a true O-O design and modified the base class, but this works for me.
I think you meant: create a derived class and add the extended functionality there.
There are only 10 kind of people in the world: those who understand binary and those who don't.
|
|
|
|
|
re:
//I think you meant: create a derived class and add the extended functionality there.
no, what I meant is that I would have liked to modify only ListViewTextSort whilst in the current implementation, I added the try..catch in each class:
* ListViewTextSort;
* ListViewTextCaseInsensitiveSort;
* ListViewDateSort;
* ListViewInt32Sort;
* ListViewInt64Sort;
* ...
I meant that I would have liked to implement error-checking only in ListViewTextSort so the others could inherit it - but it was easier to duplicate the code in each subclass.
The reason why the date failed on me was probably due to culture information - feeding a date in a non-American date format. So I will maybe spend some more time on it later to add culture info: I would like to do the comparison somewhat like this:
return DateTime.Parse(lhs,m_dateFormat,System.Globalization.DateTimeStyles.NoCurrentDateDefault).CompareTo(
DateTime.Parse(rhs,m_dateFormat,System.Globalization.DateTimeStyles.NoCurrentDateDefault));
where dateFormat is a System.Globalization.CultureInfo.
This will give me the control to determine whether the dates are in "dd-mm-yyyy", "mm-dd-yyyy", or "yyyy-mm-dd".
re:
//I think that ill-formed data shouldn't be allowed to be inserted in the list in the first place.
I agree, but in my experience, what should happen and what actually happens in a development environment are not the same.
Greetings
Willem Semmelink
|
|
|
|
|
WillemSe wrote:
//I think you meant: create a derived class and add the extended functionality there.
no, what I meant is that I would have liked to modify only ListViewTextSort whilst in the current implementation
Well, what I really meant is that the OOP way is to derive a class, not to modify the base class. Now, in the real world you have the base class code so you can do change it to suit you needs.
WillemSe wrote:
The reason why the date failed on me was probably due to culture information - feeding a date in a non-American date format.
I agree that the predefined sorters for dates and numbers aren't culture friendly. This has to change.
WillemSe wrote:
I agree, but in my experience, what should happen and what actually happens in a development environment are not the same.
This is true, but I don't think it is an excuse for allowing "invalid" data to get in the system. Now, because the date sorter is culture-agnostic, your perfectly valid dates (in you culture) are considered invalid in the sorter. You have two options:- Format the data according to the needs of the sorter and make you users unhappy (not very attractive)
- Change the sorter to accept dates expressed according to different cultural requirements. (This is the obvious and best solution)
This is why I believe that adding code to sorter to validate the dates is not the best solution, making the sorter culture-aware is. I'll check it out and thank you for bringing this to my attention.
There are only 10 kind of people in the world: those who understand binary and those who don't.
|
|
|
|
|
Eddie wrote:
I'll check it out and thank you for bringing this to my attention.
I am happy if I could contribute something. Thanks for pointing out to me the performance implications.
Best wishes
Willem
|
|
|
|
|
I can't seem to get the class to work right if the column width is preset to -1 or -2. It just resizes the column wide enough for the column label. Have you run into this problem
|
|
|
|
|
Great addition. Your listview sorter is easy to use, fast, and exactly what I needed. Thanks!
- Scott
|
|
|
|
|
I'm glad you liked it.
There are only 10 kind of people in the world: those who understand binary and those who don't.
|
|
|
|
|
TextAlign seems to be forced to the Left after you order by a column. Fix within ShowHeaderIcon is something like :
if (columnIndex < 0)
hd.fmt = HDF_LEFT | HDF_STRING | HDF_BITMAP_ON_RIGHT;
else if (list.Columns[columnIndex].TextAlign == HorizontalAlignment.Left)
hd.fmt = HDF_LEFT | HDF_STRING | HDF_BITMAP_ON_RIGHT;
else if (list.Columns[columnIndex].TextAlign == HorizontalAlignment.Center)
hd.fmt = HDF_CENTER | HDF_STRING | HDF_BITMAP_ON_RIGHT;
else
hd.fmt = HDF_RIGHT | HDF_BITMAP_ON_RIGHT | HDF_STRING;
although I'm no expert - I'd prefer it if the bitmap is displayed on the left on a right-aligned column.
JwB
|
|
|
|
|
Yes, it is a bug. I'll fix it as soon as I have a chance (probably this weekend). Your proposed solution works ok, however I would like to make a generic fix: allow the user to set the bitmap alignment, something like a new BitmapAlign property or something.
There are only 10 kind of people in the world: those who understand binary and those who don't.
|
|
|
|
|
That would be useful. I have tried to embed your solution into a class myListView that inherits from the default ListView so that ordering is automatically inherited without the programmer doing any further work (unless he wishes to override the default text-based ordering). This also works fine except the ordering icons no longer work (they seem to interfere with the listview's smallimagelist and largeimagelist). Would be useful if you could extend your solution to this.
JwB
|
|
|
|
|
I wanted to know if anyone else has experience the following. I took the demo and added the following code.
System.Windows.Forms.ListViewItem.ListViewSubItem aSubItem = new System.Windows.Forms.ListViewItem.ListViewSubItem();
for (int i = 1; i <= 8000; i++)
{
ListViewItem item1 = new ListViewItem("item" + i.ToString());
item1.SubItems.Add("A" + i.ToString());
item1.SubItems.Add("B" + i.ToString());
item1.SubItems.Add("C" + i.ToString());
item1.SubItems.Add("D" + i.ToString());
item1.SubItems.Add("E" + i.ToString());
//Add the items to the ListView.
m_list.Items.Add (item1);
}
This code adds 8000 rows to the listview if I add before a sort the add is very quick, but if I sort then add the rows it takes several minutes to add the records.
Thanks
|
|
|
|
|
Hi MAF,
The problem is that the "listView2.ListViewItemSorter" is never set to null again and keeps the last selected sort method in memmory.
I did the following and it solved the problem:
public void Sort(Int32 column, SortOrder order)
{
if(order != SortOrder.None)
{
ListViewTextSort comp = (ListViewTextSort)Activator.CreateInstance (m_comparers[m_column],new Object[]{m_column,order == SortOrder.Ascending } );
listView2.ListViewItemSorter = comp;
}
else
{
listView2.ListViewItemSorter = null;
}
}
Jacques Victor
|
|
|
|
|
Thanks for your answer to this problem. I apologize for "abandoning" this article for a while but I've been really busy with other stuff.
The nice thing about C++ is that only your friends can handle your private parts.
|
|
|
|
|
You have made a very good tool. It's very Great !!!
But I've seen some problems when I use dynamic Listviews.
For example : I have a Listview with 4 columns (Text,Text,Date,Int32).
First, I do a sort on the 3d column (Date)
So in memory I have an array Type[4] (TextSort,TextSort,DateSort and Int32Sort) with a sorting on the Date.
But now, I change dynamicaly the Listview (with a click on a treenode) and now I have only 3 columns (Text,Int32,Text) (I reload a ListViewSortManager with the good Type[])
And when I try to sort the 3d column (wich is a Text now) I've an Exception because the soft try to compare Date (in a first passage).
Why the 'ListViewSortManager control' do a first sort with the old values ? Why he keep in memory the first array type[] with the sort on the dates ?
Thanks (and sorry for my poor english )
|
|
|
|
|
This update looks great. The bitmap now works correctly using theme support and WindowBlinds. The automatically sorting based on column click is working as advertised, however....
I would recommend leaving the sort method exposed. I ran into the following example:
When you clear all the items in the listview, add a new set of items; you may want to specify which column is sorted on by default. Without the sort method exposed, you have no way to specify which column is the default column to be sorted with the new items in the list.
Having the sort method exposed may not be enough though. Might possibly need some sort of reset or be able specify one of three states for the sorting (off, ascending, descending)).
Just some thoughts.
|
|
|
|
|
I just posted an update that implements the modifications you suggested
There are only 10 kind of people in the world: those who understand binary and those who don't.
|
|
|
|
|
Excellent sample. Here's tiny modification you could make to further ease of use...
Why not implement ColumnClicked event handler INSIDE of the sort manager? You could hook this up using the constructor. Then make the Sort method private.
Viola! The sort manager takes care of all sorting... all the user has to do is pass the relevant info during construction and the listview is fully sort-enabled.
Kieran
|
|
|
|
|
Good idea. I look into it.
There are only 10 kind of people in the world: those who understand binary and those who don't.
|
|
|
|
|
This is a very nice improvement, thanks for taking the time to update your code to include the graphics.
I did notice a minor issue with the transparency. Using the standard xp look and feel, when the mouse is over the column the sort triangle's background does not get updated to the highlighted column header's background.
Thanks again,
Carlo
|
|
|
|
|
I noticed the same thing. While converting the code to vb.net, I found how you can correct this by modifying one line and adding a new line.
Inside of GetArrowBitmap, modify the FillRectable that is currently using SystemBrushes.ControlLight to use System.Drawing.Brushes.Magenta. This will be used by the next line.
Inside the constructor (New), when the two images are being aded to the imagelist, add a new line after the Images.Add methods.
m_imgList.TransparentColor = System.Drawing.Color.Magenta
This will allow the image to be displayed using a transparent color, thus removing the block you are seeing. Much cleaner this way. Hope this is helpful.
I've been looking for a simple way to add these up down arrows to the listview for some time now. Cheers to the author.
|
|
|
|
|
This is a good solution to this glitch. I actually just noted it last night when I added XP theme support to my app.
The common control library included with XP has support for arrows, so I think it would be a good idea to detect this situation and act accordingly. What do you guys think? Is it worth it?
There are only 10 kind of people in the world: those who understand binary and those who don't.
|
|
|
|
|
If it's possible to detect; I would say that it would be worth it. That way you would be using the theme generated arrows rather than the built in one. I'm not sure how this would interact with products like WindowBlinds though. If you want, I can test this for you if you do move forward with this.
[update] BTW, the current version is working great with WindowBlinds.
|
|
|
|