I opet malo programiranja, ovog puta o proširivanju postojećih asp.net severskih kontrola na primeru GridView-a.
Cilj ovog teksta je da pokaže kako se postojeće ASP.NET serverske kontrole mogu relativno lako dograditi tako da im se poveća funkcionalnost. Kao primer sam uzeo GridView koji u osnovnoj postavi nema multiselect ali uz malo dorade postaje vrlo sličan gridovima koje srećemo u windows programiranju. Ideja je da možemo da izaberemo više redova u GridView tako što ćemo prilikom izbora držati taster CTRL.
Realizacija je sledeća: kad god kliknemo na neki red u GridView kontroli izvrši se registracija tog klika i nakon postback-a se čitaju indeksi kliknutih redova na osnovu kojih vraćamo redove u kontroli koji su izabrani.
Prvi korak je da kreiramo novu klasu koja će predstavljati DataGrid sa multiselect opcijom. Ona će biti izvedena iz System.Web.UI.WebControls.GridView klase i čime obezbeđujemo nasleđivanje svih osobina i metoda ove klase na koje smo navikli.
public class MultiSelectGridView : System.Web.UI.WebControls.GridView
Zatim je potrebno da na svaki red koji generiše osnovna GridView klasa “zalepimo” onclick javascript događaj (event handler). To radimo tako što promenimo OnRowDataBound metodu iz osnovne klase.
1: protected override void OnRowDataBound(GridViewRowEventArgs e)
2: {
3: base.OnRowDataBound(e);
4:
5: if (e.Row.DataItem != null)
6: {
7: e.Row.Attributes.Add("OnClick", "msel(event)");
8:
9: _id = this.PageIndex + "." + e.Row.RowIndex.ToString();
10: e.Row.Attributes.Add("index", _id);
11: }
12: }
Glavna stvar se nalazi u liniji 7, gde na Row element dodajemo atribut OnClick koji će izvršavati msel(event) javascript funkciju na svaki klik. Javascript funkcija msel registruje gde se izvršio click događaj i upisuje indeks kliknutog elementa u skriveno (hidden) polje čiju vrednost ćemo kasnije, u postback-u, da parsiramo. Ovu funkciju ćemo da kreiramo i registrujemo kasnije.
Sledeća stvar se nalazi na linijama 9 i 10. Naime, tu kreiramo indeks elementa koji je kliknut i vezujemo ga za sam element. Njegova konstrukcija je vrlo jednostavna, “_id = broj stranice + tačka + indeks trenutnog reda”. Dobijeni indeks se upisuje kao dodatni atribut u element koji predstavlja red u GridView-u.
Da bi na klijentskoj strani znali koji red je izabran kreiramo Javascript funkciju “msel“ koju ćemo kasije da registrujemo na strani (podvučeni delovi code-a se automatski generišu u runtime-u).
Pre nego što prokomentarišem javascript code, da kažem par reči o html code-u koji generiše standardni GridView. GridView je u stvari obična tabela tj <table><tr><td>... konstrukcija. <tr> tag predstavlja red u našoj kontroli (Row) i na njega se “lepi” OnClick event. Sam event se zapravo okida na <td> elementu ali se propagira do prvog roditeljskog (parent) elementa koji ima handler tj definisan onclick a to je <tr> element. I evo javascript-a koji se generiše.
Da prokomentarišemo javascript.
1:<script language="javascript">
2: function msel(e, o)
3: {
kreiramo referencu na skriveni (hidden) html element koji u sebi sadrži informacije koji redovi su selektovani
4: var selected = document.getElementById(o);
Zatim kreiramo referencu prema Event-u koji je inicirao izvršenje funkcije. Ova referenca nam treba da bi saznali koji je objekat inicirao Event i da li je pri kliku bio pritisnut taster CTRL. Linija 6 daje neposredni objekat koji je inicirao event, u ovom slučaju <td> tj ćeliju koja se nalazi unutar <tr> elementa. Na liniji 7 hvatamo sam <tr> element koji nosi informaciju o tome koji je indeks kliknutog elementa.
5: e = e ? e : ((window.event) ? event : null);
6: var obj = e.target ? e.target : ((e.srcElement) ? e.srcElement : null);
7: obj = obj.parentElement ? obj.parentElement : obj.parentNode;
8: var objParent = obj.parentElement ? obj.parentElement : obj.parentNode;
Na linijama 9 i 10 kreiramo objekte String tipa koji redom nose podatke o izabranim redovima i o trenutno kliknutom objektu tj njegovom indeksu.
9: var sel = new String(selected.value);
10: var id = new String(obj.getAttribute("index"));
11: var cssclass;
12:
Linija 13 ispituje da li je prilikom klika bio pritisnut i taster CTRL. Ukoliko jeste na liniji 14 proveravamo da li je red već izabran. Ukoliko jeste, ukloni ga iz liste izabranih i promeni css klasu za tekuću liniju. U suprotnom ubaci ga na listu izabranih i promeni css klasu.
13: if (e.ctrlKey) {
14: if (sel.indexOf("-" + id + "-") > -1) {
15: selected.value = sel.replace("-" + id + "-", "");
16: obj.className = "CssClassRow";
17: }
18: else {
19: selected.value = sel + "-" + id + "-";
20: obj.className = "CssClassRowSelected";
21: }
22: }
Ukoliko CTRL nije pritisnut (linija 23) lista izabranih indeksa svodi se na trenutno izabran a css klase se menjaju tako da su svi objekti neselektovani.
23: else {
24: if (sel.indexOf("-" + id + "-") > -1) {
25: cssclass = "CssClassRow"; selected.value = "";
26: }
27: else {
28v cssclass = "CssClassRowSelected";
29: selected.value = "-" + id + "-";
30: }
31: var trs = objParent.getElementsByTagName("tr");
32: for(i = 0; i < trs.length; i++)
33: if (trs[i].getAttribute("index"))
34: trs[i].className = "CssClassRow";obj.className = cssclass;
35: }
36: }
Napomena: podvučeni delovi se generišu
Funkcija disableSelection samo ne dozvoljava da se na kliknutim redovima izvrši selekcija teksta. Za detalje pogledajte http://www.dynamicdrive.com/dynamicindex9/noselect.htm.
37: function disableSelection(target) {
38: if (typeof target.onselectstart!="undefined")
39: target.onselectstart=function(){return false;};
40: else if (typeof target.style.MozUserSelect!="undefined")
41: target.style.MozUserSelect="none";
42: else
43: target.onmousedown=function(){return false;};
44: target.style.cursor = "default";
45: }
46:</script>
Ok, sledeća metoda koju menjamo u odnosu na originalnu GridView klasu je OnRowCreated.
1:protected override void OnRowCreated(GridViewRowEventArgs e)
2:{
3: base.OnRowCreated(e);
4:
5: _id = this.PageIndex + "." + e.Row.RowIndex.ToString();
6:
7: if (selected != null && selected.IndexOf("-" + _id + "-") > -1)
8: e.Row.CssClass = CssClassRowSelected;
9: else
10: e.Row.CssClass = CssClassRow;
11:}
Opet kreiramo indeks, linija 5, i ako je trenutni red u listi selektovanih onda mu menjamo css klasu.
I na kraju menjamo CreateChildcontrols metodu. Zapravo samo je proširujemo. Cilj je da između svakog postback-a očuvamo stanje selektovanih redova.
1:protected override void CreateChildControls()
2:{
Naravno pustimo da se kreiraju sve potrebne kontrole koje se kreiraju za GridView (Linija 3)
3: base.CreateChildControls();
4:
5: string[] selectedArray;
6:
7: if (!string.IsNullOrEmpty(selected))
8: {
Izdvojimo indekse redova selektovanih na client strani (linija 9.) .
9: selectedArray = selected.Split('-');
10:
Kreiramo listu int vrednosti koja će sadržati indekse izabranih redova.
11: SelectedIndexs = new List<int>();
Kreiramo string niz koji će u sebi sadržati tekuću stranu i tekući index na strani.
12: string[] pageAndIndex;
Za svaki element u selectedArray a na osnovu stranice i indeksa preračunamo pravi indeks i ubacimo u SelectedIndexs.
13: foreach (string s in selectedArray)
14: {
15: if (s.Length > 0)
16: {
17: pageAndIndex = s.Split('.');
18: SelectedIndexs.Add(int.Parse(pageAndIndex[0]) * this.PageSize + int.Parse(pageAndIndex[1]));
19: }
20: }
21: }
22:
Registrujemo skriveno html polje u koje upisujemo koji su izabrani redovi.
23: this.Page.ClientScript.RegisterHiddenField(selectedKey, selected);
Registrujemo blok sa javascript funkcijama opisanim gore.
24: this.Page.ClientScript.RegisterClientScriptBlock( ( (System.Web.UI.WebControls.GridView)this).GetType(), "MultiselectGridView_js", js);
25:
I na kraju na GridView kontrolu dodamo još jednu literal kontrolu koja u sebi sadrži poziv funkcije disableSelection koja ne dozvoljava da se selektuje tekst na upravo kreiranoj kontroli.
26: Literal l = new Literal();
27: l.Text = "<script language=\"javacript\" type=\"text/javascript\">disableSelection(document.getElementById(\"" + this.ClientID + "\"));</script>";
28: this.Controls.Add(l);
29:}
I to je to, kompletan code možete da skinete ovde.