ASP.NET Edycja nagłówka w GridView

 

Tytuł posta może być nieco mylący, chciałbym bowiem przedstawić w jaki sposób dodać dodatkowy wiersz do standardowej kontrolki GridView w celu zmiany wyglądu części nagłówkowej. Jeśli jednak mamy już header, to trzymając się ściśle terminologii dodana część powinna być określana jako subheader. Jakkolwiek brzmiałby polski odpowiednik tego słowa, zdecydowałem, że przynajmniej na potrzeby tego posta będę używał zwrotu “nagłówek”.

Jednym z podstawowych zadań aplikacji zezwalającej na interakcję z użytkownikiem jest przedstawianie mu danych. W przypadku ASP.NET zapewne najczęściej będą to dane pochodzące z przygotowanej wcześniej bazy danych. Zanim porwiemy się w wir tworzenia własnych kontrolek, dopasowanych do naszych wymagań, warto zastanowić się, co oferują kontrolki, które już zostały wbudowane przez twórców Visual Studio. Ze względu na możliwości prostego zarządzania danymi z pewnością jedną z częściej wykorzystywanych kontrolek będzie GridView.

Standardowo, poprzez przyjazny interfejs Visual Studio, po wstawieniu kontrolki GridView możemy zażyczyć sobie, aby oferowała ona sortowanie. Domyślne zachowanie jest takie, że po kliknięciu w nazwę kolumny następuje sortowanie względem niej. Po pierwszym kliknięciu jest to sortowanie rosnące, po drugim natomiast malejące. Z powodu mojego widzimisię takie rozwiązanie nie jest zadowalające. Chciałbym mieć możliwość:

  • od razu wybrania sortowania malejącego
  • patrząc na nagłówek widzieć w jakiej kolejności posortowana jest dana kolumna (to już tak z czystej ciekawości w jaki sposób można zrealizować tę funkcjonalność)

Do realizacji wymienionych funkcjonalności niezbędne będą dwa obrazki, pozwalające odróżnić kierunek sortowania. Dla moich potrzeb wybrałem trójkąty obrócone względem siebie o 180 stopni. Prościej mówiąc, z dziubkiem w górę i z dziubkiem w dół.

Do realizacji pierwszej funkcjonalności wykorzystane zostało zdarzenie RowDataBound dla kontrolki GridView. Cechuje je to, że występuje zanim kontrolka zostanie wyrenderowana, co umożliwia przeprowadzenie zmian w jej wyglądzie przed wyświetleniem jej użytkownikowi. W zdarzeniu tym musimy określić, że chodzi o wstawienie wiersza poniżej istniejącego już nagłówka. Z punktu widzenia tego, że być może będzie istniała kiedyś potrzeba wstawienia jakiegoś wiersza rozgraniczającego pewne grupy danych, proponuje dokonywanie zmian na wierszach z danymi, a nie z nagłówkiem. Szkielet takiego rozwiązania wygląda wtedy następująco:

protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.DataRow)
    {
        if (e.Row.RowIndex == 1)
        {
            ...
        }
        ...
    }
    ...
}

Od tego momentu podawany kod powinien oczywiście znaleźć się wewnątrz drugiej klauzuli if.

ASP.NET oferuje kontrolkę ImageButton, czyli przycisk o wyglądzie obrazka. Zaletą tej kontrolki jest to, że po kliknięciu w obrazek otrzymujemy zachowanie takie, jakie posiada standardowy Button. Obrazki dla odpowiednich przycisków przechowywane są w katalogu Images znajdującym się w solucji. Do katalogu tego obrazki dodawane są po kliknięciu prawym przyciskiem myszy w Solution Explorer na nazwie katalogu i wybraniu opcji Add->Existing Item. Odpowiednie kontrolki mogą być utworzone w następujący sposób:

//Chosen cell to change in subheader
TableCell cell = new TableCell();

//Ascending sort Button
ImageButton imgBtn = new ImageButton();
imgBtn.ID = "NameAscImageButton";
imgBtn.ImageUrl = @"/Images/triangle.jpg";
imgBtn.Width = Unit.Pixel(50);
imgBtn.Click += new ImageClickEventHandler(imgBtn_Click);

//Descending sort Button
ImageButton imgBtn2 = new ImageButton();
imgBtn2.ID = "NameDescImageButton";
imgBtn2.ImageUrl = @"/Images/triangle_desc.jpg";
imgBtn2.Width = Unit.Pixel(50);
imgBtn2.Click += new ImageClickEventHandler(imgBtn2_Click);

//Add controls to cell in asc-desc order
cell.Controls.Add(imgBtn);
cell.Controls.Add(imgBtn2);

W pierwszej kolejności konieczne jest utworzenie komórki wchodzącej w skład przyszłego wiersza, w której znajdą się wybrane elementy. Utworzone przyciski są do niej dodawane w sposób podany w dwóch ostatnich liniach powyższego kodu. Mamy zatem komórkę zawierającą przyciski do sortowania (w tym opisie ograniczam się jedynie do zapewnienia możliwości sortowania jednej, wybranej przeze mnie kolumny). Każdy wiersz z tabeli składa się jednak z większej liczby komórek, z tego powodu pozostałe komórki również powinny być utworzone dla uzyskania jednakowej struktury.
 
//Create row containing cells
GridViewRow row = new GridViewRow(0, 0, DataControlRowType.DataRow, DataControlRowState.Normal);

for (int i = 0; i < e.Row.Cells.Count; i++)
{
  //Column named "Name"
  if (i == 1)
  {
      //Add earlier created TableCell with Buttons
      row.Cells.Add(cell);
  }
  else
  {
      row.Cells.Add(new TableCell());
  }
}

Na zakończenie utworzony wiersz musi zostać dodany w odpowiednim miejscu do już istniejącej tabeli:
 
Table tbl = (Table)e.Row.Parent;
tbl.Rows.AddAt(1, row);

Zmiana w ostatniej linii z wartości 1 na 0 spowoduje umieszczenie utworzonego wiersza powyżej standardowego nagłówka kontrolki GridView.

Czas na zaimplementowanie drugiej funkcjonalności, czyli wyświetlenie w nagłówku sposobu w jaki aktualnie sortowana jest kolumna. Tym razem wykorzystane zostanie zdarzenie RowCreated dla kontrolki GridView. Przede wszystkim potrzebujemy metody umożliwiającej odnalezienie kolumny według której dane są sortowane. Zadanie to jest realizowane poprzez przejrzenie wszystkich pól pośród kolumn znajdujących w GridView. Kolumny w tym kontekście to pola nagłówka. Następnie uzyskujemy indeks aktualnie sortowanej kolumny:

protected int GetSortColumnIndex()
{
    foreach (DataControlField field in GridView1.Columns)
    {
        if (field.SortExpression == GridView1.SortExpression)
        {
            return GridView1.Columns.IndexOf(field);
        }
    }

    return -1;
}

W następnej partii kodu dla wiersza, który jest nagłówkiem, dodajemy dla sortowanej aktualnie kolumny obrazek:
 
protected void GridView1_RowCreated(object sender, GridViewRowEventArgs e)
{
    if (e.Row.RowType == DataControlRowType.Header)
    {
        int sortIndexColumn = GetSortColumnIndex();

        if (sortIndexColumn > 0)
        {
            AddSortImage(sortIndexColumn, e.Row);
        }
    }
}

Należy przy tym zauważyć, że z konstrukcji wykorzystywanej w tym przykładzie kontrolki GridView wynika, że obrazek może się znaleźć najwcześniej w kolumnie oznaczonej indeksem równym 1. Metoda dodająca obrazek zaimplementowana może być następująco:
 
protected void AddSortImage(int columnIndex, GridViewRow headerRow)
{
    Image sortImage = new Image();
    sortImage.Width = Unit.Pixel(50);
    sortImage.ImageAlign = ImageAlign.Right;

    if (GridView1.SortDirection == SortDirection.Ascending)
    {
        sortImage.ImageUrl = @"Images\triangle.jpg";
    }
    else
    {
        sortImage.ImageUrl = @"Images\triangle_desc.jpg";
    }
    headerRow.Cells[columnIndex].Controls.Add(sortImage);
}

Efekt końcowy wszystkich działań wyglądą następująco (grafika nie powala 😉 ):

zrzut z ekranu

Dodaj komentarz