.NET – Anketa 3.díl

Anketa je už hotová, provozuschopná, ale zatím se nové otázky a ankety dají přidávat pouze přes databázi. Dnes si začneme vytvářet administrativní systém a v příštím dílu jej dokončíme.

Vytvořit administrativní systém naší komponenty je poněkud časově náročnější, proto jsem se rozhodl jej rozdělit do dvou částí. Kdo je nedočkavý, může se již teď podívat, jak systém funguje, přidat si svoji anketku a čekat na hlasy dalších čtenářů :-). Náš plán vypadá asi takto:

  • Dnešní díl :
    • Úvod do komponenty DataGrid
    • Zpracování událostí DataGridu
    • Stránkování
    • Zobrazování vztahů master/detail

Výsledkem bude stránkovaná tabulka, v níž bude možné označit jednotlivé ankety.

  • Příští díl
    • Úvod do komponenty Repeater
    • Přidávání, editace a mazání anket
    • Přidávání, editace a mazání jednotlivých otázek

Zdrojový kód bude dnes obsahovat jak Anketu-komponentu, tak i kompletní admin systém. Nepočítám, že projekt bude úplně bez chyb, nejedná se už o úplně triviální projekt. Proto vám chci nabídnou k testování tuto verzi s tím, že v diskusích můžeme probrat a opravit její případné chyby. Pokud někdo přijde s nápadem, jemuž se nedá odolat, můžeme jej také implementovat.

DataGrid

Je to naprosto klíčová komponenta ASP.NET umožňující během 10 minut napsat kompletní web-interface k tabulce. Umožňuje spoustu vyspělých funkcionalit, jako je stránkování, seřazení dat, výběr a editace záznamů. Velice jednoduché je také zobrazení vztahů master/detail. Pod tím je myšleno například zobrazení vztahu tabulek Autor, Článek (jeden autor má více článků). Více si o implementaci takových vztahů povíme trochu níže v tomto článku. Začněme ale pěkně popořádku. Ve všech příkladech a ukázkách budu používat dříve vytvořenou třídu DBLayer, která nám umožní soustředit se pouze na problematiku DataGridů - nemusíme se zabývat spojením s databází.

"Hello world" ve světě datagridů by mohl vypadat následovně:

Klepněte pro větší obrázek
ukázat příklad

Jak můžeme na příkladu vidět, vytvoří DataGrid struktury tabulky automaticky na základě datového zdroje. Co ale uděláme, pokud chceme sami nadefinovat, jak mají sloupce vypadat? Můžeme použít jednu (nebo více) z následných definic sloupců:

Název sloupce Popis
BoundColumn Standardní sloupec
HyperLinkColumn Místo textu vkládá do tabulky odkazy.
ButtonColumn Vloží tlačítko, které může "probublat" událost do události. DataGrid.ItemCommand.
TemplateColumn Definuje šablonu pro sloupec, která může obsahovat prakticky cokoliv včetně HTML a ASPX tagů. Nejmocnější, ale také nejpracnější ze všech typů sloupců
EditCommandColumn Zobrazí text společně s tlačítky pro Editaci, Uložení a Storno v závislosti na nastavené vlastnosti. DataGrid.EditItemIndex.

Pokud nenastavíme ani jeden ze sloupců (první příklad), DataGrid automaticky vygeneruje seznam sloupců podle datové předlohy. Toto způsobuje parametr AutoGenerateColumns, který má defaultně hodnotu true. Další ukázka ukazuje, jak můžeme nadefinovat vlastní sloupce:

Klepněte pro větší obrázek
ukázat příklad

Všimněte si, jak jsou data vázána na jednotlivé sloupce, aby vytvořily to, co chceme vidět ve výsledné tabulce.

Další ukázka je právě ukázkou vztahu master/detail. Po výběru autora v první tabulce se do druhé tabulky načte odpovídající seznam článků:

Klepněte pro větší obrázek
ukázat příklad

Jak jsem daného efektu docílil? Stránka obsahuje dva DataGridy - Authors a Articles. Podívejme se blíže na metodu Authors_SelectedIndexChanged, která obsluhuje událost SelectedIndexChanged, vyvolanou po výběru záznamu v tabulce Authors:

Sub Authors_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As System.EventArgs)
  Dim AuthorID As Integer = CInt(Authors.SelectedItem.Cells(1).Text)
  Articles.DataSource = ExecuteSQLReader("SELECT * FROM Article WHERE AuthorID=" & AuthorID)
 
Articles.DataBind()
End Sub

První řádek vytáhne z druhého sloupce DataGridu Authors ID označeného autora. Dále je pak už všechno jasné - vytvoříme SQL příkaz, kterým vybereme jenom odpovídající články a navážeme je na DataGrid Articles.

První sloupec se jménem Označit je typu ButtonColumn. Takový sloupec bude obsahovat data zobrazená do tlačítek. Ta můžou být vykreslená buď jako hyperlinky, nebo jako standardní buttony. Pro naši tabulku jsem ponechal defaultní nastavení použití hyperlinků. Parametr CommandName určuje příkaz spojený s daným tlačítkem.

<asp:ButtonColumn Text="Výběr" HeaderText="Označit" CommandName="Select">

V našem případě je to Select, který DataGridu sděluje, že má po stlačení označit záznam jako vybraný. Při tom je také vyvolána událost SelectedIndexChanged.

Vidíme, že zobrazení tabelovaných relačních dat je v ASP.NET velice jednoduché a příjemné.

Tyto tři uvedené příklady nám daly nezbytný základ, který budeme potřebovat pro náš administrativní systém. Kdo by se chtěl dozvědět více o komponentě DataGrid a jejím použití, ať se podívá na některý z těchto článků :

GotDotNet - DataGrid Reference Výborný tutoriál obsahující spoustu velice přínosných příkladů
MSDN Magazine - May 2001 Série článků od podle mě nejlepšího autora v oblasti ASP.NET Dina Esposita popisující názorně komponentu DataGrid
MSDN Magazine - March 2001 Opět výborný článek od Dina Esposita. Tentokrát vysvětluje, jak vyrobit vlastní komponentu založenou na DataGridu pro zobrazování hierarchických dat.

Administrace

V našem systému budeme potřebovat následující funkce:

  • V tabulce mít vypsané všechny ankety
  • Po výběru konkrétní ankety se tato anketa zobrazí vedle tabulky

Vidíme, že náš úkol je velice podobný poslednímu příkladu ze sekce DataGrid - jedná se o klasické zobrazení master/detail. Změna bude spočívat v tom, že místo tabulky použijeme pro zobrazení ankety upravenou verzi komponenty z minulého dílu.

Nejdříve si tedy vytvoříme tabulku vypisující všechny ankety pro výběr. Jako aktuální výběr označíme první záznam:

Klepněte pro větší obrázek

Tady je výpis HTML části:

<asp:datagrid id=Topics runat="server" AutoGenerateColumns="False" CellPadding="3" BackColor="White" BorderWidth="1px" BorderStyle="None" BorderColor="#666666" AllowPaging="True">
 
  <SelectedItemStyle ForeColor="White" BackColor="#669999"/>
  <ItemStyle ForeColor="#000066" CssClass="elementText"/>
  <HeaderStyle Font-Bold="True" ForeColor="White" CssClass="elementTitle" BackColor="#006699"/>
  <FooterStyle ForeColor="#000066" BackColor="White"/>

  <Columns>
    <asp:BoundColumn DataField="ID" HeaderText="ID"/>
    <asp:TemplateColumn HeaderText="Název"/>
    <asp:ButtonColumn DataTextField="Name" HeaderText="Název" CommandName="Select"/>
    <asp:BoundColumn DataField="Hits" HeaderText="Hlasovalo"/>
    <asp:BoundColumn DataField="LastHit" HeaderText="Poslední hlas"/>
  </Columns>

  <PagerStyle HorizontalAlign="Left" ForeColor="#000066" BackColor="White" CssClass="elementSubTitle" Mode="NumericPages"/>

</asp:datagrid>

Na tomto příkladě vidíme, že u DataGridu můžeme velice hezky také přizpůsobovat vzhled. Definice jednotlivých stylů tady probírat nebudu, spíše se dále zaměříme na funkční část. Za povšimnutí stojí parametr AllowPaging="True", který zobrazí v levé dolní části číselník, umožňující nám přechod mezi jednotlivými stránkami, pokud je záznamů více. Defaultní hodnota je 10 záznamů na stránku.

Podívejme se nyní, jak naplníme tuto tabulku daty. Vytvořil jsem pro tento úkol funkci FillTopics, která zajistí vše potřebné:

Sub FillTopics()

  Topics.DataSource = ExecuteSQLDataTable("SELECT * FROM (SELECT ID, Name, (SELECTCOUNT<) FROM POLLhit,POLLQuestion WHERE POLLquestionID=POLLquestion.ID AND POLLtopicID=POLLtopic.ID) As Hits, (SELECT TOP 1 VoteDateTime FROM POLLhit,POLLQuestion WHERE POLLquestionID=POLLquestion.ID AND POLLtopicID=POLLtopic.ID ORDER BY VoteDateTime DESC)As LastHit  FROM POLLTopic) ORDER BY LastHit,ID DESC")
 
  Topics.DataBind()
End Sub

Můžeme vidět, že ta se v podstatě skládá jenom s jednoho delšího SQL dotazu. Na závěr přidáme magický příkaz Topics.DataBind(), který uskuteční samotné provázání. Možná se ptáte, k čemu je tento příkaz dobrý, když jsme už vlastně datový zdroj definovali přiřazením Topics.DataSource = ….. Je to kvůli tomu, že někdy chceme, aby data v části stránky zůstala nezměněná. Vraťme se například k našemu vztahu master / detail. Po výběru autora chceme pouze změnit obsah tabulky Articles, samotný DataGrid Authors si může data ponechat v nezměněné podobě. Právě příkazem DataBind() se provede provázání a aktualizace dat z datového zdroje. Topics.DataBind naváže data pouze pro DataGrid Topics. DataBind funguje rekurzivně, to znamená, že zavolání Page.DataBind() vyvolá provázání dat rekurzivně ve všech objektech vaší stránky.

Další věc, kterou musíme implementovat, je stránkování. Komponenta DataGrid to za nás udělá více méně automaticky, jediné, co musíme udělat, je, že po přechodu na jinou stránku v DataGridu změníme aktuálně vybraný záznam za první záznam na nové stránce:

Private Sub Topics_PageChanged(ByVal sender As System.Object, ByVal e As DataGridPageChangedEventArgs)
  Topics.CurrentPageIndex = e.NewPageIndex
  FillTopics()
  Topics.SelectedIndex = 0
  FillPoll()
End Sub

To bychom měli "master" část naší relace. Zkusme nyní přidat zobrazení ankety, která bude obsahovat detaily pro vybranou anketu:

Klepněte pro větší obrázek

HTML kód v podstatě zkopírujeme z minulého dílu. Potřebujeme ale mít možnost editace otázek, nemůžeme proto použít přímo komponentu PollControl.ascx. Jak tedy bude vypadat upravený HTML kód?

<asp:panel id=PollSection runat="server">
  <TABLE cellSpacing=0 cellPadding=0>
  <TR>
    <TD class=elementTitle>
      <asp:label id=TopicName runat="server" Visible="<%#Not(ModeEdit) %>"/>
      <asp:textbox CssClass="formTitle" id=TopicNameInput runat="server" Visible="<%#ModeEdit %>"/>
    </TD>
  </TR>
  <TR>
    <TD class=elementSubTitle>
      <asp:label id=TopicDescription runat="server" Visible="<%#Not(ModeEdit) %>"/>
      <asp:textbox CssClass="formSubtitle" id=TopicDescriptionInput runat="server" Visible="<%#ModeEdit %>"/>
    </TD>

  <asp:repeater id=PollControl runat="server">
<ItemTemplate>
<TR>
  <TD colspan="3">
      <input type=hidden id="POLLquestionID" value=`<%# DataBinder.Eval(Container.DataItem, "ID") %>` runat=server/>
      <asp:Label Runat=server CssClass="elementText" Text=`<%#(DataBinder.Eval(Container.DataItem, "Question")) %>` Visible="<%#not(ModeEdit)%>"/>
    <asp:TextBox CssClass="formText" ID=QuestionInput Text=`<%#DataBinder.Eval(Container.DataItem, "Question")  %>` Runat=server Visible="<%#ModeEdit%>"/>
  </TD>
  </TR>
  <TR>
  <TD width="210px">
    <asp:Image id=ImageButton1 runat="server" ImageUrl="img/bar.gif" Width=`<%# WebControls.Unit.Parse(2*Cint(DataBinder.Eval(Container.DataItem, "Hits"))) %>` Height="8px"/><asp:Image id="ImageButton2" runat="server" ImageUrl="img/bar_end.gif" Width="5px" Height="8px" />
</TD>
<td align="left" width="20px">
<asp:Label CssClass="elementText" id="Linkbutton2" Runat="server" Text=`<%#(DataBinder.Eval(Container.DataItem, "Hits")&"%")%>`/>
<td align="left" width="35px" valign="middle" align="right">
<asp:ImageButton ImageUrl="img/delete.gif" AlternateText="Smazat odpověď" CommandName="Delete" ID="DeleteQuestionButton" Visible="<%#Not(ModeEdit)%>" Runat=server />
</td>
</TR>
</ItemTemplate>
</asp:repeater>

<TR>
<TD colSpan=3>
<asp:linkbutton id=NewQuestionButton Visible="<%#Not(ModeEdit)%>" CssClass="elementLink" Text="Přidat odpověď" Runat="server"/>
</TD>
</TR>

<TR>
        <TD class=elementText align=middle colSpan=3>
          Celkove hlasovalo <B><asp:label id=TotalVotes Runat="server"/></B>lidí
        </TD>
      </TR>
      </table>
  <asp:button id=NewButton runat="server" Visible="<%#(not(ModeEdit)) %>" Text="Nová anketa"/>
<cc1:confirmbutton id=DeleteButton runat="server" Visible="<%#(ModeEdit)%>" Text="Smazat" ConfirmMessage="Určitě chcete záznamy o anketě odstranit?"/>
<asp:button id=SaveButton runat="server" Visible="<%#(ModeEdit)%>" Text="Uložit"/>
<asp:button id=CancelButton runat="server" Visible="<%#(ModeEdit)%>" Text="Storno"/>
<asp:button id=EditButton runat="server" Visible="<%#(not(ModeEdit)) %>" Text="Upravit"/>
</asp:panel>

Studiem samotného kód zjistíte, že pro vykreslení ankety je opět použita komponenta Repeater. Úvod do této komponenty jsme absolvovali v prvním dílu o anketě a více si o ní popovídáme v příštím dílu.

Dnes se ještě na závěr pokusme rozluštit předchozí hromadu kódu. Zkusím základní rozdělení na funkční části:

  • Na začátku můžeme najít 2 komponenty Label zobrazující název a popis ankety.
  • V prostřední části je Repeater, který vykreslí pro každou otázku odpovídající HTML kód. Obsahuje název, procentuální vyjádření počtu hlasů a samozřejmě odpovídající část grafu. Vkládá také u každé otázky tlačítko umožňující vymazat danou otázku.
  • V poslední části nalezneme tlačítka pro přidání nové otázky, tlačítka pro editaci, mazání, uložení a zrušení změn pro celou anketu.

Doufám, že vám tato osnova pomůže v navigaci kódem. Funkce FillPoll, která nám propojí HTML s daty, je velice jednoduchá:

Sub FillPoll()
  CurrentPoll = New Poll(GetTopicID)
  TopicName.Text = CurrentPoll.Name
  TopicNameInput.Text = CurrentPoll.Name
  TopicDescription.Text = CurrentPoll.Description
  TopicDescriptionInput.Text = CurrentPoll.Description
  TotalVotes.Text = CStr(CurrentPoll.TotalVotes)
  PollControl.DataSource = CurrentPoll.Results
  PollSection.DataBind()
End Sub

Function GetTopicID() As Integer
  Return CInt(Topics.SelectedItem.Cells(0).Text)
End Function

Private Sub Topics_SelectedIndexChanged(ByVal sender As System.Object, ByVal e As DataGridCommandEventArgs) Handles Topics.ItemCommand

  If e.Item.ItemIndex >= 0 Then
    Topics.SelectedIndex = e.Item.ItemIndex
    FillPoll()
  End If
End Sub

Vidíme, že funkce FillPoll vytváří instanci naší třídy Poll vytvořené v minulém dílu. ID aktuálně vybrané ankety načte funkce GetTopicID z prvního sloupce DataGridu Topics. Poté jsou informace doplněny do stránky (název a popis). Nakonec je objektu PollControl, který představuje náš Repeater, přiřazena tabulka výsledků Results. Ten data v ní zobrazí na stránku. Vše ukončíme provázáním dat v celé oblasti příkazem PollSection.DataBind().

Závěr

Příště komponentu dokončíme. Zbývá nám umožnit editaci, přidávání a mazání anket a jednotlivých otázek.

Váš názor Další článek: Intel bude opět snižovat ceny

Témata článku: Software, Programování, Ankety, Sloupec, Tlačítko umožňující, Dino, Order, Jednotlivý díl, První článek, Repeater, Nota, Master, Hits, Anketa, Detail, Editace, Select, Button, Label, Nejlepší auto, Jednotlivé komponenty, Zobrazená data, From


Určitě si přečtěte

Podívejte se, jak vypadá mikrofon nebo blecha pod elektronovým mikroskopem

Podívejte se, jak vypadá mikrofon nebo blecha pod elektronovým mikroskopem

** Z Brna pochází třetina světové produkce elektronových mikroskopů ** První československý kus vyrobila Tesla už v 50. letech ** Dnes na ni navazuje třeba brněnský Tescan

Jakub Čížek | 19

Pojďme programovat elektroniku: České chytré zásuvky Netio pro kutily i firmy

Pojďme programovat elektroniku: České chytré zásuvky Netio pro kutily i firmy

** Wi-Fi zásuvky nevyrábí pouze Čína ** Vyzkoušeli jsme českou Netio PowerCable ** Je přímo určená pro vývojáře, má totiž jednoduché JSON API

Jakub Čížek | 43


Aktuální číslo časopisu Computer

Megatest: 20 powerbank s USB-C

Test: mobily do 3 500 Kč

Radíme s výběrem routeru

Tipy na nejlepší vánoční dárky