Makale Özeti

Bu makalemizde TreeView kontrolünde görüntülenecek verileri kullanıcıdan gelen isteklere göre yükleme işlemini inceleyeceğiz. TreeView kontrolünün TreeNodePopulate olayı üzerinden gerçekleştirecek bu işlemi detayları ile inceleyip, ASP.NET AJAX kontrolleri ile etkili şekilde kullanımına göz atacağız.

Makale

TreeView kontrolü ASP.NET uygulamalarında site gezinme işlemlerinde sıklıkla kullanılan kontrollerden biridir. Ağaç görünümünde linkler ve alt linkleri hiyerarşik biçimde listeleyen bu kontrol açılır kapanır bir yapı sunduğu için özellikle karmaşık ve çok sayıdaki içerikleri listelerken oldukça kullanışlı olmaktadır. TreeView kontrolünün içeriğini sitemap dosyasından okuyabildiğimiz gibi veritabanı vb. bir veri kaynağından da okuyabilmekteyiz. TreeView kontrolünün farklı veri kaynaklarıyla kullanımı ile ilgili bilgi edinmek için daha önceden yazmış olduğum makaleye bu linkten göz atabilirsiniz. Veritabanından getirilen bu veriler eğer çok fazla ise tüm verileri bir defada getirmek performans açısından olumsuz olabilir. Kullanıcının sadece görüntülemek istediği içerikleri yüklemek sayfanın daha hızlı çalışmasını sağlayacaktır. Bu konuyla ilgili olarak oldukça somut bir örnek üzerinde durabiliriz. Eminim ki hepimiz MSDN sitesini gezmişiz ve araştırma yaparken sol kısımdaki menüyü kullanarak birçok konuya göz atmışızdır. Eğer dikkat ettiyseniz buradaki ağaç görünümlü kontrolün tüm içeriği ilk başta yüklenmemektedir. Kullanıcı bir linke tıkladığında o konuya ait alt linkler bir veri kaynağından dinamik şekilde getirilerek yüklenir. Zira oldukça geniş konu ve alt konuyu saklayan bu menünün bir defada yüklenmesi sayfanın ilk yüklenmesi esnasında oldukça uzun sürecektir.

Çok sayıda içeriği bulunan veritabanlarında bu bilgileri TreeView kontrolüne yüklemek istediğimizde verilerin isteğe göre yüklenmesi bizim için önemli olabilmektedir. Yazımızda bu işlemi nasıl gerçekleştirebileceğimizi inceliyor olacağız. TreeView kontrolüne başlangıçta belirli verileri yüklemek ve bir linke tıklandığında alt linklerini yükleme işlemi bu kontrolün TreeNodePopulate olayı (event) ile gerçekleşmektedir. TreeView kontrolü üzerinde yapılacak işlemleri ve gerekli ayarları makalenin ilerleyen kısımlarında göreceğiz. Bir örnek üzerinden konuyu incelemeye başlayalım. Spor kulüpleri ile bilgileri sunan bir web portalımız olduğunu düşünelim. Avrupa ve dünya liglerindeki futbol kulüplerini ve her futbol kulübünde bulunan oyuncuları bir TreeView kontrolünde listelemek istiyoruz. Ziyaretçinin seçtiği futbolcu ile ilgili bilgileri ise sayfamızda listeleyeceğiz. Veritabanımızda yaklaşık 20 tane ligi saklayacak olursak, her ligde ortalama 18 futbol takımını ve her futbol takımında ortalama 25 futbolcu olduğunu da düşünürsek TreeView kontrolüne 3 ayrı tablodan yaklaşık 9000 satırlık bilgiyi yüklememiz gerekecektir. Farklı tablolardan bu kadar veriyi kontrole yüklemek elbetteki uzun zaman alacaktır ve tüm verilerin getirilmesi de böyle bir senaryoda gereksiz olacaktır. Bu işlem yerine sayfa ilk yüklendiğinde sadece liglerin listesini getirilecek olursak, bir lige tıklandığında o ligin futbol kulüplerini, bir kulübe tıklandığında da o kulübün oyuncuları listeye eklenecektir. Böylece her tıklamada istenilen verilerin getirilmesi işlemini gerçekleştirmek gerekecektir. Ziyaretçi sayfa ile etkileşime geçtiğinde veritabanından veriler getirileceği için çalışma esnasında biraz yavaş işleyiş olsa da sayfa ilk çalıştığında daha hızlı yüklenecektir. Microsoft Access'te oluşturacağımız bir veritabanı üzerinden örneğimizi gerçekleştirelim. Bu veritabanında Ligler, Kulupler ve Oyuncular adında verilerin saklandığı tablolarımız olacaktır. Aşağıdaki resimde bu tabloların bilgileri görülmektedir.


Resim: Spor.mdb isimli veritabanı dosyamızda bulunan tabloların yapıları 

Az öncede bahsettiğimiz gibi sayfa ilk yüklendiğinde TreeView'a sadece liglerin listesi getirilecektir. Ne zaman ki kullanıcı bir lige tıklarsa veritabanından sadece o ligin kulüpleri getirilecektir. Kullanıcı bir kulübe tıkladığında da TreeView'ın node'larına o kulübün oyuncularının listesi eklenecektir. Bu işlemi gerçekleştirmek için boş bir ASP.NET sayfasına bir TreeView kontrolü ve bir Label kontrolü ekleyelim. TreeView kontrolüne yüklenen bir lige tıklandığında o ligin kulüplerinin getirilmesi için sunucu tarafında bir metot çalıştırıp lige ait kulüpleri çekmemiz ve listeye eklememiz gerekecektir. Getirilen liglerden birine tıklanması durumunda TreeView kontrolünün TreeNodePopulate olayı gerçekleşecektir. Bu olay sunucu getirilen yeni verilerin o an tıklanan node'a eklenmesini sağlayacaktır. Bu olayın tetiklenebilmesi için TreeView kontrolüne ait ExpandDepth özelliğinin (property) değerini "0" olarak belirlememiz ve sadece en üstteki node'ların yükleneceğini belirtmemiz gerekmektedir. Aşağıda TreeView kontrolünde yapılan değişiklikler ve sayfamızın kodları bulunmaktadır.

Default.aspx

<form id="form1" runat="server">
<div>
    <table border="0" cellpadding="4" style="width: 100%">
       <tr>
          <td width="250">
             <asp:TreeView ID="tvLig" runat="server" ExpandDepth="0" OnTreeNodePopulate="tvMenu_TreeNodePopulate" ImageSet="Simple">
             <NodeStyle Font-Names="Tahoma" Font-Size="10pt" ForeColor="Black" HorizontalPadding="0px" NodeSpacing="0px" VerticalPadding="0px" />
             </asp:TreeView>
          </td>
          <td valign="top">
             <asp:Label ID="lblOyuncuBilgiler" runat="server"></asp:Label>
          </td>
       </tr>
    </table>
</div>
</form>

TreeView kontrolü ilk yüklendiğinde sadece en üst seviyedeki node'lar gelecektir. Sayfamızın code-behind dosyasında yazacağımız kodlarla TreeView'a liglerin yüklenmesini sağlıyoruz.

Default.aspx.cs

public partial class _Default : System.Web.UI.Page
{
    string yol;
    protected void Page_Load(object sender, EventArgs e)
    {
       yol = Server.MapPath("~/App_Data") + "/Spor.mdb";
       if(!IsPostBack)
          LigleriYukle();
    }

    private void LigleriYukle()
    {
       OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OleDB.4.0; Data Source=" + yol);
       OleDbCommand cmd = new OleDbCommand("Select LigID, Ad From Ligler", con);
       con.Open();

       OleDbDataReader dr = cmd.ExecuteReader();
       while (dr.Read())
       {
          TreeNode kategori = new TreeNode(dr["Ad"].ToString(), dr["LigID"].ToString());
          kategori.PopulateOnDemand = true;
          tvLig.Nodes.Add(kategori);

       }
       con.Close();
    } 

    protected void tvMenu_TreeNodePopulate(object sender, TreeNodeEventArgs e)
    {
        // Bu metot az sonra doldurulacak 
    }
}

Bağlanılan veritabanınnda çalıştırılan sorgu sonucu sadece ligler getirildi ve tvLig adındaki TreeView kontrolüne veriler eklendi. Buradaki en önemli noktalardan bir tanesi de eklenen TreeNode nesnelerine ait PopulateOnDemand ("isteğe göre besle" diyebiliriz) özelliğidir. bool tipinden değer alan bu özelliği true belirlememiz durumunda eklenen ligler istek geldikçe alt node'larını getirecektir. Oluşturulan lig node'larına text olarak Ad, value olarak LigID değerlerini atayıp PopulateOnDemand özelliğine true değerini verdikten sonra tvLig adındaki TreeView kontrolüne ekledik. Sayfanın sorunsuz şekilde çalışması için TreeNodePopulate olayını bağladığımız tvMenu_TreeNodePopulate metodunu şimdilik içi boş bir şekilde tanımladık. Sayfa çalıştırıldığında sadece liglerin TreeView'a eklendiğini göreceğiz.


Resim: Sadece ligler TreeView kontrolüne eklendi

Bir sonraki adımda kullanıcı herhangi bir linke tıklayarak o ligin altındaki kulüpleri listelemek isteyecektir. Bu noktada TreeNodePopulate olayı tetiklenecek ve code-behind sayfamızda yazdığımız tvMenu_TreeNodePopulate metodu çağrılacaktır. Bu metodun parametrelerine bakacak olursak ikinci parametrenin TreeNodeEventArgs tipinden e adında bir nesne olduğunu görürüz. e nesnesine ait Node özelliği tıklanılan node'un nesne örneğini getirecektir. Örneğin Türkiye Süper Lig'e tıklanması durumunda bu node'un nesne örneğine TreeNode tipinden erişebiliriz. Lige ait kulüpleri veritabanından çekerek ChildNodes koleksiyonuna eklememiz durumunda artık kulüpler ligin alt node'ları olarak eklenecektir. Bir sonraki adımda da bir kulübe tıklandığında oyuncular listeleneceği için burada tıklanılan linkin lig mi olduğu yoksa kulüp mü olduğunun ayırt edilmesi gerekecektir. Eğer bir lige tıklanmışsa Kulupler tablosundan, bir kulübe tıklanmışsa da Oyuncular tablosundan veriler getirilecektir. TreeNode nesnesine ait Depth özelliği elde edilen node'un hiyerarşik olarak hangi derinlikte olduğunu belirtmektedir. Ligler TreeView'da 0. derinlikte, kulüpler ise 1. derinliktedir. Bu durumu da göze alarak aşağıdaki şekilde sayfamızın kodlarına eklemeler yapıyoruz.

Default.aspx.cs

protected void tvMenu_TreeNodePopulate(object sender, TreeNodeEventArgs e)
    {
       if (e.Node.Depth == 0) // Lige tıklanmış ise 
          KulupleriYukle(e.Node);

       else if (e.Node.Depth == 1 // Kulübe tıklanmış ise
          OyunculariYukle(e.Node);
    } 
   
    private void KulupleriYukle(TreeNode ligNode)
    {
       OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OleDB.4.0; Data Source=" + yol);
       OleDbCommand cmd = new OleDbCommand("Select KulupID, Ad From Kulupler Where LigID=@id", con);
       cmd.Parameters.AddWithValue("@id", ligNode.Value);
       con.Open();

       OleDbDataReader dr = cmd.ExecuteReader();
       while (dr.Read())
       { 
              TreeNode kategori = new TreeNode(dr["Ad"].ToString(), dr["KulupID"].ToString()); 
              kategori.PopulateOnDemand = true; 
              kategori.SelectAction = TreeNodeSelectAction.Expand; 
              ligNode.ChildNodes.Add(kategori);

       }
       con.Close();
    }
   
    private void OyunculariYukle(TreeNode kulupNode)
    {
       OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OleDB.4.0; Data Source=" + yol);
       OleDbCommand cmd = new OleDbCommand("Select OyuncuID, Ad From Oyuncular Where KulupID=@id", con);
       cmd.Parameters.AddWithValue("@id", kulupNode.Value);
       con.Open();

       OleDbDataReader dr = cmd.ExecuteReader();
       while (dr.Read())
       {
          string navigateUrl = "Default.aspx?OyuncuID=" + dr["OyuncuID"].ToString();
          TreeNode kategori = new TreeNode(dr["Ad"].ToString(), "", "", navigateUrl, "");
          kulupNode.ChildNodes.Add(kategori);

       }
       con.Close();
    }

Görüldüğü gibi kulüplerin getirilmesi KulupleriYukle, oyuncuların getirilmesi OyunculariYukle adındaki iki metot ile gerçekleştiriliyor. Her iki metoda da parametre olarak tıklanılan TreeNode nesnesi gönderiliyor. Value özelliğinden erişilen ID bilgisine göre alt node'ların kayıtları getirilerek tıklanan TreeNode nesnesinin ChildNodes koleksiyonuna ekleniyor. OyunculariYukle metodunda TreeNode nesnesinin PopulateOnDemand ve SelectAction özelliklerine herhangi bir atama yapılmadığını görülmektedir; çünkü oyuncuların altında herhangi bir alt node yer almayacağı için beslenmesine gerek yoktur. Bu metotla ilgili bir diğer farkta oyuncu ismine tıklandığında sayfamıza QueryString ile oyuncunun ID bilgisi taşınacağı için TreeNode nesnesinin farklı şekilde oluşturulmasıdır. Sayfayı çalıştırdığımızda aşağıdaki gibi bir sonuç alabiliriz.


Resim: Kullanıcıdan gelen isteklere göre TreeView içeriği dinamik şekilde dolduruldu

Tüm verilerin bir defada gelmediğini görebilmek için sayfa götürülendikten sonra Kaynağı Görüntüle seçeneğinden sayfanın HTML kodları kontrol edilebilir. Yine linklere tıklandığında TreeNodePopulate olayının tetiklenmesini de tvMenu_TreeNodePopulate metoduna bir breakpoint ekleyerek görebiliriz.


Resim: "İspanya La Liga" linkine tıklandığında breakpoint eklediğimiz noktada seçilen node'un bilgilerini görebiliriz

Breakpoint'i kaldırarak sayfanın çalışma şeklini tekrar gözlemleyelim. Liglere ve kulüplere tıklandığında veritabanından verilerin getirilmesine rağmen sayfada herhangi bir postback işlemi olmadığını göreceğiz. Aslında TreeView kontrolü varsayılan davranış şekline göre TreeNodePopulate olayı tetiklendiğinde callback tekniğini kullanarak sunucu ile asenkron şekilde iletişime geçmektedir. Böylece getirilen veriler hem daha hızlı şekilde kontrole eklenmektedir, hem de postback işlemi gerçekleşmediği için sayfa kaybolmadan yenilenir. TreeView kontrolüne bu davranışı kazandıran PopulateNodesFromClient özelliğidir. bool tipinden değer alan bu özelliğin varsayılan değeri true'dur ve node'ların istemci (client) tarafında asenkron şekilde güncellenmesini sağlar. Bu özellik false değerine atanır ve sayfa tekrar çalıştırılırsa liglere ve kulüplere tıklandığında sayfada postback işleminin yapıldığı görülecektir.

Son olarakta QueryString ile taşınan bilgileri alarak lblOyuncuBilgiler etiketine yazdırabiliriz. Bu işlemi Page_Load metodu içerisinde gerçekleştiriyoruz. Aşağıda sayfaya eklenen kodlar ve sayfanın çıktısı yer almaktadır.

Default.aspx.cs

protected void Page_Load(object sender, EventArgs e)
{
    ...
    if (Request.QueryString["OyuncuID"] != null)
       OyuncuBilgileriniGetir(Request.QueryString["OyuncuID"].ToString());
 
}

private void OyuncuBilgileriniGetir(string oyuncuID)
{
    OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OleDB.4.0; Data Source=" + yol);
    OleDbCommand cmd = new OleDbCommand("Select Ad, DogumTarih, Mevki From Oyuncular Where OyuncuID=@id", con);
    cmd.Parameters.AddWithValue("@id", oyuncuID);
    con.Open();

    OleDbDataReader dr = cmd.ExecuteReader(CommandBehavior.SingleRow);
    dr.Read();
    lblOyuncuBilgiler.Text = "Ad-Soyad: " + dr["Ad"].ToString() + 
                                           "<br>Doğum Tarihi: " + dr["DogumTarih"].ToString() +
                                           "<br>Oynadığı Mevki: " + dr["Mevki"].ToString();

    con.Close();
}


Resim: Seçilen oyuncunun bilgileri getirildi


ASP.NET AJAX Kontrolleri ile TreeView Kontrolüne Dinamik Veri Ekleme

TreeView kontrolüne veri eklenirken kullanıcıyı bilgilendirmek için işlem yapıldığı esnada bir metin görüntülemek istenilebilir. Bu tip bir işlemi ASP.NET AJAX kontrolleri ile kolay şekilde gerçekleştirebiliriz. ScriptManager, UpdatePanel ve UpdateProgress kontrolü ile yapılacak bu işlemde dikkat etmemiz gereken nokta TreeView kontrolünün PopulateNodesFromClient özelliğidir. Bu özelliğin true olması durumunda TreeView callback yöntemiyle sunucuyla iletişime geçecek ve kullandığımız AJAX kontrolleri etkin olmayacaktır. Bundan dolayı da UpdateProgress kontrolü ile kullanıcıyı haberdar edemeyiz. Eğer TreeView kontrolü UpdatePanel içerisinde atılırsa ve PopulateNodesFromClient özelliğine de false değeri atanırsa, kontrol normal bir postback işlemi yapmak isteyecek fakat UpdatePanel'e bağlı çalıştığı içinde kısmi postback (partial postback) işlemi gerçekleşecektir. Böylece sayfa yukarıda anlattığımız şekilde çalışacaktır. Visual Studio 2005'te yeni açtığımız bir ASP.NET AJAX-Enabled Web Site projesine (veya Visual Studio 2008'de .NET Framework 3.5 ile açılan normal bir ASP.NET projesine) önceki projedeki kodlarımızı aynen taşıyalım. Yapacağımız tek değişiklik Default.aspx sayfasında TreeView kontrolünü bir UpdatePanel içerisine bırakmak olacaktır. Tabii ki TreeView kontrolünün PopulateNodesFromClient özelliğini false yapmamız da gerekecektir. Aşağıda oluşturduğumuz AJAX sayfasındaki kodlar ve sayfanın çıktısı yer almaktadır.

Default.aspx

...
<asp:ScriptManager ID="ScriptManager1" runat="server"></asp:ScriptManager>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
    <ContentTemplate> 
        <asp:TreeView ID="tvLig" runat="server" OnTreeNodePopulate="tvMenu_TreeNodePopulate" ImageSet="Simple" ExpandDepth="0" PopulateNodesFromClient="False"
            <NodeStyle Font-Names="Tahoma" Font-Size="10pt" ForeColor="Black" HorizontalPadding="0px" NodeSpacing="0px" VerticalPadding="0px" /> 
        </asp:TreeView>
    </ContentTemplate>
</asp:UpdatePanel>
<asp:UpdateProgress ID="UpdateProgress1" runat="server" DisplayAfter="0">
    <ProgressTemplate>
       Veriler yükleniyor. Bekleyiniz...
    </ProgressTemplate>
</asp:UpdateProgress>
...


Resim: Dinamik yükleme işleminin UpdatePanel ve UpdateProgress ile gerçekleştirilmesi

TreeView kontrolüne dinamik veri ekleme işlemini ASP.NET AJAX kontrolleri ile gerçekleştirmeyi de görmüş olduk. Bu makalemizde TreeView kontrolüne kullanıcıdan gelen isteklere göre dinamik şekilde veri ekleme işlemini inceledik. Özellikle çok sayıda node'lardan oluşan yapılarda TreeView kontrolünün içeriğini dinamik şekilde oluşturmak uygulamanın performansı açısından oldukça önemli olabilmektedir. Bir başka makalede görüşmek dileğiyle.

Uğur UMUTLUOĞLU
www.nedirtv.com
www.umutluoglu.com