ASPNL logo (1 kb)
Saturday, February 04, 2012




Microsoft MVP

.NET Codewise Community
<< vorige | overzicht | volgende >>

ASP.NET onder de Motorkap: Controls maken

Door Michiel van Otegem
29 juni 2009

Ik heb een haat/liefde verhouding met ASP.NET custom controls. Aan de ene kant vind ik de elegantie van custom controls geweldig, maar aan de andere kant zit ik altijd weer te stoeien met de juiste oplossing voor ViewState, event- en postback handling en rendering van HTML. Zo ook weer met een recente control die ik wilde bouwen, een control die ik de AddRemoveListBox noem.

Het idee achter de AddRemoveListBox is simpel. Voor ontwikkelaars die de control gebruiken moet deze control zich gedragen als een ListBox waarbij meerdere items tegelijk geselecteerd kunnen worden. Een voorbeeld van de gebruikersinterface zie je in afbeelding 1.


Afbeelding 1: De AddRemoveListBox

Een vergelijkbare control is wel beschikbaar van 3rd party vendors, maar met een groot ontwikkelteam gaat dat aardig in de papieren lopen. Ook gratis varianten die ik had bekeken deden net niet wat ik wilde. Perfectionistisch en eigenwijs als ik ben dacht ik zelf wel “eventjes” een variant te maken die wel aan mijn eisen voldeed. De belangrijkste eisen:
1) redelijk eenvoudig van opzet;
2) minimaal gebruik van ViewState.
Het resultaat voldoet weliswaar aan m’n eisen en is goedkoper (voor ons team dan) dan kopen, maar toch kostte het meer moeite en zit er eigenlijk een lelijke hack in.

Om de control eenvoudig te houden ben ik uitgegaan van System.Web.UI.WebControls.ListControl. Dit is een abstracte class die alles al in zich heeft voor ListBox-achtige functionaliteit, zoals een collectie van ListItem objecten en ondersteuning voor DataBinding. In feite hoef je alleen de UI te verzorgen. Om de code simpel gebruik ik child controls, zodat de HTML rendering beperkt blijft tot het groeperen van de controls. De opmaak van child controls kun je wijzigbaar maken door de ControlStyle-eigenschap van de controls toegankelijk te maken voor de buitenwereld, zoals in Listing 1.

   1:  public Style AddButtonStyle
   2:  {
   3:      get { return AddButton.ControlStyle; }
   4:  }
Listing 1: De stijl van een child control publiek maken.

Met twee Label-controls voor de kopjes, twee ListBox-controls voor de selectie en twee Button-controls voor de acties was ik klaar en had ik vrij snel een aardig werkende control die goed opgemaakt kon worden. Aan m’n tweede eis was nu echter nog niet voldaan. Aangezien de ListControl al alle items in de ViewState opslaat, is het overbodig dat de twee ListBox-controls voor de geselecteerde- en beschikbare items dat ook doen. Dit is simpel op te lossen door EnableViewState = false in te stellen op deze controls, maar dan werkt de PostBack afhandeling (en dus de hele control) niet meer. Een poging om dit te doen met de IPostBackDataHandler-interface mislukte. Deze interface gebruik je normaal gezien in een custom control om ervoor te zorgen dat data uit een PostBack afgehandeld wordt, maar door alles in child controls te regelen wordt de LoadPostData-methode op deze interface niet aangeroepen… wat nu?

IPostBackDataHandler moet het afhandelen van Postback data elegant maken, doordat je een collectie met data krijg die alleen van toepassing is op de control zelf. Als deze niet werkt, moet je echter anders te werk gaan. Dan is het handig dat ASP.NET gelaagd is opgebouwd is, waardoor je terug kunt grijpen op het "good old" Request-object, die al in "Classic" ASP een belangrijke rol vervulde. Met behulp van de UniqueID van de child control waarvoor je de teruggestuurde data wilt opvragen haal je de waarde(s) uit het Request-object. Omdat er sprake is van multi-select, kan een ListBox-control meerdere waardes retourneren. In dat geval retourneert Request.Form[myControl.UniqueID] een komma-gescheiden string, hetgeen niet handig is als een waarde al een komma bevat. Minder bekend is dat je ook Request.Form.GetValues(myControl.UniqueID) kunt gebruiken om een array met strings op te vragen, zoals in Listing 2.

   1:  private void AddItems_Click(object sender, EventArgs e) {
   2:      String[] itemsToAdd = Page.Requres.Form.GetValues(SelectList.UniqueID);
   3:      if(itemsToAdd != null && itemsToAdd.Length > 0) {
   4:          foreach (String item in itemsToAdd) {
   5:              Items.FindByValue(item).Selected = true;
   6:          }
   7:          OnSelectedIndexChanged(new EventArgs());
   8:      }
   9:  }
Listing 2: Met het Request-object geselecteerd items uitvragen.

Listing 2 is een event handler voor de knop waarmee items toegevoegd kunnen worden aan de selectie. De waardes die geselecteerd zijn in de lijst met te selecteren items worden uitgelezen en aan de hand daarvan wordt het overeenkomstige item in de Items-collectie van de ListControl geselecteerd.

De AddRemoveListBox-control kun je downloaden van mijn blog

Dit artikel is eerder verschenen op de website van SDN

<< vorige | ^ naar boven | overzicht | volgende >>
copyright 2000-2007 ASPNL