# Programmeren in ASP.NET/GridView

De GridView is de opvolger van de DataGrid-control in ASP.NET 2.0. Hoewel de DataGrid-control nog altijd bestaat in ASP.NET 2.0 is het beter om de GridView-control te gebruiken. Het voordeel van de GridView-control is dat je in veel gevallen een taak kan verrichten zonder code te schrijven. De GridView-control kan records tonen, sorteren, pagineren en wijzigen zonder de code die je daarvoor nodig had bij de DataGrid. Daarenboven laat de GridView toe om records te sorteren en pagineren zonder een postback van de hele pagina naar de server.

## De GridView-control gebruiken

Om te beginnen maak je een nieuwe pagina gridview.aspx. Vervolgens sleep je een GridView-control (vanuit de "Data"-sectie van de toolbox) op je pagina.
Open nu het Smart Tag-paneel, kies "Choose Data Source…", en vervolgens "<New data source…>". Je krijgt de Datasource Configuration Wizard, net zoals bij het databinden van lijsten.
Kies SQL Data Source, en vervolgens "New connection...". Selecteer de juiste SQL Server databank (bijvoorbeeld "Pubs" op (local) met Windows Integrated Security. Vervolgens krijg je de optie de connectiestreng te bewaren in het web.config-bestand. Dit gebeurt altijd bij SQL Server-databronnen (niet bij Access). We zullen dit later gebruiken, voorlopig vinken we dit af.
In de volgende stap moet je het SELECT-statement samenstellen. Kies “SELECT * FROM Publishers”. Merk op dat je optioneel een DISTINCT-clause (het aankruisvakje "Return only unique rows"), een WHERE-clause en een ORDER BY-clause kan instellen. We zullen die later gebruiken. Via "Advanced" kan je eventueel de overeenkomstige INSERT-, UPDATE-, en DELETE- commando's genereren. Je hebt deze nodig als je ook gegevens wilt wijzigen. Als je klaar bent, ziet de HTML-code er ongeveer zo uit:
gridview.aspx
Merk op dat er weer geen enkele regel code in de pagina staat. De GridView gebruikt de SqlDataSource-control om de records terug te vinden. De GridView-control is verbonden met de SqlDataSource-control via de DataSourceID-property.

### Oefening

* Maak in Access een tabel met adresgegevens. Maak een pagina die de adressen uit de tabel toont.

## De connectiestring in web.config bewaren

Als je dezelfde connectie in meerdere pagina's gebruikt, kan je beter kiezen voor de optie om ze in web.config te bewaren. Het voordeel is dan dat je ze later maar op één plaats moet veranderen indien nodig. In web.config zal deze instelling toegevoegd worden:
Merk op dat er hiervoor sinds ASP.NET 2.0 een speciaal element <connectionStrings> bestaat.
In de pagina gebruik je een speciale syntax met een dollarteken om aan te geven dat de variabele uit web.config komt:
In dit geval wordt dit (in de pagina):

## Alleen bepaalde velden tonen

Na het doorlopen van de wizard toont de GridView alle velden die in de datasource voorkomen. Achteraf kan je dit nog veranderen. In Visual Web Developer kan je de kolommen het gemakkelijkst aanpassen door in het smart tag-menu te klikken op "Edit columns...".
De GridView ondersteunt de volgende veldtypes:
* BoundField – toont gewoon het veld als een string.
* ButtonField – toont een knop.
* CheckboxField – toont een aankruisvak als het veld een boolean is.
* CommandField – genereert een knop voor een commando zoals Edit, Update, of Cancel.
* HyperLinkField – toont een hyperlink.
* ImageField – toont een afbeelding als het veld de url van een afbeelding bevat.
* TemplateField – geeft je volledige controle om zelf de inhoud van de kolom te bepalen.
De GridView hieronder gebruikt twee BoundFields om de namen uit de Employees-tabel te tonen (Northwind-databank). gridviewvelden.aspx
Merk op dat het BoundField een NullDisplayText-property heeft. Deze bepaalt de tekst die getoond wordt als een kolom een null-waarde bevat.
Opmerkingen
* Als je een datum toevoegt bij de BoundFields, dan wordt deze getoond als "1/2/2005 0:00:00". Meestal wil je dit laatste deel (de tijd) er niet bij. Daarom kan je bij een BoundField de DataFormatString-property instellen, die gebruikt wordt om de databankgegevens in een stringpatroon te gieten. Zet ook HtmlEncoding=false. Om een datumveld te tonen als een korte datum (dd/mm/yy), gebruik je bijvoorbeeld het patroon {0:d}.
* Je kan ook het <colomns>-element met alle BoundFields compleet verwijderen en AutoGenerateColumns op True zetten. Dan maakt de grid zijn kolommen automatisch aan bij de databinding (maar dan heb je verder geen controle erover).

### Oefeningen

* Maak opnieuw een pagina die de adressen uit de tabel AdresTabel (databank Adressen) toont. Toon alleen de naam en de voornaam
* Maak een kleine tabel Leveranciers met 2 kolommen: Naam, URL. Maak een GridView die de tabel toont. De URL wordt getoond als hyperlink (de site wordt getoond als een popup).
* Zelfde oefening als oefening 1, maar nu toon je de naam en de voornaam samen als hyperlink. Als je erop klikt krijg je een tweede (popup-) venster, met het ganse adres.

## Gegevens zoeken met de GridView-control

Net zoals bij een Repeater kan je de GridView gebruiken om alleen bepaalde gegevens uit de tabel te tonen, bijvoorbeeld alleen de adressen van een bepaalde gemeente.

### Oefeningen

* Maak een pagina met een tekstvak, een knop en een gridview. De gebruiker vult een gemeente in en bij het klikken op de knop toont de GridView alle adressen uit die gemeente. Gebruik de tabel met adressen.
* Zelfde opdracht, maar nu met een dropdownlist en een GridView. De dropdownlist toont alle gemeentes uit de tabel met adressen). Bij het selecteren van een gemeente toont de GridView alle adressen uit die gemeente.

## Lege tabel

De GridView heeft ook een <EmptyDataTemplate> dat je kan gebruiken om een boodschap te tonen als er geen gegevens gevonden worden.

## Sorteren en pagineren met de GridView-control

Sorteren en pagineren met de GridView-control is nog gemakkelijker dan met de DataGrid. Je gebruikt hiervoor de AllowSorting- en de AllowPaging-property's, en ook wederom is het niet nodig om aparte code te schrijven:
gridviewsort.aspx
De GridView-control ondersteunt ook automatisch oplopend en aflopend sorteren. Als je op een kolomhoofd klikt, verandert dus de volgorde. Als Paging aan staat, heb je ook meerdere mogelijkheden voor de navigatie dan bij de DataGrid. Je kan deze instellen via de PagerSettings- and PagerStyles- property's. Naast NextPrevious en Numeric heb je ook NextPreviousFirstLast en NumericFirstLast navigatie. Bovendien kan je in plaats van gewone links ook afbeeldingen gebruiken:

### Oefening

* Breid de pagina van de vorige oefening uit met zowel sorteren als pagineren.

## Client-side sorteren en pagineren

Met de GridView-control kan je ook sorteren en pagineren zonder dat er een postback nodig is van de ganse pagina naar de server.
Je doet dit door de EnableSortingAndPagingCallbacks-property op True in te stellen.
gridviewcallback.aspx
Deze pagina toont de datum en de tijd, en de inhoud van de Titles-tabel. Als je sorteert of pagineert in de GridView, merk je dat de tijd niet verandert op de pagina.
Achter de schermen gebruikt de GridView het XMLHTTPRequest-object van Microsoft Internet Explorer om te communiceren met de web-server. Dit object wordt ondersteund vanaf Internet Explorer 5.0. In andere browsers werkt het sorteren en pagineren ook wel, maar met een gewone postback.

## Selecteren met de GridView-control

Met de GridView kan je ook gegevens selecteren. Je doet dit door in de smart tag "Enable Selection" aan te vinken. Als je formattering toegevoegd hebt, worden geselecteerde items in een andere kleur getoond. Je kan ook het OnSelectedIndexChanged-event opvangen om bij elke selectie een actie te ondernemen, bijvoorbeeld in een apart panel meer informatie tonen. Je kan ook de AutoGenerateSelectButton-property op true zetten. De GridView heeft geen SelectedItemTemplate zoals de DataList. Er is wel een SelectedRowStyle.

## Wijzigen met de GridView-control

Met de GridView kan je ook de gegevens wijzigen. Je doet dit door in de smart tag "Enable Editing" aan te vinken. Je kan ook de AutoGenerateEditButton-property op true zetten. De DataSource-control waaraan de GridView gekoppeld is, moet wel een geldige UpdateCommand-property hebben (dit moet een SQL UPDATE-commando zijn). Je kan dit commando zelf toevoegen, of je kan de wizard gebruiken:
* Selecteer de DataSource-control, klik op de smart tag en kies "Configure Data Source..."
* Klik twee keer op Next
* Na het instellen van het SELECT-statement, klik je op "Advanced...". Je kan nu INSERT-, UPDATE-, en DELETE- commando's automatisch laten aanmaken (als deze optie grijs is komt dit doordat het SELECT-statement geen primary key bevat).
* Werk de wizard verder af.
gridviewedit.aspx
Merk op dat er een UpdateCommand, een DeleteCommand en een InsertCommand toegevoegd zijn, met opgave van de overeenkomstige parameters. De databank wordt automatisch bijgewerkt, en in tegenstelling tot de DataGrid moet je geen code schrijven.
Als je de links Nederlandstalig wil hebben kan je het CommandField als volgt aanpassen:
In de meeste gevallen kan de GridView automatisch de juiste datatypes van de kolommen die je wil bijwerken bepalen. In het voorbeeld hierboven wordt de waarde van het ProductID tekstvak automatisch omgezet in een integer, en de waarde van het Discontinued-aankruisvak wordt automatisch omgezet naar een BIT vóór het UPDATE-commando uitgevoerd wordt.
Opmerkingen
* Merk op dat de BoundColumn voor ProductID ingesteld staat met ReadOnly="True". Automatisch wordt dan bij het wijzigen van gegevens in die kolom geen tekstvak getoond, maar een gewoon label. Dit is nuttig voor bijvoorbeeld een "Autonumber"-veld, dat toch niet kan gewijzigd worden.
* Bij een AccessDataSource wordt soms het verkeerde INSERT-commando aangemaakt als er AutoNumber-velden zijn. Je moet dit dan manueel wijzigen. Als er bijvoorbeeld staat InsertCommand="INSERT INTO tbl ([ID], [Naam], [Adres]) VALUES (?,?,?)" waarin ID een AutoNumber-veld is, dan moet je dit zelf veranderen in InsertCommand="INSERT INTO tbl ([Naam], [Adres]) VALUES (?,?)" Verwijder dan ook ID bij de InsertParameters. Bij UPDATE en DELETE heb je dit probleem niet.
* Als het tekstvak te klein is, kan je via de ControlStyle (een eigenschap van de BoundColumn) deze breedte groter instellen.
* Visual Web Developer 2008 Express. Error bij opslaan datumveld: "Serverfout in toepassing /Test_DB1. De gebruikte versie van SQL Server biedt geen ondersteuning voor gegevenstype date". Oplossing: in properties datakeynames zetten, en wijzig sqlDataSource1.UpdateQuery property datatype van object naar datetime.

## Verwijderen met een GridView

Kies Edit Columns in het menu van de GridView, selecteer CommandField, en het subtype Delete. Voeg die kolom toe. Deze kolom zal automatisch een Delete-knop produceren in elke rij, en bij het klikken op die knop wordt die rij verwijderd.
Het verwijderen gebeurt echter onmiddellijk, zonder bevestiging. Dit betekent dat als je per ongeluk op de delete-knop klikt je het record verwijdert. En dit kan je meestal niet ongedaan maken.
Daarom is het goed bij verwijderen altijd bevestiging te vragen. Een goede manier om dit te doen is een client-side JavaScript toe te voegen aan de delete-knop. Dit kan gemakkelijk door het CommandField om te zetten in een TemplateField (zie verder), en dan op de delete-knop de property OnClientClick in te stellen op

### Oefening

* Maak een GridView voor de AdresTabel zodat je de adressen ook kan wijzigen en verwijderen
* Breid de oefening met de tabel Leveranciers (met 2 kolommen: Naam en URL) uit zodat je de naam en de url ook kan wijzigen.

## Een TemplateField toevoegen

Je kan ook zoals bij de DataList met een EditItemTemplate werken, maar dan moet je een TemplateField gebruiken. Soms wil je nog meer controle over hoe de kolommen getoond worden. Je wil bijvoorbeeld naast het tekstvak een klein pictogram, of je wil op sommige vakken of knoppen een ToolTip met uitleg toevoegen. Dergelijke problemen kan je oplossen met een TemplateField.
Selecteer de grid weer, kies "Edit fields..." in de smart tag, en selecteer de kolom "ProductName" in de lijst onderaan. Klik nu op "Convert this field into a TemplateField". Dit is het resultaat:
Deze code doet net hetzelfde als de vorige, maar binnen het TemplateField zijn er nu verschillende templates, die je elk apart manueel (en via het designvenster – kies Edit Templates...) kan aanpassen.
Merk ook op hoe de gegevens gekoppeld worden aan de juiste control binnen het template: met Bind en Eval. Bind wordt gebruikt voor binding in twee richtingen (lezen en schrijven), Eval voor binding in één richting: alleen lezen.

### Oefeningen

* Breid de oefening met de Leveranciers-tabel uit zodat in de plaats van de url een afbeelding (pictogram) getoond wordt. De afbeelding werkt wel nog als hyperlink om de website te openen. Je kan hier geen HyperlinkField gebruiken (geen afbeelding) en ook geen ImageField (geen hyperlink), maar het kan wel met een TemplateField. Tip: maak eerst een HyperlinkField, en zet die om naar een TemplateField, dan heb je minder werk.
* Breid de tabel Leveranciers uit met een 3e kolom: email. Maak een GridView die ook het e-mailadres toont als een hyperlink. Tip: voor het e-mailadres moet je een templatefield gebruiken, omdat e-mailadressen in een hyperlinkfield voor de veiligheid geblokkeerd worden.
* Wijzig de pagina waar je adresgegevens kan wijzigen zodanig dat de link “Wijzigen” een ToolTip toont met meer uitleg.
* Toon uit AdresTabel de naam en het telefoonnummer en toon vóór elk nummer het telefoonsymbool (als gif). Als de persoon geen telefoon heeft, toon je niets.

## Foutverwerking

De voorbeelden hierboven bevatten geen code voor foutverwerking. Bij databanken kunnen er allerlei fouten optreden, en zoals het nu is loopt de pagina in de soep.
Om dit te vermijden, kan je foutverwerking toevoegen door een event handler aan te maken voor het RowDeleted-event en het RowUpdated-event van de GridView, en kan je daar de fout gracieus opvangen. Om ook foutverwerking te doen bij het tonen van de gegevens (SELECT-commando), moet je op de DataSourceControl het OnSelected-event afvangen.
De pagina hieronder illustreert dit:
gridviewerror.aspx
De GridViewDeleted-methode wordt uitgevoerd telkens een GridView-rij verwijderd wordt en de GridViewUpdated-methode telkens een rij bijgewerkt wordt. De tweede parameter van deze methodes (GridViewUpdatedEventArgs en GridViewUpdatedEventArgs) heeft telkens property's die je kan gebruiken voor foutverwerking:
* de Exception-property geeft je de exception die optrad tijdens het UPDATE-commando
* de ExceptionHandled-property kan je gebruiken om aan te geven of je zelf de fout wilt verwerken. Als je deze property op true zet, dan zal de server geen fout meer tonen. Als ExceptionHandled false is, dan zal de exception daarna toch doorgaan, en krijgt de gebruiker de gewone gebruikersonvriendelijke foutmelding van de server.

## Master/Detail met twee GridView-controls

Tenslotte maken we een Master/Detail formulier door twee GridView-controls te combineren. Met de eerste GridView-control toon je een lijst met leveranciers (tabel Suppliers uit Northwind). Als je een leverancier selecteert, dan zal de tweede GridView-control alle producten tonen van die leverancier. Maak de eerste GridView aan met alleen de kolommen SupplierID, CompanyName en CompanyContact. Selecteer ook de optie "Enable Selection". De tweede GridView-control toont de producten van de leverancier die geselecteerd is in de eerste GridView. Deze koppeling gebeurt door bij de DataSource-control van de tweede GridView een WHERE-conditie op te geven.
Merk op dat de pagina twee SqlDataSource-controls bevat; één voor elke GridView-control.
Opmerking
* Je kan de tweede GridView zelfs binnen de eerste tonen, dit noemt men een geneste GridView. Je kan ook een GridView binnen een Repeater of een DataList tonen, daar is het gemakkelijker om de bladschikking dan compact in te stellen.

## OnRowDataBound

Net zoals het ItemDataBound-event bij de DataList kan je het RowDataBound-event gebruiken om nog aanpassingen te doen op het moment dat de gegevens in de rijen terechtkomen.
Het RowDataBound-event krijgt als tweede parameter (e) een object van het type GridViewRowEventArgs. Dit kan je gebruiken in je code om toegang te krijgen tot het item:
* e.Row is de GridViewRow. Dit is de rij van de GridView-tabel die overeenkomt met één rij in de databanktabel.
* e.Row.RowType is het type van de rij. Hiermee kan je controleren of het een gewone rij is (DataRow), een header-rij of een footer-rij.
* e.Row.RowState is de status van de rij. Hiermee kan je controleren of het een normale rij is (Normal), een alternerende rij, een edit-rij of een geselecteerde rij.
* e.Row.RowIndex is de index van het item, te beginnen vanaf 0. Dit geldt alleen voor gewone items en alternating items.
* e.Row.DataItem is een object dat overeenkomt met de rij uit de databank waarmee het item overeenstemt. Dit geldt alleen voor gewone items en alternating items.
* DataBinder.Eval(e.Row.DataItem,"naamveld") geeft de waarde van het opgegeven veld in de databank voor het item.
* Om toegang te krijgen tot een bepaalde control binnen een BoundField, kan je e.Row.Cells(i).Controls(0) gebruiken. De index i is daarin het volgnummer van de kolom (te beginnen vanaf 0).
* Om toegang te krijgen tot een bepaalde control binnen een TemplateField, kan je e.Row.Cells(i).FindControl("IDControl") gebruiken. De index i is daarin het volgnummer van de kolom en IDControl is de ID van de control binnen het template.

### Oefeningen

* Zoek op het Internet hoe je onderaan een GridView totalen kunt tonen. Pas dit toe in een pagina.
* Zoek op het Internet hoe je in een GridView de records kan groeperen. Maak een pagina met een GridView waarbij de adressen per gemeente worden gegroepeerd.
* Zoek op het Internet hoe je een GridView kan maken waar je alle rijen tegelijk kan wijzigen. Pas dit toe in een pagina.