Programmeren in ASP.NET/Custom controls
Inleiding
[bewerken]Custom controls zijn gecompileerde controls die door de programmeur zelf ontworpen worden en die werken zoals web-controls.
Ze hebben meer mogelijkheden dan de simpele user-controls uit het vorige hoofdstuk:
- Ze kunnen toegevoegd worden aan de Toolbox in Visual Web Developer
- Je kan hun eigenschappen rechtstreeks instellen in de editor (met het property-paneel)
- Ze kunnen gebruikt worden zonder de broncode
- Ze kunnen gedeeld worden tussen verschillende applicaties op dezelfde server.
Custom controls kunnen op drie manieren gemaakt worden:
- door een bestaande control als basis te nemen en daarvan een nieuwe custom control af te leiden (bv. door een gespecialiseerd tekstvak af te leiden van asp:textbox). Dit noemt men een derived custom control.
- door een nieuwe custom control samen te stellen uit twee of meer bestaande controls. Dit noemt men composite custom control.
- door een nieuwe custom control te maken vanaf nul (afgeleid van de basis control klasse Webcontrol of Control) Dit noemt men een full custom control.
Opmerking:
- Wanneer je custom controls gebruikt in Visual Web Developer, dan is een aantal extra "designer"-methodes nodig om de control ook te laten werken in de editor zelf. Dit zorgt ervoor dat de control er al min of meer als zichzelf uitziet in de editor (anders krijg je alleen een weinig zeggend label). In deze cursus worden deze methodes niet besproken. Zonder deze methodes werkt de control normaal als je hem test vanaf een website, hij werkt alleen niet binnen de editor.
Een simpele custom control maken
[bewerken]In het vorige hoofdstuk zag je dat je voor een user-control een .ascx-bestand gebruikt. Voor een custom control maak je een .vb-bestand (of .cs als je met C# werkt). Alles wat je moet doen om een simpele custom control te maken is een klasse aanmaken die afgeleid is van Control (of WebControl) en de Render()-methode daarvan te overschrijven. De Render()-methode heeft één argument van type System.Web.UI.HtmlTextWriter. De HTML die je control naar de client wil sturen moet doorgegeven worden als een string naar de Write()-methode van HtmlTextWriter.
Kies File-New File in Visual Web Developer, en kies vervolgens Class en geef het bestand de naam simpel.vb. Merk op dat er voor dit bestand een folder App_Code aangemaakt wordt. Alle custom controls (en klasses) komen automatisch in deze folder terecht.
Het volgende voorbeeld toont een simpele control die een boodschapstreng rendert.
simpel.vb
Imports System
Imports System.Web
Imports System.Web.UI
Namespace Wikibooks
Public Class Simpel : Inherits Control
Protected Overrides Sub Render(writer As HtmlTextWriter)
writer.Write("<H2>Welkom bij Wikibooks!</H2>")
End Sub
End Class
End Namespace
In de pagina moet je nu de control registreren ongeveer zoals bij een user control. Je moet een TagPrefix opgeven en een Namespace. De control wordt automatisch opgezocht in de folder App_Code via de namespace.
simpel.aspx
<%@ Page Language="VB" %>
<%@ Register TagPrefix="cc1" Namespace="Wikibooks" %>
<html>
<head>
<title>Simpele custom control</title>
</head>
<body>
<form runat="server">
<cc1:Simpel id="MijnControl" runat=server/>
</form>
</body>
</html>
Een assembly maken
[bewerken]Om een control aan de toolbox toe te voegen moet hij eerst gecompileerd worden. Bij het compileren wordt de code omgezet in een zogenaamd assembly-bestand met extensie .dll (dynamic link library) in de bin-folder.
Maak een tekstbestand en noem het maaksimpel.bat.
maaksimpel.bat
SET source=App_Code\simpel.vb SET target=bin\EenvoudigeControls.dll SET assemblies=system.dll,system.web.dll SET compiler=%WINDIR%\Microsoft.NET\Framework\v2.0.50727 MKDIR bin %COMPILER%\vbc /t:library /out:%target% /r:%assemblies% %source% PAUSE
(controleer wel of de versie van het framework 2.0.50727 overeenstemt met die op jouw server)
Hierin is:
source | de naam van het bronbestand met de code |
target | de naam (en het pad) van de assembly die moet gemaakt worden |
assemblies | de modules die je nodig hebt om de code te compileren (als je bijvoorbeeld ook databanken gebruikt, moet je hier nog system.data.dll aan toevoegen). In de Helpmodule van je editor kan je opzoeken tot welke assembly een bepaald object behoort. |
compiler | de locatie van de Visual Basic compiler (vbc.exe) op jouw computer |
pause | niet verplicht, maar zorgt ervoor dat het venster niet onmiddellijk sluit, zodat je eventuele fouten nog kan lezen. |
Dubbelklik in Windows Explorer op dit bestand om het uit te voeren. Het programma vbc (Visual Basic Compiler) zal nu het bestand simpel.vb compileren en de uitvoer EenvoudigeControls.dll wordt in een subfolder "bin" geplaatst. Eventuele fouten worden gemeld.
Onthoud dat telkens je wijzigingen aanbrengt in simpel.vb, je het bestand opnieuw moet compileren.
Voor andere compilaties moet je maaksimpel.bat kopiëren en aanpassen. Stel iedere keer "source" en "target" in op de juiste waarde.
Pas nu simpel.aspx aan, zodat die met de assembly werkt, en niet met de code in App_Code. De referentie Assembly="EenvoudigeControls" in simpel.aspx moet verwijzen naar deze assembly (zonder de DLL-extensie, en zonder bin). Verwijder het bestand simpel.vb uit de App_Code-folder, anders krijg je nu een conflict.
simpel.aspx
<%@ Page Language="VB" %>
<%@ Register TagPrefix="cc1" Namespace="Wikibooks"
Assembly="EenvoudigeControls" %>
<html>
<body>
<form method="POST" action="Simpel.aspx" runat=server>
<cc1:Simpel id="MijnControl" runat=server/>
</form>
</body>
</html>
ASP.NET zoekt automatisch naar alle dll's in de bin-folder (en alleen daar).
Eenmaal de control gecompileerd is kan je hem toevoegen aan de toolbox in Visual Studio. Klik rechts op de toolbox, en kies "Choose items...". Blader naar je dll en voeg hem toe.
Als je deze control nu in een ander project op een pagina sleept, zal Visual Studio automatisch op dat moment de dll ook kopiëren naar de bin-folder van die applicatie.
Let op: als je achteraf de dll wijzigt, moet je de bijgewerkte dll zelf kopiëren naar de applicaties die ze gebruiken.
Eenvoudige property's geven aan de control
[bewerken]We werken hier weer met getters en setters omdat ze toelaten data te verbergen en ze worden ondersteund door visuele designers zoals Visual Studio.
Het volgende voorbeeld toont hoe je simpele property's toevoegt. Het voorbeeld maakt een property Grootte met het type Integer.
simpelproperty.vb
Imports System
Imports System.Web
Imports System.Web.UI
Namespace Wikibooks
Public Class SimpelProperty : Inherits Control
Private _grootte As Integer
Public Property Grootte As Integer
Get
Return _grootte
End Get
Set
_grootte = Value
End Set
End Property
Protected Overrides Sub Render(writer As HtmlTextWriter)
writer.Write("<H" & _grootte & ">Wikibooks</H" & _grootte & ">")
End Sub
End Class
End Namespace
simpelproperty.aspx
<%@ Page Language="VB" %>
<%@ Register TagPrefix="cc1" Namespace="Wikibooks" %>
<html>
<head>
<title>Simpele custom control</title>
</head>
<body>
<form runat="server">
<cc1:SimpelProperty Grootte="3" runat="server" />
</form>
</body>
</html>
Als je deze control toevoegt aan je pagina, dan kan je de property Grootte terugvinden in het Property Panel.
Oefeningen
[bewerken]- Maak een control die een horizontale balkgrafiek produceert door een afbeelding te herhalen of uit te rekken. De lengte van de grafiek is een property.
Een samengestelde control maken
[bewerken]Je kan nieuwe controls ontwerpen door bestaande controls samen te voegen. Een typisch voorbeeld is een zogenaamde "spin"- of "up-down"-control. Dit is een control waar je een getal kan invoeren door het in te tikken, of door het in te stellen met twee knoppen: "omhoog" en "omlaag". Deze control bestaat uit 3 bestaande controls: één invoervak en twee knoppen.
Samengestelde controls komen overeen met user-controls die ontworpen worden met ASP.NET-pagina-syntax. Het grootste verschil tussen user-controls en samengestelde controls is dat user-controls bewaard worden als .ascx tekstbestanden, terwijl samengestelde controls gecompileerd worden en daarna bewaard worden in assembly's.
De stappen in het ontwerp van een samengestelde control zijn:
- Overschrijf de
CreateChildControls
-methode (overgeërfd van Control) om instanties aan te maken van de benodigde child-controls en voeg deze instanties toe aan de Controls-collectie. - Als je samengestelde control meerdere keren kan voorkomen op dezelfde pagina, en je control bevat bijvoorbeeld een tekstvak, dan bestaat er het gevaar dat je op die pagina meerdere tekstvakken krijgt met dezelfde ID. Om dit te vermijden moet je de System.Web.UI.INamingContainer-interface implementeren (zie het voorbeeld hieronder). Wanneer die interface wordt gebruikt door een control, zal het ASP.NET-pagina-framework automatisch nieuwe namen geven aan de child-controls.
Je moet de Render()-methode hier niet overschrijven, omdat de child-controls de rendering voor hun rekening nemen. Je kan property's maken die op hun beurt property's van de child controls bepalen.
Het volgende voorbeeld maakt een samengestelde control, Samengesteld1, die een LiteralControl combineert met een TextBox. Samengesteld1 stelt een eigen property, Waarde, van type Integer, ter beschikking die een string doorsluist van en naar de Text-property van TextBox.
samengesteld1.vb
Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Namespace Wikibooks
Public Class Samengesteld1 : Inherits Control : Implements INamingContainer
Public Property Waarde As String
Get
Me.EnsureChildControls()
Dim tb1 As TextBox = Me.FindControl("tb1")
Return tb1.Text
End Get
Set
Me.EnsureChildControls()
Dim tb1 As TextBox = Me.FindControl("tb1")
tb1.Text = Value
End Set
End Property
Protected Overrides Sub CreateChildControls()
' maak hier de child-controls aan
Dim lbl1 As New Label()
lbl1.Text = "Waarde"
Me.Controls.Add(lbl1)
Dim tb1 As New TextBox()
tb1.Text = "0"
tb1.ID="tb1"
Me.Controls.Add(box)
End Sub
End Class
End Namespace
samengesteld1.aspx
<%@ Page Language="VB" %>
<%@ Register TagPrefix="cc1" Namespace="Wikibooks" %>
<script language="VB" runat=server>
Private Sub VerhoogBtn_Click(Sender As Object, E As EventArgs)
MijnControl.Waarde = MijnControl.Waarde + 1
End Sub
</script>
<html>
<head>
<title>Samengestelde custom control</title>
</head>
<body>
<form runat="server">
<cc1:Samengesteld1 id="MijnControl" Waarde="0" runat="server" />
<br>
<asp:button text="Verhoog" OnClick="VerhoogBtn_Click" runat=server/>
</form>
</body>
</html>
Oefening
[bewerken]- Maak een nummervak-control, dit is een tekstbox waar je alleen cijfers kan invullen. Gebruik een tekstvak samen met een validatorcontrol. Zorg ook dat het cijfer rechts uitgelijnd is.
Events verwerken in een samengestelde control
[bewerken]Een samengestelde control kan events verwerken die veroorzaakt worden door zijn child controls. Dit gebeurt door event handling-methodes te voorzien voor de events die door de child controls opgewekt worden.
In de volgende samengestelde control, Samengesteld2, zit de verhoogknop in de samengestelde control zelf. Daarom moet de samengestelde control ook zelf de event handling-methode voorzien voor het Click-event van de knop. Deze methode verhoogt de property Waarde van Samengesteld2.
Je kan deze handler niet koppelen aan de knop op de normale manier, met een OnClick-attribuut in de tag, want er zijn hier geen tags. In de plaats daarvan wordt AddHandler gebruikt in de CreateChildControls()-methode. Het eindresultaat is een control die zijn eigen eventverwerking doet. Wanneer de Verhoog knop aangeklikt wordt, wordt de waarde in het tekstvak verhoogd.
samengesteld2.vb
Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Namespace Wikibooks
Public Class Samengesteld2 : Inherits Control : Implements INamingContainer
Public Property Waarde As Integer
Get
Me.EnsureChildControls()
Dim tb As TextBox = CType(Me.FindControl("tb1"),TextBox)
Return CInt(tb.Text)
End Get
Set
Me.EnsureChildControls()
Dim tb As TextBox = CType(Me.FindControl("tb1"),TextBox)
tb.Text = CStr(Value)
End Set
End Property
Protected Overrides Sub CreateChildControls()
Dim tb As New TextBox()
tb.Text="0"
tb.Style("text-align")="right"
tb.ID="tb1"
Me.Controls.Add(tb)
Dim btn As New Button()
btn.Text="Verhoog"
Me.Controls.Add(btn)
AddHandler btn.Click, AddressOf btn_Click
End Sub
Private Sub btn_Click(Sender As Object,e As EventArgs)
Dim tb As TextBox = CType(Me.FindControl("tb1"),TextBox)
tb.Text = tb.Text + 1
End Sub
End Class
End Namespace
samengesteld2.aspx
<%@ Page Language="VB" %>
<%@ Register TagPrefix="cc1" Namespace="Wikibooks" %>
<html>
<head>
<title>Samengestelde custom control</title>
</head>
<body>
<form runat="server">
<cc1:Samengesteld2 id="MijnControl" Waarde="0" runat=server/>
</form>
</body>
</html>
Oefeningen
[bewerken]- Maak een uitklapbaar panel. Normaal zie je een linkbutton "meer info". Als je erop klikt, zie je meer informatie en verandert de link in "minder info".
Eigen events opwekken vanuit een samengestelde control
[bewerken]In het vorige voorbeeld verwerkte de samengestelde control zelf de events van zijn child-controls. Misschien wil je ook in de pagina deze events nog verder verwerken, of wil je een nieuw event maken waarop de pagina kan reageren.
Een samengestelde control kan eigen events hebben, die de control zelf opwekt als gevolg van events die door zijn child controls doorgegeven worden.
Het volgende voorbeeld toont een samengestelde control, Samengesteld3, die een custom event opwekt, Change, als antwoord op de Click-event van de Button-child-control.
Net zoals we reeds zagen bij de user-control, voeg je een Change-event toe, en een protected OnChange-methode. Verder roep je OnChange op als er op de knop geklikt wordt.
samengesteld3.vb
Imports System
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls
Namespace Wikibooks
Public Class Samengesteld3 : Inherits Control : Implements INamingContainer
Public Property Waarde As Integer
Get
Me.EnsureChildControls()
Dim tb As TextBox = CType(Me.FindControl("tb1"),TextBox)
Return CInt(tb.Text)
End Get
Set
Me.EnsureChildControls()
Dim tb As TextBox = CType(Me.FindControl("tb1"),TextBox)
tb.Text = CStr(Value)
End Set
End Property
Protected Overrides Sub CreateChildControls()
Dim tb As New TextBox()
tb.Text="0"
tb.Style("text-align")="right"
tb.ID="tb1"
Me.Controls.Add(tb)
Dim btn As New Button()
btn.Text="Verhoog"
Me.Controls.Add(btn)
AddHandler btn.Click, AddressOf Button1_Click
End Sub
Private Sub Button1_Click(Sender As Object,e As EventArgs)
Dim tb As TextBox = CType(Me.FindControl("tb1"),TextBox)
tb.Text = tb.Text + 1
OnChange(EventArgs.Empty)
End Sub
Public Event Change(Sender As Object, e As EventArgs)
Protected Sub OnChange(e As EventArgs)
RaiseEvent Change(Me,e)
End Sub
End Class
End Namespace
samengesteld3.aspx
<%@ Page Language="VB" %>
<%@ Register TagPrefix="cc1" Namespace="Wikibooks" %>
<script language="VB" runat=server>
Private Sub Samengesteld_Change(Sender As Object, E As EventArgs)
Label1.Text = "De waarde is veranderd!"
End Sub
</script>
<html>
<head>
<title>Samengestelde custom control</title>
</head>
<body>
<form runat="server" >
<cc1:Samengesteld3 id="MijnControl" Waarde="0"
OnChange="Samengesteld_Change" runat=server/>
<asp:Label id="Label1" runat="server" />
</form>
</body>
</html>
Als je deze control toevoegt aan de toolbox in Visual Studio, komt het Change-event ook te voorschijn in de lijst met events die bij de control horen.
Oefeningen
[bewerken]- Maak een custom control die werkt zoals een checkbox, maar die dubbel zo groot is. Doe dit door je eigen afbeeldingen te gebruiken voor "aangekruist" en "niet aangekruist".
Custom controls downloaden
[bewerken]Op het Internet kan je custom controls vinden die je kan downloaden en zelf installeren op je toolbox. Sommige zijn gratis, anderen niet.
Een goede plaats om te zoeken is www.asp.net en vervolgens klikken op "Resources" en "Control Gallery".