25 Şubat 2011 Cuma

Nested (İçiçe) DataList

Web uygulamalarında veri gösterme kontrollerinden daha önce bahsetmiştik. Veritabanından, xml'den ...vs. bilgiler geldiği zaman, bu bilgileri, bahsettiğimiz veri gösterme kontrollerinden gösteriyoruz. DataList, GridView, Repeater vs. kullanıyoruz. Çoğu zaman tek bir kontrolle tüm bu bilgileri gösterebiliyoruz.


Senaryomuz gereği, bir kullanıcı birden fazla eğitim bilgilerine sahipse, ve bu eğitim bilgileri database'den birden fazla satır halinde geliyorsa, ne yapmamız gerekir?


İşte burada yardımımıza nested datalist giriyor. DataList içindeki ItemTemplate'ine bir datalist daha oluşturursak, her eğitim bilgisi için, ikinci yazdığımız DataList çalışacak ve biz eğitim bilgilerini datasource olarak verdiğimiz taktirde, her itemtemplate'de datalist çalışacak ve eğitim bilgileri birden fazla da olsa gösterilecek.


Kodlamaya gelirsek, ana düşüncemiz, dışardaki DataList için bir kaynak vermek olmalı ve daha sonra da ItemDataBound isimli eventini tetiklemek olmalı. Peki ne işe yarıyor bu event? ItemDataBound eventi, verileri ekrana yazmadan onlara erişmemizi sağlayan, bir takım işlemleri yapmamıza olanak sağlayan eventtir. Örneğin labellardan bazılarının visible'ını false yapmak. Veya kendi örneğimizden içerdeki diğer datalist'lere erişip, onlara datasource vererek çalışmalarını sağlamak vs..


Önce PageLoad'da en dıştaki DataList'imize veri kaynağı gösterelim.


public partial class _Default : System.Web.UI.Page
{
   if (!IsPostBack)
       {
    a = 0;
    id = Request.QueryString["UserID"].ToString();
    string query = "select * from XTable";
    DataTable dt = VerTabanından dönen sonuç
    dlResult.DataSource = dt;
    dlResult.DataBind();
        }
 }


protected void dlResult_ItemDataBound(object sender, DataListItemEventArgs e)
{       
   if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
{
   DataList dlPersonal =(DataList)e.Item.FindControl("dlPersonal");
   DataList dlEducation =(DataList)e.Item.FindControl("dlEducation");
   DataList dlJobExp = (DataList)e.Item.FindControl("dlJobExp");

   dlPersonal.DataSource = Verikaynağı 1
   dlEducation.DataSource = Verikaynağı 2
   dlJobExp.DataSource = Verikaynağı 3
   dlJobExp.DataBind();
   dlEducation.DataBind();
   dlPersonal.DataBind();           
}
}

Yukarıdaki kodlarda açıklama yapmak gerekirse, dlResult_ItemDataBound eventi, tam dlResult.DataBind() çalıştıktan sonra tetiklenir. Eventin içerisine girdiğimizde, bir kontrol yapmamız gerekir. Bu kontrol, DataList'in içinde footer,header,seperator,item, ve alternating template bulunduğundan, her bir template için bir kere çalışacaktır bu event. Dolayısıyla bizim, sadece item template ve alternating template için çalışmasını istediğimizden bu kontrolü koyuyoruz. Daha sonra bu DataList içindeki DataList'lere erişmek için FindControl metodu yardımıyla DataListlerimize erişip, onlara datasourcelarını veriyoruz. Ben burada, DataList içerisindeki labelların text'lerini Eval yardımıyla doldurdum, istersek içerdeki DataListlerin de ItemDataBound eventini tetikleyip, o datalistlerin içindeki labelları bulup, text'lerini değiştirebilirdik. Eval daha kısa ve mantıklı kullanış olacaktır.


Buradaki senaryo, DataList üzerinden gerçekleşti fakat, aynı event, aynı mantık GridView ve repeater için de geçerli. Sadece ufak birkaç kod değişikliği olacaktır. Onlar da kontrollere erişme kısmında olacaktır. Önemli olan mantığı oturtmak ki, bu örnekteki düşünce yapısıyla rahatlıkla yapılacaktır. 

16 Şubat 2011 Çarşamba

Xml'de Karakter Sorunu

Xml oluştururken,  database'den vs. gibi bir veri kaynağından gelen verilerde Türkçe karakter sorunu yaşayabiliriz. Bu durumda bize

An error occurred while parsing EntityName

hatasını verir ve bizim de bu durumda yapmamız gereken encoding kısmını şu şekilde düzeltmek olmalıdır.

< ?xml version=\"1.0\" encoding=\"windows-1254\"? >

Bu işlemi yaptıktan sonra, ayrıca gerek duyulursa, '&' karakteri sorun çıkarması durumunda da, gelen veriyi replace ederek "&" yapmalıyız. Yani;

Replace("&", "& amp;")

yapmalıyız. Bu durumda herhangi bir hata almayacağız. 

12 Şubat 2011 Cumartesi

C#'da yield Kavrami

Daha onceki makalelerde Foreach iterasyonu hakkinda bilgi vermistim. Kisaca hatirlatacak olursak, IEnumerable interfacesini implement etmeyen bir class foreach iterasyonuna sahip olamiyordu. Bunun icin, o class'i IEnumerable interfacesinden implement edip, bu implementle birlikte override edilmesi gereken GetEnumerator metodunun icini doldurdugumuz zaman, artik bizim yazdigimiz sinifta da foreach ile gezebilecek duruma geliyorduk. Sunu da unutmayalim ki, GetEnumerator metodunu override ederken, MoveNext(), Reset() metotlarini ve Current isimli uyesinin iclerini doldurmamiz gerekiyordu.

Mantigi oldukca basit ama bazi projelerde, uygulamaya entegrasyonu sıkıntı çıkarabiliyor. Iste bu  sıkıntıları gidermek için C# 2.0 ile birlikte gelen yield keywordu sayesinde bu islemlerin tumu bizim icin arka planda yapilmis olacak.

Kullanimi oldukca basit;

Yine ayni sekilde, UrunYonetim sinifini olusturup, IEnumerable sinifindan kalitalim ve GetEnumerator sinifini override edelim.


namespace yieldKullanimi
{
    class UrunYonetim : IEnumerable
    {
        Urun[] _urunler;

        public UrunYonetim()
        {
            _urunler = VeriTabanindanOku();
        }

        private Urun[] VeriTabanindanOku()
        {
            Urun[] urunler = { new Urun(1, "Monitor", 200), new Urun(2, "Klavye", 80), new Urun(3, "Mouse", 40) };

            return urunler;
        }

        public IEnumerator GetEnumerator()
        {
            for (int i = 0; i < _urunler.Length; i++)
            {
                yield return _urunler[i].Ad;  
            }
        }
    }
}

Gordugunuz uzere, GetEnumerator sinifinda, onceden bir numarator sinifi olusturup(IEnumerator interfacesini implement eden sinif) nesnesini burada olusturup return ediyorduk. Daha sonra da MoveNext,Reset ve Current gibi uyelerin icini dolduruyorduk. Tum bunlari, GetEnumeratorun icine yazdigimiz kodlar yapiyor iste. for dongusuyle sayesinde, MoveNext metodunun ici yaziliyor. int i=0 diyerek Reset() metodunu cagiriyoruz, yield return_urunler[i].Ad diyerek de aslinda Current field'ini okuyoruz. Bunun sonucunda da, biz Urun sinifinin icinden string tipinden dolasip, urun isimlerini elde edebilecegiz.

Simdi de IL Disassambler(ILDASM) acarak, fiziksel olarak yazmadigimiz halde hangi metotlarin(uyelerin) olusturulduguna bakalim.


Gordugunuz uzere, biz MoveNext,Reset ve Current gibi uyeler yazmamamiza ragmen, yield keywordu sayesinde, arka planda bunlarin yazildigini goruyoruz. Programimiza buyuk olcude rahatlik getiren, is yukunu azaltan ayni zamanda da kod okulurlugu acisindan profesyonel yapisini koruyan yield'lar sayesinde hayat artik daha da guzel. :)

* Resme tıklarsanız, yeni ekranda, orjinal boyutunda erişebilirsiniz.

10 Şubat 2011 Perşembe

GridView Paging ve Sorting(Sayfalama ve Sıralama)

En sık kullandığımız veri gösterme kaynaklarından biridir GridView. Verileri gösterirken de, kullanıcıya bazı işlevsellikler katmak isteyebiliriz. Şimdi bunları kısaca size anlatmaya çalışacağım.

GridView'da verileri getirirken DataSource kullanmışsak (yani veri kaynağı olarak, gridview'in kendi wizardını kullanmışsak), sevgili Visual Studio bize kendi kodlarını yazıp, sorting ve paging'i hiç bize zahmet vermeden kendisi yazıyor. Peki, ya biz verileri code-behind tarafında veritabanına bağlanıp çekiyorsak?

Bu durumda kendi kodlarımızı kendimiz yazacağız. Oldukça basit ve az kod yazacağımızdan, rahatlıkla anlaşılacaktır zaten.

Bu işlemleri yapmadan önce, unutmamamız gereken şey, GridView'ın özelliklerinden AllowPaging ve AllowSorting'i true yapmak. Ayrıca sıralama işlemleri için, belirlediğimiz her bir boundfield'ın SortExpression özelliğine o kolonun ismini vermek.


<asp:BoundField DataField="Name" HeaderText="Adı ve soyadı" SortExpression=" Name" />
<asp:BoundField DataField="TCKimlikNo" HeaderText="TC Kimlik No" SortExpression=" TCKimlikNo" />


Paging:

Sayfalama için yapmamız gereken, GridView'in PageIndexChanging eventini tetiklemek. Daha sonra da şu kodları yazdığımız taktirde, istediğimiz sayfalamayı yapacaktır.

 
    protected void GridView1_PageIndexChanging(object sender, GridViewPageEventArgs e)
    {
        GridView1.PageIndex = e.NewPageIndex;
        GridView1.DataSource = dt;
        GridView1.DataBind();
    }

Hatırlatma: GridView'da bir sayfada, gösterilmek istenen veri sayısını, GridView'in özelliklerinden PageSize'ini değiştirerek yapabilirsiniz..

Sorting:
 Sıralama için de yine aynı şekilde Sorting eventinin tetiklenmesi gerekir. Daha sonra da sıralamanın azalan mı artan mı olduğunu belirlemek için bir property tanımlayıp, oradan sıralama bilgisini aldım. Daha sonra da DataView'in Sort isimli üyesini çağırıp, sıralama şeklini(azalan-artan) belirterek GridView'i istediğimiz şekilde sıralamış olduk.


    protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
    {
        string sortType = "";
        if (GridViewSortDirection == SortDirection.Ascending)
        {
            GridViewSortDirection = SortDirection.Descending;
            sortType = " DESC";
        }
        else
        {
            GridViewSortDirection = SortDirection.Ascending;
            sortType = " ASC";
        }

        if (dt != null)
        {
            DataView dv = new DataView(dt);
            dv.Sort = e.SortExpression + " " + sortType;

            GridView1.DataSource = dv;
            GridView1.DataBind();
        }

    }
    public SortDirection GridViewSortDirection
    {
        get
        {
            if (ViewState["sortDirection"] == null)
                ViewState["sortDirection"] = SortDirection.Ascending;
            return (SortDirection)ViewState["sortDirection"];
        }
        set { ViewState["sortDirection"] = value; }
    }

Hatırlatma: Yukarıda da belirttiğim gibi, sıralamanın gerçekleşmesi için, GridView'da göstermek istediğimiz alanların(BoundField) SortExpression özelliğini dolu geçmemiz gerekir.

5 Şubat 2011 Cumartesi

C#'da Foreach Iterasyonu

Uygulama gelistirirken, belli bir dizinin veya koleksiyonun elemanlari icinde gezerken foreach kullaniriz. Foreach ile ekrana yazdirma, deger atama ... vs. gibi yapmak istedigimiz isleri yapariz. Buraya kadar her sey cok normal, direk yazip geciyoruz. Ama, aslinda bilmeyiz ki, foreach ile bu isleri yaparken, arkada IEnumerable interface'sini implemet ediyor, bunun sonucunda IEnumerator tipinden bir GetEnumerator metodunu override etmemizi istiyor ve bu GetEnumerator de kendi icindeki uyelerle bizim foreach ile yaptigimiz isleri yapiyor.

Buraya kadar her sey cok karisik gelmistir. Ufak bir senaryo uzerinden daha iyi anlayacagiz simdi. string,int,ArrayList gibi tipler IEnumerable'i implement ettiginden foreach ile islem yapilmaya uygundurlar. Ya bizim elimizde, bu interface'yi implement etmeyen bir class bulunursa? Iste asil olay burada basliyor.

Diyelim ki elimizde Personel sinifi bulunsun. Personel'in Id'si,Adi ve Maasi bulunsun. Personel nesnelerimiz de su sekilde bulunsun.

Id;Ad;Maas

1;Ali;100
2;Veli;200
3;Deli;300

Bu personel nesnesinin icinde gezip Id'lerini ekrana yazdirmak istersek, basitce su foreach yapisini yazmamiz gerekirdi.



            Personel prs = new Personel();

            foreach (int id in prs)
            {
                Console.WriteLine(id);
            }


Bu islemi yapip calistirdigimizda su hatayi aliriz;

foreach statement cannot operate on variables of type 'ForeachIterasyonu.Personel' because 'ForeachIterasyonu.Personel' does not contain a public definition for 'GetEnumerator'

Bu hatanin sebebi, Bizim yazdigimiz Personel sinifinin IEnumerable interfacesini implement etmeyisidir. Bunun uzerine sinifimiza gelip IEnumerable interfacesini uygularsak, bizden GetEnumerator metodunun icini doldurmamizi ister ve bunun sonucunda foreach ile gezme islemini yapabilmis oluruz.

Fakat bizim senaryomuz, id'ler uzerinden islem yapmak oldugundan; biz once tum personelleri tutup IEnumerable interfacesini implement eden bir sinif yazacagiz, daha sonra da Id'ler uzerinden islem yapan ve IEnumerator interfacesini implement eden bir class yazacagiz.

Bu ana kadar IEnumerator hakkinda konusmadik. IEnumerator interfacesi, IEnumerable interfacesinin bir sinifa uygulandiktan sonra ici doldurulmasi istenen metodun(GetEnumerator) geri donus tipidir. Gorevi,iterasyonu saglamaktir. IEnumerator'un icinde MoveNext,Current ve Reset uyelerini bulundurur. Bu uyelerin iclerini doldurdugumuzda artik bizim yazdigimiz sinif da foreach ile iterasyon yapilabilecek duruma gelecektir.

Ornegimize geri donersek;
PersonelYonetim isminde  bir class yaratip IEnumerable interfacesini implement edersek, bizden IEnumerator tipinden GetEnumerator metodunu doldurmamiz istenecek. Geri donus tipi IEnumerator olmasi, bizim o metotdan IEnumerator interfacesini implement eden herhangi bir uye geri donderebilecegimizi gosterir. O zaman biz de kendi uyemizi kendimiz yaratalim. Bir class acip, IEnumerator interfacesini uygulayip, MoveNext,Current ve Reset gibi uyelerin iclerini dolduralim.

Yeni yazacagimiz class IdNumarator isminde olsun ve IEnumerator interfacesini uygulasin. Uyguladiktan sonra bizden bazi uyelerin iclerini doldurmamizi isteyecek. Nedir bu uyeler ve uyelerin gorevleri;

Current : O anki koleksiyondaki deger geri doner.
Reset : Sayaci -1'e dondurur.
MoveNext: Siradaki elemana ulasmamizi saglar. Sonraki eleman yoksa da false doner, ulasiyorsak ture doner.


class IdNumarator:IEnumerator
    {
        Personel[] _prs; //Personellerimizi bir dizi icerisinde tutalim
        int _sayac;

        public IdNumarator(Personel[] prs)
        {
            _prs = prs;
            Reset();
        }

        public object Current
        {
            get { return _prs[_sayac].Id; } //O anki personelin id'sini geri dondurelim.
        }

        public bool MoveNext()
        {
            _sayac++;
            return _sayac < _prs.Length; //dizinin boyutunu asmamaliyiz.
        }

        public void Reset()
        {
            _sayac = -1; //Once  movenext calisacagindan, sayaci -1 yapmaliyiz, cunku dizi[0] 'dan baslamali.
        }
    }



Simdi yapmamiz gereken tek sey, PersonelYonetim sinifindaki GetEnumerator metodunda bu classimizi orneklemek (Hatirlatma: GetEnumerator metodu, IEnumerator tipini implement eden herhangi bir uyeyi geri donderdiginden, bizim classimiz da IEnumerator'u implement ettiginden kullanabiliriz.)


Personel[] prs;

public IEnumerator GetEnumerator()
        {
            return new AdNumarator(prs);
        }


Tum bu islemlerden sonra, artik biz de Personel sinifimizda foreach ile Id'lerde gezebilecegiz.


            PersonelYonetim personel = new PersonelYonetim(); 
            foreach (int id in personel)
            {
                Console.WriteLine(id);
            }



NOT: Iterasyonlar, belli bir kurala gore bir nesneye ulasmaktir. Dolayisiyla foreachde de her bir iterasyon kurali, bizim icin IEnumerator interfacesini implement eden bir class'tir.(Hatirlatma:Bu makaledeki ornekte, IdNumarator'dur.)