ListView Sorting by Column

O

Owen T. Soroke

Using VB.NET

I have a ListView with several columns.

Two columns contain integer values, while the remaining contain string
values.

I am confused as to how I would provide functionality to sort columns
based on the column header the user has clicked in both Ascending and
Descending formats.

If anyone could offer some useful code, or suggestions as how this can
best be accomplished, it would be much appreciated.

Regards,

Owen
 
O

Owen T. Soroke

Thnak you for your suggestions Tom. I will try this out and let you
know how things turned out.

A little more difficult than VS6.

Thanks again,

Owen
 
O

Owen T. Soroke

Thanks Jay,

This will save me having to read and work with multiple languages.

Regards,

Owen
 
T

Tom Spink

I only came across that article today, because I've been doing it today :)

I thought it was a bit long winded. Not 'difficult'. Wrong choice of words
<g>

--
Happy to help,
-- Tom Spink
([email protected])

"Go down with your server"

http://dotnetx.betasafe.com >> On The Mend

Please respond to the newsgroup,
so all can benefit
 
O

Owen T. Soroke

Tom,

I think you're correct in saying this is a bit long winded, when
compared to doing the same in VS6.

Owen
 
J

Jay B. Harlow [MVP - Outlook]

Tom,
I'll give you long winded ;-)

IMHO this is part of the flexibility of OOP... The extra complexity
(indirection) is what gives you the flexibility.

The IComparer interface is actually used in a number of places in .NET. For
example you can use it with Array.Sort & ArrayList.Sort to enable sorting a
collection of objects based on different attributes of the contained
objects. Objects themselves can implement the IComparable interface to allow
the object itself to know how to compare itself with another object. Again
Array.Sort & ArrayList.Sort can sort objects that implement IComparable, if
you do not give them a IComparer object...

Array.Sort only needs to care about IComparer & IComparable. The objects
themselves are enabled to compare each other...

Hope this helps
Jay
 
T

Tom Spink

Hi, I'm aware of where it's used, and I also appreciate OOP and it's
flexibility. I also enjoy complexity, I'm not actually complaining, however,
people who have started VB.NET and had no previous experience of true OOP
may find it daunting.

I also know about IComparable.

--
Happy to help,
-- Tom Spink
([email protected])

"Go down with your server"

http://dotnetx.betasafe.com >> On The Mend

Please respond to the newsgroup,
so all can benefit
 
H

Herfried K. Wagner [MVP]

Hello,

Owen T. Soroke said:
I have a ListView with several columns.

Two columns contain integer values, while the remaining contain string
values.

I am confused as to how I would provide functionality to sort columns
based on the column header the user has clicked in both Ascending and
Descending formats.

C#, but maybe it points you into the right direction:

http://www.codeproject.com/cs/miscctrl/sortedlistview.asp

HTH,
Herfried K. Wagner
 
O

Owen T. Soroke

Thank you Herfried.

This was exactly what I was looking to accomplish. I will now begin
attempting to make this code relevant to my project.

Best Regards,

Owen T. Soroke
 
J

Jay B. Harlow [MVP - Outlook]

Tom,
I've seen your name (here) enough, I'm figured you already knew.

It was included for Owen & lurkers more then you specifically.

Its one of those: do I give the 1 liner and you don't know. Or give the
dissertation and you do know. ;-)

Later
Jay
 
J

Jay B. Harlow [MVP - Outlook]

Owen,
The ListViewColumnSorter class provides that ability (item #8 in the
article).

Actually the following line:
' Compare the two items.
compareResult =
ObjectCompare.Compare(listviewX.SubItems(ColumnToSort).Text,
listviewY.SubItems(ColumnToSort).Text)

The ObjectCompare.Compare call does the actual comparisons.

CaseInsensitiveComparer is one of those objects that knows about IComparable
that I mentioned in my other post. The CaseInsensitiveComparer.Compare
method accepts objects, to it is able to compare other object types. All the
normal .NET data types (Byte, Short, Integer, Long, Single, Double, Decimal,
String, DateTime) implement IComparable, so the CaseInsensitiveComparer
object can compare them.

Hope this helps
Jay

Owen T. Soroke said:
Jay, Tom,

You example previously provided from the MSDN resources includes the
following code:

Dim columnheader As ColumnHeader ' Used for creating column
headers.
Dim listviewitem As ListViewItem ' Used for creating ListView
items.

' Make sure that the view is set to show details.
ListView1.View = View.Details

' Create some ListView items consisting of first and last names.
listviewitem = New ListViewItem("Mike")
listviewitem.SubItems.Add("Nash")
Me.ListView1.Items.Add(listviewitem)

listviewitem = New ListViewItem("Kim")
listviewitem.SubItems.Add("Abercrombie")
Me.ListView1.Items.Add(listviewitem)

listviewitem = New ListViewItem("Sunil")
listviewitem.SubItems.Add("Koduri")
Me.ListView1.Items.Add(listviewitem)

listviewitem = New ListViewItem("Birgit")
listviewitem.SubItems.Add("Seidl")
Me.ListView1.Items.Add(listviewitem)

' Create some column headers for the data.
columnheader = New ColumnHeader()
columnheader.Text = "First Name"
Me.ListView1.Columns.Add(columnheader)

columnheader = New ColumnHeader()
columnheader.Text = "Last Name"
Me.ListView1.Columns.Add(columnheader)

' Loop through and size each column header to fit the column header
text.
For Each columnheader In Me.ListView1.Columns
columnheader.Width = -2
Next

How would you include the ability to sort additional data types
including integer and date/time types using similar code included
above.

Regards,

Owen
<<snip>>
 
C

Cor

Hi Owen,
I did change a litte bit the Microsoft example, because that does not sort
decimals in a value order but in a string order.
This is VB and not C#
It sorts dates, strings and values

Private Sub lsv_ColumnClick(ByVal sender As Object, ByVal e As
System.Windows.Forms.ColumnClickEventArgs) Handles lsv.ColumnClick
lsv.BeginUpdate()
If e.Column <> sortColumn Then
sortColumn = e.Column
lsv.Sorting = SortOrder.Ascending
Else
If lsv.Sorting = SortOrder.Ascending Then
lsv.Sorting = SortOrder.Descending
Else
lsv.Sorting = SortOrder.Ascending
End If
End If
lsv.Sort()
lsv.ListViewItemSorter = New clsListViewSorter(e.Column,
lsv.Sorting)
lsv.EndUpdate()
End Sub



Option Strict On
Imports System.Globalization
Public Class clsListViewSorter
Implements IComparer
Private col As Integer
Private order As SortOrder

Public Sub New()
col = 0
order = SortOrder.Ascending
End Sub

Public Sub New(ByVal column As Integer, ByVal order As SortOrder)
col = column
Me.order = order
End Sub


Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer
Implements System.Collections.IComparer.Compare
Dim returnVal As Integer

Try
Dim firstDate As System.DateTime = DateTime.Parse(CType(x,
ListViewItem).SubItems(col).Text)
Dim secondDate As System.DateTime = DateTime.Parse(CType(y,
ListViewItem).SubItems(col).Text)
returnVal = DateTime.Compare(firstDate, secondDate)
Catch
Try
Dim myComparer As New Comparer(New
CultureInfo(CultureInfo.CurrentCulture.ToString, False))
Dim a As Object = Int64.Parse(CType(x,
ListViewItem).SubItems(col).Text)
Dim b As Object = Int64.Parse(CType(y,
ListViewItem).SubItems(col).Text)
returnVal = myComparer.Compare(a, b)
Catch
returnVal = [String].Compare(CType(x,
ListViewItem).SubItems(col).Text, CType(y, ListViewItem).SubItems(col).Text)
End Try
End Try
If order = SortOrder.Descending Then
returnVal *= -1
End If
Return returnVal
End Function


End Class
 
O

Owen T. Soroke

Good Morning,

I have been able to implement the column sorting code in VB.NET and
have one more question, if I can draw on your expertise.

I am trying to add arrows representing the current position of the
columns as they are sorted. I have tried using ASCII 30 & 31, and 129,
130 & 53, 54 with no luck.

VB.NET seems to display these characters incorrectly.

------- = --------

Example:

' determine what the last sort order was and change it

If lvwIndex.Sorting = SortOrder.Ascending Then
lvwIndex.Sorting = SortOrder.Descending
lvwIndex.Columns.Item(e.Column).Text =_
lvwIndex.Columns.Item(e.Column).Text & Chr(53)

Else
lvwIndex.Sorting = SortOrder.Ascending
lvwIndex.Columns.Item(e.Column).Text =_
lvwIndex.Columns.Item(e.Column).Text & Chr(54)

End if

------- = --------

I have read some C++ and C# code which has the system draw the arrow.
Other than that, I have had no success in finding an easier solution.

Thank you for your prervious suggestions, I hope you can help me with
this one.

Regards,

Owen

Hi Owen,
I did change a litte bit the Microsoft example, because that does not sort
decimals in a value order but in a string order.
This is VB and not C#
It sorts dates, strings and values

Private Sub lsv_ColumnClick(ByVal sender As Object, ByVal e As
System.Windows.Forms.ColumnClickEventArgs) Handles lsv.ColumnClick
lsv.BeginUpdate()
If e.Column <> sortColumn Then
sortColumn = e.Column
lsv.Sorting = SortOrder.Ascending
Else
If lsv.Sorting = SortOrder.Ascending Then
lsv.Sorting = SortOrder.Descending
Else
lsv.Sorting = SortOrder.Ascending
End If
End If
lsv.Sort()
lsv.ListViewItemSorter = New clsListViewSorter(e.Column,
lsv.Sorting)
lsv.EndUpdate()
End Sub



Option Strict On
Imports System.Globalization
Public Class clsListViewSorter
Implements IComparer
Private col As Integer
Private order As SortOrder

Public Sub New()
col = 0
order = SortOrder.Ascending
End Sub

Public Sub New(ByVal column As Integer, ByVal order As SortOrder)
col = column
Me.order = order
End Sub


Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer
Implements System.Collections.IComparer.Compare
Dim returnVal As Integer

Try
Dim firstDate As System.DateTime = DateTime.Parse(CType(x,
ListViewItem).SubItems(col).Text)
Dim secondDate As System.DateTime = DateTime.Parse(CType(y,
ListViewItem).SubItems(col).Text)
returnVal = DateTime.Compare(firstDate, secondDate)
Catch
Try
Dim myComparer As New Comparer(New
CultureInfo(CultureInfo.CurrentCulture.ToString, False))
Dim a As Object = Int64.Parse(CType(x,
ListViewItem).SubItems(col).Text)
Dim b As Object = Int64.Parse(CType(y,
ListViewItem).SubItems(col).Text)
returnVal = myComparer.Compare(a, b)
Catch
returnVal = [String].Compare(CType(x,
ListViewItem).SubItems(col).Text, CType(y, ListViewItem).SubItems(col).Text)
End Try
End Try
If order = SortOrder.Descending Then
returnVal *= -1
End If
Return returnVal
End Function


End Class
 
J

Jay B. Harlow [MVP - Outlook]

Own,
I would consider using one of the Unicode characters, for example: U+25BC
Black Down-Pointing Triangle & U+25B2 Black Up=Pointing Triangle instead. I
used Character Map to find the characters.

Then its a matter of ensuring the font you are using supports those
characters, which means that Win98 & Win ME may not support the Unicode
character. Arial supports the characters, Microsoft Sans Serif (the default)
does not.

Hope this helps
Jay

Owen T. Soroke said:
Good Morning,

I have been able to implement the column sorting code in VB.NET and
have one more question, if I can draw on your expertise.

I am trying to add arrows representing the current position of the
columns as they are sorted. I have tried using ASCII 30 & 31, and 129,
130 & 53, 54 with no luck.

VB.NET seems to display these characters incorrectly.

------- = --------

Example:

' determine what the last sort order was and change it

If lvwIndex.Sorting = SortOrder.Ascending Then
lvwIndex.Sorting = SortOrder.Descending
lvwIndex.Columns.Item(e.Column).Text =_
lvwIndex.Columns.Item(e.Column).Text & Chr(53)

Else
lvwIndex.Sorting = SortOrder.Ascending
lvwIndex.Columns.Item(e.Column).Text =_
lvwIndex.Columns.Item(e.Column).Text & Chr(54)

End if

------- = --------

I have read some C++ and C# code which has the system draw the arrow.
Other than that, I have had no success in finding an easier solution.

Thank you for your prervious suggestions, I hope you can help me with
this one.

Regards,

Owen

Hi Owen,
I did change a litte bit the Microsoft example, because that does not sort
decimals in a value order but in a string order.
This is VB and not C#
It sorts dates, strings and values

Private Sub lsv_ColumnClick(ByVal sender As Object, ByVal e As
System.Windows.Forms.ColumnClickEventArgs) Handles lsv.ColumnClick
lsv.BeginUpdate()
If e.Column <> sortColumn Then
sortColumn = e.Column
lsv.Sorting = SortOrder.Ascending
Else
If lsv.Sorting = SortOrder.Ascending Then
lsv.Sorting = SortOrder.Descending
Else
lsv.Sorting = SortOrder.Ascending
End If
End If
lsv.Sort()
lsv.ListViewItemSorter = New clsListViewSorter(e.Column,
lsv.Sorting)
lsv.EndUpdate()
End Sub



Option Strict On
Imports System.Globalization
Public Class clsListViewSorter
Implements IComparer
Private col As Integer
Private order As SortOrder

Public Sub New()
col = 0
order = SortOrder.Ascending
End Sub

Public Sub New(ByVal column As Integer, ByVal order As SortOrder)
col = column
Me.order = order
End Sub


Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer
Implements System.Collections.IComparer.Compare
Dim returnVal As Integer

Try
Dim firstDate As System.DateTime = DateTime.Parse(CType(x,
ListViewItem).SubItems(col).Text)
Dim secondDate As System.DateTime = DateTime.Parse(CType(y,
ListViewItem).SubItems(col).Text)
returnVal = DateTime.Compare(firstDate, secondDate)
Catch
Try
Dim myComparer As New Comparer(New
CultureInfo(CultureInfo.CurrentCulture.ToString, False))
Dim a As Object = Int64.Parse(CType(x,
ListViewItem).SubItems(col).Text)
Dim b As Object = Int64.Parse(CType(y,
ListViewItem).SubItems(col).Text)
returnVal = myComparer.Compare(a, b)
Catch
returnVal = [String].Compare(CType(x,
ListViewItem).SubItems(col).Text, CType(y, ListViewItem).SubItems(col).Text)
End Try
End Try
If order = SortOrder.Descending Then
returnVal *= -1
End If
Return returnVal
End Function


End Class
 
O

Owen T. Soroke

Thanks for your response Jay:

When issuing a unicode value to display, I have tried using the
Char.GetUnicodeCategory(). This isn't working.

How can I display these unicode values in VB.NET code?

Thanks,

Owen

Own,
I would consider using one of the Unicode characters, for example: U+25BC
Black Down-Pointing Triangle & U+25B2 Black Up=Pointing Triangle instead. I
used Character Map to find the characters.

Then its a matter of ensuring the font you are using supports those
characters, which means that Win98 & Win ME may not support the Unicode
character. Arial supports the characters, Microsoft Sans Serif (the default)
does not.

Hope this helps
Jay

Owen T. Soroke said:
Good Morning,

I have been able to implement the column sorting code in VB.NET and
have one more question, if I can draw on your expertise.

I am trying to add arrows representing the current position of the
columns as they are sorted. I have tried using ASCII 30 & 31, and 129,
130 & 53, 54 with no luck.

VB.NET seems to display these characters incorrectly.

------- = --------

Example:

' determine what the last sort order was and change it

If lvwIndex.Sorting = SortOrder.Ascending Then
lvwIndex.Sorting = SortOrder.Descending
lvwIndex.Columns.Item(e.Column).Text =_
lvwIndex.Columns.Item(e.Column).Text & Chr(53)

Else
lvwIndex.Sorting = SortOrder.Ascending
lvwIndex.Columns.Item(e.Column).Text =_
lvwIndex.Columns.Item(e.Column).Text & Chr(54)

End if

------- = --------

I have read some C++ and C# code which has the system draw the arrow.
Other than that, I have had no success in finding an easier solution.

Thank you for your prervious suggestions, I hope you can help me with
this one.

Regards,

Owen

Hi Owen,
I did change a litte bit the Microsoft example, because that does not sort
decimals in a value order but in a string order.
This is VB and not C#
It sorts dates, strings and values

Private Sub lsv_ColumnClick(ByVal sender As Object, ByVal e As
System.Windows.Forms.ColumnClickEventArgs) Handles lsv.ColumnClick
lsv.BeginUpdate()
If e.Column <> sortColumn Then
sortColumn = e.Column
lsv.Sorting = SortOrder.Ascending
Else
If lsv.Sorting = SortOrder.Ascending Then
lsv.Sorting = SortOrder.Descending
Else
lsv.Sorting = SortOrder.Ascending
End If
End If
lsv.Sort()
lsv.ListViewItemSorter = New clsListViewSorter(e.Column,
lsv.Sorting)
lsv.EndUpdate()
End Sub



Option Strict On
Imports System.Globalization
Public Class clsListViewSorter
Implements IComparer
Private col As Integer
Private order As SortOrder

Public Sub New()
col = 0
order = SortOrder.Ascending
End Sub

Public Sub New(ByVal column As Integer, ByVal order As SortOrder)
col = column
Me.order = order
End Sub


Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer
Implements System.Collections.IComparer.Compare
Dim returnVal As Integer

Try
Dim firstDate As System.DateTime = DateTime.Parse(CType(x,
ListViewItem).SubItems(col).Text)
Dim secondDate As System.DateTime = DateTime.Parse(CType(y,
ListViewItem).SubItems(col).Text)
returnVal = DateTime.Compare(firstDate, secondDate)
Catch
Try
Dim myComparer As New Comparer(New
CultureInfo(CultureInfo.CurrentCulture.ToString, False))
Dim a As Object = Int64.Parse(CType(x,
ListViewItem).SubItems(col).Text)
Dim b As Object = Int64.Parse(CType(y,
ListViewItem).SubItems(col).Text)
returnVal = myComparer.Compare(a, b)
Catch
returnVal = [String].Compare(CType(x,
ListViewItem).SubItems(col).Text, CType(y, ListViewItem).SubItems(col).Text)
End Try
End Try
If order = SortOrder.Descending Then
returnVal *= -1
End If
Return returnVal
End Function


End Class
 
O

Owen T. Soroke

Jay, Tom, Herfried.... Thanks once again.

As requested, this is the article that has been posted. It believe it
is written in C++.

-------------------------- ========= ------------------------

The list view control does not provide any visual feedback on whether
the list is sorted.

To give feedback to the users, we can use the owner draw feature of
the header control and display a triangle pointing downwards or
pointing updwards, indicating whether the list is sorted in the
ascending or the descending order.

This, of course, is applicable only if the list view control is in the
report view mode.

Since we have to use an owner drawn header control, we will need to
derive a class from CHeaderCtrl and add the functionality in this
class. Here are the steps involved.


Step 1: Derive class from CHeaderCtrl

If you don't have a class derived from CHeaderCtrl, derive one now.
You can use the Class Wizard to create one for you. I used the name
CMyHeaderCtrl for the derived class.

Step 2: Add member variables

Add member variables in CMyHeaderCtrl to track the column that is
sorted and the sorting order. These variables are declared as
protected members and we will provide a function to set them.
protected:
int m_nSortCol;
BOOL m_bSortAsc;

Step 3: Initialize the variables in the constructor

Initialize the m_nSortCol variable to -1 in the constructor. This
indicates that the list is not sorted.
CMyHeaderCtrl:: CMyHeaderCtrl()
{
m_nSortCol = -1;
}

Step 4: Add function SetSortImage()

Add a function SetSortImage() to the CMyHeaderCtrl class. This is the
function that will be used to set the sort indicator. The
SetSortImage() function takes the column number as an argument and
also a boolean value to indicate whether it is sorted in the ascending
order or in the descending order.

After setting the internal variables, the function set the header item
to owner drawn. This will ensure that the DrawItem() function will get
called. The function then invalidates the header control so that any
previous sort indicator is removed and the new one is displayed.
int CMyHeaderCtrl::SetSortImage( int nCol, BOOL bAsc )
{
int nPrevCol = m_nSortCol;

m_nSortCol = nCol;
m_bSortAsc = bAsc;

// Change the item to ownder drawn
HD_ITEM hditem;

hditem.mask = HDI_FORMAT;
GetItem( nCol, &hditem );
hditem.fmt |= HDF_OWNERDRAW;
SetItem( nCol, &hditem );

// Invalidate header control so that it gets redrawn
Invalidate();
return nPrevCol;
}

Step 5: Override DrawItem()

The DrawItem() is where the sort indicator actually gets drawn.
Besides drawing the sort triangle, this function is now also
responsible for drawing the column label itself. The DrawItem()
function is called for each item in the header control that has the
HDF_OWNERDRAW format.

These are the step we take in the DrawItem() function to draw the
column label and the triangular image to indicate the sort order:

1. Attach the device context handle passed in through the
argument to a CDC object for easier device context handling. The
handle is detached from the CDC object before the function returns. If
we did not detach the handle then the DC would be released when the
CDC object is destroyed.

2. We save the DC and change the clipping region so that all the
updates are contrained within the header item for which the DrawItem()
function is called. The device context is restored before the function
returns.

3. We compute the offset used when drawing the label and the sort
triangle. The offset is used to leave a margin around the label and is
equal to twice the width of a space character.

4. We determine the format to be used when drawing the column
label. Since the column label can be aligned left, center or right, we
have to choose an appropriate format for the DrawText() function. You
will also notice the flag DT_END_ELLIPSIS. This tells the DrawText()
function that if the text doesn't fit with the rectangle specified,
then the text should be shortened and three dots appended to the text
so that the result fits within the rectangle.

5. We next adjust the rectangle within which the label will be
drawn and then draw the lable using DrawText().

6. Finally we draw the triangle to indicate the sort order. We
use two different color to draw the triangle so that it matches the
other GUI elements in Widnows. The COLOR_3DHILIGHT color is used for
edges facing the light source, and the COLOR_3DSHADOW color is used
for the shadow.

void CMyHeaderCtrl::DrawItem( LPDRAWITEMSTRUCT lpDrawItemStruct )
{
CDC dc;

dc.Attach( lpDrawItemStruct->hDC );

// Get the column rect
CRect rcLabel( lpDrawItemStruct->rcItem );

// Save DC
int nSavedDC = dc.SaveDC();

// Set clipping region to limit drawing within column
CRgn rgn;
rgn.CreateRectRgnIndirect( &rcLabel );
dc.SelectObject( &rgn );
rgn.DeleteObject();

// Draw the background
dc.FillRect(rcLabel, &CBrush:):GetSysColor(COLOR_3DFACE)));

// Labels are offset by a certain amount
// This offset is related to the width of a space character
int offset = dc.GetTextExtent(_T(" "), 1 ).cx*2;


// Get the column text and format
TCHAR buf[256];
HD_ITEM hditem;

hditem.mask = HDI_TEXT | HDI_FORMAT;
hditem.pszText = buf;
hditem.cchTextMax = 255;

GetItem( lpDrawItemStruct->itemID, &hditem );

// Determine format for drawing column label
UINT uFormat = DT_SINGLELINE | DT_NOPREFIX | DT_NOCLIP
| DT_VCENTER |
DT_END_ELLIPSIS ;

if( hditem.fmt & HDF_CENTER)
uFormat |= DT_CENTER;
else if( hditem.fmt & HDF_RIGHT)
uFormat |= DT_RIGHT;
else
uFormat |= DT_LEFT;

// Adjust the rect if the mouse button is pressed on it
if( lpDrawItemStruct->itemState == ODS_SELECTED )
{
rcLabel.left++;
rcLabel.top += 2;
rcLabel.right++;
}

// Adjust the rect further if Sort arrow is to be displayed
if( lpDrawItemStruct->itemID == (UINT)m_nSortCol )
{
rcLabel.right -= 3 * offset;
}

rcLabel.left += offset;
rcLabel.right -= offset;

// Draw column label
if( rcLabel.left < rcLabel.right )
dc.DrawText(buf,-1,rcLabel, uFormat);

// Draw the Sort arrow
if( lpDrawItemStruct->itemID == (UINT)m_nSortCol )
{
CRect rcIcon( lpDrawItemStruct->rcItem );

// Set up pens to use for drawing the triangle
CPen penLight(PS_SOLID, 1,
GetSysColor(COLOR_3DHILIGHT));
CPen penShadow(PS_SOLID, 1,
GetSysColor(COLOR_3DSHADOW));
CPen *pOldPen = dc.SelectObject( &penLight );

if( m_bSortAsc )
{
// Draw triangle pointing upwards
dc.MoveTo( rcIcon.right - 2*offset, offset-1);
dc.LineTo( rcIcon.right - 3*offset/2,
rcIcon.bottom - offset );
dc.LineTo( rcIcon.right - 5*offset/2-2,
rcIcon.bottom - offset );
dc.MoveTo( rcIcon.right - 5*offset/2-1,
rcIcon.bottom - offset-1 );

dc.SelectObject( &penShadow );
dc.LineTo( rcIcon.right - 2*offset, offset-2);
}
else
{
// Draw triangle pointing downwords
dc.MoveTo( rcIcon.right - 3*offset/2,
offset-1);
dc.LineTo( rcIcon.right - 2*offset-1,
rcIcon.bottom - offset + 1 );
dc.MoveTo( rcIcon.right - 2*offset-1,
rcIcon.bottom - offset );

dc.SelectObject( &penShadow );
dc.LineTo( rcIcon.right - 5*offset/2-1, offset
-1 );
dc.LineTo( rcIcon.right - 3*offset/2, offset
-1);
}

// Restore the pen
dc.SelectObject( pOldPen );
}

// Restore dc
dc.RestoreDC( nSavedDC );

// Detach the dc before returning
dc.Detach();
}

Step 6: Add member variable for header control in list view class

Now that we are done with the CMyHeaderCtrl class, we have to add a
member to the CListCtrl or the CListView derived class so that we can
access the extended functionality. Add a protected member.
protected:

CMyHeaderCtrl m_headerctrl;

Step 7: Subclass the header control

We have to sub-class the header control so that the DrawItem()
function in CMyHeaderCtrl can get called. If you are using a CListView
derived class, you can place the sub-classing code in
OnInitialUpdate(). If you are using a CListCtrl derived class, then
put the code in PreSubclassWindow(). In either case, make sure you
call the base class version of the function before subclassing the
header control. If the listview control was not created in the report
view mode, then you have to change the style of control before trying
the subclass the control. You can use ModifyStyle() for this. The
reason why we need to change the style to the report view mode is that
the header control is created only when the control first taken to the
report view mode.

void CMyListCtrl::preSubclassWindow()
{
CListCtrl::preSubclassWindow();

// Add initialization code
m_headerctrl.SubclassWindow( ::GetDlgItem(m_hWnd,0) );
}

Step 8: Use SetSortImage() to indicate sort order

Now you are all set to add the sort order indicator. Whenever you sort
the list view control, call the CMyHeaderCtrl::SetSortImage() function
with the column number on which the list is sorted and the order of
sorting. E.g.

m_headerctrl.SetSortImage( nCol, bAscending );

-------------------------- ========= ------------------------

Hope you can help,

Regards,

Owen





Thanks for your response Jay:

When issuing a unicode value to display, I have tried using the
Char.GetUnicodeCategory(). This isn't working.

How can I display these unicode values in VB.NET code?

Thanks,

Owen

Own,
I would consider using one of the Unicode characters, for example: U+25BC
Black Down-Pointing Triangle & U+25B2 Black Up=Pointing Triangle instead. I
used Character Map to find the characters.

Then its a matter of ensuring the font you are using supports those
characters, which means that Win98 & Win ME may not support the Unicode
character. Arial supports the characters, Microsoft Sans Serif (the default)
does not.

Hope this helps
Jay

Owen T. Soroke said:
Good Morning,

I have been able to implement the column sorting code in VB.NET and
have one more question, if I can draw on your expertise.

I am trying to add arrows representing the current position of the
columns as they are sorted. I have tried using ASCII 30 & 31, and 129,
130 & 53, 54 with no luck.

VB.NET seems to display these characters incorrectly.

------- = --------

Example:

' determine what the last sort order was and change it

If lvwIndex.Sorting = SortOrder.Ascending Then
lvwIndex.Sorting = SortOrder.Descending
lvwIndex.Columns.Item(e.Column).Text =_
lvwIndex.Columns.Item(e.Column).Text & Chr(53)

Else
lvwIndex.Sorting = SortOrder.Ascending
lvwIndex.Columns.Item(e.Column).Text =_
lvwIndex.Columns.Item(e.Column).Text & Chr(54)

End if

------- = --------

I have read some C++ and C# code which has the system draw the arrow.
Other than that, I have had no success in finding an easier solution.

Thank you for your prervious suggestions, I hope you can help me with
this one.

Regards,

Owen


Hi Owen,
I did change a litte bit the Microsoft example, because that does not sort
decimals in a value order but in a string order.
This is VB and not C#
It sorts dates, strings and values

Private Sub lsv_ColumnClick(ByVal sender As Object, ByVal e As
System.Windows.Forms.ColumnClickEventArgs) Handles lsv.ColumnClick
lsv.BeginUpdate()
If e.Column <> sortColumn Then
sortColumn = e.Column
lsv.Sorting = SortOrder.Ascending
Else
If lsv.Sorting = SortOrder.Ascending Then
lsv.Sorting = SortOrder.Descending
Else
lsv.Sorting = SortOrder.Ascending
End If
End If
lsv.Sort()
lsv.ListViewItemSorter = New clsListViewSorter(e.Column,
lsv.Sorting)
lsv.EndUpdate()
End Sub



Option Strict On
Imports System.Globalization
Public Class clsListViewSorter
Implements IComparer
Private col As Integer
Private order As SortOrder

Public Sub New()
col = 0
order = SortOrder.Ascending
End Sub

Public Sub New(ByVal column As Integer, ByVal order As SortOrder)
col = column
Me.order = order
End Sub


Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer
Implements System.Collections.IComparer.Compare
Dim returnVal As Integer

Try
Dim firstDate As System.DateTime = DateTime.Parse(CType(x,
ListViewItem).SubItems(col).Text)
Dim secondDate As System.DateTime = DateTime.Parse(CType(y,
ListViewItem).SubItems(col).Text)
returnVal = DateTime.Compare(firstDate, secondDate)
Catch
Try
Dim myComparer As New Comparer(New
CultureInfo(CultureInfo.CurrentCulture.ToString, False))
Dim a As Object = Int64.Parse(CType(x,
ListViewItem).SubItems(col).Text)
Dim b As Object = Int64.Parse(CType(y,
ListViewItem).SubItems(col).Text)
returnVal = myComparer.Compare(a, b)
Catch
returnVal = [String].Compare(CType(x,
ListViewItem).SubItems(col).Text, CType(y, ListViewItem).SubItems(col).Text)
End Try
End Try
If order = SortOrder.Descending Then
returnVal *= -1
End If
Return returnVal
End Function


End Class
 
J

Jay B. Harlow [MVP - Outlook]

Owen,
The easiest way is to "type" the character into your source file! Again you
can use Character Map (Under Start - Programs - Accessories - System Tools)
to find the key sequence to be able to 'type' the character or I normally
just select, copy from Character Map, then paste into my source. Of course
with these characters follow the instructions that VS.NET gives you about
saving your source with encoding.

The other option is if you know the ordinal value of the character use ChrW
to convert the ordinal value to a Char.

Black Down-Pointing Triangle
Black Up-Pointing Triangle
Note use ChrW to get Unicode characters, as Chr does a conversion based on
the code page for the current thread.

Hope this helps
Jay

Owen T. Soroke said:
Thanks for your response Jay:

When issuing a unicode value to display, I have tried using the
Char.GetUnicodeCategory(). This isn't working.

How can I display these unicode values in VB.NET code?

Thanks,

Owen
<<snip>>
 
O

Owen T. Soroke

Thanks Jay,

I will try the copy / paste solution to this question.

I am also looking at the possiblilty of having the system draw the
triangles for graphical display.

Failing that, I will use the Unicode character solution.

Regards,

Owen T. Soroke
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top