Makale Özeti

Bu makalemizde site navigasyon kontrollerinden olan TreeView'ın verileri saklayış şeklini görüp, veritabanından veya XML dosyalardan okunan verileri bu kontrole nasıl aktarabileceğimizi inceleyeceğiz.

Makale

ASP.NET 2.0 ile birlikte gelen site navigasyonu kontrolleri ile site haritalarının kullanıcılara dinamik menülerle görüntülenmesi sağlanabilmekte ve daha profesyonel görünümler elde edilebilmektedir. Bu kontrollerden biri olan TreeView’da ağaç görünümünde menüler oluşturmamızı sağlamaktadır. Genellikle proje içerisinde XML tabanlı oluşturulan bir .sitemap dosyası ile birlikte kullanılır. Sitemap dosyaları gerek oluşturulması gerekse site navigasyon kontrolleri ile kolay ve uyumlu çalışması açısından biz yazılım geliştiricilere büyük kolaylıklar sağlamaktadır.

Sitemap dosyası belirli standartlar dahilinde yazılmakta ve genellikle site içerisindeki linklerin hiyerarşik yapısını saklamaktadır. TreeView kontrolü site haritasını taşımak amacıyla kullanılabileceği gibi farklı içerikleri de ilişkisel şekilde kullanıcılara sunulmasını da sağlayabilecek bir yapıya sahiptir. Örneğin kendi hazırladığımız XML dokümanındaki verilerin süzülerek üst düğüm – alt düğüm ilişkisi ile bu kontrole yüklenmesini isteyebiliriz. Böyle bir durumda akla gelen soru TreeView kontrolünü farklı yapıdaki XML dosyaları ile nasıl konuşturabileceğimizdir. Makalemizin ilk kısmında bu sorunun çözümünü inceleyeceğiz.

Site haritasının sitemap dosyasında saklanmasının bazı dezavantajları olduğunu da söyleyebiliriz. Zira XML tabanlı bir doküman olduğu için gerek yapısını çözümlemek gerekse güncellemeler yapmak sıkıntı doğurabilmektedir. Özellikle kategori-alt kategori yapısı geniş olan ve sıklıkla güncellenen sitelerde (örneğin alışveriş sitelerinde) verileri XML dokümanında saklamak yerine veritabanında saklamak daha sık tercih edilmektedir. Peki sitemizin haritasını veritabanındaki tablolarda saklıyorsak bu verileri TreeView kontrolünde nasıl görüntüleriz? Makalemizin ikinci kısmında da bu sorunun çözümünü adım adım inceleyeceğiz.

Dilerseniz bu iki işlemin detaylarını incelemeden önce TreeView kontrolünün yapısı hakkında önemli bir noktaya değinelim. TreeView kontrolü Nodes koleksiyonu içerisinde TreeNode tipiden nesneler saklayarak linklerin oluşturulmasını sağlar. Aslında sayfa çalıştırıldığında TreeView’da görünen tüm linkler birer TreeNode nesnesidir. TreeNode nesnesi de kendi içerisindeki ChildNodes koleksiyonu içerisinde yine kendi tipinden yani TreeNode nesneleri taşıyabildiği için iç içe linkler oluşturulabilmektedir. Sitemap dosyasını TreeView kontrolüne kaynak olarak gösterdiğimizde kullanılan SiteMapDataSource kontrolünün yapmış olduğu işlem .sitemap dosyasının içerisindeki yapıyı çözümlemek ve bunları TreeNode nesneleri olarak TreeView kontrolüne bağlamaktır. Aşağıdaki şekil TreeView kontrolüne bağlanan TreeNode’ların yapısını simgelemektedir.


Şekil: TreeView kontrolüne eklenen TreeNode nesnelerinin hiyerarşik yapısı

TreeNode sınıfının (class) yapıcı metotları (constructor) aracılığı ile farklı şekilde nesneler oluşturulabilir. Burada oluşturulan nesneler TreeView kontrolündeki linkler olacağı için sayfa yüklendiğinde LinkButton veya HyperLink olarak getirilecektir. Dolayısıyla oluşturulan link ya aynı sayfa üzerinde postback işlemi yapacak ya da başka bir sayfaya link verecektir. Aşağıda bir TreeNode nesnesinin farklı oluşturulma şekilleri bulunmaktadır.

// Oluşan linkin metnini ilk parametre belirler. Bu linke tıklandığında Deger1 değeri ile postback işlemi yapar
TreeNode anaMenu1 = new TreeNode("Postback Link", "Deger1");

// Bir önceki link gibi çalışır. Linkin sol kısmında son parametrede yer alan resim görüntülenir
TreeNode anaMenu2 = new TreeNode("Resimli Link", "Deger2", "menu.jpg");

// Oluşan link 4. parametrede belirlenen sayfaya gider. Son parametre ise hedef sayfanın açılma şeklini belirler (“_blank” yeni bir pencerede açılmasını sağlar). 3. parametre burada boş bırakılmıştır fakat geçerli bir resim adresi verilerek linkin solunda resim görüntülenebilir
TreeNode anaMenu3 = new TreeNode("Sayfaya Link", "", "", "Sayfa.aspx", "_blank");

Bir TreeNode nesnesi ChildNodes koleksiyonunda kendi tipinden nesneler taşıyabilir ve bunlarda o nesnenin alt linklerini oluşturur. Oluşan ana menü de TreeView kontrolünün Nodes koleksiyonuna eklenerek menü içerisinde görüntülenmesi sağlanır. Aşağıda oluşturulan iki alt menü bir ana menüye bağlanmakta ve ana menüde TreeView kontrolüne bağlanmaktadır.

Default.aspx
<form id="form1" method="post" runat="server">
   <asp:TreeView ID="agacMenu" runat="server" />
</form>

Default.aspx.cs
// Ana düğüm oluşturuluyor
TreeNode anaMenu1 = new TreeNode("Ana menü - 1", "", "", "AnaMenu1.aspx", "");

// Alt düğümleri oluşturması için iki TreeNode nesnesi oluşturuluyor
TreeNode altMenu1 = new TreeNode("Alt menü - 1", "", "", "AltMenu1.aspx", "");
TreeNode altMenu2 = new TreeNode("Alt menü - 2", "", "", "AltMenu2.aspx", "");

// Alt düğümler ana menü nesnesine ekleniyor
anaMenu1.ChildNodes.Add(altMenu1);
anaMenu1.ChildNodes.Add(altMenu2);

// Oluşan ana menü sayfadaki agacMenu kontrolüne ekleniyor
agacMenu.Nodes.Add(anaMenu1);

Sayfa çalıştırıldığında oluşan basit menü aşağıdaki şekilde görülmektedir.


Resim: Programatik şekilde oluşturulan TreeView kontrolü

Bu şekilde menülerin TreeView içerisinde programatik olarak nasıl yerleştirileceğini görmüş olduk. Dilerseniz XML dosyadan ve veritabanında getirilen verileri nasıl TreeView’a aktarabileceğimizi inceleyelim


Veri Kaynağı Olarak XML Dosyalarını Kullanmak

Sitemap dosyaları ASP.NET 2.0 ile birlikte gelen yeni bir dosya tipidir. XML tabanlı olan bu dosya SiteMapDataSource ile navigasyon kontrollerine veri kaynağı oluşturmaktadır. Bazı durumlarda kendi hazırladığımız özel yapıdaki bir XML dosyasındaki verileri de TreeView kontrolü ile kullanmak isteyebiliriz. Böyle bir durumda XML dosyasını XmlDataSource kontrolü ile TreeView’a bağlamamız gerekecektir. Tabii ki dosyamızdaki yapıyı çözümleyebilmesi için de TreeView kontrolünde bazı özel ayarlamalar yapmamız gerekir. Bir askeri birlikteki askerlerin listesini içeren XML dosyasını TreeView kontrolüne yükleyelim. Öncelikli olarak işe bir XML dosyası oluşturarak başlayalım.

Askerler.xml

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

<KarargahDestekGrubu>

    <Birim Ad="Karargah Bölüğü">
       <Asker Id="109" Ad="Ahmet YÜKSEL" />
       <Asker Id="186" Ad="Metin DİLEK" />
       <Asker Id="214" Ad="Umut NACAK" />
       <Asker Id="304" Ad="Onb. Eyüp ERDEM" />
       <Asker Id="319" Ad="Çvş. Uğur UMUTLUOĞLU" />
       <Asker Id="341" Ad="Çvş. Muhammet KURT" />
    </Birim>
    <Birim Ad="Muhafız Bölüğü">
       <Asker Id="177" Ad="Bahadır ARSLAN" />
       <Asker Id="192" Ad="Emrah USLU" />
       <Asker Id="265" Ad="Çvş. Burak BATUR" />
       <Asker Id="329" Ad="Onb. Osman ÇOKAKOĞLU" />
    </Birim>
    <Birim Ad="Lojistik Destek">
       <Asker Id="155" Ad="Onb. Bülent SÖZGE" />
       <Asker Id="281" Ad="Çvş. Özgür ALTUNTAŞ" />
       <Asker Id="299" Ad="Burak Selim ŞENYURT" />
    </Birim>

</KarargahDestekGrubu>

Bu noktadan sonra XMLDataSource kontrolü ile dosyayı TreeView'a bağlamamız ve gerekli ayarları yapmamız gerekecektir. TreeView kontrolünün DataBindings koleksiyonuna eklenecek TreeNodeBinding nesneleri ile bu işlemi gerçekleştirebiliriz. Bu işlemi programatik yollarla gerçekleştirebileceğimiz gibi dekleratif şekilde de yapabiliriz. Aşağıdaki kodlarda sayfamıza eklenen TreeView ve XmlDataSource kontrolleri ile TreeView'in DataBindings koleksiyonuna eklenen TreeNodeBindings nesneleri görülmektedir.

Default.aspx

<asp:TreeView ID="AskerListe" runat="server" DataSourceID="XmlDSAskerListe">
    <DataBindings>
      <asp:TreeNodeBinding DataMember="KarargahDestekGrubu" Text="Karargah Destek Grubu"></asp:TreeNodeBinding>
      <asp:TreeNodeBinding DataMember="Birim" TextField="Ad"></asp:TreeNodeBinding>
      <asp:TreeNodeBinding DataMember="Asker" TextField="Ad" ValueField="Id"></asp:TreeNodeBinding>
   </DataBindings>
</asp:TreeView>

<asp:XmlDataSource ID="XmlDSAskerListe" Runat="server" DataFile="Askerler.xml" />

TreeNodeBinding tanımlamalarında bulunan DataMember özelliği XML'den okunacak etiket ismini, TextField ise görüntülenecek verinin bulunduğu niteliği belirlemektedir. Sayfa çalıştırıldığında okunan verilerin TreeView'a hiyerarşik şekilde doldurulduğu görülecektir. TreeView kontrolünün önemli özelliklerinden biri de listelenen linklere resim eklenebileceği gibi CheckBox gibi bir kontrolde eklenmesini sağlayabilmesidir. Yaptığımız örnekte belirli bir bölükte bulunan askerlerden bazılarının seçilmesi ve sayfa postback edildiğinde seçili elemanları okunması isteniliyorsa bu işlem kolay bir şekilde yapılabilir. Amaç askerlerin seçim işleminin yapılması olacağı için asker isimlerinin eklendiği TreeNodeBinding elementinin ShowCheckBox niteliği true olarak ayarlanırsa listelenen asker isimlerinin sol kısmına birer CheckBox kontrolü eklenecektir. Aşağıda bu değişikliğin nasıl yapılacağı, CheckBox'lar olmadan TreeView'in görünümü ve CheckBox kontrolleri eklendiğinde TreeView kontrolünün görünümü bulunmaktadır.

Default.aspx

<asp:TreeNodeBinding DataMember="Asker" TextField="Ad" ValueField="Id" ShowCheckBox="true"></asp:TreeNodeBinding>


Resim: XML dosyasını kaynak olarak kullanan TreeView'ın a) normal şekilde oluşturulması, b) linklerinin CheckBox ile birlikte oluşturulması

Bu şekilde TreeView kontrolünü kendi oluşturacağımız bir XML dosyası ile nasıl besleyebileceğimizi görmüş olduk. Burada kilit noktamız XmlDataSource kontrolü ile veri kaynağını belirlememiz ve TreeView'in DataBindings özelliğine ekleyeceğimiz TreeNodeBinding nesneleri ile XML dosyadaki hangi etiketleri ve nitelikleri süzeceğimizi belirlemektir. Son kısımda menümüzdeki asker isimlerine eklediğimiz CheckBox'ları seçim durumlarına göre bir liste elde etme işlemini de aşağıdaki kodlarda gördüğünüz şekilde gerçekleştirebiliriz. Öncelikli olarak sayfamızın HTML kısmında bir Button nesnesi ekleyerek bu butona tıklanması durumunda listedeki seçili elemanları ekrana yazdıralım.

Default.aspx

<asp:Button ID="btnSec" runat="server" Text="Çarşı İznine Çıkacak Askerleri Seç" OnClick="btnSec_Click" />

Default.aspx.cs
protected void btnSec_Click(object sender, EventArgs e)
{
    // En üstte tek node olduğu için bu node'un içerisindeki kısımları tek tek ele alıyoruz
    foreach(TreeNode node in AskerListe.Nodes[0].ChildNodes)
    {
       Response.Write(node.Value + " kısmındaki askerler:<br>");
       // Her kısımda bulunan askerleri ele alıyoruz
       foreach(TreeNode altNode in node.ChildNodes)
       {
          if(altNode.Checked) // Eğer seçilmiş ise
             Response.Write(" - " + altNode.Text + "<br>");
       }
   }
}

Seçim işlemi yapıldıktan sonra aşağıdaki şekilde görüleceği gibi seçim listesini elde edebiliyoruz.


Resim: 1) Listeden seçim yapılması, 2) Seçilen askerlerin listesinin ve bağlı olduğu kısımların elde edilmesi


Veri Kaynağı Olarak Veritabanı Kullanmak

Geldik makalemizin belki de en can alıcı kısmına! Zira veritabanında saklanan site haritalarını TreeView veya Menu gibi kontrollere aktarabilmek biz programcılar için oldukça kolaylaştırıcı bir işlem olacaktır. Veritabanındaki tablolarda saklanan kategori-alt kategori tarzındaki ilişkili verileri TreeView'e aktarmak için dilerseniz alışveriş sitelerindeki gibi bir yapı üzerinden hareket edelim. Ürünlerimizin bulunduğu ana kategorilerimiz olsun; Elektronik Eşya, Bilgisayar gibi... Yine bu ana kategorilerin altında da ürünlerimizi ayrıştırmak için alt kategorilerimiz olsun; Bilgisayar kategorisinin altında Dizüstü Bilgisayar ve Masaüstü Bilgisayar gibi... Bu kategorilere aitte ürünlerimiz olsun. Yani 3 tane ana tablomuz içerisinde bu bilgiler saklansın: Kategoriler, AltKategoriler ve Urunler. Aşağıdaki şekillerde örnekte kullanacağım tabloların alan isimleri ve veri tipleri bulunmaktadır.


Şekil: Kategoriler tablosu


Şekil: Alt Kategoriler tablosu


Şekil: Ürünler tablosu

Tabloları verilerle doldurduktan sonra öncelikli olarak normal bir veri işlemi ile üç tablodaki verileri seçerek bir DataSet nesnesi içerisine dolduralım. Burada işimizi kolaylaştırmak için DataSet içerisinde 3 farklı DataTable içerisinde saklayacağımız bu verileri DataRelation nesnesi ile ilişkili şekilde tutmak mantıklı olacaktır. Zira bir sonraki adımda ilişkili kayıtları okurken elde edilen DataRow nesnelerinin GetChildRows() metodu ilişkili alt tablodaki kayıtlara kolayca erişmemizi sağlayacaktır. Tablo yapıları incelenecek olunursa, Kategoriler > AltKategoriler > Urunler şeklinde bir ilişkinin olduğu görülecektir. DataSet'e ekleyeceğimiz DataRelation nesneleri de bu düzende belirlenecektir.

Default.aspx.cs

protected DataSet MenuListe()
{
    OleDbConnection con = new OleDbConnection("Provider=Microsoft.Jet.OleDB.4.0; Data Source=C:\\Inetpub\\wwwroot\\TreeView_test\\Veriler.mdb");
    OleDbDataAdapter daKat = new OleDbDataAdapter("Select * From Kategoriler", con);
    OleDbDataAdapter daAltKat = new OleDbDataAdapter("Select * From AltKategoriler", con);
    OleDbDataAdapter daUrunler = new OleDbDataAdapter("Select * From Urunler", con);

    // Tablolardan gelen verileri DataSet içerisine tablolar halinde dolduruyoruz
    DataSet ds = new DataSet();
    daKat.Fill(ds, "Kategoriler");
    daAltKat.Fill(ds, "AltKategoriler");
    daUrunler.Fill(ds, "Urunler");


    // Tablolar arasında ilişkili kolonlar üzerinden ilişkileri belirliyoruz
    ds.Relations.Add("Kat_AltKat", ds.Tables["Kategoriler"].Columns["KategoriID"], ds.Tables["AltKategoriler"].Columns["KategoriID"]);
    ds.Relations.Add("AltKat_Urun", ds.Tables["AltKategoriler"].Columns["AltKategoriID"], ds.Tables["Urunler"].Columns["AltKategoriID"]);
    return ds;
}

Makalemizin ilk kısımlarına dönecek olursak TreeView kontrolünün TreeNode nesnelerinde oluştuğundan bahsetmiştik. Programatik olarak TreeView'e düğümler (node) ekleyip, düğümler altına da alt düğümler (child node) oluşturarak menüler elde edebilmiştik. Bu noktadan sonra yapacağımız işlem döngüler içerisinde önce kategorilere erişmek ve kategori için TreeNode nesneleri oluşturmak. Oluşan kategori düğümüne alt kategorileri, alt kategorilere de kendilerine ait ürünlerin eklenmesini sağlamamız gerekiyor. Oluşan tüm kategori nesnelerini de TreeView'in Nodes koleksiyonuna ekleyecek olursak menümüzü oluşturabileceğiz.

Default.aspx
<form id="form1" method="post" runat="server">
    <asp:TreeView ID="tvMenu" runat="server" />
</form>

Default.aspx.cs
private void Page_Load(object sender, EventArgs e)
{
    DataSet dsMenu = MenuListe();

    // Kategorileri oku
    foreach(DataRow dr in dsMenu.Tables["Kategoriler"].Rows)
    {
       TreeNode anaMenu = new TreeNode(dr["Ad"].ToString(), "", "", "Kategori.aspx?Id=" + dr["KategoriID"].ToString(), "");
       // Alt kategorileri oku
       foreach(DataRow drAlt in dr.GetChildRows("Kat_AltKat"))
       {
          TreeNode altMenu = new TreeNode(drAlt["Ad"].ToString(), "", "", "AltKategori.aspx?Id=" + drAlt["AltKategoriID"].ToString(), "");
          // Ürünleri oku
          foreach(DataRow drUrun in drAlt.GetChildRows("AltKat_Urun"))
          {
             TreeNode urunMenu = new TreeNode(drUrun["Ad"].ToString(), "", "", "Urun.aspx?Id=" + drUrun["UrunId"].ToString(), "");
             altMenu.ChildNodes.Add(urunMenu);
          }
          anaMenu.ChildNodes.Add(altMenu);
       }
       tvMenu.Nodes.Add(anaMenu);
    }
}

Sayfayı çalıştırdığımızda veritabanındaki 3 tablodan getirilen verilerin hiyerarşik şekilde TreeView kontrolüne yüklendiğini görebiliriz. Aşağıdaki resimde sayfamızın çıktısını görebilirsiniz.


Resim: Veritabanından getirilen veriler TreeView kontrolüne yüklendi

Görüldüğü gibi veritabanındaki kayıtları TreeView kontrolüne yükleme işlemi oldukça pratik halde gerçekleştirilebiliyor. Burada bizim için en önemli noktalardan biri tabii ki veritabanında saklanan kayıtların tablolarda ilişkisel bir şekilde saklanmasıdır. Bu tip bir işlemi yukarıdaki şekilde gerçekleştirebileceğimiz gibi kendi Kategori, AltKategori ve Urun sınıflarımızı oluşturup her bir sınıf içerisinde bir alt birimindeki nesnelere ait liste koleksiyonlarını taşıyarak ta gerçekleştirebilirdik.

Bu makalemizde TreeView kontrolünün nesneleri taşıyış şekline değinerek XML veri kaynaklarından veya bir veritabanında okunan verileri TreeView kontrolüne nasıl aktarabileceğimizi inceledik. Sitemap dosyaları kullanmaksızın da TreeView kontrolünü etkin şekilde uygulamalarımızda kullanabiliriz. Burada anlatılan bir çok konu aslında bir diğer navigasyon kontrolü olan Menu kontrolü için de geçerli olduğunu söyleyebiliriz. Bir başka makalede görüşmek dileğiyle.


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