Tworzenie dostosowanego interfejsu użytkownika sortowania (C#)
Podczas wyświetlania długiej listy posortowanych danych można bardzo pomóc w grupowaniu powiązanych danych, wprowadzając wiersze 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óżnic. 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 zbadaniem produktów, które należą do kategorii Owoce morza. Na stronie zawierającej listę wszystkich produktów w jednym elemecie GridView użytkownik może zdecydować, że najlepszym rozwiązaniem jest sortowanie wyników według kategorii, które grupują razem wszystkie produkty z owoców morza. Po sortowaniu według kategorii użytkownik musi wyszukać listę, szukając miejsca rozpoczęcia i zakończenia produktów pogrupowanych na owoce morza. Ponieważ wyniki są uporządkowane alfabetycznie według nazwy kategorii znajdowanie produktów z owoców morza nie jest trudne, ale nadal wymaga ścisłego skanowania listy elementów w siatce.
Aby ułatwić wyróżnianie granic między posortowanych grup, wiele witryn internetowych używa interfejsu użytkownika, który dodaje separator między takimi grupami. Separatory, takie jak pokazane na rysunku 1, umożliwiają użytkownikowi szybkie 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 pełnowymiarowy)
W tym samouczku zobaczymy, jak utworzyć taki interfejs użytkownika sortowania.
Krok 1. Tworzenie standardowego, sortowalnego elementu GridView
Zanim dowiesz się, jak rozszerzyć element GridView w celu zapewnienia ulepszonego interfejsu sortowania, najpierw utwórzmy standardowy, sortowalny element GridView zawierający listę produktów. Zacznij od otwarcia CustomSortingUI.aspx
strony w folderze PagingAndSorting
. Dodaj element GridView do strony, ustaw jej ID
właściwość na ProductList
, i powiąż ją z nowym obiektem ObjectDataSource. Skonfiguruj obiekt ObjectDataSource, aby użyć ProductsBLL
metody s GetProducts()
klasy do wybierania rekordów.
Następnie skonfiguruj kontrolkę GridView tak, aby zawierała ProductName
tylko pola , CategoryName
, SupplierName
i UnitPrice
BoundField oraz pole CheckBoxField zakończone. Na koniec skonfiguruj element GridView do obsługi sortowania, zaznaczając pole wyboru Włącz sortowanie w tagu inteligentnym GridView (lub przez ustawienie jej AllowSorting
właściwości na true
). Po dodaniu tych dodatków do CustomSortingUI.aspx
strony deklaratywne znaczniki powinny wyglądać podobnie do następujących:
<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 zobaczyć 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. Dane elementu GridView sortowalnego są uporządkowane według kategorii (kliknij, aby wyświetlić obraz pełnowymiarowy)
Krok 2. Eksplorowanie technik dodawania wierszy separatora
Po zakończeniu sortowania GridView wszystkie pozostałe elementy mają być w stanie dodać wiersze separatora w siatce przed każdą unikatową posortowaną grupą. Ale jak można wstrzyknąć takie wiersze do GridView? Zasadniczo musimy iterować wiersze 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 na podstawie danych , ta procedura obsługi zdarzeń jest często używana podczas stosowania formatowania na poziomie wiersza na podstawie danych wiersza. Jednak program RowDataBound
obsługi zdarzeń nie jest tutaj rozwiązaniem, ponieważ nie można dodać wierszy do kontrolki GridView programowo z tego programu obsługi zdarzeń. Kolekcja GridView jest Rows
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 elementem GridView
- Po powiązaniu elementu GridView z danymi dodaj dodatkowe
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 GridView
Utworzenie niestandardowej kontroli serwera byłoby najlepszym rozwiązaniem, jeśli ta funkcja była potrzebna na wielu stronach internetowych lub w kilku witrynach internetowych. Jednak wymagałoby to sporo kodu i dokładnej eksploracji głębi wewnętrznych prac 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 elementem GridView i manipulowania kolekcją kontrolek GridView po jej powiązaniu — atakują problem inaczej i zasługują na dyskusję.
Dodawanie wierszy do danych powiązanych z elementem GridView
Gdy element GridView jest powiązany ze źródłem danych, tworzy dla GridViewRow
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 obejmuje dodawanie 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 jakoś oznaczyć flagę, że konkretny rekord w źródle danych służy jako separator, a nie normalny wiersz danych. W naszych przykładach ponownie utworzymy powiązanie wystąpienia z elementem ProductsDataTable
GridView, który składa się z ProductRows
elementu . 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 skorzystać z tej techniki, należy wykonać następujące kroki:
- Programowe pobieranie danych w celu powiązania z elementem GridView (wystąpieniem
ProductsDataTable
) - Sortowanie danych na podstawie właściwości GridView s
SortExpression
SortDirection
- Iteracja w
ProductsRows
obiekcieProductsDataTable
, wyszukując, gdzie leżą różnice w posortowanej kolumnie - W każdej granicy grupy wstrzyknąć wystąpienie rekordu
ProductsRow
separatora do tabeli DataTable, które 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 musimy również udostępnić 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
ustawienie miało wartość -1
. Jeśli tak, prawdopodobnie chcemy dostosować 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ż udostępnić procedurę obsługi zdarzeń dla zdarzenia GridView Sorting
i śledzić SortExpression
wartości i SortDirection
.
Manipulowanie kolekcją kontrolek GridView po tym, jak została utworzona dane
Zamiast komunikatów o danych przed powiązaniem ich z obiektem GridView, możemy dodać wiersze separatora po powiązaniu danych z elementem GridView. Proces powiązania danych tworzy hierarchię kontrolki GridView, która w rzeczywistości jest po prostu wystąpieniem Table
składającym 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 z klasy) dla każdego rekordu TableRow
powiązanego DataSource
z elementem GridView, oraz TableCell
obiekt w każdym GridViewRow
wystąpieniu dla każdego pola danych w elemecie DataSource
.
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 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 kontrolki GridView jest aktualizowana w celu uwzględnienia potrzebnych wierszy separatora. Rysunek 4 ilustruje ten proces.
Rysunek 4. Alternatywna technika manipuluje hierarchią kontrolki GridView (kliknij, aby wyświetlić obraz pełnowymiarowy)
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 kontrolki GridView s
Ponieważ chcemy dodać tylko wiersze separatora do hierarchii kontrolki GridView po utworzeniu hierarchii sterowania i utworzeniu jej po raz ostatni na tej wizycie strony, chcemy wykonać to dodanie na końcu cyklu życia strony, ale zanim rzeczywista hierarchia kontrolki GridView została renderowana w formacie 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 override void Render(HtmlTextWriter writer)
{
// Add code to manipulate the GridView control hierarchy
base.Render(writer);
}
Page
Gdy oryginalna Render
metoda klasy jest wywoływanabase.Render(writer)
, 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 manipulowaliśmy hierarchią sterowania kontrolki GridView przed wywołaniem base.Render(writer)
metody , tak aby wiersze separatora zostały dodane do hierarchii kontrolki GridView przed jego renderowaniem.
Aby wstrzyknąć nagłówki grup 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 element GridView został posortowany według określonej kolumny po pierwszym załadowaniu strony, wywołaj metodę GridView Sort
na pierwszej stronie (ale nie w kolejnych postbacks). 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 stronicowania i sortowaniaSort
danych raportu.
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 znajdzie kolumnę, według której dane zostały posortowane:
protected override void Render(HtmlTextWriter writer)
{
// Only add the sorting UI if the GridView is sorted
if (!string.IsNullOrEmpty(ProductList.SortExpression))
{
// Determine the index and HeaderText of the column that
//the data is sorted by
int sortColumnIndex = -1;
string sortColumnHeaderText = string.Empty;
for (int i = 0; i < ProductList.Columns.Count; i++)
{
if (ProductList.Columns[i].SortExpression.CompareTo(ProductList.SortExpression)
== 0)
{
sortColumnIndex = i;
sortColumnHeaderText = ProductList.Columns[i].HeaderText;
break;
}
}
// TODO: Scan the rows for differences in the sorted column�s values
}
Jeśli element GridView nie został jeszcze posortowany, 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 przeciwnym razie musimy określić indeks kolumny, według której dane zostały posortowane. Jest to realizowane przez pętlę w kolekcji GridView Columns
, wyszukując kolumnę, której SortExpression
właściwość jest równa właściwości GridView s SortExpression
. Oprócz indeksu kolumny pobieramy HeaderText
również 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 elementu 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 override void Render(HtmlTextWriter writer)
{
// Only add the sorting UI if the GridView is sorted
if (!string.IsNullOrEmpty(ProductList.SortExpression))
{
// ... Code for finding the sorted column index removed for brevity ...
// Reference the Table the GridView has been rendered into
Table gridTable = (Table)ProductList.Controls[0];
// Enumerate each TableRow, adding a sorting UI header if
// the sorted value has changed
string lastValue = string.Empty;
foreach (GridViewRow gvr in ProductList.Rows)
{
string currentValue = gvr.Cells[sortColumnIndex].Text;
if (lastValue.CompareTo(currentValue) != 0)
{
// there's been a change in value in the sorted column
int rowIndex = gridTable.Rows.GetRowIndex(gvr);
// Add a new sort header row
GridViewRow sortRow = new GridViewRow(rowIndex, rowIndex,
DataControlRowType.DataRow, DataControlRowState.Normal);
TableCell sortCell = 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;
}
}
}
base.Render(writer);
}
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:
string currentValue = string.Empty;
if (gvr.Cells[sortColumnIndex].Controls.Count > 0)
{
if (gvr.Cells[sortColumnIndex].Controls[0] is CheckBox)
{
if (((CheckBox)gvr.Cells[sortColumnIndex].Controls[0]).Checked)
currentValue = "Yes";
else
currentValue = "No";
}
// ... Add other checks here if using columns with other
// Web controls in them (Calendars, DropDownLists, etc.) ...
}
else
currentValue = gvr.Cells[sortColumnIndex].Text;
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.