使用vb.net 對 Windows Form 按列排序 ListView 項目
2024-07-10 13:00:44
供稿:網友
菜鳥學堂:
使用 windows form 按列排序 listview 項目
摘要: 說明如何根據所單擊的列在 microsoft .net 中的 listview 控件提供項目排序。
簡介
listview 控件是顯示文件系統信息和顯示 xml 或數據庫數據的非常好的方式。listview 控件通常用于顯示表示項目以及項目文本的圖形圖標。此外,listview 控件還可以用于顯示有關子項目中項目的其他信息。例如,如果 listview 控件顯示一列文件,您可以配置 listview 控件來顯示作為子項目的諸如文件大小和屬性的詳細信息。要顯示 listview 控件中的子項目信息,必須將 view 屬性設置為 view.details。此外,您必須創建 columnheader 對象并將這些對象分配給 listview 控件的 columns 屬性。在設置這些屬性后,項目以行和列格式進行顯示,類似于 datagrid 控件。以這種方式顯示項目的能力使 listview 控件為從任意類型的數據源顯示數據提供了快速、簡便的解決方案。
對 listview 控件進行排序是通過使用 listview 的 sorting 屬性而提供的。這使您可以定義要應用到項目的排序類型。如果您只想按項目排序,這是一個非常好的功能。如果您要按子項目排序,必須使用 listview 控件的自定義排序功能。本文將說明如何在 listview 控件中執行自定義排序,以及在排序時如何處理特殊的數據類型條件。
listview 控件的自定義排序功能
listview 控件提供了您可以使用排序的功能,而不是由 sorting 屬性提供。當 listview 控件使用 sorting 屬性排序項目時,它使用一個實現 system.collections.icomparer 接口的類。這個類提供用于排序每個項目的排序功能。為了按子項目進行排序,您必須創建自己的類,來實現反過來可以實現 listview 控件所需排序的 icomparer 接口。該類利用構造函數進行定義,該構造函數可以指定 listview 控件排序所用的列。在您創建這個類后(通常將其作為窗體的嵌套類),您可以創建該類的一個實例,并將其分配到 listview 的 listviewitemsorter 屬性。當調用 sort 方法時,這會確定 listview 控件將要使用的自定義排序類。sort 方法執行 listview 項目的實際排序。
升序排序
以下部分提供的基本示例說明了在 listview 控件中基于其子項目的排序。該示例說明了以升序排序 listview 控件中的項目。升序排序或降序排序將會在本文的稍后部分進行說明。此處的目標就是說明在 listview 控件中進行自定義排序的基本要求。
初始化控件
如果要開始,請創建 listview 控件的實例,并將其添加到窗體中。在控件位于窗體上后,使用 items 屬性將項目添加到 listview 控件。您可以添加任意多的項目,只要確保每個項目的文本都是唯一的。在您創建項目時,為每個項目添加兩個子項目。第一個子項目應該包含數字信息,第二個子項目包含日期信息。下面的表格示例說明該信息在 listview 控件中可能如何顯示。
項目
子項目 1
子項目 2
alpha
1.0
4/5/1945
charlie
3.5
1/9/1920
bravo
2.4
12/8/1930
創建兩個 columnheader 對象,并將它們分配到 listview 控件的 columns 屬性中。將 view 屬性設置為 view.details。
處理 columnclick 事件
為了確定按哪個子項目集進行排序,您需要了解用戶何時單擊某個子項目的列標題。為此,您需要為 listview 的 columnclick 事件創建一個事件處理方法。將事件處理方法作為窗體中的一個成員,并確保它包含的簽名相似于下面代碼示例所顯示的簽名。
'visual basic
private sub listview1_columnclick(sender as object, e as system.windows.forms.columnclickeventargs)
end sub
//c#
private void listview1_columnclick(object sender, system.windows.forms.columnclickeventargs e)
{
}
通過向窗體的構造函數中添加代碼,將事件處理方法連接到 listview 控件,如下面的示例所示。
'visual basic
addhandler listview1.columnclick, addressof me.listview1_columnclick
//c#
this.listview1.columnclick += new system.windows.forms.columnclickeventhandler(this.listview1_columnclick);
將下面的代碼添加到用于 columnclick 事件的事件處理方法。
'visual basic
' set the listviewitemsorter property to a new listviewitemcomparer
' object.
me.listview1.listviewitemsorter = new listviewitemcomparer(e.column)
' call the sort method to manually sort.
listview1.sort()
//c#
// set the listviewitemsorter property to a new listviewitemcomparer
// object.
this.listview1.listviewitemsorter = new listviewitemcomparer(e.column);
// call the sort method to manually sort.
listview1.sort();
添加到事件處理方法的代碼會利用 listviewitemcomparer 類(在下一部分中定義)的新實例來設置 listview 控件的 listviewitemsorter 屬性,然后分配要單擊的列。被單擊的列作為事件參數的組成部分進行傳遞。在設置 listviewitemsorter 屬性后,調用 sort 方法來執行手動排序。
創建 listviewitemcomparer 類
正如前面提到的那樣,在 listview 控件中進行自定義排序的關鍵在于創建實現 system.collections.icomparer 接口的類。就是這個類為項目提供排序。對于這個示例,定義了名為 listviewitemcomparer 的類,并且將其添加為窗體的嵌套類。listviewitemcomparer 執行傳遞到其構造函數的指定列的基本升序排序。將以下類定義添加到 form 類并確保它在窗體內是正確嵌套的。
'visual basic
' implements the manual sorting of items by column.
class listviewitemcomparer
implements icomparer
private col as integer
public sub new()
col = 0
end sub
public sub new(column as integer)
col = column
end sub
public function compare(x as object, y as object) as integer _
implements system.collections.icomparer.compare
dim returnval as integer = -1
returnval = [string].compare(ctype(x, _
listviewitem).subitems(col).text, _
ctype(y, listviewitem).subitems(col).text)
return returnval
end function
end class
//c#
// implements the manual sorting of items by column.
class listviewitemcomparer : icomparer {
private int col;
public listviewitemcomparer() {
col=0;
}
public listviewitemcomparer(int column)
{
col=column;
}
public int compare(object x, object y)
{
int returnval = -1;
returnval = string.compare(((listviewitem)x).subitems[col].text,
((listviewitem)y).subitems[col].text);
return returnval;
}
}
以一個名為 compare 的 icomparer 接口的必要方法執行排序。這個方法將兩個對象作為參數,而參數會包含要進行比較的兩個項目。當在 listview 控件的 columnclick 事件處理方法中調用 sort 方法時,sort 方法會使用已定義并已分配到 listviewitemsorter 屬性的 listviewitemcomparer 對象,并且調用其 compare 方法。創建 listviewitemcomparer 對象后,分配給它被單擊的列的索引。該列的索引用于從需要進行排序的列中訪問子項目。然后,將子項目傳遞到 string.compare 方法,該方法比較項目并返回三個結果中的一個。如果 x 參數中的項目小于 y 參數中的項目,則返回一個小于零的值。如果兩個項目相同,則返回零。最后,如果 x 參數中的值大于 y 參數中的值,則返回一個大于零的值。compare 方法返回的值傳遞回 sort 方法,這確定正在比較的每個項目在列中的位置。sort 方法可以根據在所選擇列中排序所有子項目的需要對 compare 方法調用任意多次。
前面的示例就完成了。如果您運行該示例并單擊 listview 控件的列標題,項目將會按字母順序或數字順序進行適當地排序。唯一不能正確排序的列就是包含日期信息的列。稍后本文將介紹排序日期列。
這個示例說明了在 listview 控件中執行基本手動項目排序所需要的基本要素。接下來的部分會擴展這個示例,提供升序和降序排序功能。
升序或降序排序
listview 控件的用戶將期望具有同時以升序和降序排序項目的功能。為了實現這個功能,需要對前面的示例進行某些改動,以便使 compare 方法可以確定要排序的項目。
對窗體的更改
通常情況下,要在升序和降序排序之間切換,您要多次單擊列標題。用戶期望如果他們單擊列標題,排序將會發生,隨后再次單擊將更改排序順序。前面的代碼示例需要能夠確定何時多次單擊列。為此,您可以將一個私有整數變量添加到 form 類。這個變量存儲上一次單擊的列。columnclick 事件處理方法將使用這個變量來比較上一次的列與當前單擊的列,并確定它們是否相同。將以下成員定義添加到 form 類中。
'visual basic
dim sortcolumn as integer = -1
//c#
private int sortcolumn = -1;
columnclick 事件處理方法的更改
在前面的示例中定義的 columnclick 事件處理方法需要進行修改,以便跟蹤單擊過的列和當前排序順序。添加以下代碼以替換在前面的示例中創建的 columnclick 事件處理方法中的代碼。
'visual basic
private sub listview1_columnclick(sender as object, e as
system.windows.forms.columnclickeventargs)
' determine whether the column is the same as the last column clicked.
if e.column <> sortcolumn then
' set the sort column to the new column.
sortcolumn = e.column
' set the sort order to ascending by default.
listview1.sorting = sortorder.ascending
else
' determine what the last sort order was and change it.
if listview1.sorting = sortorder.ascending then
listview1.sorting = sortorder.descending
else
listview1.sorting = sortorder.ascending
end if
end if
' call the sort method to manually sort.
listview1.sort()
' set the listviewitemsorter property to a new listviewitemcomparer
' object.
listview1.listviewitemsorter = new listviewitemcomparer(e.column, _
listview1.sorting)
end sub
//c#
private void listview1_columnclick(object sender,
system.windows.forms.columnclickeventargs e)
{
// determine whether the column is the same as the last column clicked.
if (e.column != sortcolumn)
{
// set the sort column to the new column.
sortcolumn = e.column;
// set the sort order to ascending by default.
listview1.sorting = sortorder.ascending;
}
else
{
// determine what the last sort order was and change it.
if (listview1.sorting == sortorder.ascending)
listview1.sorting = sortorder.descending;
else
listview1.sorting = sortorder.ascending;
}
// call the sort method to manually sort.
listview1.sort();
// set the listviewitemsorter property to a new listviewitemcomparer
// object.
this.listview1.listviewitemsorter = new listviewitemcomparer(e.column,
listview1.sorting);
}
該代碼在設置 listviewitemsorter 屬性和調用 sort 方法之前添加了一些邏輯。增加的代碼確定當前單擊的項目與上一次已經單擊過的列是否相同。如果不同,就會設置 sortcolumn 變量,并且將 sortorder.ascending 值分配給 sorting 屬性。如果 sortcolumn 變量和當前單擊的列相同,則 sorting 屬性將更改為相反的排序順序。在這個示例中,sorting 屬性用作定義項目排序順序的方法。因為您在使用自定義的比較程序類來排序項目,所以設置 sorting 屬性對排序操作沒有任何影響。在該示例中,它只是簡單地用作一個變量。
在指定排序順序后,對 columnclick 事件處理方法代碼的唯一其他更改就是將附加參數值添加到 listviewitemcomparer 對象的創建中,而該對象是您要分配到 listviewitemsorter 屬性的對象。正如這個示例的后面部分所示,listviewitemcomparer 類包含一個指定排序順序的新參數。您使用 listview.sorting 屬性的值,將值分配給該參數。
listviewitemcomparer 類的更改
使該示例可以以升序或降序進行排序的上面的一系列更改就是對 listviewitemcomparer 類的更改。增加的代碼將執行以兩種排序模式之一比較項目所需的邏輯。添加以下代碼以替換在前面的示例中為 listviewitemcomparer 定義的代碼。
'visual basic
' implements the manual sorting of items by columns.
class listviewitemcomparer
implements icomparer
private col as integer
private order as sortorder
public sub new()
col = 0
order = sortorder.ascending
end sub
public sub new(column as integer, order as sortorder)
col = column
me.order = order
end sub
public function compare(x as object, y as object) as integer _
implements system.collections.icomparer.compare
dim returnval as integer = -1
returnval = [string].compare(ctype(x, _
listviewitem).subitems(col).text, _
ctype(y, listviewitem).subitems(col).text)
' determine whether the sort order is descending.
if order = sortorder.descending then
' invert the value returned by string.compare.
returnval *= -1
end if
return returnval
end function
end class
//c#
// implements the manual sorting of items by columns.
class listviewitemcomparer : icomparer {
private int col;
private sortorder order;
public listviewitemcomparer() {
col=0;
order = sortorder.ascending;
}
public listviewitemcomparer(int column, sortorder order)
{
col=column;
this.order = order;
}
public int compare(object x, object y)
{
int returnval= -1;
returnval = string.compare(((listviewitem)x).subitems[col].text,
((listviewitem)y).subitems[col].text);
// determine whether the sort order is descending.
if(order == sortorder.descending)
// invert the value returned by string.compare.
returnval *= -1
return returnval;
}
}33
該代碼將排序順序參數添加到構造函數,并且創建一個用于存儲該值的私有變量。compare 方法的代碼已更改,以便確定排序順序是否為降序。如果是,則將string.compare 方法的返回值乘以 -1 以更改該值,這樣 compare 方法返回的值就與 string.compare 返回的值相反。
運行該代碼并單擊列。該列以升序順序進行排列。單擊相同的列,列就會以降序進行排序。同樣,日期列沒有正確的排序,因為它被存儲為一個字符串而不是日期。在接下來的部分中,通過添加功能來按日期或者按字符串進行排序(這取決于數據的類型),從而完成該示例。
排序日期
作為項目而置于 listview 控件中的數據顯示為文本并以文本的形式進行存儲。這使得使用 icomparer 類中的 string.compare 方法進行排序變得非常簡單。string.compare 可以對字母字符和數字進行排序。但是,使用 string.compare 不能對特定數據類型進行正確排序,例如日期和時間信息。因此,system.datetime 結構具有一個與 string 類相同作用的 compare 方法。這個方法可以用來基于時間順序執行相同類型的排序。在本部分中,您只需修改 compare 方法就可以正確排序日期。
添加以下代碼以替換為 compare 方法(該方法是前面示例中定義的 listviewitemcomparer 類的方法)而定義的代碼。
'visual basic
public function compare(byval x as object, byval y as object) as
integer implements system.collections.icomparer.compare
dim returnval as integer
' determine whether the type being compared is a date type.
try
' parse the two objects passed as a parameter as a datetime.
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)
' compare the two dates.
returnval = datetime.compare(firstdate, seconddate)
' if neither compared object has a valid date format,
' compare as a string.
catch
' compare the two items as a string.
returnval = [string].compare(ctype(x, _
listviewitem).subitems(col).text, ctype(y,listviewitem).subitems(col).text)
end try
' determine whether the sort order is descending.
if order = sortorder.descending then
' invert the value returned by string.compare.
returnval *= -1
end if
return returnval
end function
//c#
public int compare(object x, object y)
{
int returnval;
// determine whether the type being compared is a date type.
try
{
// parse the two objects passed as a parameter as a datetime.
system.datetime firstdate =
datetime.parse(((listviewitem)x).subitems[col].text);
system.datetime seconddate =
datetime.parse(((listviewitem)y).subitems[col].text);
// compare the two dates.
returnval = datetime.compare(firstdate, seconddate);
}
// if neither compared object has a valid date format, compare
// as a string.
catch
{
// compare the two items as a string.
returnval = string.compare(((listviewitem)x).subitems[col].text,
((listviewitem)y).subitems[col].text);
}
// determine whether the sort order is descending.
if (order == sortorder.descending)
// invert the value returned by string.compare.
returnval *= -1;
return returnval;
}
通過將 x 和 y 參數存放到 datetime 對象中,啟動已添加用于替換 compare 方法早期版本的代碼。通過強制將兩個正在比較的對象存放到 datetime 對象中,在 try/catch 塊中執行這個摘錄以捕獲可能出現的異常。如果出現異常,它發信號通知代碼正在轉換的類型是無效日期或時間,可以使用 string.compare 方法進行排序。如果兩個類型都是日期,它們使用 datetime.compare 方法進行排序。
運行該示例代碼的這個新版本,并單擊任意列。您將注意到它們正確地排序子項目,包括日期列。現在,該示例中的 listview 控件可以正確地處理它所顯示的所有數據類型。
小結
listview 控件能夠提供以多種方式顯示數據的能力。它可以用于顯示單獨項目,也可以顯示包含子項目信息的項目。使用由 listview 控件提供的排序功能,您還可以使用戶基于那些子項目排序 listview 控件中的項目,無需考慮出現的數據類型。這種對項目及其子項目進行排序的能力使您的應用程序能夠以 microsoft® windows® explorer 和其他應用程序的用戶所熟悉的方式表現其行為,它們提供數據的 listview 顯示和排序其內容的能力。