Tworzenie dostosowanego interfejsu użytkownika sortowania (VB)
Autor : Scott Mitchell
Podczas wyświetlania długiej listy posortowanych danych bardzo pomocne może być grupowanie powiązanych danych przez wprowadzenie wierszy separatora. W tym samouczku zobaczymy, jak utworzyć taki interfejs użytkownika sortowania.
Wprowadzenie
Podczas wyświetlania długiej listy posortowanych danych, w których istnieje tylko kilka różnych wartości w posortowanej kolumnie, użytkownik końcowy może trudno rozpoznać, gdzie dokładnie występują granice różnicy. Na przykład w bazie danych istnieje 81 produktów, ale tylko dziewięć różnych opcji kategorii (osiem unikatowych kategorii plus NULL
opcja). Rozważmy przypadek użytkownika, który jest zainteresowany badaniem produktów, które należą do kategorii Owoce morza. Na stronie zawierającej listę wszystkich produktów w jednym siatce GridView użytkownik może zdecydować, że najlepszym rozwiązaniem jest posortowanie wyników według kategorii, które grupuje razem wszystkie produkty z owoców morza. Po posortowaniu według kategorii użytkownik musi następnie wyszukać na liście, gdzie zaczynają się i kończą produkty pogrupowane na owoce morza. Ponieważ wyniki są uporządkowane alfabetycznie według nazwy kategorii znalezienie produktów z owoców morza nie jest trudne, ale nadal wymaga dokładnego skanowania listy elementów w siatce.
Aby ułatwić wyróżnianie granic między posortowanych grup, wiele witryn internetowych korzysta z interfejsu użytkownika, który dodaje separator między takimi grupami. Separatory, takie jak pokazane na rysunku 1, umożliwiają użytkownikowi szybszą znalezienie określonej grupy i zidentyfikowanie jej granic, a także ustalenie, jakie odrębne grupy istnieją w danych.
Rysunek 1. Każda grupa kategorii jest wyraźnie zidentyfikowana (kliknij, aby wyświetlić obraz o pełnym rozmiarze)
W tym samouczku zobaczymy, jak utworzyć taki interfejs użytkownika sortowania.
Krok 1. Tworzenie standardowego, sortowalnego obiektu GridView
Zanim dowiesz się, jak rozszerzyć obiekt GridView w celu zapewnienia ulepszonego interfejsu sortowania, najpierw utwórzmy standardowy, sortowalny obiekt GridView, który wyświetla listę produktów. Zacznij od otwarcia CustomSortingUI.aspx
strony w folderze PagingAndSorting
. Dodaj obiekt GridView do strony, ustaw jej ID
właściwość na ProductList
, a następnie powiąż ją z nową wartością ObjectDataSource. Skonfiguruj obiekt ObjectDataSource do używania ProductsBLL
metody s GetProducts()
klasy do wybierania rekordów.
Następnie skonfiguruj obiekt GridView tak, aby zawierał ProductName
tylko pola , CategoryName
, SupplierName
i UnitPrice
BoundField oraz pole CheckBoxField zakończone. Na koniec skonfiguruj kontrolkę GridView do obsługi sortowania, zaznaczając pole wyboru Włącz sortowanie w tagu inteligentnym GridView (lub przez ustawienie jego AllowSorting
właściwości na true
). Po dodaniu tych dodatków do CustomSortingUI.aspx
strony znacznik deklaratywny powinien wyglądać podobnie do następującego:
<asp:GridView ID="ProductList" runat="server" AllowSorting="True"
AutoGenerateColumns="False" DataKeyNames="ProductID"
DataSourceID="ObjectDataSource1" EnableViewState="False">
<Columns>
<asp:BoundField DataField="ProductName" HeaderText="Product"
SortExpression="ProductName" />
<asp:BoundField DataField="CategoryName" HeaderText="Category"
ReadOnly="True" SortExpression="CategoryName" />
<asp:BoundField DataField="SupplierName" HeaderText="Supplier"
ReadOnly="True" SortExpression="SupplierName" />
<asp:BoundField DataField="UnitPrice" DataFormatString="{0:C}"
HeaderText="Price" HtmlEncode="False" SortExpression="UnitPrice" />
<asp:CheckBoxField DataField="Discontinued" HeaderText="Discontinued"
SortExpression="Discontinued" />
</Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
OldValuesParameterFormatString="original_{0}" SelectMethod="GetProducts"
TypeName="ProductsBLL"></asp:ObjectDataSource>
Poświęć chwilę, aby wyświetlić postęp do tej pory w przeglądarce. Rysunek 2 przedstawia posortowany element GridView, gdy jego dane są sortowane według kategorii w kolejności alfabetycznej.
Rysunek 2. Sortowalne dane kontrolki GridView są uporządkowane według kategorii (kliknij, aby wyświetlić obraz o pełnym rozmiarze)
Krok 2. Eksplorowanie technik dodawania wierszy separatora
Po zakończeniu sortowania Kontrolka GridView wystarczy dodać wiersze separatora w siatce przed każdą unikatową posortowaną grupą. Ale jak można wstrzyknąć takie wiersze do kontrolki GridView? Zasadniczo musimy iterować wiersze kontrolki GridView, określić, gdzie występują różnice między wartościami w posortowanej kolumnie, a następnie dodać odpowiedni wiersz separatora. Myśląc o tym problemie, wydaje się naturalne, że rozwiązanie leży gdzieś w procedurze obsługi zdarzeń GridView RowDataBound
. Jak omówiono w samouczku Niestandardowe formatowanie oparte na danych , ten program obsługi zdarzeń jest często używany podczas stosowania formatowania na poziomie wiersza na podstawie danych wierszy. Jednak RowDataBound
program obsługi zdarzeń nie jest tutaj rozwiązaniem, ponieważ nie można dodać wierszy do kontrolki GridView programowo z tej procedury obsługi zdarzeń. Kolekcja GridView Rows
jest w rzeczywistości tylko do odczytu.
Aby dodać dodatkowe wiersze do kontrolki GridView, mamy trzy opcje:
- Dodaj te wiersze separatora metadanych do rzeczywistych danych powiązanych z kontrolką GridView
- Po powiązaniu kontrolki GridView z danymi dodaj kolejne
TableRow
wystąpienia do kolekcji kontrolek GridView - Tworzenie niestandardowej kontrolki serwera, która rozszerza kontrolkę GridView i zastępuje te metody odpowiedzialne za konstruowanie struktury kontrolki GridView
Utworzenie niestandardowej kontrolki serwera byłoby najlepszym rozwiązaniem, jeśli ta funkcja była potrzebna na wielu stronach sieci Web lub w kilku witrynach internetowych. Jednak wiązałoby się to z dość dużą ilością kodu i szczegółową eksploracją głębi wewnętrznych funkcji GridView. W związku z tym nie rozważymy tej opcji dla tego samouczka.
Pozostałe dwie opcje dodawania wierszy separatora do rzeczywistych danych powiązanych z obiektem GridView i manipulowania kolekcją kontrolek GridView po jej powiązaniu — inaczej atakują problem i zasługują na dyskusję.
Dodawanie wierszy do danych powiązanych z kontrolką GridView
Gdy obiekt GridView jest powiązany ze źródłem danych, tworzy GridViewRow
element dla każdego rekordu zwróconego przez źródło danych. W związku z tym możemy wstrzyknąć wymagane wiersze separatora, dodając rekordy separatora do źródła danych przed powiązaniem go z elementem GridView. Rysunek 3 ilustruje tę koncepcję.
Rysunek 3. Jedna technika polega na dodawaniu wierszy separatora do źródła danych
Używam rekordów separatora terminów w cudzysłowie, ponieważ nie ma specjalnego rekordu separatora; zamiast tego musimy w jakiś sposób oznaczać, że określony rekord w źródle danych służy jako separator, a nie normalny wiersz danych. W naszych przykładach ponownie tworzymy powiązanie wystąpienia z elementem ProductsDataTable
GridView, który składa się z elementu ProductRows
. Możemy oznaczyć rekord jako wiersz separatora, ustawiając jego CategoryID
właściwość na -1
(ponieważ taka wartość nie mogła istnieć normalnie).
Aby użyć tej techniki, należy wykonać następujące czynności:
- Programowe pobieranie danych w celu powiązania z elementem GridView (wystąpieniem
ProductsDataTable
) - Sortowanie danych na podstawie właściwości gridView
SortExpression
SortDirection
- Iteruj element
ProductsRows
w obiekcieProductsDataTable
, wyszukując, gdzie znajdują się różnice w posortowanej kolumnie - Na każdej granicy grupy, wstrzyknąć wystąpienie rekordu
ProductsRow
separatora do tabeli DataTable, który ma ustawionąCategoryID
wartość-1
(lub dowolne oznaczenie zostało wybrane, aby oznaczyć rekord jako rekord separatora ) - Po wstrzyknięciu wierszy separatora programowo powiąż dane z kontrolką GridView
Oprócz tych pięciu kroków należałoby również podać procedurę obsługi zdarzeń dla zdarzenia GridView RowDataBound
. W tym miejscu sprawdziliśmy każdy DataRow
z nich i ustaliliśmy, czy był to wiersz separatora, którego CategoryID
ustawieniem było -1
. Jeśli tak, prawdopodobnie chcemy dostosować jego formatowanie lub tekst wyświetlany w komórkach.
Użycie tej techniki do wstrzykiwania granic grup sortowania wymaga nieco więcej pracy niż opisane powyżej, ponieważ należy również zapewnić procedurę obsługi zdarzeń dla zdarzenia GridView Sorting
i śledzić SortExpression
wartości i SortDirection
.
Manipulowanie kolekcją kontrolek GridView po tym, jak został on przychodzący
Zamiast wysyłać komunikaty o danych przed powiązaniem ich z obiektem GridView, możemy dodać wiersze separatora po powiązaniu danych z kontrolką GridView. Proces wiązania danych tworzy hierarchię sterowania kontrolki GridView, która w rzeczywistości jest po prostu wystąpieniem składającym Table
się z kolekcji wierszy, z których każda składa się z kolekcji komórek. W szczególności kolekcja kontrolek GridView zawiera Table
obiekt w jego katalogu głównym, GridViewRow
(który pochodzi od TableRow
klasy) dla każdego rekordu powiązanego DataSource
z obiektem GridView, oraz TableCell
obiekt w każdym wystąpieniu dla każdego GridViewRow
pola danych w DataSource
obiekcie .
Aby dodać wiersze separatora między poszczególnymi grupami sortowania, możemy bezpośrednio manipulować tą hierarchią sterowania po jej utworzeniu. Możemy mieć pewność, że hierarchia sterowania kontrolki GridView została utworzona po raz ostatni przez czas renderowania strony. W związku z tym to podejście zastępuje metodę Page
klasy s Render
, w której końcowa hierarchia kontrolek GridView jest aktualizowana w celu uwzględnienia potrzebnych wierszy separatora. Rysunek 4 ilustruje ten proces.
Rysunek 4. Alternatywna technika manipuluje hierarchią kontrolek GridView (kliknij, aby wyświetlić obraz o pełnym rozmiarze)
W tym samouczku użyjemy tego ostatniego podejścia, aby dostosować środowisko użytkownika sortowania.
Uwaga
Kod, który przedstawiam w tym samouczku, jest oparty na przykładzie podanym w wpisie w blogu Teemu Keiski, Playing a Bit with GridView Sort Grouping (Granie trochę za pomocą grupowania sortowania GridView).
Krok 3. Dodawanie wierszy separatora do hierarchii kontrolek GridView
Ponieważ chcemy tylko dodać wiersze separatora do hierarchii kontrolek kontrolki GridView po utworzeniu hierarchii sterowania i utworzeniu jej po raz ostatni na tej wizycie strony, chcemy wykonać ten dodatek na końcu cyklu życia strony, ale zanim rzeczywista hierarchia kontrolki GridView została renderowana w kodzie HTML. Ostatnim możliwym punktem, w którym możemy to osiągnąć, jest Page
zdarzenie klasy Render
, które możemy zastąpić w naszej klasie kodu za pomocą następującego podpisu metody:
Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)
' Add code to manipulate the GridView control hierarchy
MyBase.Render(writer)
End Sub
Page
Po wywołaniu base.Render(writer)
oryginalnej Render
metody klasy każda kontrolka na stronie zostanie renderowana, generując znaczniki na podstawie ich hierarchii sterowania. W związku z tym należy wywołać base.Render(writer)
metodę , tak aby strona była renderowana, i że manipulujemy hierarchią sterowania kontrolki GridView przed wywołaniem base.Render(writer)
metody , tak aby wiersze separatora zostały dodane do hierarchii formantów GridView przed jej renderowaniem.
Aby wstrzyknąć nagłówki grupy sortowania, najpierw musimy upewnić się, że użytkownik zażądał sortowania danych. Domyślnie zawartość kontrolki GridView nie jest sortowana i dlatego nie musimy wprowadzać żadnych nagłówków sortowania grup.
Uwaga
Jeśli chcesz, aby kontrolka GridView została posortowana według określonej kolumny po pierwszym załadowaniu strony, wywołaj metodę GridView Sort
na pierwszej stronie (ale nie w kolejnych ogłaszaniach zwrotnych). Aby to zrobić, dodaj to wywołanie w procedurze Page_Load
obsługi zdarzeń w ramach warunkowego if (!Page.IsPostBack)
. Aby uzyskać więcej informacji na temat metody, zapoznaj się z samouczkiem Dotyczącym danych raportów stronicowania i sortowaniaSort
.
Przy założeniu, że dane zostały posortowane, następnym zadaniem jest określenie kolumny, według której dane zostały posortowane, a następnie przeskanowanie wierszy, które szukają różnic w wartościach tej kolumny. Poniższy kod gwarantuje, że dane zostały posortowane i znajdują kolumnę, według której dane zostały posortowane:
Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)
' Only add the sorting UI if the GridView is sorted
If Not String.IsNullOrEmpty(ProductList.SortExpression) Then
' Determine the index and HeaderText of the column that
'the data is sorted by
Dim sortColumnIndex As Integer = -1
Dim sortColumnHeaderText As String = String.Empty
For i As Integer = 0 To ProductList.Columns.Count - 1
If ProductList.Columns(i).SortExpression.CompareTo( _
ProductList.SortExpression) = 0 Then
sortColumnIndex = i
sortColumnHeaderText = ProductList.Columns(i).HeaderText
Exit For
End If
Next
' TODO: Scan the rows for differences in the sorted column�s values
End Sub
Jeśli właściwość GridView nie została jeszcze posortowana, właściwość GridView SortExpression
nie zostanie ustawiona. W związku z tym chcemy dodać tylko wiersze separatora, jeśli ta właściwość ma pewną wartość. W takim przypadku należy określić indeks kolumny, według której dane zostały posortowane. Jest to realizowane przez pętlę w kolekcji GridViewColumns
, wyszukując kolumnę, której SortExpression
właściwość jest równa właściwości GridView.SortExpression
Oprócz indeksu kolumn pobieramy również HeaderText
właściwość , która jest używana podczas wyświetlania wierszy separatora.
W przypadku indeksu kolumny, według której dane są sortowane, ostatnim krokiem jest wyliczenie wierszy kontrolki GridView. Dla każdego wiersza musimy określić, czy posortowana wartość kolumny różni się od wartości kolumny posortowanej w poprzednim wierszu. Jeśli tak, musimy wstrzyknąć nowe GridViewRow
wystąpienie do hierarchii sterowania. Jest to realizowane za pomocą następującego kodu:
Protected Overrides Sub Render(ByVal writer As HtmlTextWriter)
' Only add the sorting UI if the GridView is sorted
If Not String.IsNullOrEmpty(ProductList.SortExpression) Then
' ... Code for finding the sorted column index removed for brevity ...
' Reference the Table the GridView has been rendered into
Dim gridTable As Table = CType(ProductList.Controls(0), Table)
' Enumerate each TableRow, adding a sorting UI header if
' the sorted value has changed
Dim lastValue As String = String.Empty
For Each gvr As GridViewRow In ProductList.Rows
Dim currentValue As String = gvr.Cells(sortColumnIndex).Text
If lastValue.CompareTo(currentValue) <> 0 Then
' there's been a change in value in the sorted column
Dim rowIndex As Integer = gridTable.Rows.GetRowIndex(gvr)
' Add a new sort header row
Dim sortRow As New GridViewRow(rowIndex, rowIndex, _
DataControlRowType.DataRow, DataControlRowState.Normal)
Dim sortCell As New TableCell()
sortCell.ColumnSpan = ProductList.Columns.Count
sortCell.Text = String.Format("{0}: {1}", _
sortColumnHeaderText, currentValue)
sortCell.CssClass = "SortHeaderRowStyle"
' Add sortCell to sortRow, and sortRow to gridTable
sortRow.Cells.Add(sortCell)
gridTable.Controls.AddAt(rowIndex, sortRow)
' Update lastValue
lastValue = currentValue
End If
Next
End If
MyBase.Render(writer)
End Sub
Ten kod rozpoczyna się programowo odwołując Table
się do obiektu znajdującego się w katalogu głównym hierarchii kontrolek GridView i tworząc zmienną ciągu o nazwie lastValue
. lastValue
służy do porównywania wartości kolumny posortowanej w bieżącym wierszu z wartością poprzedniego wiersza. Następnie kolekcja GridView Rows
jest wyliczana, a dla każdego wiersza wartość posortowanej kolumny jest przechowywana w zmiennej currentValue
.
Uwaga
Aby określić wartość posortowanej kolumny określonego wiersza, należy użyć właściwości komórki Text
. Działa to dobrze w przypadku obiektów BoundFields, ale nie będzie działać zgodnie z potrzebami w przypadku pól szablonów, pól CheckBoxFields itd. Wkrótce przyjrzymy się, jak uwzględnić alternatywne pola GridView.
Zmienne currentValue
i lastValue
są następnie porównywane. Jeśli różnią się one, musimy dodać nowy wiersz separatora do hierarchii sterowania. Jest to realizowane przez określenie indeksu GridViewRow
kolekcji obiektów Rows
Table
, utworzenie nowych GridViewRow
i wystąpień, a następnie dodanie TableCell
elementu i TableCell
GridViewRow
do hierarchii sterowania.
Należy pamiętać, że samotny TableCell
wiersz separatora jest sformatowany w taki sposób, że obejmuje całą szerokość elementu GridView, jest sformatowany przy użyciu SortHeaderRowStyle
klasy CSS i ma jej Text
właściwość, tak aby wyświetlała zarówno nazwę grupy sortowania (taką jak Category ) i wartość grupy (na przykład Napoje). lastValue
Na koniec jest aktualizowana do wartości currentValue
.
Klasa CSS używana do formatowania wiersza SortHeaderRowStyle
nagłówka grupy sortowania musi być określona Styles.css
w pliku. Możesz korzystać z dowolnych ustawień stylu, które cię podobają; Użyłem następujących elementów:
.SortHeaderRowStyle
{
background-color: #c00;
text-align: left;
font-weight: bold;
color: White;
}
W bieżącym kodzie interfejs sortowania dodaje nagłówki grup sortowania podczas sortowania według dowolnego pola ograniczenia (zobacz Rysunek 5, który przedstawia zrzut ekranu podczas sortowania według dostawcy). Jednak w przypadku sortowania według dowolnego innego typu pola (takiego jak CheckBoxField lub TemplateField) nagłówki grup sortowania nie są nigdzie znajdowane (zobacz Rysunek 6).
Rysunek 5. Interfejs sortowania zawiera nagłówki grup sortowania podczas sortowania według pól granic (kliknij, aby wyświetlić obraz pełnowymiarowy)
Rysunek 6. Brak nagłówków grup sortowania podczas sortowania pola CheckBoxField (kliknij, aby wyświetlić obraz pełnowymiarowy)
Przyczyną braku nagłówków grup sortowania podczas sortowania według pola CheckBoxField jest to, że kod obecnie używa tylko TableCell
właściwości s Text
do określenia wartości posortowanej kolumny dla każdego wiersza. W przypadku pola CheckBoxFields TableCell
właściwość s Text
jest pustym ciągiem. Zamiast tego wartość jest dostępna za pośrednictwem kontrolki sieci Web CheckBox znajdującej TableCell
się w kolekcji.Controls
Aby obsłużyć typy pól innych niż BoundFields, musimy rozszerzyć kod, w którym currentValue
jest przypisana zmienna, aby sprawdzić istnienie kontrolki CheckBox w TableCell
Controls
kolekcji. Zamiast używać metody currentValue = gvr.Cells(sortColumnIndex).Text
, zastąp ten kod następującym kodem:
Dim currentValue As String = String.Empty
If gvr.Cells(sortColumnIndex).Controls.Count > 0 Then
If TypeOf gvr.Cells(sortColumnIndex).Controls(0) Is CheckBox Then
If CType(gvr.Cells(sortColumnIndex).Controls(0), CheckBox).Checked Then
currentValue = "Yes"
Else
currentValue = "No"
End If
' ... Add other checks here if using columns with other
' Web controls in them (Calendars, DropDownLists, etc.) ...
End If
Else
currentValue = gvr.Cells(sortColumnIndex).Text
End If
Ten kod sprawdza posortowaną kolumnę TableCell
dla bieżącego wiersza, aby określić, czy w kolekcji Controls
znajdują się jakiekolwiek kontrolki. Jeśli istnieją, a pierwsza kontrolka to CheckBox, currentValue
zmienna jest ustawiona na Wartość Tak lub Nie, w zależności od właściwości CheckBox.Checked
W przeciwnym razie wartość jest pobierana z TableCell
właściwości s Text
. Tę logikę można replikować w celu obsługi sortowania dla dowolnych pól szablonów, które mogą istnieć w elemecie GridView.
Po dodaniu powyższego kodu nagłówki grup sortowania są teraz obecne podczas sortowania według przerwanego pola CheckBoxField (patrz Rysunek 7).
Rysunek 7. Nagłówki grup sortowania są teraz obecne podczas sortowania pola CheckBoxField (kliknij, aby wyświetlić obraz pełnowymiarowy)
Uwaga
Jeśli masz produkty z wartościami NULL
bazy danych dla CategoryID
pól , SupplierID
lub UnitPrice
, te wartości będą wyświetlane jako puste ciągi w siatce domyślnie, co oznacza, że tekst wiersza separatora dla tych produktów z wartościami NULL
będzie odczytywany jak Kategoria: (oznacza to, że nie ma nazwy po Kategorii: podobnie jak kategoria: napoje). Jeśli chcesz wyświetlić tutaj wartość, możesz ustawić właściwość BoundFields NullDisplayText
na tekst, który chcesz wyświetlić, lub dodać instrukcję warunkową w metodzie Render podczas przypisywania currentValue
wartości do właściwości wiersza separatoraText
.
Podsumowanie
Element GridView nie zawiera wielu wbudowanych opcji dostosowywania interfejsu sortowania. Jednak przy użyciu kodu niskiego poziomu można dostosować hierarchię sterowania GridView w celu utworzenia bardziej dostosowanego interfejsu. W tym samouczku pokazano, jak dodać wiersz separatora grup sortowania dla posortowalnego elementu GridView, który łatwiej identyfikuje odrębne grupy i te granice. Aby uzyskać dodatkowe przykłady dostosowanych interfejsów sortowania, zapoznaj się z wpisem na blogu Scott Guthrie s A Few ASP.NET 2.0 GridView Sortuj porady i wskazówki.
Szczęśliwe programowanie!
Informacje o autorze
Scott Mitchell, autor siedmiu książek ASP/ASP.NET i założyciel 4GuysFromRolla.com, współpracuje z technologiami internetowymi firmy Microsoft od 1998 roku. Scott pracuje jako niezależny konsultant, trener i pisarz. Jego najnowsza książka to Sams Teach Yourself ASP.NET 2.0 w ciągu 24 godzin. Można do niego dotrzeć pod adresem mitchell@4GuysFromRolla.com. Lub za pośrednictwem swojego bloga, który można znaleźć na stronie http://ScottOnWriting.NET.