CascadingDropDown
Ezzel az extender-rel olyan DropDownList-eket egészíthetünk ki AJAX-os funkcionalitással, melyek értékei függenek egy másik legördülő-listától. Magyarul egy DDL-t úgy töltjük fel kódból elemekkel, hogy az előző DDL-ben kiválasztott elemet vesszük alapul.
Ezzel az egyetlen nagy baj az volt, hogy mindegyik DDL AutoPostBack-re volt állítva, így gyakran ugrált/vibrált az oldalt. Jött az AJAX-szal a megoldás, mindegyiket beszórtuk egy UpdatePanel-be, és felvettük a SelectedIndexChanged eseményeket az aszinkronkioldóhoz (AsyncPostBackTrigger). Ettől a felhasználó már valamivel jobban érezte magát, de még mindig ott volt az esetleges hosszú várakozás, amelyet valamilyen chili-vili animációval próbáltunk elfedni. Na és itt jön képbe a CDD, ami mindent megváltoztat, mert gyorsabb, szebb és egyszerűbb is. (most kéne jönnie a „de” résznek… de nincs ).
A gyorsasága abban mutatkozik meg, hogy mindig csak az éppen szükséges adatért fog a szerverhez fordulni az objektum, így csökken az adatforgalom.
A szépsége a villódzás megszüntetésén kívül még abban is megnyilvánul, hogy pl.: addig nem enged választani a második DDL-ből míg az elsőből nem választottunk.
Az egyszerűségét két dolog példázza igazán. Az egyik például az, hogy egyetlen webmetódus segítségével is akár feltölthetjük az összes ddl-t, ha egy hierarchikus tárolóból (például XML) olvassuk ki az adatokat. A másik pedig, hogy lehetőségünk van arra is, hogy akár az adott oldalon írt webmetódust hívjuk meg.
Mint ahogy a fentebbi bevezetőből kitalálható ennek az Extender-nek is a lelkét egy webszolgáltatás adja, mint az AutoCompleteExtender-nek.
Most egy olyan ws-t fogunk elkészíteni, amelyet három olyan DDL-hez kapcsolunk majd, melyek a naptárvezérlőt hivatottak helyesíteni. A webszolgáltatásunk legnehezebb része a szökőév ellenőrzés lesz. Sokan úgy gondolják, hogy minden néggyel osztható év szökőév, pedig nem! A százzal osztható évek nem szökőévek, kivéve a 400-szal is oszthatóak. Tehát ezek alapján 1500 nem, de 1600 már szökőév lesz, (mármint volt…).
Nézzük a webszolgáltatásunkat! Adjunk a projektünkhöz egy WebService-t, aminek legyen a neve Get_Date.asmx. Az alábbi névtereket ajánlott using-olni:
System.Collections.Generic, AjaxControlToolkit, System.Collections.Specialized
Adjuk az osztálynak egy [System.Web.Script.Services.ScriptService] attribútumot. Törüljük ki az osztályon belül automatikusan létrehozott dolgokat! Most azt a webmetódust fogjuk megírni, amely az éveket fogja visszaadni. Először nézzük a kódot, majd utána jöhet a magyarázat:
public CascadingDropDownNameValue[] Get_Years(string knownCategoryValues, string category)
{
List<CascadingDropDownNameValue> result = new List<CascadingDropDownNameValue>();
{
result.Add(new CascadingDropDownNameValue(i.ToString(),i.ToString()));
}
}
A két kötelező paraméter közül az első (knowCategoryValues), egy olyan szöveges változó, amely felfogható egy naplóként. Vagyis ebben tárolódik el az, hogy melyik kategóriából (dropdownlist-ből), melyik értéket választottuk ki. Például, ha az első ddl-ünkhöz a Year kategóriát rendeljük, akkor a hónap webmetódus hívásánál a paraméter ezt fogja pl. tartalmazni: Year:1908; . További webmetódus hívások esetén a kategóriákat és a választott értékeket önmagához fogja hozzákonkatenálni.
A második paraméter az éppen aktuális kategória nevét tartalmazza. Tehát pl.: az év kiválasztása után ez a paraméter a Month értéket fogja felvenni.
public CascadingDropDownNameValue[] Get_Days(string knownCategoryValues, string category)
{
List<CascadingDropDownNameValue> result = new List<CascadingDropDownNameValue>();
bool szokoev = false;
if ((y % 4 == 0 && y % 100 != 0) || y % 400 == 0) szokoev = true;
int daynumber = 0;
switch (month)
{
case "Január": case "Március":
case "Május": case "Július":
case "Augusztus": case "Október":
case "December":
daynumber = 31;
break;
case "Április": case "Június":
case "Szeptember": case "November":
daynumber = 30;
break;
}
if (month == "Február" && szokoev) daynumber = 29;
else if (month == "Február" && !szokoev) daynumber = 28;
{
result.Add(new CascadingDropDownNameValue(i.ToString(), i.ToString()));
}
}
Az utána jövő két sor felelős a szökőév vizsgálatáért. Alapban azt feltételezzük, hogy nem szökőévet választott a kliens.
Utána egy switch/case segítségével meghatározzuk, hogy egyes hónapokban hány nap legyen, majd a választott hónapnak megfelelően, feltöltjük a CascadingDropDownNameValue listánkat.
Készen is vagyunk a webszolgáltatásunkkal, most már csak a hónapokat feltöltő webmetódust kell megírnunk. A Default.aspx.cs fájlba akarjuk mindezt megvalósítani. Itt az alábbi három névtérre lesz szükségünk:
System.Collections.Generic, AjaxControlToolkit, System.Web.Services
Nézzünk is a kódot:
[System.Web.Script.Services.ScriptMethod]
public CascadingDropDownNameValue[] Get_Months(string knownCategoryValues, string category)
{
List<CascadingDropDownNameValue> result = new List<CascadingDropDownNameValue>();
result.Add(new CascadingDropDownNameValue("Február", "Február"));
result.Add(new CascadingDropDownNameValue("Március", "Március"));
result.Add(new CascadingDropDownNameValue("Április", "Április"));
result.Add(new CascadingDropDownNameValue("Május", "Május"));
result.Add(new CascadingDropDownNameValue("Június", "Június"));
result.Add(new CascadingDropDownNameValue("Július", "Július"));
result.Add(new CascadingDropDownNameValue("Augusztus", "Augusztus"));
result.Add(new CascadingDropDownNameValue("Szeptember", "Szeptember"));
result.Add(new CascadingDropDownNameValue("Október", "Október"));
result.Add(new CascadingDropDownNameValue("November", "November"));
result.Add(new CascadingDropDownNameValue("December", "December"));
}
Most már csak az aspx oldalt kell összedobnunk, és utána szólhat is rock&roll!
Húzzunk a formunkra, egymás alá 3 DropDownList-et és a nevek legyenek sorra: ddl_year, ddl_month, ddl_day. Szükségünk lesz egy UpdatePanel-re és benne egy Label-re. Húzzunk még az oldalra 3db CascadingDropDown Extender-t, majd váltsunk source nézetbe!
Az első CDD-t így állítsuk be:
TargetControlID="ddl_year"
ServicePath="Get_Date.asmx"
ServiceMethod="Get_Years"
Category="Year"
PromptText="Kérlek, válassz évet…"
LoadingText="Adatok letöltése…">
</ajaxToolkit:CascadingDropDown>
A PromptText szöveg, akkor jelenik meg, amikor egyetlen elem sincs kiválasztva még! A LoadingText pedig akkor fog látszani, amikor éppen lekéri a szervertől a javascript az új listát.
A második CDD kódja:
TargetControlID="ddl_month"
ServiceMethod="Get_Months"
Category="Month"
ParentControlID="ddl_year"
LoadingText="Adatok letöltése…"
PromptText="Kérlek, válassz hónapot…">
</ajaxToolkit:CascadingDropDown>
Végül a harmadik kódja a teljesség kedvéért, magyarázat nélkül:
TargetControlID="ddl_day"
ServicePath="Get_Date.asmx"
ServiceMethod="Get_Days"
Category="Day"
ParentControlID="ddl_month"
PromptText="Kérlek, válassz napot…"
LoadingText="Adat letöltése…">
</ajaxToolkit:CascadingDropDown>
Egy-két okosság
Ha feliratkozunk valamelyik DDL valamelyik eseményére, akkor a kódunk nem fog mindaddig végre hajtódni, amíg az oldal EnabledEventValidation tulajdonságát false-ra nem állítjuk. (állítólag már dolgoznak ezen hiba orvoslásán).
A CascadingDropDown objektumnak a ParseKnownCategoryValuesString() metóduson kívül van még egy hasznos metódusa, a QuerySimpleCascadingDropDownDocument(). Ez a metódus automatikusan képes generálni a webmetódusok eredményt, de csak hierarchikus felépítésű szerkezetekből! (erről írtam már az elején, hogy egyetlen metódus generálja az összes DDL elemeit). A metódus négy paramétert vár. Az első egy XmlDocument objektum, mely forrásként szolgál. A második egy string gyűjtemény, mely leírja a fájl hierarchiáját. A harmadik egy StringDictionary objektum, melyből nyeri a history-t. Az utolsó paraméter pedig értelemszerűen a category paramétere a webservice-nek.
Bővebb infó erről az SDK-ban!
Még egy dologra szeretném felhívni a figyelmeteket, ez pedig nem más, mint egy Walkthrough, amiben leírják, hogyan kell használni a CDD-t adatbázissal. Íme a linkje:
http://asp.net/AJAX/AjaxControlToolkit/Samples/Walkthrough/CCDWithDB.aspx