Ana Sayfa | English Blog | Seminer TV | Dil Cookie Sil  Blog'u Mail ile takip et!       
Daron Yöndem - ASP.NET
bir yazılımcının tasarıları...
 Sunday, November 29, 2009

INETA NEXT etkinliklerimizle ilgili duyuruyu bundan yaklaşık bir hafta önce sizinle paylaşmıştım. Maalesef o zamanlarda içeriği paylaşma şansımız yoktu çünkü içeriğimizde daha Microsoft tarafından lansmanı yapılmamış ürünlere dair oturumlar olacaktı :) Eh artık zamanı geldi ve içeriğimizi paylaşmaktan gurur duyuyorum.

Diyarbakır - Dicle Üniversitesi, Tıp Fakültesi Konferans Salonu
5 Aralık
11.00-12.30 Visual Basic  10 Yenilikleri - Daron Yöndem (yazgelistir.com)
14.00-15.30 Windows 7'de neler var? - Muammer Benzeş (birliktegelistir.com)
16.00-17.00 Silverlight 4 - Daron Yöndem (yazgelistir.com)
6 Aralık
11.00-12.30 C# 4.0 Yenilikleri - Erkan Balaban (ceviz.net)
14.00-15.30 WPF 4 ile 3D Animasyon Programlama - Daron Yöndem  (yazgelistir.com)
16.00-17.30 ASP.NET 4.0 - Erkan Balaban (ceviz.net)

İstanbul - Microsoft Ofisi (Kroki)
12 Aralık
;
10.00-11.00 Silverlight 4 - Daron Yöndem (yazgelistir.com)
11.00-12.00 ASP.NET MVC 2 - Oğuz Yağmur (csharpnedir.com)
14.00-15.00 IIS Media Services - Muammer Benzeş (birliktegelistir.com)
15.15-16.15 Visual Basic 10 Yenilikleri - Daron Yöndem (yazgelistir.com)
13 Aralık
11.00-12.30 ASP.NET 4.0 Yenilikleri - Uğur Umutluoğlu (nedirtv.com)
14.30-15.45 Workflow Foundation 4.0 - Burak Selim Şenyurt (csharpnedir.com)
16.00-17.00 C# 4.0 Yenilikleri - Erkan Balaban (ceviz.net)
17.15-18.00 IE 8 Toolbar Geliştirme  - Barış Kanlıca (yazgelistir.com)

Kayseri - Erciyes Üniversitesi Mühendislik Fakültesi Konferans Salonu
19 Aralık
11.00-12.30 C# 4.0 Yenilikleri -Burak Selim Şenyurt  (csharpnedir.com)
14.00-15.30 Silverlight 4 - Daron Yöndem (yazgelistir.com)
15.45-16.45 ASP.NET 4.0 - Selçuk Yavuz (ceviz.net)

20 Aralık
11.00-12.30 ASP.NET MVC 2 - Selçuk Yavuz (ceviz.net)
14.00-15.30 Visual Basic 10 Yenilikleri - Daron Yöndem (yazgelistir.com)
15.45-16.45 Workflow Foundation 4.0 - Burak Selim Şenyurt (csharpnedir.com) 

Denizli - Pamukkale Üniversitesi Kongre Kültür Merkezi Ana Salon
26 Aralık
11.00-12.30 Silverlight 4.0 - Daron Yöndem (yazgelistir.com)
14.00-15.30 C# 4.0 Yenilikleri - Oğuz Yağmur (csharpnedir.com)
15.45-16.45 Asp.NET 4.0 Yenilikleri - Uğur Umutluoğlu (nedirtv.com)
27 Aralık
11.00-.12.30 Visual Basic 10 Yenilikleri - Daron Yöndem (yazgelistir.com)
14.00-15.30 Asp.NET MVC 2 - Oğuz Yağmur (csharpnedir.com)
15.45-16.45 ASP.NET AJAX 4.0 - Uğur Umutluoğlu (nedirtv.com)

Kayıt için www.inetatr.org adresini ziyaret etmeyi unutmayın!

Bu arada ilk etkinlik duyurumuzda da yer alan "Bir katılımcıya yeni bir beyin verilecektir" :) cümlesi biraz merak uyandırmış. Zaten amacı da buydu söz konusu cümlenin. Merak etmeye devam etmeniz gerekecek fakat emin olun bir katılımcıya çekilişle yeni bir beyin vereceğiz :) Bizde yalan yok :) Ayrıca birçok da sürpriz hediye olacak, benden söylemesi.

Sunday, November 29, 2009 2:43:22 PM (GTB Standard Time, UTC+02:00)  #    Comments [11]   .NET Framework 4.0 | ASP.NET | ASP.NET 4.0 | C# | IIS 7.0 | Seminer | Silverlight 4 | Visual Basic 2010 | Visual Studio 2010 | WCF | WF | Windows 7 | WPF  | 
 Thursday, October 01, 2009

AJAX'tan ilk bahsetmeye başladığımız yıllarda en çok aldığım sorulardan biri asenkron File Upload işlemi ile ilgiliydi. Asenkron işlem yapabilmek ve sayfanın Refresh atmaması çok hoş bir durumdu ve bu durumu istemciden sunucuya dosya yüklerken de yaşayabilmek istiyorduk. Bugünler bu gibi bir sorunu çözmek için Silverlight kullanmayı tercih etsem de özellikle 2GB'dan ufak dosyaların yüklenmesi noktasında aslında hala eski taktikler kullanılabilir.

Eminim bazılarınız "Yapılıyor zaten AJAX ile" şeklinde içinden cevap verecektir. Tabi ki yapılır fakat pek de kolay değil. İnternette bulunan çoğu çözüm seksen tane ekstra ayar gerektirirken hiçbir ayar gerektirmeden çalışan ASP.NET FileUpload kontrollerinin çoğu ise ücretli olarak karşımıza çıkıyor. Kişisel yorumumla geç kalınmış olsa da artık sonunda AJAX Control Toolkit içerisinde bir FileUpload kontrolü var!

ASP.NET için asenkron çalışan FileUpload kontrolü!

Her zamanki gibi tüm AJAX Control Toolkit kontrollerinde yaptığımız üzere ilk olarak Toolkit'in en güncel DLL'ini sitesinden bilgisayarımıza indiriyor sonra da Visual Studio içerisinde yarattığımız ASP.NET projesine Reference olarak Toolkit içerisinde DLL'i ekliyoruz. Eğer sürekli olarak AJAX Control Toolkit kontrollerini kullanacaksanız Toolbox'a da bu kontrolleri ekleyebilirsiniz.

[ASP.NET]

<%@ Register assembly="AjaxControlToolkit" namespace="AjaxControlToolkit" tagprefix="cc1" %>

Eğer Toolbox'a eklemediyseniz yukarıdaki şekilde Control Toolkit'i kullanacağınız her sayfada elle gerekli tanımlamaları yapmanız gerekiyor ki mark-up tarafında bu assembly içerisinde kontrolleri kullanabilelim. Eğer Toolbox'a kontrolleri eklerseniz zaten sürükle-bırak işlemi ile kontrolü sayfaya yerleştirebilir hale gelirsiniz. Böylece yukarıdaki kod da otomatik olarak yaratılır. İtiraf etmek gerekirse ben de otomatik yaratılanı yukarıya yapıştırdım :)

[Default.aspx]

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" %>

<%@ Register assembly="AjaxControlToolkit" namespace="AjaxControlToolkit" tagprefix="cc1" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head runat="server">

    <title></title>

</head>

<body>

    <form id="form1" runat="server">

    <cc1:ToolkitScriptManager ID="ToolkitScriptManager1" runat="server">

    </cc1:ToolkitScriptManager>

    <div>

        <cc1:AsyncFileUpload ID="AsyncFileUpload1" runat="server" />

    </div>

    <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>

    </form>

</body>

</html>

Yukarıda basit bir ASP.NET sayfasında AsyncFileUpload kontrolünü bulabilirsiniz. Tabi sayfada ayrıca bir de ToolkitScriptManager bulunuyor ki Toolkit kontrollerini kullanabilelim. Son olarak alt tarafta göreceğiniz Label kontrolünü ise sadece sayfanın Refresh atıp atmadığını kontrol etmek için kullanacağız. Dikkatinizi çektiyse AsyncFileUpload kontrolü herhangi bir UpdatePanel içerisinde değil. Zaten normal olanda budur. Fakat eğer isterseniz AsyncFileUpload kontrolünü bir UpdatePanel içerisinde de rahatlıkla kullanabilirsiniz. İşlevsellikte herhangi bir değişiklik olmuyor.

[VB]

Partial Class _Default

    Inherits System.Web.UI.Page

 

    Protected Sub AsyncFileUpload1_UploadedComplete(ByVal sender As Object, ByVal e As AjaxControlToolkit.AsyncFileUploadEventArgs) Handles AsyncFileUpload1.UploadedComplete

        AsyncFileUpload1.SaveAs(MapPath("~/Konum/") & IO.Path.GetFileName(e.filename))

    End Sub

 

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        Label1.Text = Date.Now.ToLongTimeString

    End Sub

End Class

Yukarıdaki kodun ilk kısmında önemli olan şey AsyncFileUpload kontrolünün UploadedComplete eventını yakalamak. Adından da anlaşılacağı üzere Upload işlemi bittiğinde bu event çalışıyor ve biz de rahatlıkla AsyncFileUpload kontrolünün SaveAs metodu ile dosyamızı sunucuya kaydedebiliyoruz. Kaydederken de diskteki uygun bir yolu vermekte fayda var.

Alt kısımda göreceğiniz kod ise sayfa her refresh attığında sayfaya o anki saat bilgisini saniyesi ile yazdırıyor. Yaptığınız denemede göreceksiniz ki dosya yüklenmesine rağmen saat bilgisi hiç değişmeyecek. Bu da sayfanın tamamen refresh atmadığının bir kanıtı.

Animasyon göstermek istersek?

Yükleme işlemi asenkron olsa da kullanıcıya her zamanki AJAX animasyonlarından birini göstermek çok önemli. Malum zaten AJAX animasyonu olarak bahsettiğim şey aslında basit ve sürekli aynı animasyonu gösteren bir GIF dosyasından farklı değil.

Örnek AJAX Loading animasyonu.
Örnek AJAX Loading animasyonu.

Bu GIF dosyasını bildiğimiz şekilde ASP.NET sayfasına ekledikten sonra AsyncFileUpload kontrolünün ThrobberID özelliğine Image kontrolümüzün ID'sini vermemiz yeterli olacaktır.

[Default.aspx]

<%@ Page Language="VB" AutoEventWireup="false" CodeFile="Default.aspx.vb" Inherits="_Default" %>

<%@ Register assembly="AjaxControlToolkit" namespace="AjaxControlToolkit" tagprefix="cc1" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">

<head runat="server">

    <title></title>

</head>

<body>

    <form id="form1" runat="server">

    <cc1:ToolkitScriptManager ID="ToolkitScriptManager1" runat="server">

    </cc1:ToolkitScriptManager>

    <div>

        <asp:Image ImageUrl="~/ajax-loader.gif" ID="Image1" runat="server" />

        <cc1:AsyncFileUpload ThrobberID="Image1" ID="AsyncFileUpload1" runat="server" />

    </div>

    <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label>

    </form>

</body>

</html>

Eğer kontrolün tasarımında da hızlı bir değişiklik yapmak isterseniz şimdilik iki farklı tasarım hazır olarak sizi bekliyor.

Kontrolde kullanılabilecek hazır iki farklı tasarım var.
Kontrolde kullanılabilecek hazır iki farklı tasarım var.

Hepinize kolay gelsin.

Thursday, October 01, 2009 9:27:14 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   AJAX | ASP.NET | ASP.NET 3.5  | 
 Friday, January 02, 2009

Yeni yılda hayatınızı kolaylaştırma yolunda iddialıyım :) Bu sefer de benim işimi çok kolaylaştıran bir aracı sizlerle paylaşacağım.

Visual Studio içerisinde F5'e bastığımızda System Tray'de beliren "ASP.NET Development Server"'ı hatırlarsınız. Aslında adı "Cassini" olan bu server işimizi epeyce kolaylaştırır ve bizim her sererinde IIS'te sitemizi veya uygulamamızı ayarlamamızı gerektirmeden uygulamanın sanal bir sunucu ortamında test edilmesini sağlar.

Peki hiç herhangi bir projenizi doğrudan çalıştırıp görmek istediğinizde mecburen Visual Studio ile açıp F5'e bastığınız oldu mu? :) Veya mecburen IIS'te web site ayarlamak zorunda kaldınız mı? sadece sitenizi çalıştırabilmek için? İşte bu işkence aslında sürekli yaşadığımız bir süreç ve çok kolay bir çözümü var. Biz de Visual Studio gibi gidip Cassini'yi kullanabiliriz.

Aslında Cassini bizim bilgisayarlarımızda WebDev.WebServer.exe olarak yüklü bulunuyor. Tabi ki EXE'yi doğrudan alıp taşıyamazsınız, çok sayıda bağlantılı DLL vs de söz konusu. Ama biz bu EXE'ye doğru parametreleri gönderirsek aynı Visual Studio'nun kullandığı gibi herhangi bir klasördeki dosyaları web sunucudaymış gibi çalıştırabiliyor. Bu EXE'nin nasıl kullanıldığını merak edenleri veya birazdan sizlerle tanıştıracağım uygulamanın nasıl yazıldığını merak edenleri İngilizce blogumdaki bir yazıya davet edebilirim :)

Sağ tıkla sunucudaaymış gibi çalıştır!
Sağ tıkla sunucudaaymış gibi çalıştır!

Gelelim sadede... Infragistics'te çalışan J. Ambrose Little tüm bunları yaparak uygulamayı da bir SETUP paketi şeklinde hazırlamış ve bununla da kalmamış ve bu sistemi işletim sisteminin context menü'süne bağlamış. Yani özetle; herhangi bir klasöre sağ tıklayıp "ASP.NET 2.0 Web Server Here" dediğizde Cassini açılıyor ve söz konusu klasör içerisindeki uygulama server üzerinden çalıştırılıyormuş gibi karşınıza çıkıyor. Aynı Visual Studio'da F5'e basmış gibi :)

Uygulamayı aşağıdaki linkten bilgisayarınıza indirip yükleyebilirsiniz.

Cassini Web Server Launcher - 02012009_1.msi (355 KB)

ASP.NET 2.0 dediğine bakmayın 3.5 SP1 ile herhangi bir sorunu yok.

Hepinize kolay gelsin...

Friday, January 02, 2009 12:57:26 PM (GTB Standard Time, UTC+02:00)  #    Comments [10]   AJAX | ASP.NET | ASP.NET 3.5 | Silverlight | Silverlight 2.0  | 
 Tuesday, September 23, 2008

Başlık olarak “Reflection” yazdıktan sonra ardına sayfalarca açıklama ve örnek konulabilir. Hatta bu konuda ayrı bir kitap bile yazılabilir. Reflection’ın çok farklı kullanımlar var. Özetleyerek hızlı bir şekilde tanımlamak istersek aslında Reflection bize hakkında bilgi sahibi olmadığınız programatik nesnelerle ilgili çalışma zamanında (run-time) bilgi alabilmemize olanak tanıyan bir metottur. Peki böyle bir şeye neden ihtiyacımız olsun? En basit örnek gerçek zamanlı olarak uygulamalara farklı DLL dosyalarının bağlandığı durumları gösterebiliriz. Böyle bir durumda kaynak konumdaki sınıflar veya metotlar ile ilgili herhangi bir bilgi bulunmaz. Söz konusu bu bilgilerin program çalışırken elde edilerek kullanılması gerekir. Gelin ilk olarak Reflection’ın yapısını ve sistemini tanımak adına tek bir uygulama içerisinde nasıl kullanılabileceğimize göz atalım. Örnek uygulamamızda aşağıdaki şekli ile tanımlanmış bir Urun sınıfı kullanacağız.

[VB]

Public Class Urun

 

    Private PAdi As String

    Public Property Adi() As String

        Get

            Return PAdi

        End Get

        Set(ByVal value As String)

            PAdi = value

        End Set

    End Property

 

    Sub New()

 

    End Sub

 

    Sub New(ByVal adi As String)

        Me.Adi = adi

    End Sub

 

    Function Uyari() As String

        Return "Ürünün adı: " & Me.Adi

    End Function

End Class

[C#]

public class Urun

{

 

    private string PAdi;

    public string Adi

    {

        get { return PAdi; }

        set { PAdi = value; }

    }

 

    public Urun()

    {

 

    }

 

    public Urun(string adi)

    {

        this.Adi = adi;

    }

 

    public string Uyari()

    {

        return "Ürünün adı: " + this.Adi;

    }

}

Uygulamamız içerisinde iki adet düğme yer alacak ve kullanacağımız Windows penceresinde global olarak tanımlanmış bir de Object tipinde değişkenimiz bulunacak.

[VB]

Dim BirUrun As Object   

[C#]

object BirUrun;

Uygulama içerisindeki düğmelerden birine basıldığında global BirUrun değişkenimiz yeni bir Urun değişkenine dönüştürülecek.

[VB]

Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

    BirUrun = New Urun

End Sub

[C#]

    private void button1_Click_1(object sender, EventArgs e)

    {

        BirUrun = new Urun();

    }

Programımız içerisinde diğer düğmeye basıldığında BirUrun adındaki değişkenimizin Adi özelliğini değiştirerek Uyari adındaki metodunu kullanmak istiyoruz. Fakat Visual Studio içerisinde maalesef ki BirUrun adındaki değişkenle beraber Urun tipine ait Intellisense desteği gelmeyecektir. Aslında bu durumun haklı bir nedeni var. İkinci düğmeye basıldığında BirUrun adındaki değişkenin tipininin Object mi yoksa Urun mü olacağı belli değil. İşte tam da istediğimiz ortamı yaratmış olduk. Kullanacağımız nesnenin tipi belirsiz ve biz ona ait bazı özellikleri kullanmak istiyoruz. Bu durumda ilk olarak ikinci düğmeye basıldığında gerçekten BirUrun değişkeninin tipi Urun mü yoksa değil mi sorusunu kontrol etmemiz lazım.

[VB]

       If TypeOf (BirUrun) Is Urun Then

 

        End If

[C#]

        if ((BirUrun) is Urun)

        {

 

        }

Buraya kadar her şey çok kolay. Bundan sonra eğer IF kontrollerimize olumlu sonuç dönüyorsa ilk olarak gidip nesnenin Adi özelliğini bulmamız ve ona bir değer aktarmamız gerekiyor.

[VB]

BirUrun.GetType.GetProperty("Adi").SetValue(BirUrun, "Daron", Nothing)

[C#]

BirUrun.GetType().GetProperty("Adi").SetValue(BirUrun, "Daron", null);   

Yukarıdaki kod ile elimizdeki nesnenin tipini bilmeden onun Adi adındaki özelliğini (property) yakalayarak değerini Daron olarak değiştiriyoruz. Kodumuzu detaylı olarak adım adım bakacak olursak ilk aşamada nesnenin tipini GetType ile alıyoruz. Sonrasında ise tipini yakaladığımız nesnenin GetProperty ile Adi adındaki özelliğini alarak SetValue ile söz konusu özelliğin değerini değiştiriyoruz. SetValue metodu toplam üç parametre alıyor; bunlardan ilki değer değişikliği yapılacak nesnenin kendisi, ikincisi yeni atanacak olan değer, üçüncüsü ise eğer değiştirilecek olan özellik (property) indeksli ise söz konusu indeks değeri. Bizim örneğimizde indeksli bir özellik olmadığı için bu parametreyi boş geçiyoruz.

Değer atamamızı tamamladığımıza göre bu sefer de sıra geldi BirUrun değişkenimize ait Uyari metodunu çalıştırmaya. Metodumuz bize bir String döndürecek biz de onu doğrudan bir mesaj kutusu ile kullanıcıya göstereceğiz.

[VB]

BirUrun.GetType.InvokeMember("Uyari", Reflection.BindingFlags.InvokeMethod, Nothing, BirUrun, Nothing)

[C#]

BirUrun.GetType().InvokeMember("Uyari", System.Reflection.BindingFlags.InvokeMethod, null, BirUrun, null).ToString();

Reflection kullanarak türü bilinmeyen bir nesnenin bir metodunu çalıştırmak için InvokeMember metodundan faydalanmamız gerekiyor. InvokeMember aslında çok geniş kullanımı olan bir metod, biz şimdilik sadece bir çeşit kullanımına değineceğiz. Örneğimizde InvokeMember bir metod çalıştıracağı için ilk parametresinde çalıştırılacak olan metodun adını ikincisinde BindingFlags.InvokeMethod ile bir Metod çalıştırılacağını belirtiyoruz. Üçüncü parametre bizim şimdilik kullanım alanımız dışında kalan Binding’lerle ilgili, aynı şekilde beşinci parametre de boş bırakılarak geçilecek. Dördüncü parametrede ise hedef nesnemiz olan BirUrun değişkenimizi atayacağız. Böylece metodumuzu da çalıştırmış olduk.

Dinamik DLL Kullanımı

Kabaca Reflection’ın nasıl kullanılabildiğine dair bir örnek yaptıktan sonra artık sıra geldi harici bir DLL dosyasının çalışma anında programımıza ekleyerek içerisindeki yapıları kullanmaya. Bu çeşit bir işlevselliği özellikle gerçek zamanlı DLL derlemesi ile birleştirdiğinizde çok farklı bir dünyaya kapı açmış olacaksınız. Hedef olarak kullanacağımız DLL dosyasını aşağıdaki kodlardan yaratacağız.

[VB]

Public Class Deneme

    Function Metin() As String

        Return "Çalışıyor"

    End Function

End Class

[C#]

    public class Deneme

    {

        string Metin()

        {

            return "Çalışıyor";

        }

Yarattığımız DLL dosyasını uygulamamız ile aynı konuma yerleştirdikten sonra aşağıdaki kod ile DLL’imizi kullanmaya başlayabiliyoruz.

[VB]

Dim BirAssembly As Reflection.Assembly = Reflection.Assembly.LoadFrom("ornek2.dll")

[C#]

System.Reflection.Assembly BirAssembly = System.Reflection.Assembly.LoadFrom("ornek2.dll");

Artık yukarıda tanımladığımız Assembly üzerinden Reflection kullanarak ilerleyebiliriz. İlk olarak Deneme adında sınıfımızdan bir instance almamız gerekecek. Bunun için Deneme tipini bulmamız lazım.

[VB / C#]

BirAssembly.GetModule("Ornek2.dll").GetType("Deneme")

Assembly üzerinden modülümüzü yakalıyor sonra da Deneme adındaki tipinizi buluyoruz. Tabi tipi bulmak yeterli değil, söz konusu tipte bir değişken yaratmamız gerekiyor. Activator sınıfını kullanarak bu tip üzerinden bir instance yaratarak Sinif adında bir değişkene aktaracağız.

[VB]

Dim Sinif = Activator.CreateInstance(BirAssembly.GetModule("Ornek2.dll").GetType("Deneme"))   

[C#]

object Sinif = Activator.CreateInstance(BirAssembly.GetModule("Ornek2.dll").GetType("Deneme"));

Yarattığımız sınıfın maalesef özellikleri otomatik olarak gelmeyecek. O nedenle Metin adındaki metodumuzu da elle bularak çalıştırmak zorundayız.

[VB]

BirAssembly.GetModule("Ornek2.dll").GetType("Deneme").GetMethod("Metin").Invoke(Sinif, Nothing)

[C#]

BirAssembly.GetModule("Ornek2.dll").GetType("Deneme").GetMethod("Metin").Invoke(Sinif, null)

Yine Assembly üzerinden yola çıkarak bu sefer daha da ileri gidiyoruz. Deneme sınıfımızı bulduktan sonra içerisinde Metin adındaki metodumuzu buluyor ve doğrudan Invoke ile söz konusu metodu çalıştırıyoruz. Invoke metodu bizden iki parametre istiyor; bunlardan ilki ana sınıfın kendisi. Bir önceki adımda yakaladığımız sınıfı buraya parametre olarak aktarıyoruz. Diğeri ise bizim kullanmayacağımız Binding parametresi.

Metin metodumuz çalıştırıldığında geriye bir String değişkeni döndürüyor. Bu değişkeni de bir mesaj kutusu ile kullanıcıya göstermek istersek uygulamamızın tam kodunun aşağıdaki şekilde sonlanması gerekiyor.

[VB]

Public Class Form2

 

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Dim BirAssembly As Reflection.Assembly = Reflection.Assembly.LoadFrom("ornek2.dll")

 

        Dim Sinif = Activator.CreateInstance(BirAssembly.GetModule("Ornek2.dll").GetType("Deneme"))

        MsgBox(BirAssembly.GetModule("Ornek2.dll").GetType("Deneme").GetMethod("Metin").Invoke(Sinif, Nothing))

    End Sub

End Class

[C#]

namespace CSReflection

{

    public partial class Form2 : Form

    {

        public Form2()

        {

            InitializeComponent();

        }

 

        private void button1_Click(object sender, EventArgs e)

        {

            System.Reflection.Assembly BirAssembly = System.Reflection.Assembly.LoadFrom("ornek2.dll");

 

            object Sinif = Activator.CreateInstance(BirAssembly.GetModule("Ornek2.dll").GetType("Deneme"));

            MessageBox.Show(BirAssembly.GetModule("Ornek2.dll").GetType("Deneme").GetMethod("Metin").Invoke(Sinif, null).ToString());

        }

    }

}

Böylece harici bir DLL dosyasını yükleyerek istediğimiz metodu dinamik olarak kullanabildik. Farklı durumlarda isterseniz bir DLL içerisinde tüm metod, sınıf ve özelliklerin listelerini alabilir hatta bunları LINQ sorguları ile tarayabilirsiniz.

[VB]

Dim Metodlar = From Gelenler In BirAssembly.GetModule("Ornek2.dll").GetTypes Where Gelenler.GetMethod("Metin") IsNot Nothing

[C#]

var Metodlar = from Gelenler in BirAssembly.GetModule("Ornek2.dll").GetTypes() where Gelenler.GetMethod("Metin") != null select Gelenler;

Örneğin yukarıdaki LINQ sorgumuz ile harici DLL dosyası içerisinde Metin adında metodu olan tüm sınıfların bir listesini alıyoruz.

Hepinize kolay gelsin.

Tuesday, September 23, 2008 6:51:06 PM (GTB Standard Time, UTC+02:00)  #    Comments [8]   .NET Framework 3.0 | .NET Framework 3.5 | ASP.NET | ASP.NET 3.5 | LINQ | Silverlight 2.0 | Visual Basic 2005 | Visual Basic 2008 | Visual Studio 2005 | Visual Studio 2008 | WCF | WF | WPF  | 
 Sunday, September 21, 2008

Bir uygulama düşünün kendini programlayabilen. Konumuz “Star Trek” veya “Geleceğe Dönüş” değil. Emin olun gerçek dünyadan ve yapılabileceklerden bahsediyorum. Uygulamalarınızın dış sistemlerle ciddi bir bağlantı içerisinde olduğu durumlarda bazen kendi içlerinde dış sistemlere uygun kodlar üreterek kullanmaları gerekebilir. Bunu bazen uygulamaların kendi içlerindeki yapay zeka ile yapabilecekleri gibi bazen ise başka bir dış kaynaktan aldıkları yeni parametrelerden yola çıkarak kendi kodlarında değişiklik yapabilirler. Eğer bunların hiçbiri size gerçekçi gelmiyorsa başka bir seçenek olarak da harici uygulamaların kullanabileceği DLL dosyaları yaratacak bir uygulama yazmak istediğinizde yapmanız gerekenlerden bahsedebiliriz. Aslında her ikisi de aynı kapıya çıkıyor.

Bize dinamik olarak uygulamalar tarafından kullanılabilecek DLL dosyaları yaratacak bir kod lazım. Kullanacağımız nesnelerin çoğunun bulunduğu esas namespace System.CodeDom.Compiler olacak. Bunun haricinde C# veya VB için ayrı ayrı uygun namespace’leri kullanmamız gerek. Eğer VB kodu derleyecekseniz VB sınıflarını C# kodu derleyecekseniz tabi ki C# sınıflarını kullanmalısınız. Çapraz işlem yaparak C# kodunuz ile VB kodundan DLL üretme şansınız da var. Biz örneklerimizde C# ile C#’dan derleme, VB kodu ile de VB’den derleme yapacağız.

[VB]

Dim KodUretici As New Microsoft.VisualBasic.VBCodeProvider

Dim Derleyici As System.CodeDom.Compiler.CodeCompiler = KodUretici.CreateCompiler()

 

Dim Referanslarim As String() = {"System.dll"}

Dim AssemblyAdi As String = "Ornek.dll"

[C#]

Microsoft.CSharp.CSharpCodeProvider KodUretici = new Microsoft.CSharp.CSharpCodeProvider();

System.CodeDom.Compiler.ICodeCompiler Derleyici = KodUretici.CreateCompiler();

 

String[] Referanslarim =  {"System.dll"};

String AssemblyAdi= "Ornek.dll";

Kodumuzun başlangıcında ilk olarak birer CodeProvider nesnesi yaratıyoruz. Elimizdeki hazır kodu derleyecek olan nesneler olarak bu sınıflar VB ve C# için farklılaşıyor. CodeProvider’lar üzerinden birer de derleyici nesnesi aldıktan sonra sıra geliyor derleyeceğimiz kodun referanslarına karar vermeye. Referansları DLL isimleri ile bir String dizisine aktarmanız şart. Windows uygulamalarında en azından System.dll’in web uygulamalarında da System.Web.dll’in referans alınmış olması gerekiyor. Son olarak üreteceğimiz DLL dosyasının adını da başka bir değişkene aktararak yolumuza devam edelim.

[VB]

Dim DerlemeParametreleri As New System.CodeDom.Compiler.CompilerParameters(Referanslarim, AssemblyAdi)

DerlemeParametreleri.GenerateExecutable = True

DerlemeParametreleri.GenerateInMemory = False

[C#]

System.CodeDom.Compiler.CompilerParameters DerlemeParametreleri = new System.CodeDom.Compiler.CompilerParameters(Referanslarim, AssemblyAdi);

DerlemeParametreleri.GenerateExecutable = false;

 DerlemeParametreleri.GenerateInMemory = false;

Derleme işlemini yaparken yapmamız gereken ayarlar var. Bu ayarları derleyicimize bir CompilerParameters nesnesi olarak aktaracağız. DerlemeParametreleri değişkenimizi yaratırken referanslarımızı ve DLL adını aktardıktan sonra özel olarak GenerateExecutable özelliğini false olarak ayarlıyoruz. Böylece derleyicimiz bize tek başına çalışabilir bir dosya yaratmaktansa bir DLL dosyası yaratacak. Bir sonraki adımda da GenerateInMemory özelliğini false yaparak yaratılacak dosyanın uygulamamız ile aynı konuma, diske yazdırılmasını sağlıyoruz. Aksi halde yaratılan Assembly sadece hafızada tutulacak ve diske yazılmayacaktır. Sıra geldi dinamik olarak derlemeyeceğimiz kodu bir değişkene aktarmaya.

[VB]

Dim Kodum As String = <Kod>Public Class Deneme

    Function Metin() As String

        Return "Çalışıyor"

    End Function

End Class</Kod>.Value

[C#]

System.IO.StreamReader Okuyucu = new System.IO.StreamReader("Class1.cs");

string Kodum = Okuyucu.ReadToEnd();

Okuyucu.Close();

Bu noktada VB ile C# arasında farklı işlemler yaptım. VB’de doğrudan yaratacağım kodu uygulamanın içerisine gömerken C#’da derleyeceğim C# kodunu harici bir Class1.cs dosyasından çektim. Siz kendi uygulamalarınızda ister bu kodları farklı dosyalardan çekin ister metin işlemleri ile dinamik kod yaratın. İhtiyaçlarınıza göre uygun çözümü üretmek tamamen size kalmış. Önemli olan tek nokta aslında bu kodlarda hiçbir hatanın olmaması gerektiği, aksi halde derleme işlemi yapılamayacaktır.

[VB]

Dim Sonuc As System.CodeDom.Compiler.CompilerResults = KodUretici.CompileAssemblyFromSource(DerlemeParametreleri, Kodum)

[C#]

System.CodeDom.Compiler.CompilerResults Sonuc = KodUretici.CompileAssemblyFromSource(DerlemeParametreleri, Kodum);

Tüm ayarlarımız tamamlandığında göre doğrudan CodeProvider nesnemizin CompileAssemblyFromSource metodunu kullanarak derleme işlemini başlatabiliriz. Tabi bu esnada daha önce hazırlamış olduğumuz DerlemeParametrelerini de metoda parametre olarak aktarıyoruz. Derleme işlemimizi baştan sona tamamlayan kodumuzu bir bütün olarak inceleyelim.

[VB]

Dim KodUretici As New Microsoft.VisualBasic.VBCodeProvider

Dim Derleyici As System.CodeDom.Compiler.CodeCompiler = KodUretici.CreateCompiler()

 

Dim Referanslarim As String() = {"System.dll"}

Dim AssemblyAdi As String = "Ornek.dll"

 

Dim DerlemeParametreleri As New System.CodeDom.Compiler.CompilerParameters(Referanslarim, AssemblyAdi)

        DerlemeParametreleri.GenerateExecutable = True

        DerlemeParametreleri.GenerateInMemory = False

 

Dim Kodum As String = <Kod>Public Class Deneme

    Function Metin() As String

        Return "Çalışıyor"

    End Function

End Class</Kod>.Value

 

Dim Sonuc As System.CodeDom.Compiler.CompilerResults = KodUretici.CompileAssemblyFromSource(DerlemeParametreleri, Kodum)

[C#]

Microsoft.CSharp.CSharpCodeProvider KodUretici = new Microsoft.CSharp.CSharpCodeProvider();

System.CodeDom.Compiler.ICodeCompiler Derleyici = KodUretici.CreateCompiler();

 

String[] Referanslarim =  {"System.dll"};

String AssemblyAdi= "Ornek.dll";

 

System.CodeDom.Compiler.CompilerParameters DerlemeParametreleri = new System.CodeDom.Compiler.CompilerParameters(Referanslarim, AssemblyAdi);

DerlemeParametreleri.GenerateExecutable = false;

DerlemeParametreleri.GenerateInMemory = false;

 

System.IO.StreamReader Okuyucu = new System.IO.StreamReader("Class1.cs");

string Kodum = Okuyucu.ReadToEnd();

Okuyucu.Close();

 

System.CodeDom.Compiler.CompilerResults Sonuc = KodUretici.CompileAssemblyFromSource(DerlemeParametreleri, Kodum);

Dinamik olarak DLL dosyası derlemek işte bu kadar kolay. Dinamik kod yaratma araçları son dönemde çok popüler. Veritabanına bağlanarak veritabanındaki nesneleri algılayıp uygun “Veri Katmanı” kodunu dinamik olarak oluşturan hazır uygulamalar olduğu gibi bazı durumlarda özel kodlar yazmak da gerekebiliyor. Böyle bir durumda artık siz de uygulamalarınıza farklı kaynaklardaki şartlara uygun kodu dinamik olarak üretebilir ve bir DLL olarak farklı uygulamalara aktarabileceğiniz gibi kendi uygulamalarınızda da kullanabilirsiniz. Yarattığınız DLL dosyasını hemen uygulamanızda kullanmak isterseniz bu sefer dinamik olarak Assembly kullanımını ve Reflection konusuna eğilmenizde fayda var.

Hepinize kolay gelsin.

Sunday, September 21, 2008 7:04:21 PM (GTB Standard Time, UTC+02:00)  #    Comments [3]   .NET Framework 3.0 | .NET Framework 3.5 | ASP.NET | ASP.NET 3.5 | Silverlight 2.0 | Visual Basic 2005 | Visual Basic 2008 | Visual Studio 2005 | Visual Studio 2008 | WCF | WPF  | 
 Friday, September 19, 2008

İster VB olsun ister C#, ister web ister Windows uygulaması olsun yazdığımız tüm kodların derlenerek (Compile) bir EXE veya DLL haline dönüştürüldüğünü biliyoruz. Aslında .NET içerisinde yapılan işlem sizin yazdığınız herhangi bir .NET dilindeki kodun MSIL (Microsoft Intermediate Language)’a çevrilmesidir. İşte tam bu noktada akla gelen ilk soru; acaba bu çeviri işleminin tersini yapmak mümkün mü? Yani elimizdeki DLL veya EXE dosyasından yola çıkarak VB veya C# kodumuzu geri alabilir miyiz? Cevap: Evet.

Şu andan itibaren yapacaklarımız hedef olarak kullanacağınız uygulamanın lisans sözleşmesine göre yeri geldiğinde suç teşkil edebilir. O nedenle sizi özellikle uyarmak istiyorum. Çoğu zaman De-Compile işlemleri yaparkenki amacımız yazdığımız kodun nasıl derleyici tarafında MSIL’e çevrildiğini incelemek veya kaynak kodunu kaybettiğimiz ve bize ait olan bir uygulamanın kodlarına ulaşmak olacaktır. Diğer yandan lisans sözleşmesi ile aykırı düşmediği sürece farklı uygulamaları da De-Compile ederek arka planda farklı işlemlerin nasıl yapıldığını inceleme şansınız da olabilir.

.NET tarafına geçtiğimizde herhangi bir DLL veya EXE’nin aslında MSIL kodları içerdiğinden bahsetmiştik. Tabi ki bu MSIL kodları doğrudan bilgisayarlar tarafından çalıştırılabilir kodlar değiller. O nedenle içerisinde MSIL bulunan bir .NET yapısının çalışabilmesi için hedef makinede .NET Framework’ün yüklü olması gerekiyor. .NET Framework içerisindeki CLR (Common Language Runtime) bizim MSIL kodumuzu makine diline çevirerek çalışmasını sağlayacaktır. Kabaca baktığımızda De-Compile yolunda bizim ilk olarak elimizdeki DLL veya EXE içerisinden MSIL kodunu alarak çıkarmamız gerekecek. Bunun için doğrudan .NET Framework SDK paketi ile beraber gelen MSIL DisAssembler (ILDASM) uygulamasını kullanabiliriz.

IL DASM Kullanımı

Bilgisayarınıza .NET Framework SDK paketini kurduktan sonra doğrudan “Başlat” menüsünden ulaşabileceğiniz ILDASM programını Visual Studio yükleme konumu içerisinde SDK klasörü altında da bulabilirsiniz. Programı açtıktan sonra “File / Open” menüsünden istediğiniz bir .NET DLL veya EXE dosyasını açma şansınız olacaktır.Deneme amaçlı olarak gelin mini bir Windows uygulaması yazalım ve ILDASM ile açarak alacağımız sonucu görelim. Uygulamamız içerisinde birer TextBox, Button ve Label bulunacak. Basit bir şekilde düğmeye basıldığında TextBox içerisindeki değeri Label içerisine aktaracağız.

[VB]

Public Class Form1

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click

        Label1.Text = TextBox1.Text

    End Sub

End Class

[C#]

namespace WindowsFormsApplication1

{

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

        }

 

        private void button1_Click(object sender, EventArgs e)

        {

            label1.Text = textBox1.Text;

        }

    }

}

Yukarıda yazdığımız kodlar ile oluşturduğumuz uygulamayı ILDASM ile açarak sonucu inceleyelim. Uygulamanın ilk açılan penceresinde bizim EXE’ye ait tüm sınıflar ve namespace’ler gözüküyor olacaktır. Eğer herhangi bir nesnenin tanımı veya metodu ile ilgili MSIL kodunu görmek isterseniz doğrudan çift tıklayarak yeni bir pencerede kodların açılmasını sağlayabilirsiniz.

ILDASM içerisinde EXE’mizin MSIL kodları açıkça gözüküyor
ILDASM içerisinde EXE’mizin MSIL kodları açıkça gözüküyor

Hazırladığımız örnek uygulamanın Button_Click durumundaki MSIL kodunu bulduğumuzda aşağıdaki sonuç ile karşılaşıyoruz.

[MSIL]

.method private instance void  Button1_Click(object sender,

                                            class [mscorlib]System.EventArgs e) cil managed

{

  // Code size       23 (0x17)

  .maxstack  8

  IL_0000:  ldarg.0

  IL_0001:  callvirt   instance class [System.Windows.Forms]System.Windows.Forms.Label WindowsApplication1.Form1::get_Label1()

  IL_0006:  ldarg.0

  IL_0007:  callvirt   instance class [System.Windows.Forms]System.Windows.Forms.TextBox WindowsApplication1.Form1::get_TextBox1()

  IL_000c:  callvirt   instance string [System.Windows.Forms]System.Windows.Forms.TextBox::get_Text()

  IL_0011:  callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Label::set_Text(string)

  IL_0016:  ret

} // end of method Form1::Button1_Click

Yukarıdaki MSIL kodu normal şartlarda CLR tarafından makine koduna çevrilerek hedef ortamda çalıştırılıyor. Artık MSIL kodumuzu aldığımıza göre bu kodu VB veya C# koduna çevirmemiz lazım. Tabi bu iş o kadar kolay değil ve tek tek elle yapılabilecek bir iş de değil. O nedenle bu sefer de farklı bir araç kullanacağız.

Reflector iş başında

Lutz Roeder tarafından yazılmış bir program olarak Reflector’ı http://www.red-gate.com/products/reflector/ adresinden bilgisayarınıza indirebilirsiniz. Program aslında bir önceki adımda anlattığım MSIL çözme işlemini de kendi içinde yapabiliyor. Bununla kalmayıp çözdüğü MSIL kodunu istediğiniz .NET diline de çevirebiliyor.

Programı çalıştırdıktan sonra “File / Open” menüsünden istediğiniz bir EXE veya DLL dosyasını seçebilirsiniz. Uygulamanın ana penceresindeki sınıf listesine hemen seçtiğiniz program da gelecektir.

Reflector ile kaynak kodunu görebiliyoruz.
Reflector ile kaynak kodunu görebiliyoruz.

Ufak bir gezinti ile istediğiniz sınıfın veya metodun koduna doğrudan ulaşabilirsiniz. Reflector arayüzündeki “Programlama Dili” seçeneğinde VB, C#, Delphi ve IL seçenekleri bulunuyor. Bir önceki bölümde hazırladığımız uygulamamızı açarak Button.Click durumundaki kodu farklı dillerde Reflector ile alıp inceleyelim.

[VB]

Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs)

    Me.Label1.Text = Me.TextBox1.Text

End Sub

[C#]

private void Button1_Click(object sender, EventArgs e)

{

    this.Label1.Text = this.TextBox1.Text;

}

Yazdığımız kodlar ile Reflector’ın bize verdiği kodlar tam olarak aynı değil. Bu durum zaten çok normal. Çünkü MSIL koduna çeviri esnasında aslında çoğu şey değişiyor. Örneğin tanımladığımız değişkenlerin bize özel olan isimlendirmeleri yok oluyor veya bizim kullandığımız bazı kısa metotlar uzun şekilleri ile yazılabiliyor. Hatta özellikle VB içerisindeki casting kolaylıkları Compile esnasında farklı değişikliklere neden olabiliyor. Bu durumda De-Compile ile aldığımız kod da yazdığımız koddan biraz farklı oluyor. Yine de elimizde çalışır durumda bir kod olduğuna kesin gözü ile bakabiliriz.

Nasıl engelleriz? Obfuscation!

Herhalde çoğunuz “tüm kodlarımız gözler önünde” endişesi içerisindesiniz. Aslında durum gerçekten de öyle. Tabi bu durumun birçok faydası var. Kişisel olarak itiraf etmek gerekirse farklı yazılımları De-Compile ederek çok şey öğrendiğimi söyleyebilirim. Bir defasında da kendi ürettiğimiz bir yazılımı De-Compile etmemiz gerekmişti, gerçekten hayat kurtarmıştı. Peki bunu nasıl engelleyebiliriz? İlk olarak şunu açıkça belirtiyim, herhangi bir .NET uygulamasından MSIL kodunun alınmasını engellemenin hiçbir yolu yok. Yapabileceğimiz tek şey MSIL kodunun okunabilirliliğini azaltmak için işlevsel olarak aynı işi gören fakat daha karışık bir MSIL kodu yaratmak. Bu işlem obfuscation olarak adlandırılıyor.

Obfuscation ile ilgili sektörde çok sayıda ücretli yazılım bulabilirsiniz. Biz bunlardan Xenocode'aait Postbuild 2008 adındaki ticari yazılımı kullanarak obfuscation ile neler yapabildiğimize bakacağız. XenoCode’u ilk açtığımızda karşımıza hemen bir uygulama listesi geliyor. Bu listeye bir önceki adımda kendi hazırladığımız EXE dosyasını ekleyerek uygulamanın üst menüsünden “Protect” tabına geçiyoruz. Burada sadece Windows’da çalışacak EXE dosyalarına uygulanabilecek özel bir koruma yöntemi olan “Surpress ILDASM” seçeneğinin işaretini kaldırmamız gerek. Bu seçenek DLL’lere zaten uygulanamayacaktır. Ekranın sağ tarafında korumak istediğimiz sınıfların ve metodların bir listesini işaretleyebiliyoruz. Tüm ayarları tamamladıktan sonra uygulamanın sağ altındaki “XenoCode Application” düğmesine basıyoruz.

Obfuscation işlemi için yollardayız
Obfuscation işlemi için yollardayız

Obfuscation işlemini tamamladıktan sonra sıra geldi testlerimizi yapmaya. İlk olarak uygulamamızı ILDASM ile açarak bakalım MSIL kodumuz ne hale gelmiş.

[MSIL]

.method private instance void  x44d0c0526a414989(object xe0292b9ed559da7d,

                                                class [mscorlib]System.EventArgs xfbf34718e704c6bc) cil managed

{

  // Code size       23 (0x17)

  .maxstack  8

  IL_0000:  ldarg.0

  IL_0001:  callvirt   instance class [System.Windows.Forms]System.Windows.Forms.Label WindowsApplication1.xaa4f033827d75b4d::get_x029e304eb4c44750()

  IL_0006:  ldarg.0

  IL_0007:  callvirt   instance class [System.Windows.Forms]System.Windows.Forms.TextBox WindowsApplication1.xaa4f033827d75b4d::get_x77691a2cfb8f8048()

  IL_000c:  callvirt   instance string [System.Windows.Forms]System.Windows.Forms.TextBox::get_Text()

  IL_0011:  callvirt   instance void [System.Windows.Forms]System.Windows.Forms.Label::set_Text(string)

  IL_0016:  ret

} // end of method xaa4f033827d75b4d::x44d0c0526a414989

Gördüğünüz gibi aslında çok büyük bir değişiklik yok. Sadece sınıfların ve metodların isimleri değiştirilerek karışık isimler verilmiş. Aynı uygulamayı Reflector ile açtığımızda ise aşağıdaki kodları elde ediyoruz.

[VB]

Private Sub x44d0c0526a414989(ByVal xe0292b9ed559da7d As Object, ByVal xfbf34718e704c6bc As EventArgs)

    Me.x029e304eb4c44750.Text = Me.x77691a2cfb8f8048.Text

End Sub

[C#]

private void x44d0c0526a414989(object xe0292b9ed559da7d, EventArgs xfbf34718e704c6bc)

{

    this.x029e304eb4c44750.Text = this.x77691a2cfb8f8048.Text;

}

Kodlar epey okunurluluğunu kaybetmiş durumda. Bizim örneğimizde sadece tek bir satır kod bulunduğu için neyin ne olduğunu anlamak çok zor olmuyor. Fakat binlerde satırdan oluşan uygulamaların kodlarından anlaşılabilir bir sonuç çıkarmak neredeyse imkânsız olacaktır.

Hepinize kolay gelsin.

Friday, September 19, 2008 6:27:11 PM (GTB Standard Time, UTC+02:00)  #    Comments [11]   .NET Framework 3.0 | .NET Framework 3.5 | ASP.NET | ASP.NET 3.5 | Silverlight 2.0 | Visual Basic 2005 | Visual Basic 2008 | Visual Studio 2005 | Visual Studio 2008  | 
 Thursday, June 19, 2008

Çok önceleri İngilizce bloguma yazdığım fakat ilginç bir şekilde Türkçe'ye yazmadığım bir araçtan bahsetmek istiyorum. Özellikle sunumlarda çok işime yarıyor ve sıkça kullanıyorum. En son Soru Merkezi'nde de bir arkadaşımız sorduğu için buraya hemen yazmaya karar verdim.

Gördüğünüz gibi aslında araç çok basit bir iş yapıyor. Sisteminizde herhangi bir klasöre sağ tuş ile tıkladığınızda karşınıza gelen menüden doğrudan ASP.NET Development Server çalıştırabiliyorsunuz. Bu da ortalama 3 saniye içerisinde o klasörün içeriğini gösteren bir IIS anlamına geliyor. Özellikle hızlı bir şekilde örnekleri veya uygulamaları incelemek için çok başarılı bir araç. Visual Studio ile projeyi açıp F5'e basmaktan veya klasör için IIS üzerinde ayar yapmaktan pratik olduğu kesin.

ASP.NET Development Server Kısayol Aracı - 19062007_1.msi (355 KB)

Uygulamayı yazan Infragistics'ten J. Ambrose Little'a buradan teşekkürlerimizi de yolluyoruz ;)

Thursday, June 19, 2008 8:53:36 PM (GTB Standard Time, UTC+02:00)  #    Comments [10]   ASP.NET | ASP.NET 3.5  | 
 Tuesday, June 03, 2008

Bugün Edirne M.P. Anadolu Ticaret Meslek ve  Ticaret Meslek Lisesi'nin organizasyonunu üstlendiği Edirne'deki meslek liseleri'nin yer aldığı bir seminer serisindeydim. Semineri Trakya Birlik Genel Müdürlüğü Konferans Salonu'nda yaptık. Salon şu ana kadar gördüğüm en ilginç ve güzel salonlardan biriydi. Balkonuyla beraber iki katlı seminer salonunda alt kattaki ve üst kattaki dinleyiciler için ayrı projeksiyon sistemlerinin bulunması dinleyici için çok ergonomik bir ortam sağlıyor.

Edirne'ye gidip Selimiye Cami fotoğrafı çekmemek olmaz :)
Edirne'ye gidip Selimiye Cami fotoğrafı çekmemek olmaz :)

Sabah 09.00 gibi seminerlere başladığımız için bir gün önceden Edirne'ye gitmek durumunda kaldım. Dün akşam 20.00 gibi Edirne'deydim, Meriç kenarında bir yemek yeme şansına sahip olmak muhteşemdi. Aslında otobüsle iki buçuk saat gibi kısa bir sürede gidilebilen Edirne'yi (kendi adıma konuşayım) sanki biraz farkında değiliz veya unutmuşuz gibime geldi. Rahatlıkla günübirlik gidip gezilebilecek bir şehir.

Selimiye Kubbesi İç Motifler
Selimiye Kubbesi İç Motifler

09.00'da başladıktan sonra öğleden sonra 18.00'a kadar WPF, Silverlight ,AJAX ve LINQ konularına değindik. Bu benim ikinci Meslek Lisesi deneyimim oldu. Lise çağındaki gençlere hitap edebilmek adına daha çok çalışmam gerektiğini açıkça itiraf edebilirim :) Yine de aldığım tepkilere göre faydalı bir gün geçirdik, parıldayan genç gözler gördüm.

Edirne, MP (Anadolu) Ticaret Meslek Lisesi, WPF, Silverlight, AJAX, LINQ Seminerleri
Edirne, MP (Anadolu) Ticaret Meslek Lisesi, WPF, Silverlight, AJAX, LINQ Seminerleri

Aktivite boyunca Edirne TV :) de oradaydı. Hatta sitelerinde de aktiviteyi haber yyapmışlar. Özellikle sitedeki fotoğrafta biraz çok agresif çıkmışım :) Zaten herkes sahnede rahat durmadığım için fotoğraf çekememekten şikayetçi :)

Organizasyondaki emeklerinden dolayı sevgili Serdar Daloğlu'na , Aziz Bayıroğlu ve Oğuzcan Şahin'e burada bin bir teşekkür ediyorum. Beni muhteşem bir şekilde ağırlamanın yanı sıra organizasyondaki her tür detayı çok başarılı bir şekilde yürüttüler.

Tuesday, June 03, 2008 10:45:51 PM (GTB Standard Time, UTC+02:00)  #    Comments [3]   AJAX | ASP.NET | ASP.NET 3.5 | LINQ | Silverlight | WPF  | 
 Saturday, May 31, 2008

Varsayalım ki web sitenizde belirli aktiviteleri paylaşıyorsunuz veya belki de toplantıları. İnsanların buluşma tarihlerini bir kenara not almalarını ve sonra da günü geldiğinde hatırlamalarını istiyorsunuz. Artık çoğu kullanıcının bir şekilde dijital ortamda takvim tuttuğunu varsayarsak neden onlara söz konusu aktivitelerini hızlı bir şekilde takvimlerine ekleme olanağı tanımlayalım? Şükür ki bu konuda genel geçer bir standart var; VCalendar!

VCalendar standardı çoğu dijital takvim uygulaması ile uyumlu bir standart. Yapacağımız örnekte bir Generic Handler (ashx) kullanacağız. Söz konusu handler bize VCalendar davetiye dosyaları oluşturarak verecek. Siz uygulamalarınızda ashx dosyasına QueryString üzerinden parametreler göndererek farklı VCalendar dosyaları oluşturabilirsiniz.

        context.Response.ContentType = "application/download"

İlk olarak yukarıdaki şekilde istemciye göndereceğimiz verinin download edilmesi gereken bir uygulama dosyası olduğunu belirtelim. Böylece istemcideki tarayıcı bir download penceresi açacaktır. VCalendar dosyaları vcs uzantılıdır, istemciye doğrudan gönderdiğimiz istemcide kullanıcı bunu ister Outlook ile açar ister farklı bir uygulama ile kullanır, karar tamamen kullanıcıya kalmış.

[VB]

        Dim BasTarih As Date = Date.Now

        Dim SonTarih As Date = Date.Now.AddDays(1)

        Dim Konum As String = "İstanbul - MS Binası"

        Dim Konu As String = "VCalendar Toplantısı"

        Dim Detay As String = "Toplantı VCalendar standartları görüşülecek. "

[C#]

        System.DateTime BasTarih = System.DateTime.Now;

        System.DateTime SonTarih = System.DateTime.Now.AddDays(1);

        string Konum = "İstanbul - MS Binası";

        string Konu = "VCalendar Toplantısı";

        string Detay = "Toplantı VCalendar standartları görüşülecek. ";

Bir sonraki adımda yukarıdaki şekilde parametrelerimizi tanımlayalım. Bu parametreleri siz uygulamalarınızda veritabanına bağlayabilirsiniz. Yaratacağımız VCalendar dosyası buradaki bilgileri içerecek.

[VB]

        Dim BirStream As New IO.MemoryStream()

        Dim Yazici As New IO.StreamWriter(BirStream)

        Yazici.AutoFlush = True

[C#]

        System.IO.MemoryStream BirStream = new System.IO.MemoryStream();

        System.IO.StreamWriter Yazici = new System.IO.StreamWriter(BirStream);

        Yazici.AutoFlush = true;

VCalendar dosyaları özünde text (metin) bazlı dosyalardır. O nedenle aslında sunucu tarafında bir metin yaratarak Binary olarak istemciye göndereceğiz. Bu nedenle istemciye göndereceğimiz tüm içeriği oluştururken elimizde bir Stream ve StreamWriter olması gerekiyor. Söz konusu Stream herhangi bir konuma yazdırılmayacağı için havada bir MemoryStream yaratarak ilerliyoruz. Söz konusu StreamWriter'ın AutoFlush özelliğini True olarak aktardığımızda artık tüm içerik otomatik olarak Stream'e aktarılıyor olacak.

        Yazici.WriteLine("BEGIN:VCALENDAR")

        Yazici.WriteLine("PRODID:-//daron.yondem.com")

        Yazici.WriteLine("BEGIN:VEVENT")

Yukarıdaki VCalendar standartlarına uygun dosyamızı yaratmaya başladık. PRODID özelliğine aktardığınız değer çok önemli değil. Aslında bu VCalendar dosyasını yaratan programın adını içermesi yeterli.

[VB]

        Yazici.WriteLine("DTSTART:" & BasTarih.ToUniversalTime.ToString("yyyyMMdd\THHmmss\Z"))

        Yazici.WriteLine("DTEND:" & SonTarih.ToUniversalTime.ToString("yyyyMMdd\THHmmss\Z"))

        Yazici.WriteLine("LOCATION:" & Konum)

        Yazici.WriteLine("DESCRIPTION;ENCODING=QUOTED-PRINTABLE:" & Detay)

        Yazici.WriteLine("SUMMARY:" & Konu)

[C#]

        Yazici.WriteLine("DTSTART:" + BasTarih.ToUniversalTime().ToString("yyyyMMdd\\THHmmss\\Z"));

        Yazici.WriteLine("DTEND:" + SonTarih.ToUniversalTime().ToString("yyyyMMdd\\THHmmss\\Z"));

        Yazici.WriteLine("LOCATION:" + Konum);

        Yazici.WriteLine("DESCRIPTION;ENCODING=QUOTED-PRINTABLE:" + Detay);

        Yazici.WriteLine("SUMMARY:" + Konu);

Gördüğünüz gibi neredeyse tüm parametrelerimizi burada dosyamıza ekledik. Önemli olan noktalardan biri tüm tarih bilgilerinin ilk olarak UniversalTime'a yani global olarak geçerli zaman dilimine uyarlanması gerektiği. İkincisi ise tarih dosyaya yazdırılırken uyulması gereken format. Sonraki satırlarda diğer parametrelerimizi de aktardıktan sonra devam edebiliriz.

[VB]

        Yazici.WriteLine("PRIORITY:3")

 

        Yazici.WriteLine("BEGIN:VALARM")

        Yazici.WriteLine("TRIGGER:-PT30M")

        Yazici.WriteLine("ACTION:DISPLAY")

        Yazici.WriteLine("DESCRIPTION:" & Konu)

        Yazici.WriteLine("END:VALARM")

 

        Yazici.WriteLine("END:VEVENT")

        Yazici.WriteLine("END:VCALENDAR")

[C#]

        Yazici.WriteLine("PRIORITY:3")

 

        Yazici.WriteLine("BEGIN:VALARM")

        Yazici.WriteLine("TRIGGER:-PT30M")

        Yazici.WriteLine("ACTION:DISPLAY")

        Yazici.WriteLine("DESCRIPTION:" + Konu)

        Yazici.WriteLine("END:VALARM")

 

        Yazici.WriteLine("END:VEVENT")

        Yazici.WriteLine("END:VCALENDAR")

Kodumuzun ilk satırında bu aktivitenin ne kadar önemli olduğuna dair bir değer aktardıktan sonra aktivite ile ilgili bir alarm ayarlıyoruz. PT30M ayarı ile aktiviteden yarım saat önce kullanıcının uyarılması gerektiğini belirtmiş olduk. Siz bu ayarları istediğiniz gibi değiştirebilirsiniz. Son olarak hem VEVENT gem de VCALENDAR'ı kapatarak dosyamızı sonlandırıyoruz.

[VB]

        context.Response.AppendHeader("Content-Disposition", "attachment; filename=aktivite.vcs")

        context.Response.AppendHeader("Content-Length", BirStream.Length.ToString())

        context.Response.BinaryWrite(BirStream.ToArray())

[C#]

        context.Response.AppendHeader("Content-Disposition", "attachment; filename=aktivite.vcs");

        context.Response.AppendHeader("Content-Length", BirStream.Length.ToString());

        context.Response.BinaryWrite(BirStream.ToArray());

Sıra geldi oluşturduğumuz dosyayı istemciye göndermeye. İlk olarak istemciye gönderdiğimiz HTTPResponse'a bazı header bilgileri eklememiz gerekiyor. Bunlardan ilki istemcinin aldığı dosyayı hangi isimle açacağı. Bizim örneğimizde dosya adı aktivite.vcs şeklinde yer alıyor. İkincisi ise oluşturduğumuz dosyanın boyutu. Son olarak Response.BinaryWrite ile elimizdeki Stream'i istemciye gönderiyoruz.

Eğer istemci bu adresi doğrudan açarsa karşısına bir VCalendar download penceresi gelecektir. Yarattığımız ashx dosyasını farklı aspx dosyaları içerisinde linkleyerek kullanıcıların linklere bastıklarında VCalendar dosyalarını indirebilmelerini sağlayabilirsiniz.

Generic Handler'ımızın son hali ile aşağıdaki şekilde;

[VB]

<%@ WebHandler Language="VB" Class="vcalendar" %>

 

Imports System

Imports System.Web

 

Public Class vcalendar : Implements IHttpHandler

 

    Public Sub ProcessRequest(ByVal context As HttpContext) Implements IHttpHandler.ProcessRequest

        context.Response.ContentType = "application/download"

 

        Dim BasTarih As Date = Date.Now

        Dim SonTarih As Date = Date.Now.AddDays(1)

        Dim Konum As String = "İstanbul - MS Binası"

        Dim Konu As String = "VCalendar Toplantısı"

        Dim Detay As String = "Toplantı VCalendar standartları görüşülecek. "

 

        Dim BirStream As New IO.MemoryStream()

        Dim Yazici As New IO.StreamWriter(BirStream)

        Yazici.AutoFlush = True

 

        Yazici.WriteLine("BEGIN:VCALENDAR")

        Yazici.WriteLine("PRODID:-//daron.yondem.com")

        Yazici.WriteLine("BEGIN:VEVENT")

 

        Yazici.WriteLine("DTSTART:" & BasTarih.ToUniversalTime.ToString("yyyyMMdd\THHmmss\Z"))

        Yazici.WriteLine("DTEND:" & SonTarih.ToUniversalTime.ToString("yyyyMMdd\THHmmss\Z"))

        Yazici.WriteLine("LOCATION:" & Konum)

        Yazici.WriteLine("DESCRIPTION;ENCODING=QUOTED-PRINTABLE:" & Detay)

        Yazici.WriteLine("SUMMARY:" & Konu)

 

        Yazici.WriteLine("PRIORITY:3")

 

        Yazici.WriteLine("BEGIN:VALARM")

        Yazici.WriteLine("TRIGGER:-PT30M")

        Yazici.WriteLine("ACTION:DISPLAY")

        Yazici.WriteLine("DESCRIPTION:" & Konu)

        Yazici.WriteLine("END:VALARM")

 

        Yazici.WriteLine("END:VEVENT")

        Yazici.WriteLine("END:VCALENDAR")

 

        context.Response.AppendHeader("Content-Disposition", "attachment; filename=aktivite.vcs")

        context.Response.AppendHeader("Content-Length", BirStream.Length.ToString())

        context.Response.ContentType = "application/download"

        context.Response.BinaryWrite(BirStream.ToArray())

    End Sub

 

    Public ReadOnly Property IsReusable() As Boolean Implements IHttpHandler.IsReusable

        Get

            Return False

        End Get

    End Property

 

End Class

[C#]

<%@ WebHandler Language="C#" Class="Handler" %>

 

using System;

using System.Web;

 

public class Handler : IHttpHandler {

 

    public void ProcessRequest (HttpContext context) {

        context.Response.ContentType = "application/download";

 

        System.DateTime BasTarih = System.DateTime.Now;

        System.DateTime SonTarih = System.DateTime.Now.AddDays(1);

        string Konum = "İstanbul - MS Binası";

        string Konu = "VCalendar Toplantısı";

        string Detay = "Toplantı VCalendar standartları görüşülecek. ";

 

        System.IO.MemoryStream BirStream = new System.IO.MemoryStream();

        System.IO.StreamWriter Yazici = new System.IO.StreamWriter(BirStream);

        Yazici.AutoFlush = true;

 

        Yazici.WriteLine("BEGIN:VCALENDAR");

        Yazici.WriteLine("PRODID:-//daron.yondem.com");

        Yazici.WriteLine("BEGIN:VEVENT");

 

        Yazici.WriteLine("DTSTART:" + BasTarih.ToUniversalTime().ToString("yyyyMMdd\\THHmmss\\Z"));

        Yazici.WriteLine("DTEND:" + SonTarih.ToUniversalTime().ToString("yyyyMMdd\\THHmmss\\Z"));

        Yazici.WriteLine("LOCATION:" + Konum);

        Yazici.WriteLine("DESCRIPTION;ENCODING=QUOTED-PRINTABLE:" + Detay);

        Yazici.WriteLine("SUMMARY:" + Konu);

 

        Yazici.WriteLine("PRIORITY:3");

 

        Yazici.WriteLine("BEGIN:VALARM");

        Yazici.WriteLine("TRIGGER:-PT30M");

        Yazici.WriteLine("ACTION:DISPLAY");

        Yazici.WriteLine("DESCRIPTION:" + Konu);

        Yazici.WriteLine("END:VALARM");

 

        Yazici.WriteLine("END:VEVENT");

        Yazici.WriteLine("END:VCALENDAR");

 

        context.Response.AppendHeader("Content-Disposition", "attachment; filename=aktivite.vcs");

        context.Response.AppendHeader("Content-Length", BirStream.Length.ToString());

        context.Response.BinaryWrite(BirStream.ToArray());

    }

 

    public bool IsReusable {

        get {

            return false;

        }

    }

 

}

Hepinize kolay gelsin.

Saturday, May 31, 2008 12:53:37 PM (GTB Standard Time, UTC+02:00)  #    Comments [7]   ASP.NET | ASP.NET 3.5  | 
 Friday, May 30, 2008

ASP.NET uygulamalarında Date.Now() gibi çoğu sınıf o anki CurrentCulture üzerinden gerekli bilgileri alarak çalışır. Yani uygulamanın çalıştığı makinen Regional Settings'i Türkiye'ye göre ayarlanmış ise tarih bilgisi ona göre gelecektir. Bu durum genelde sıkıntı yaratmasa da özellikle yurt dışından hosting (barındırma) hizmeti aldığınızda sıkıntılar baş göstermeye başlar. Eğer uygulamanız farklı ülkelere ve dillere hizmet edecek şekilde bir altyapıya sahip değilse veya buna gerek yoksa aslında uygulamanın her şart altında belirli bir CurrentCulture ile çalışmasını isteyebilirsiniz.

Varsayalım satın aldığınız hosting firması yurt dışında olduğu için sunucularını yüklerken "Regional Settings"de sunucu ayarı olarak İngilizceyi seçtiler. Bu durumda Date.Now() dahil kullandığınız çoğu sınıf geriye farklı formatlarda değerler döndürecektir. Örneğin bizde sıfırdan küçük sayılar virgül ile ayrılırken İngilizce ayarlanmış bir sunucuda nokta ile ayrılacaktır. Bu gibi sıkıntıları toptan çözmek için Web.Config içerisinde yapabileceğiniz bir ayar var.

<system.web>

  <globalization culture="tr-TR" uiCulture="tr-TR" />

</system.web>

Yukarıdaki şekilde Web.Config içerisindeki System.Web tagları arasına uygun satırı yerleştirmeniz yeterli. Artık tüm uygulama herşeyden bağımsız olarak Türkiye'ye ve Türkçe'ye göre çalışacaktır.

<%@ Page UICulture="tr-TR" Culture="tr-TR" %>

Ayrıca isterseniz bu ayarı her aspx (web form) için ayrı ayrı da yapabilirsiniz. Yukarıdaki şekilde herhangi bir web forumun mark-up kısmına Page için UICulture ve Culture ayarlarını tanımlayabilirsiniz.

Hepinize kolay gelsin...

Friday, May 30, 2008 12:10:17 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   ASP.NET | ASP.NET 3.5  | 
 Thursday, May 22, 2008

Herhangi bir web sitesine bir ASP.NET uygulaması yerleştirdiğinizde otomatik olarak uygulama ile beraber Web.Config dosyasını da sunucuya yerleştirmiş olursunuz. Bazı durumlarda (benim blogda olduğu üzere) birden çok ASP.NET uygulamasını tek bir alan adı altında çalıştırmanız gerekebilir. Bu uygulamaları iç içe klasörlerle ayırmak vs pek işe yaramaz çünkü kök klasörde bulunan Web.Config içerisinde tüm ayarlar alt klasörleri de otomatik olarak etkiler. Yani bir "inheritance" durumu söz konusudur.

Kök dizindeki Web.Config içerisindeki tüm ayarları tek tek iç dizinlerdeki Web.Config'ler içerisinde "remove" etmek mümkün fakat çok zahmetli bir iş. Diğer yandan bazı durumlarda farklı ASP.NET sürümlerini de aynı site içerisinde kullanmak isteyebilirsiniz, örneğin benim blog ASP.NET 2.0 kullanırken aktivite kayıt uygulamam ASP.NET 3.5 ile hazırlandı.

İşin kolayı!

Herhangi bir Web.Config dosyası içerisindeki ayarları "inheritance" ile alt klasörlerini geçmesini istemiyorsanız aslında yapacağınız çok ufak bir ayar var.

<location path="." inheritInChildApplications="false">

<system.web>

<!--Tüm ayarlar burada-->

</system.web>

</location>

Web.Config dosyanız içerisinde tüm ayarların bulunduğu System.Web taglarını ayrıca bir location tagı içerisine alarak söz konusu location tagının da inheritInChildApplications özelliğini False olarak ayarlarsanız artık kök dizindeki ayarlarınız diğer dizinler tarafından kullanılmayacaktır. Böylece rahatlıkla yeni uygulamalarınızı aynı alan adına yükleyebilirsiniz.

Hepinize kolay gelsin...

Thursday, May 22, 2008 11:04:24 AM (GTB Standard Time, UTC+02:00)  #    Comments [0]   ASP.NET | ASP.NET 3.5  | 
 Monday, January 21, 2008

Web projelerinin dağıtımında kaynak kodların korunması veya en azından alenen ortaya konmaması genelde ticari projelerde istenen bir durumdur. Kendi adıma konuşmam gerekirse ben hem daha kolay kullanım hem de müşteriye uzun vadede kaynak kodu da değiştirerek daha esnek bir yapı ve kolaylık sağlanması adına yazılımların kaynak kodları ile verilmesinden yanayım. Fakat tabi ki durum projenin niteliklerine göre değişecektir.

Özellikle Visual Studio 2003 sonrasında web sitelerinin kod dosyalarının DLL'lere çevrilmesine alışmıştık. Hatta bu durum hoşumuza bile gitmişti. Sonrasında VisualStudio 2005 ile bu durum değişti ve varsayılan ayarlarda VS artık DLL oluşturmamaya başladı. Tabi aspnet_compiler.exe ve aspnet_merge.exe komut satırı araçları kullanılarak bu işlemler Visual Studio 2005 ile de yapılabiliyor fakat komut satırı için artık pek kullanışlı bir araç diyemeyiz.

Bahsettiğim bu araçları arkaplanda otomatik olarak kullanabilecek bir Windows programı gönüllü bir programcı olan ve benim de blogunu takip ettiğim Rick Strahl tarafından hazırlanmış durumda. Aslında bu yeni bir haber değil, yıllardır ben kullanıyordum :) ama yazısı yazmak bugüne nasipmiş. Programı hemen aşağıdaki adresten indirebilirsiniz.

http://www.west-wind.com/files/tools/WestWindAspNetCompilerUI.zip (99 KB)

ASP.NET Compiler Yardımcısı
ASP.NET Compiler Yardımcısı

Programın tüm özelliklerini tek tek anlatmaktansa yukarıdaki fotoğrafta hızlıca açıklamayı tercih ettim. Özellikle üzerinden durmamız gereken noktalardan biri "ASPX files are updateable" seçeneği. Eğer bu seçeneği işaretlerseniz projenizin ASPX dosyalarındaki HTML kodları da DLL dosyalarının içerisine yerleştirilecektir. Böylece ASPX dosyaları içerisinde sadece "This is a marker file generated by the precompilation tool, and should not be deleted!" yazısı yer alacak. ASPX dosyalarının içeriklerini DLL içerisine koysak da fiziksel olarak dosyaların bu uyarı mesajı ile disk üzerinde yer alması gerekiyor, aksi halde IIS 404 File Not Found hatası verecektir.

Bir diğer önemli ayar da "Fixed Names for assemblies" seçeneği. Eğer bu seçeneği işaretlerseniz her bir sınıf, ASPX dosyası için ayrı DLL dosyaları oluşturulacaktır böylece rahatlıkla bir dosyada değişiklik yaptığınızda o dosyaya ait DLL'i sunucuya göndererek projenizi yenileyebilirsiniz.

Eğer kolay dağıtım sizin için çok önemliyse tüm projenizin HTML kodları ile beraber sadece bir DLL olarak hazırlanmasını da sağlayabilirsiniz. Bunun için "Single Assembly" seçeneğini seçerek DLL dosyası için bir isim vermeniz yeterli. Ayrıca bu işlemi projenizdeki her bir klasor için ayrı ayrı düzenlenecek şekilde de ayarlayabilirsiniz. "Merging" bölümündeki özelliklerden faydalanabilmeniz için sisteminizde Visual Studio 2005 için Visual Studio 2005 Web Deployment Projects yüklemesinin yapılmış olması gerekiyor.

Diğer Framework sürümleri?

Yukarıdaki aracı .NET Framework 2.0 için, yani ASP.NET 2.0 için rahatlıkla kullanabilirsiniz. Program içerisinde Framework seçimi yer alsa da maalesef 3.0 veya 3.5 ile çalışmıyor. Bu durumda Merging haricindeki işlemler için doğrudan Visual Studio 2005 veya 2008'i de kullanabiliriz. Build / Publish menüsünde söz konusu ayarlara ulaşabilirsiniz.

Hepinize kolay gelsin.

Monday, January 21, 2008 12:50:10 PM (GTB Standard Time, UTC+02:00)  #    Comments [1]   ASP.NET | Visual Studio 2005 | Visual Studio 2008  | 
 Thursday, December 27, 2007

Bugün sizlere biraz Refactoring'den bahsetmek isriyorum. Refactoring'i kaba bir şekilde tanımlamak gerekirse herhangi bir kodun işlevini değiştirmeden yazılışı değiştirmek ve hedef olarak da kodun okunuşu kolaylaştırmaktır diyebiliriz. Refactoring konusunda sektörde bir çok araç, üçüncü parti uygulamalar satılıyor. Visual Studio içerisinde de C# için hazır bazı ufak tefek Refactoring araçları bulunuyor. Tabi ben bir VB programcısı olarak olayın VB kısmından bahsedeceğim ve sizlere ücretsiz bir Visual Studio eklentisi olan Refactor'u tavsiye edeceğim.

http://msdn2.microsoft.com/en-us/vbasic/bb693327.aspx

Yukarıdaki adresten indirebileceğiniz yazılımın normal sürümü ücretsiz ve hem Visual Studio 2005 hem de 2008 ile uyumlu. Yazılımın daha çok özelliklere sahip bir sürümü de "Refactor Pro" adı altında satılıyor. Biz şimdilik ücretsiz sürümle yetinelim :)

Hemen bir iki örnek ile neler yapabileceğimize bakalım.

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        Dim ds As New Data.DataSet

 

        Using cnn As New SqlConnection(ConfigurationManager.ConnectionStrings("CNN").ConnectionString)

            Using cmd As New SqlCommand("SELECT * from TABLO", cnn)

                Dim da As New SqlDataAdapter(cmd)

                da.Fill(ds)

            End Using

        End Using

 

        GridView1.DataSource = ds

        GridView1.DataBind()

    End Sub

Yukarıdaki kod herhangi bir asp.net web sayfasının Page.Load durumunda çalışıyor olsun. Gördüğünüz gibi veritabanına bir Select göndererek gelen veriyi GridView'e bağlıyoruz. Eğer Select ile aldığımız veriyi sayfada başka yerlerde de almamız gerekecekse aslında bunu harici bir function olarak yazmamız daha faydalı olacaktır. Hemen aşağıdaki şekilde function içerisine almak istediğim bölümü seçiyorum.

Kodumuzu harici bir function içerisinde alıyoruz.
Kodumuzu harici bir function içerisinde alıyoruz.

"Extract Method" komutu verdiğimizde Refactor bize kodu class yapısı içerisinde nereye yazdırmak istediğimizi soruyor sonrasında da Function'ımızı aşağıdaki gibi otomatik olarak yaratıyor.

Partial Class _Default

    Inherits System.Web.UI.Page

 

    Private Shared Sub Page_LoadExtracted(ByVal ds As Data.DataSet)

        Using cnn As New SqlConnection(ConfigurationManager.ConnectionStrings("CNN").ConnectionString)

            Using cmd As New SqlCommand("SELECT * from TABLO", cnn)

                Dim da As New SqlDataAdapter(cmd)

                da.Fill(ds)

            End Using

        End Using

    End Sub

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        Dim ds As New Data.DataSet

 

        Page_LoadExtracted(ds)

 

        GridView1.DataSource = ds

        GridView1.DataBind()

    End Sub

End Class

Gördüğünüz gibi artık aynı Function'ı kullanarak sürekli aynı datayı istediğimizde bir DataSet içerisine doldurtabiliyoruz. Fakat burada sizi rahatsız edeceğinden emin olduğum bir şey var; Refactor'un yarattığı function'ın adı çok anlamsız :) Visual Studio içerisinde yaratılan fonksiyonun adını değiştirirseniz Refactor sizin için söz konusu fonksiyonun kullanıldığı yerlerdeki referansları da otomatik olarak değiştirecektir.

Gelelim bir diğer örneğe. Varsayalım ki yeni bir sınıf yapısı oluşturmaya başladınız. Oluşturduğunuz ilk sınıfın bir sürü Private değişkeni var ve bunlara da uygun Property'lerin tanımlanması gerekiyor. Set ve Get leri tek tek her biri için ayarlıyor olmak gerçekten işkenceye dönüşebilir. Gelin aşağıdaki koda bir bakalım.

    Public Class Deneme

        Private Ozellik As Integer

        Private Ozellik2 As Integer

 

        Sub New()

 

        End Sub

 

        Sub New(ByVal Ozellik As Integer, ByVal Ozellik2 As Integer)

            Me.Ozellik = Ozellik

            Me.Ozellik2 = Ozellik2

        End Sub

    End Class

Yukarıdaki sınıfımızda sadece iki adet özellik bulunuyor bunların Property'lerini oluşturmamız gerekiyor. Private değişkenlerin üzerine sağ tuşu ile tıkladıktan sonra "Refactor / Encapsulate Field" komutu veriyorum ve kodun yerleştirileceği yeri de seçtikten sonra Refactor benim için Property kodlarını otomatik olarak yazıyor.

  Public Class Deneme

        Private Ozellik As Integer

        Private Ozellik2 As Integer

 

        Sub New()

 

        End Sub

 

        Sub New(ByVal Ozellik As Integer, ByVal Ozellik2 As Integer)

            Me.Ozellik1 = Ozellik

            Me.Ozellik21 = Ozellik2

        End Sub

        Public Property Ozellik1() As Integer

            Get

                Return Ozellik

            End Get

            Set(ByVal value As Integer)

                Ozellik = value

            End Set

        End Property

        Public Property Ozellik21() As Integer

            Get

                Return Ozellik2

            End Get

            Set(ByVal value As Integer)

                Ozellik2 = value

            End Set

        End Property

    End Class

Refactor tarafından tamamlanan yukarıdaki kodda dikkatinizi çektiyse Sub New kodundaki değerlerin aktarıldığı değişkenlerin isimleri de otomatik olarak Property isimleri ile değiştirilmiş durumda. Bir önceki örnekte olduğu gibi burada da eğer elle Property isimlerini değiştirirseniz Refactor otomatik olarak sınıf içerisinde diğer referansları da değiştiriyor.

Tüm bu işlemleri kod yazarken yapıyor olmak kolaylık sağlayabilir fakat esas mesele elinizde hazır kodları tamamlanmış bir proje varsa gerçekleşiyor. Projeyi inceleyerek sadece Refactoring araçlarını kullanarak daha okunabilir bir kod yaratmaya çalışıyorsunuz, hatta çoğu zaman kodun işleyişini değiştirmeden kodu kısaltabiliyorsunuz bile.

Refactoring'e giriş yapmanızı sağlayacak Refactor eklentisi ne kadar ücretsiz olsa da daha fazla reklamını yapmayacağım :) Refactoring dünyasını keşfetmek artık size kalmış.

Hepinize kolay gelsin.

Thursday, December 27, 2007 4:21:29 PM (GTB Standard Time, UTC+02:00)  #    Comments [2]   ASP.NET | ASP.NET 3.5 | Visual Basic 2005 | Visual Basic 2008 | Visual Studio 2005 | Visual Studio 2008  | 
 Wednesday, December 19, 2007
 Saturday, November 03, 2007

Yepyeni bir webiner ile tekrar karşınızdayım :) 7 Kasım Çarşamba günü saat 15:15-16.00 arası Silverlight uygulamaları ile ASP.NET 2.0 ve ASP.NET AJAX Extension kullanımına değineceğiz. İstemci tarafında Silverlight ile ASP.NET AJAX Extension entegrasyonu sağlayarak sunucu tarafında herhangi bir veritabanından asenkron olarak veri çekerek yine Silverlight tarafında verimizi kullanacağız.

Meeting ID: 3KJ7K7
Webiner Bağlantısı : https://www112.livemeeting.com/cc/microsoft/join?id=3KJ7K7&role=attend&pw=x4G%40Ncxxd
Webinere kayıt olmak için tıklayınız.

Bu webiner Microsoft Kurumsal Webiner serisinden olduğu için yukarıdaki linkten giriş yaparak kayıt olmanız gerekiyor.

Saturday, November 03, 2007 6:16:24 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   AJAX | ASP.NET | Silverlight | Webiner  | 
 Tuesday, October 16, 2007

Yeni hazırladığınız web sitenizi SQL2005 veritabanı üzerine kurguladınız ve lokal olarak kendi makinenizde yarattığınız SQL veritabanına hazırladığınız site ile bazı verilerinizi de girdiniz. Sıra geldi web sitenizi online hale getirmeye, yani hosting sağlayıcınızın sunucularına yüklemeye. Sitenizin dosyalarını FTP üzerinden sunucuya gönderdiniz, son olarak da SQL veritabanınızı hosting sağlayıcınızın verdiği paylaşımlı SQL sunucu üzerindeki veritabanına yüklemeniz gerekiyor. İşte bu noktada sorunlar başlıyor. Eğer bilgisayarınızda Visual Studio 2005 ile beraber gelen SQL Express yüklü ise maalesef SSIS (eski DTS) sisteminizde yüklü olmayacaktır. O nedenle veritabanınızı karşıya atma şansınız yok. Diğer yandan çoğu hosting sağlayıcı kendi yönetim panelleri üzerinden sizin SQL scriptleri yükleyerek işlem yapabileceğiniz ortamlar sağlar. Aslında elinizde tüm veritabanınızı içerisindeki veriler ile birlikte yaratabilecek bir SQL Script bloğu olsu hiç sorun yaşamayacaksınız.

Karşınızda SQL Server Database Publishing Wizard 1.1

Tam olarak yukarıda bahsettiğimiz sorunu giderme amacıyla hazırlanmış olan Database Publishing Wizard ile herhangi bir SQL veritabanından SQL Script'leri yaratabiliyoruz. Bu işlemi yaparken veritabanındaki tüm objeleri yaratacak scriptlerin yanı sıra veritabanındaki veriyi de scriptler şeklinde alabiliyorsunuz. Yazılımı aşağıdaki adresten bilgisayarınıza indirebilirsiniz.

http://www.microsoft.com/downloads/details.aspx?FamilyID=56E5B1C5-BF17-42E0-A410-371A838E570A&displaylang=en

SQL Server Database Publish Wizard ile SQL Script yaratıyoruz.
SQL Server Database Publish Wizard ile SQL Script yaratıyoruz.

Database Publishing Wizard'ı bilgisayarınız Start/Başlat menüsünden çalıştırabileceğiniz gibi isterseniz Visual Studio 2005 içerisinden de kolaylıkla kullanabilirsiniz. Visual Studio 2005 içinde Server Explorer tabında herhangi bir veritabanına sağ tuş ile tıkladığınızda "Publish to provider" komutu ile karşılaşacaksınız, söz konusu komut Database Publishing Wizard'ın açılmasını sağlayacaktır.

Visual Studio 2005 ve Database Publishing Wizard
Visual Studio 2005 ve Database Publishing Wizard

Database Publishing Wizard Visual Studio 2008 ile beraber entegre olarak gelecek. İleriye dönük olarak şu anda desteklenen bir başka Publish metodu daha yer alıyor. Bir web servisi aracılığı ile hosting sağlayıcıya veritabanınızı aktarma şansı tanıyan bu metoddan ben bu yazımda bahsetmedim çünkü bu servisi şu an dünyada bile veren çok az hosting sağlayıcı var. Umarım zamanla bu servis de yaygınlaşır ve onunla ilgili bir yazıyı da bir gün yazmam gerekir :)

Hepinize kolay gelsin...

Tuesday, October 16, 2007 9:48:04 AM (GTB Standard Time, UTC+02:00)  #    Comments [1]   ASP.NET | Visual Studio 2005 | SQL Server 2005  | 
 Wednesday, October 10, 2007

İster web siteleri programlarken ister windows programlarında çoğu zaman bir ZIP dosyası oluşturmaya ihtiyaç duymuşsunuzdur. Ben genelde ZIP dosyalarını web sitelerinde yedekleme işlemleri için veya kullanıcı birden çok dosyayı siteden download edecekse hepsini birleştirip bir dosya olarak verebilmek için kullanırım. Windows programlarında da yedekleme işlemleri için veya Export ve İmport işlemlerinde dışarıyda tek bir dosya vermek için kullanabiliriz. Genel olarak baktığımızda internette bizi veri trafiğinden kurtaran ve derli doplu downloadlar sağlayan bir araç gibi kullanılabilir ZIP dosyaları.

Esas mesele .NET kodumuz ile istediğimiz dosyaları ZIPleyip var olan ZIP dosyalarını da açabilmek. Bu noktada bir sürü alternatif var, isterseniz üçüncü parti componentler satın alabilirsiniz veya ücretsiz dağıtılan bazı DLL kütüphanelerini de kullanabilirsiniz. Ama aslında bizim .NET Framework dahilinde kullanabileceğimiz yapılar da mevcut.

Microsoft.VisualStudio.Zip.dll

"Böyle bir sınıftan haberim yoktu. Bu kadar kolay mıymış?" diyenler üzülmesinler :) Haberinizin olmaması çok normal çünkü bu sınıf yapısına Visual Studio içerisinde "Add Reference" dediğinizde gelen listede karşılaşmanız mümkün değil. Aslında bu sınıf sadece Visual Studio Content Installer ve Template Wizard ile kullanılmak üzere hazırlanmış. O nedenle herhangi bir şekilde bu sınıfla ilgili Microsoft'dan destek almanız da mümkün değil.

Yukarıda ismini yazmış olduğum DLL dosyasını kendiniz gidip GAC (Global Assembly Cache) içerisinden bularak almanız gerekecek. Bunun için Start menüsünden Run diyerek c:\windows\assembly\gac_msil\ yazabilir ve direk söz konusu klasöre ulaşabilirsiniz. Benim bilgisayarımda tam yolu şu şekilde oldu: C:\windows\assembly\gac_msil\Microsoft.VisualStudio.Zip\8.0.0.0__b03f5f7f11d50a3a\Microsoft.VisualStudio.Zip.dll

DLL dosyasını projenize kopyaladıktan sonra "Add Reference" bölümünde de "Browse" diyerek DLL'i doğrudan projenize referans olarak ekleyebilirsiniz.

Imports Microsoft.VisualStudio.Zip

Yukarıdaki şekilde DLL içerisindeki hedefimizi import ettikten sonra aşağıdaki gibi basit bir kod ile ZIP dosyalarını diske açabiliyoruz.

        Dim Cozucu As New Microsoft.VisualStudio.Zip.ZipFileDecompressor("C:\Benim\dosyam\burada.zip")

        Cozucu.UncompressToFolder("C:\Dosyaları\Buraya\Koy\")


Cozucu adını verdiğimiz bir değişkene ZipFileDecompressor sınıfının bir kopyasını hedef ZIP dosyamızın tam yolunu da vererek yarattıktan sonra UncompressToFolder metodu ile istediğimiz bir yere tüm dosyaları açabiliyoruz. Şimdi sıra geldi ZIP dosyaları yaratmaya;

        Dim Dosyalar() As String = System.IO.Directory.GetFiles("C:\Sıkıştıracağım\Dosyaların\Hepsi\burada\")

        'Dosyaların tam yol listesinin bulunduğu dizide sadece dosya isimlerini bırakalım.

        For Sayac As Integer = 0 To Dosyalar.GetUpperBound(0)

            Dosyalar(Sayac) = System.IO.Path.GetFileName(Dosyalar(Sayac))

        Next

 

        Dim Sikistirici As New ZipFileCompressor("C:\ZIP\Dosyam\Buraya.zip", "C:\Sikistirilacak\Dosyaların\Bulundugu\yer\Burası\", Dosyalar, True, False)


Sıkıştırma işlemine başlamadan önce sıkıştıracağımız dosyaların bir listesini String tipinde bir diziye almamız gerekiyor. O nedenle ilk satırda System.IO.Directory.GetFiles sınıfını kullanarak Dosyalar adındaki dizimize listeyi alıyoruz. System.IO.Directory.GetFiles sınıfı dosyaların listesini geriye dosyaların tam yolu ile döndürüyor. Biz dosyaların yol bilgisinin ZIP dosyasına aktarılmasını istemediğimiz için dizideki her bir öğeyi for döngüsü ile gezerek dosya isimlerini System.IO.Path.GetFileName sınıfı ile ayıklıyoruz.

Sıra geldi sıkıştırma işlemini yapmaya. Sıkıştırma işlemi için Sikistirici adında bir ZipFileCompressor yaratıyoruz. Esasen bu değişken yaratma işlemini yaparken sıkıştırma işini de yapmış olacağız. ZipFileCompressor tanımlarken verdiğimiz parametrelerden ilki yaratılacak ZIP dosyasının adı ve tam yolu. İkinci parametre tüm sıkıştırılacak dosyaların bulunduğu klasörün tam yolu, üçüncü parametre ise dosyalarımızın adlarının bulunduğu dizinin ta kendisi. Son olarak verdiğimiz iki parametreden ilki hedef konumda başka bir ZIP dosyası varsa silinip silinmeyeceğini diğeri ise bir hata durumunda sıkıştırmanın iptal edilip edilmeyeceğini belirliyor.

Wednesday, October 10, 2007 3:19:10 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   ASP.NET  | 
 Friday, October 05, 2007

İşim gereği hayattım internette geçiyor, nitekim artık çoğumuzun hayatı internette geçiyor, fakat takip edecek o kadar çok kaynak var ki gerçekten zaman ayırmak güç. Ben hazır bu zamanı ayırmışken karşılaştığım güzel makalelerin bir kısmını sizlerle de paylaşmaya karar verdim. Belki haftalık, belki aylık veya büyük ihtimal ile belirsiz aralıklarla :) bu tarz derlemeler yaparak internette karşılaştığım içeriği sizlere sunuyor olacağım.

Ajax View JavaScript Instrumentation Proxy - Uğur Umutluoğlu
AJAX Uygulamalarındaki JavaScript kodlarının performans analizi ile ilgili bir aracın tanıtıldığı bu yazıyı okumanızı öneririm.

Web Sayfalarını Temel Bir Class'tan Kalıtmak - Uğur Umutluoğlu
Web sitenizin her sayfasında sürekli tekrar ettiğiniz bazı işlemleri kolay yoldan halletmenizi sağlayacak güzel bir makale.

AJAX kullandıgınız bir ASP.NET uygulamasında JS ile yeni pencere açmak - Çeliker Bahçeci
Makalede JavaScript ile PopUp açmak için ASP.NET AJAX Extension ile kullanabileceğiniz bir teknik inceleniyor.

İl - İlçe - Semt Veritabanı | SQL Server 2005 - Gökhan Bağcı
Farklı projelerde web sitenizin ziyaretçilerin ile göre ilçe, hatta semt seçtirmek isteyebilirsiniz. İşte size hazır veritabanı...

Friday, October 05, 2007 10:36:12 AM (GTB Standard Time, UTC+02:00)  #    Comments [0]   AJAX | ASP.NET | Haberler  | 
 Wednesday, October 03, 2007

Geçenlerde bir programcı dostumun kodlarına bakarken mail gönderim işlemleri için hala eski System.Web.Mail sınıflarını kullandığını gördüm. Bunun aslında bir zararı yok fakat yeni hali ile aslında kullanılması gereken System.Net.Mail sınıfıdır. Tabi bu noktada ufak bir sorun oluşuyor, sorun bakmadan önce gelin System.Web.Mail sınıfı ile kullanıcı adı ve şifre kullanarak SMTP Authentication desteği ile mail gönderen kodumuza bakalım.

        Dim message As New System.Web.Mail.MailMessage

        message.BodyFormat = Mail.MailFormat.Html

        message.Body = html

        message.To = "hedef@mail.com"

        message.From = "gonderen@mail.com"

        Dim server As System.Web.Mail.SmtpMail

        server.SmtpServer = "mail.sunucum.com"

        message.Fields.Add("http://schemas.microsoft.com/cdo/configuration/smtpauthenticate", "1")

        message.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendusername", "adresim@mail.com")

        message.Fields.Add("http://schemas.microsoft.com/cdo/configuration/sendpassword", "sifrem")

        server.Send(message)


Yarattığımız message nesnesine Fields.Add metodu ile SMTP Authentication kullanacağımızı, kullanıcı adını ve şifresini aktarıyoruz. System.Web.Mail üzerinden yarattığımız SMTP sunucusunun adresini de vermeyi tabi ki unutmadık. Yukarıdaki kod herhangi bir sorun olmadan çalışacaktır, fakat Microsoft tarafından tavsiye edilen daha yeni bir sınıf yapımız var; System.Net.Mail.

        Dim message As New System.Net.Mail.MailMessage

        message.IsBodyHtml = True

        message.Body = html

        message.To.Add(New System.Net.Mail.MailAddress("hedef@mail.com"))

        message.From = New System.Net.Mail.MailAddress("gonderen@mail.com")

        Dim server As New System.Net.Mail.SmtpClient

        server.Send(message)


Kodumuzda aslında aynı işlemleri biraz daha farklı bir şekilde yaptık. "Peki nerde SMTP Authentication ayarları?" dediğinizi duyar gibiyim. Gelin o ayarları da kodumuzun içerisinde koymaktansa daha kolay değiştirebileceğimiz bir yere, yani Web.Config içerisine yerleştirelim.

  <system.net>

    <mailSettings>

      <smtp from="gonderen@mail.com">

        <network host="mail.sunucum.com" port="25" userName="kullanici adi"

                password="şifre" defaultCredentials="true" />

      </smtp>

    </mailSettings>

  </system.net>


"Yok, ben ısrarla SMTP Authentication olayını kodumun içerisinde çözmek istiyorum!" diyorsanız aşağıdaki kodu kullanabilirsiniz.

        Dim message As New System.Net.Mail.MailMessage

        message.IsBodyHtml = True

        message.Body = html

        message.To.Add(New System.Net.Mail.MailAddress("hedef@mail.com"))

        message.From = New System.Net.Mail.MailAddress("gonderen@mail.com")

        Dim server As New System.Net.Mail.SmtpClient("mail.sunucum.com")

        server.Credentials = New System.Net.NetworkCredential("kullanıcı adı", "şifre")

        server.Send(message)


Hepinize kolay gelsin...

Wednesday, October 03, 2007 12:20:49 PM (GTB Standard Time, UTC+02:00)  #    Comments [7]   .NET Framework 3.0 | ASP.NET | Visual Basic 2005  | 
 Tuesday, October 02, 2007

İster internet üzerinden yayınlanan makalelerde olsun ister blog sitenize yollayacağınız bir ipucunda, maalesef sayfanıza HTML kodu veya herhangi bir kod yerleştirmek istediğinizde Visual Studio içerisinden alışık olduğumuz renklendirmeyi almamız pek mümkün olmuyor. Bu durumda çoğu sitede ekran görüntülerinden oluşan resimlerle dolu makaleler görüyoruz. Ben bugüne kadar bu sorunu biraz uğraştırıcı bir yöntemle çözüyordum :)

Bugünden sonra ise CopySourceAsHtml adındaki bir Visual Studio 2005 add-in uygulamasını kullanacağım. Yaptığım testlerde epey başarılı sonuçlar aldım. Uygulama direk Visual Studio içerisindeki renklendirme üzerinden HTML kodu yaratabiliyor. Programı aşağıdaki adresten indirebilirsiniz.

http://www.jtleigh.com/people/colin/software/CopySourceAsHtml/

Tuesday, October 02, 2007 7:43:28 PM (GTB Standard Time, UTC+02:00)  #    Comments [1]   .NET Framework 3.0 | AJAX | ASP.NET | CSS | HTML | JavaScript | Silverlight | Visual Basic 2005 | Visual Studio 2005  | 
 Monday, September 03, 2007

Web sitelerinin ekran görüntülerini (screenshot) alarak kullanıma sunan hizmetler internet üzerinde mevcut. Bu makalemizde ASP.NET tarafında nasıl başka bir sitenin ekran görüntüsü resim olarak alabileceğimizi inceleyeceğiz. İlk olarak ufak bir noktaya değinmekte fayda var; bahsedeceğimiz teknik bazı özel koşullarda (hedef web sitesinin görsel öğeleri JavaScript ile düzenlemesi) başarılı sonuç vermeyebiliyor. O nedenle eğer yüzde yüzlük bir başarı istiyorsanız ihtiyacınız olan hedef web sitesinin ekran görüntüsü otomatik olarak alan bir windows uygulaması ile web siteniz arasında entegrasyon sağlamak. Peki o zaman neden bu işi web ortamında yapıyoruz? Maalesef normal barındırma (hosting) hizmetlerinde sunucu tarafına kendi uygulamalarınızı yüklemenize izin verilemez. Makalemizde izlediğimiz teknikler ile herhangi bir barındırma alanında bu tarz bir uygulama yapabilirsiniz.

İlk olarak gelin ufak bir demo ile aslında ne yapacağımızı görelim. Aşağıdaki adreste bu makaledeki kodun kullanıldığı bir uygulama var. Uygulama John K. tarafından makalemizdeki kodun üzerine renk palet analizi de eklenerek düzenlendi. Sistem, girilen adresin ekran görüntüsünü aldıktan sonra görüntü içerisinde kullanılan ana renkleri buluyor. Siz de makalemizdeki yöntemi kullanarak eminim ki çok daha farklı uygulamalar yapabilirsiniz. Eğer beni uygulamalarınızla ilgili haberdar edebilirseniz ayrıca sevinirim.

http://www.todotoh.com/rgb/rgbanalysis.aspx

İş Başına

Peki bu işi nasıl yapacağız? İlk olarak .NET Framework'un en önemli konseptini hatırlatmakta fayda var. Web ile windows uygulamalarında aslında biz aynı Framework'ü kullanıyoruz. Yani eğer istersek windows forms uygulamalarında kullandığımız tüm kütüphaneleri ve objeleri web forms uygulamalarımızda da kullanabiliriz. Bir web sitesinin ekran görüntüsü almak için WinForms'da kullanabileceğimiz WebBrowser adında bir objemiz var.

Dim MyBrowser As New WebBrowser
MyBrowser.ScrollBarsEnabled = False
MyBrowser.Size = New Size(1027, 768)
MyBrowser.Navigate("http://daron.yondem.com")
Dim myBitmap As New Bitmap(1024, 768)
Dim DrawRect As New Rectangle(0, 0, 1024, 768)
MyBrowser.DrawToBitmap(myBitmap, DrawRect)

Yukarıdaki kodumuz içerisinde yeni bir WebBrowser objesi yarattıktan sonra ekranın kenarlarında kaydırma çubuklarının gözükmemesi için ScrollBarsEnabled özelliğini False olarak düzenliyoruz. Bir sonraki adımda Browser (Tarayıcı) objemizin boyutunu 1024*768 yaparak sanki web sitesi o çözünürlükte bir ekranda açılmışmış gibi bir ortam yaratıyoruz. WebBrowser objesine ait .Navigate metoduna verdiğimiz parametre ile sitemizin yüklenmesini sağlıyoruz. Uygun resim ve çizim alanı değişkenlerimizi de tanımladıktan sonra yine WebBrowser objesine ait .DrawToBitmap metodunu kullanarak görüntüyü değişkenimize alıyoruz.

Tabi olay bu kadar kolay değil. Elimizdeki ilk sorun web sitesinin yüklenmesini beklemek zorunda olmamız. Bunun için kör bir döngü kullanacağız.

MyBrowser.Navigate(Me.URL)
While MyBrowser.ReadyState <> WebBrowserReadyState.Complete
   Application.DoEvents()
End While

Kodumuzda WebBrowser objesinin ReadyState özelliğini kontrol ederek Complete olana kadar kısır bir döngüye sokuyoruz. Döngü içerisinde kullandığımız Application.DoEvents() kodu döngü süresince WebBrowser'daki yüklemenin kilitlenmemesini ve devam etmesini sağlayacak. Bir sonraki sorunumuz aslında çok daha kritik. WebBrowser objesi özünde bir COM objesi olduğu için Single Threaded bir Thread içerisinde çalıştırılması gerekiyor. Bu aslında biraz da işimize yarar, böylece tüm bu yükleme kodlarını başka bir Thread içerisine almış oluruz.


Dim NewTh As New Threading.Thread(AddressOf DoIt)
NewTh.SetApartmentState(Threading.ApartmentState.STA)
NewTh.Start()

Yukarıda bir Thread tanımı yer alıyor. Bu tanım içerisinde en önemli nokta ikinci satırdaki kod. Yarattığımız Thread için SetApartmentState diyerek çalışma şeklini SingleThreadedApartment haline çeviriyoruz. Tüm sorunları aştık, geriye kaldı gelen ekran görüntüsünü uygun şekilde küçültme kodumuza. Bu işlem için çok farklı teknikler kullanılabilir, ben aşağıda bu tekniklerden birine ait kodu paylaşacağım. Makalemizin esas konusu bu olmadığı için detaya girmeyeceğim.

Dim imgOutput As System.Drawing.Image = myBitmap
Dim oThumbNail As System.Drawing.Image = New Bitmap(twidth, theight, _
                imgOutput.PixelFormat)
Dim g As Graphics = Graphics.FromImage(oThumbNail)
g.CompositingQuality = Drawing2D.CompositingQuality.HighSpeed
g.SmoothingMode = Drawing2D.SmoothingMode.HighSpeed
g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBilinear
Dim oRectangle As Rectangle = New Rectangle(0, 0, twidth, theight)
g.DrawImage(imgOutput, oRectangle)

Şimdi gelin tüm bu kodları birleştirerek bir Class yapısı içerisinde projelerimizde kullanacağımız kodumuzu inceleyelim.

Imports System
Imports System.Drawing
Imports System.Drawing.Imaging
Imports System.Windows.Forms
Imports System.Diagnostics
 
Namespace GetSiteThumbnail
 
    Public Class GetImage
        Private S_Height As Integer
        Private S_Width As Integer
        Private F_Height As Integer
        Private F_Width As Integer
        Private MyURL As String
 
        Property ScreenHeight() As Integer
            Get
                Return S_Height
            End Get
            Set(ByVal value As Integer)
                S_Height = value
            End Set
        End Property
 
        Property ScreenWidth() As Integer
            Get
                Return S_Width
            End Get
            Set(ByVal value As Integer)
                S_Width = value
            End Set
        End Property
 
        Property ImageHeight() As Integer
            Get
                Return F_Height
            End Get
            Set(ByVal value As Integer)
                F_Height = value
            End Set
        End Property
 
        Property ImageWidth() As Integer
            Get
                Return F_Width
            End Get
            Set(ByVal value As Integer)
                F_Width = value
            End Set
        End Property
 
        Property WebSite() As String
            Get
                Return MyURL
            End Get
            Set(ByVal value As String)
                MyURL = value
            End Set
        End Property
 
        Sub New(ByVal WebSite As String, ByVal ScreenWidth As Integer, ByVal ScreenHeight As Integer, ByVal ImageWidth As Integer, ByVal ImageHeight As Integer)
            Me.WebSite = WebSite
            Me.ScreenWidth = ScreenWidth
            Me.ScreenHeight = ScreenHeight
            Me.ImageHeight = ImageHeight
            Me.ImageWidth = ImageWidth
        End Sub
 
        Function GetBitmap() As Bitmap
            Dim Shot As New WebPageBitmap(Me.WebSite, Me.ScreenWidth, Me.ScreenHeight)
            Shot.GetIt()
            Dim Pic As Bitmap = Shot.DrawBitmap(Me.ImageHeight, Me.ImageWidth)
            Return Pic
        End Function
    End Class
 
    Class WebPageBitmap
        Dim MyBrowser As WebBrowser
        Dim URL As String
        Dim Height As Integer
        Dim Width As Integer
 
        Sub New(ByVal url As String, ByVal width As Integer, ByVal height As Integer)
            Me.Height = Height
            Me.Width = width
            Me.URL = url
            MyBrowser = New WebBrowser
            MyBrowser.ScrollBarsEnabled = False
            MyBrowser.Size = New Size(Me.Width, Me.Height)
        End Sub
 
        Sub GetIt()
            MyBrowser.Navigate(Me.URL)
            While MyBrowser.ReadyState <> WebBrowserReadyState.Complete
                Application.DoEvents()
            End While
        End Sub
 
        Function DrawBitmap(ByVal theight As Integer, ByVal twidth As Integer) As Bitmap
            Dim myBitmap As New Bitmap(Width, Height)
            Dim DrawRect As New Rectangle(0, 0, Width, Height)
            MyBrowser.DrawToBitmap(myBitmap, DrawRect)
            Dim imgOutput As System.Drawing.Image = myBitmap
            Dim oThumbNail As System.Drawing.Image = New Bitmap(twidth, theight, imgOutput.PixelFormat)
            Dim g As Graphics = Graphics.FromImage(oThumbNail)
            g.CompositingQuality = Drawing2D.CompositingQuality.HighSpeed
            g.SmoothingMode = Drawing2D.SmoothingMode.HighSpeed
            g.InterpolationMode = Drawing2D.InterpolationMode.HighQualityBilinear
            Dim oRectangle As Rectangle = New Rectangle(0, 0, twidth, theight)
            g.DrawImage(imgOutput, oRectangle)
            Try
                Return oThumbNail
            Catch ex As Exception
            Finally
                imgOutput.Dispose()
                imgOutput = Nothing
                MyBrowser.Dispose()
                MyBrowser = Nothing
            End Try
        End Function
    End Class
 
End Namespace

Yarattığımız iki classtan biri olan GetImage programcıdan alacağı ekran görüntüsüne ait çözünürlük bilgilerini ve geri döndüreceği resmin boyut bilgilerini alıyor. Aldığı bilgiler çerçevesinde ekran görüntüsünün alınması sonrasında da görüntünün istenen boyuta küçültülmesini sağlayacak. WebPageBitmap class'ımız ise ekran görüntüsünün alınmasından ve sonrasında da tekrar boyutlandırılmasında sorumlu. Tabi tüm bu işlemleri ayrı bir Thread içerisinde yapmamız gerektiğinden bahsetmiştik. Şimdi de gelin yukarıdaki Class'ımızı sayfamızda nasıl kullanabileceğimize bakalım.

Partial Class _Default
    Inherits System.Web.UI.Page
 
    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
        Dim NewTh As New Threading.Thread(AddressOf DoIT)
        NewTh.SetApartmentState(Threading.ApartmentState.STA)
        NewTh.Start()
        While NewTh.ThreadState = Threading.ThreadState.Running
        End While
        Image1.ImageUrl = TextBox1.Text.Replace(".", "_") & ".jpg"
    End Sub
 
    Sub DoIT()
        Try
            Dim thumb As New GetSiteThumbnail.GetImage("http://" & TextBox1.Text, 1024, 768, 320, 240)
            Dim x As System.Drawing.Bitmap = thumb.GetBitmap()
            x.Save(Server.MapPath(".") & "\" & TextBox1.Text.Replace(".", "_") & ".jpg")
        Catch ex As Exception
            Dim y As System.IO.StreamWriter = System.IO.File.CreateText("C:\Inetpub\wwwroot\screeny\error.txt")
            y.WriteLine(ex.Message & vbCrLf & ex.Source)
            y.Flush()
            y.Close()
        Finally
        End Try
    End Sub
End Class 

Code-Behind dosyamızın kodunu yukarda inceleyebilirsiniz. Button1'e tıklandığında uygun Thread'i yaratarak DoIT Sub'ını çalıştıracağı şekilde hazırlıyoruz. Basit bir şekilde Thread'in bitmesini beklemek için yine kısır bir döngü kullanarak ThreadState durumunu kontrol ettik. Ekran görüntüsü alındığı gibi kullanıcıya göstermek için biraz kullanıcıyı bekletmemiz gerekecek. Eğer siz bir AJAX web sitesi hazırlıyorsanız ekran görüntüsünün alınmasını beklemektense bir AJAX Timer ile durumu kontrol edip kullanıcıya bilgi verebilirsiniz.

DoIT Sub'ımız içerisine bakarsak aslında bir önceki Class yapımızı kullandığımızı görüyoruz. Ben kodumda ekran görüntüsünü ve oluşursa herhangi bir hatayı farklı yerlere kaydettim. Siz bu konumları değiştirerek farklı yapılar kurabilirsiniz.

Tüm bu kodlara ek olarak daha yüksek performanslı bir sonuç için kodunuzun daha önce alınmış ekran görüntülerini kontrol ederek eğer söz konusu adrese ait ekran görüntüsü çok eski değilse yenisini almadan kullanıcıya eskisi de göstermesini sağlayabilirsiniz.

Hepinize kolay gelsin.

Makale Kaynak Kodu - 03092007_1.zip (3,48 KB)

Monday, September 03, 2007 1:40:54 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   ASP.NET  | 
 Monday, August 27, 2007

Hiç kendi arama motorunuzu programlamayı düşündünüz mü? Peki bu konuda hiç araştırma yaptınız mı? Emin olun hiç kolay bir iş değil. Onun yerine biraz kolaya kaçıp hazır arama motorlarından faydalanabiliriz. Gün geçtikçe gelişen Live arama motorunu kullanacağımız bu makalemizde ilk olarak arama motorunun XML web servislerini kullanabilmek için http://search.msn.com/developer adresinden Create and Manage Application IDs bölümüne girerek kendimize bir AppID (Uygulama Kimliği) yaratmamız gerekiyor.

Live Search AppID yaratma yolundayız.
Live Search AppID yaratma yolundayız.

AppID yaratma yolunda ilerlediğimizde bizden LiveID kimlik bilgilerimiz isteniyor. Giriş yaptıktan sonra arama motorumuzu kullanacağımız uygulamamızla ilgili bir anahtar isim verdikten sonra AppID'mizi hemen alabiliyoruz.

Live AppID'mizi uygulama ismi vererek alabiliyoruz.
Live AppID'mizi uygulama ismi vererek alabiliyoruz.

Tüm bu işlemleri tamamladıktan sonra artık http://soap.search.msn.com/webservices.asmx?wsdl adresindeki web servisini uygulamamızda kullanmaya başalayabiliriz. Projenize Solution Explorer içerisinde sağ tuş ile tıkladığınızda gelen menüden Add Web Reference seçeneğini seçerek tanımlama işlemlerini tamamlayabilirsiniz.

Kullanacağımız web servisini projemize web reference olarak ekliyoruz.
Kullanacağımız web servisini projemize web reference olarak ekliyoruz.

Live arama motoruna ait web servisini kullanırken özel sorgular hazırlayarak farklı arama filtreleri uygulama şansımız da var. Gelin, örneğimizde sadece üç site içerisinde arama yapan bir arama motoru oluşturalım. Kullanabileceğiniz filtreleme seçenekleri çok geniş olduğu için bu konuyu makalenin dahiline almayacağım, siz isterseniz http://msdn2.microsoft.com/en-us/library/aa905321.aspx adresinden geniş bilgi alabilirsiniz.

Kullanacağımız filtrelemede sadece belirli web siteleri içerisinde arama yaptırılarak sonuçların bize Live Search tarafından döndürülmesini istiyor olacağız. Bu nedenle ilk olarak yapacağım şey bize filtreleme sorgusunun metnini döndüren bir fonksiyon yazmak olacak.

    Function SorguYarat(ByVal Aranan) As String
        Dim Writer As New System.Text.StringBuilder
        Writer.Append(Aranan)
        Writer.Append(" (")
        Writer.Append("site:")
        Writer.Append("yazgelistir.com")
        Writer.Append(" OR ")
        Writer.Append("site:")
        Writer.Append("nedirtv.com")
        Writer.Append(" OR ")
        Writer.Append("site:")
        Writer.Append("microsoft.com.tr")
        Writer.Append(")")
        Return Writer.ToString
    End Function

Yukarıda hazırlamış olduğumuz fonksiyon bir StringBuilder tanımlayarak verdiğimiz metinleri aranacak olan metnin sonuna ard arda ekliyor. Aslına bakarsanız yolladığımız sorgu bu kod içerisinde sabit fakat ben özellikle bu şekilde yaparak sizin bu kodu veritabanına bağlamanızı kolaylaştırmak istedim. Böylece sitelerinin isimlerini eklediğimiz kodları bir döngü içerisinde kullanarak veritabanınızdan gelen site adlarını sorguya ekleyerek, içerisinde arama yapılacak sitelerin listesini dinamik olarak veritabanında çekebilirsiniz.

Şimdi sıra geldi arama işlemini yapacak olan fonksiyonu. Ben fonksiyonumu bir DataTable döndürecek şekilde yazacağım. Böyle yaparak fonksiyonumu ileride direk bir Repeater veya GridView'e bağlamamı kolaylaştırıyorum. Fonksiyonun ana yapısını oluşturmadan içerisindeki kodları yazalım.

        'Verileri depolayacağımız sana tablomuzu oluşturuyoruz.
        Dim MyData As New DataTable("Sonuclar")
        'Tablomuza toplam dört kolon ekleyeceğiz.
        MyData.Columns.Add(New DataColumn("Baslik"))
        MyData.Columns.Add(New DataColumn("Açıklama"))
        MyData.Columns.Add(New DataColumn("Görünen URL"))
        MyData.Columns.Add(New DataColumn("URL"))

Yukarıdaki kodlar ile MyData adında bir tablo yaratarak içerisine dört adet kolon ekliyorum. Bu kolonlardaki verileri yeri geldiğinde arama motorundan dönen sonuçlar ile dolduracağım. Live Search arama motorundan çok detaylı sonuçlar geliyor, isterseniz siz daha farklı kolonlar koyarak daha fazla veri alabilirsiniz. Benim için bu kadarı yeterli :)

        'Arama motorumuzu tanımlıyoruz.
        Dim MySearchEngine As New MSNSearchService
        'Arama talbeni değişkenimizi yaratıyoruz.
        Dim MySearchRequest As New SearchRequest
        MySearchRequest.AppID = "Aldığınız AppID buraya yazılacak."

Kodumuzun bu bölümünde arama servisimizi tanımlıyor sonra da arama talebimize ait ana değişkenimizi tanımlayarak AppID'mizi atıyoruz. AppID olarak yukarıda bahsettiğimiz Live web sitesinden almış olduğunuz kendi AppID değerini yazmanız gerekiyor.

        'Aranacak kaynaklar için talep oluşturuyoruz.
        Dim MySourceRequest(0) As SourceRequest
        MySourceRequest(0) = New SourceRequest
        'Kaç adet sonuç döneceğini fonksiyondan gelen parametre belirliyor.
        MySourceRequest(0).Count = Adet
        'Sadece web ortamını arayacağız.
        MySourceRequest(0).Source = SourceType.Web
        'Sonuçlardaki tüm bilgileri ve kolonları alıyoruz.
        MySourceRequest(0).ResultFields = ResultFieldMask.All
        'Arama sorgumuzu aktarıyoruz.
        MySearchRequest.Query = SorguYarat(Aranan)
        'Talebimizi talep listesine ekleyelim.
        MySearchRequest.Requests = MySourceRequest
        'Aşağıdaki kültür bilgisini isterseniz değiştirebilirsiniz.
        MySearchRequest.CultureInfo = "tr-TR"

Bu bölüm biraz karışık gibi gözükebilir. Benim tavsiyem kod içerisine yazdığım yorum satırlarını okuyarak ilerlemeniz. Önemli birkaç nokta var. Bunlardan ilki MySourceRequest(0).Count = Adet tanımlamasında yatıyor. Buraya verdiğimiz Adet değişkeni bizim fonksiyonumuza parametre olarak verilen bir değişken olacak. Kabaca aradığımız bilgiye dair kaç adet sonuç almak istediğimizi aktarıyoruz. Bir sonraki satırda web içeriği aradığımızı özellikle belirtmemiz gerekiyor, malum Live Search ile resim gibi farklı içerikler aratmak da mümkün. ResultFields satırına geldiğimizde ise sonuçlara dair tüm bilgileri istediğimizi belirtiyoruz. Belki bu noktada biraz optimizasyona gidilerek kullanmayacağımız bilgileri istemeyebilirdik. Query özelliğine geldiğimizde ise daha önce yaratmış olduğumuz SorguYarat fonksiyonunu çalıştırarak sorgumuzu aramaya aktarıyoruz. Bu yapıda aslında istersek birden çok arama yaratarak bir talep listesi olarak topluca web servisine gönderebiliriz. Bizim örneğimizde aramaları tek tek göndereceğiz. En son satırda da arama yaptıracağımız dilin kültür bilgisini aktarıyoruz.

        'Sıra geldi sonuçları almaya.
        Dim MySearchResponse As New SearchResponse
        'Aramamızı gönderip sonuçları alıyoruz.
        MySearchResponse = MySearchEngine.Search(MySearchRequest)

Artık sıra geldi aramayı motora aktarmaya ve sonuçları almaya. Bir sonuç değişkeni tanımlayarak önceden tanımladığımız arama motoru değişkenimiz ile taleplerimizi XML web servisine gönderiyoruz.

        'Sonuçlar arasında gezerek kendi tablomuza aktaracağız.
        For Each IncResponse As SourceResponse In MySearchResponse.Responses
            Dim MyResults() As Result
            MyResults = IncResponse.Results
 
            Dim ResultRow As DataRow
            'Sonuç sayısı sıfırdan yüksekse aktarmayı başlatalım.
            If IncResponse.Total > 0 Then
                For Each MyResult As Result In MyResults
                    ResultRow = MyData.NewRow
                    'Tek tek gelen bilgileri kendi yarattığımız
                    'tablomuza döngü içerisinde satır satır ekliyoruz.
                    ResultRow.Item(0) = MyResult.Title
                    ResultRow.Item(1) = MyResult.Description
                    ResultRow.Item(2) = MyResult.DisplayUrl
                    ResultRow.Item(3) = MyResult.Url
                    MyData.Rows.Add(ResultRow)
                Next
            End If
        Next

Gelen sonuçlar arasında bir For Each döngüsü ile dolaşırken bir yandan da sonuçlardaki bilgileri kendi yarattığımız tablomuza satır satır ekliyoruz. Tüm bu işlemler tamamlandıktan sonra fonksiyonumuz tablomuzu geri döndürüyor olacak. Fonksiyonumuz tam kodu aşağıdaki şekilde sonuçlanıyor.

    Function Ara(ByVal Aranan As String, ByVal Adet As Integer) As DataTable
        'Verileri depolayacağımız sana tablomuzu oluşturuyoruz.
        Dim MyData As New DataTable("Sonuclar")
        'Tablomuza toplam dört kolon ekleyeceğiz.
        MyData.Columns.Add(New DataColumn("Baslik"))
        MyData.Columns.Add(New DataColumn("Açıklama"))
        MyData.Columns.Add(New DataColumn("Görünen URL"))
        MyData.Columns.Add(New DataColumn("URL"))
 
        'Arama motorumuzu tanımlıyoruz.
        Dim MySearchEngine As New MSNSearchService
        'Arama talbeni değişkenimizi yaratıyoruz.
        Dim MySearchRequest As New SearchRequest
        MySearchRequest.AppID = "Aldığınız AppID buraya yazılacak."
 
        'Aranacak kaynaklar için talep oluşturuyoruz.
        Dim MySourceRequest(0) As SourceRequest
        MySourceRequest(0) = New SourceRequest
        'Kaç adet sonuç döneceğini fonksiyondan gelen parametre belirliyor.
        MySourceRequest(0).Count = Adet
        'Sadece web ortamını arayacağız.
        MySourceRequest(0).Source = SourceType.Web
        'Sonuçlardaki tüm bilgileri ve kolonları alıyoruz.
        MySourceRequest(0).ResultFields = ResultFieldMask.All
        'Arama sorgumuzu aktarıyoruz.
        MySearchRequest.Query = SorguYarat(Aranan)
        'Talebimizi talep listesine ekleyelim.
        MySearchRequest.Requests = MySourceRequest
        'Aşağıdaki kültür bilgisini isterseniz değiştirebilirsiniz.
        MySearchRequest.CultureInfo = "en-US"
 
        'Sıra geldi sonuçları almaya.
        Dim MySearchResponse As New SearchResponse
        'Aramamızı gönderip sonuçları alıyoruz.
        MySearchResponse = MySearchEngine.Search(MySearchRequest)
 
        'Sonuçlar arasında gezerek kendi tablomuza aktaracağız.
        For Each IncResponse As SourceResponse In MySearchResponse.Responses
            Dim MyResults() As Result
            MyResults = IncResponse.Results
 
            Dim ResultRow As DataRow
            'Sonuç sayısı sıfırdan yüksekse aktarmayı başlatalım.
            If IncResponse.Total > 0 Then
                For Each MyResult As Result In MyResults
                    ResultRow = MyData.NewRow
                    'Tek tek gelen bilgileri kendi yarattığımız
                    'tablomuza döngü içerisinde satır satır ekliyoruz.
                    ResultRow.Item(0) = MyResult.Title
                    ResultRow.Item(1) = MyResult.Description
                    ResultRow.Item(2) = MyResult.DisplayUrl
                    ResultRow.Item(3) = MyResult.Url
                    MyData.Rows.Add(ResultRow)
                Next
            End If
        Next
        'Tablomuzu geri döndürüyoruz.
        Return MyData
    End Function

Aslında Live Search arama motoru ile ilgili işlemlerimiz tamamlandı. İsterseniz gelin şimdi hızlı bir şekilde yarattığımız fonksiyonu kullanmak üzere bir web sayfası hazırlayalım. Sayfamıza tasarım endişesi olmadan bir metin kutusu, arama işlemini başlatacak bir düğme ve bir de arama sonuçlarını hemen gösterecek GridView ekleyeceğiz. Tüm bunları bir UpdatePanel içerisine ekleyerek tüm sayfada AJAX kullanacağız. Profesyonel çalışmalarda benim tavsiyem arama sonuçlarını göstermek için GridView yerine güzel tasarlanmış bir Repeater kullanmanız ve tüm sayfayı kapsayacak UpdatePanel'lerden olabildiğince kaçmanız. Performans açısından pek anlamlı bir çözüm değil. Biz konumuza dönelim ve hemen uygulamaya geçelim.

<%@ Page Language="VB" AutoEventWireup="true" CodeFile="Default.aspx.vb" Inherits="_Default" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
  <title>Untitled Page</title>
</head>
<body>
  <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server" />
    <div>
      <asp:UpdatePanel ID="UpdatePanel1" runat="server">
        <ContentTemplate>
          <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
          <asp:Button ID="Button1" runat="server" Text="Ara" OnClick="Button1_Click" /><br />
          <asp:GridView ID="GridView1" runat="server">
          </asp:GridView>
        </ContentTemplate>
      </asp:UpdatePanel>
    </div>
  </form>
</body>
</html>

Sayfamızın HTML kodu yukarıdaki şekilde olacak. Sunucu tarafındaki kodumuza baktığımızda ise hazırladığımız fonksiyonlar sayesinde aslında arama işleminin ne kadar kolaylaştığını görüyor olacaksınız.

    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        GridView1.DataSource = Ara(TextBox1.Text, 10)
        GridView1.DataBind()
    End Sub;

Gördüğünüz gibi yaptığımız tek şey hazırladığımzı Ara fonksiyonuna aranacak metni ve kaç tane sonuç istediğimizi parametre olarak vermek. Fonksiyon geriye bir DataTable döndüreceği için direk GridView'a bağlayabiliyoruz.

Projemizin code-behind sayfasında kodların tamamı aşağıdaki şekilde sonlanıyor.

Imports System.Data
Imports com.msn.search.soap
 
Partial Class _Default
    Inherits System.Web.UI.Page
 
    Function SorguYarat(ByVal Aranan) As String
        Dim Writer As New System.Text.StringBuilder
        Writer.Append(Aranan)
        Writer.Append(" (")
        Writer.Append("site:")
        Writer.Append("yazgelistir.com")
        Writer.Append(" OR ")
        Writer.Append("site:")
        Writer.Append("nedirtv.com")
        Writer.Append(" OR ")
        Writer.Append("site:")
        Writer.Append("microsoft.com.tr")
        Writer.Append(")")
        Return Writer.ToString
    End Function
 
    Function Ara(ByVal Aranan As String, ByVal Adet As Integer) As DataTable
        Dim MyData As New DataTable("Sonuclar")
        MyData.Columns.Add(New DataColumn("Baslik"))
        MyData.Columns.Add(New DataColumn("Açıklama"))
        MyData.Columns.Add(New DataColumn("Görünen URL"))
        MyData.Columns.Add(New DataColumn("URL"))
 
        Dim MySearchEngine As New MSNSearchService
        Dim MySearchRequest As New SearchRequest
        MySearchRequest.AppID = "Aldığınız AppID buraya yazılacak."
 
        Dim MySourceRequest(0) As SourceRequest
        MySourceRequest(0) = New SourceRequest
        MySourceRequest(0).Count = Adet
        MySourceRequest(0).Source = SourceType.Web
        MySourceRequest(0).ResultFields = ResultFieldMask.All
        MySearchRequest.Query = SorguYarat(Aranan)
        MySearchRequest.Requests = MySourceRequest
        MySearchRequest.CultureInfo = "en-US"
 
        Dim MySearchResponse As New SearchResponse
        MySearchResponse = MySearchEngine.Search(MySearchRequest)
 
        For Each IncResponse As SourceResponse In MySearchResponse.Responses
            Dim MyResults() As Result
            MyResults = IncResponse.Results
 
            Dim ResultRow As DataRow
            If IncResponse.Total > 0 Then
                For Each MyResult As Result In MyResults
                    ResultRow = MyData.NewRow
                    ResultRow.Item(0) = MyResult.Title
                    ResultRow.Item(1) = MyResult.Description
                    ResultRow.Item(2) = MyResult.DisplayUrl
                    ResultRow.Item(3) = MyResult.Url
                    MyData.Rows.Add(ResultRow)
                Next
            End If
        Next
        Return MyData
    End Function
 
    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        GridView1.DataSource = Ara(TextBox1.Text, 10)
        GridView1.DataBind()
    End Sub
End Class

Hepinize kolay gelsin.

Monday, August 27, 2007 9:50:04 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   ASP.NET  | 
 Sunday, August 26, 2007

Farklı web sitelerinde AJAX ile sunucudan istemciye veri yükleme teknikleri kullanmak performans açısından ciddi faydalar sağlarken bazen kontrolün elden çıkmasına da neden olabiliyor. Herhangi bir AJAX yüklemesi, yani XMLHttpRequest gerçekleşirken sunucu ile istemci arasında bir trafik sorunu oluştuğunda sitemizin ziyaretçisi çaresizce AJAX yüklemesinin bitmesini bekliyor. Oysa o yükleme hiç bitmeyecek. Bir diğer senaryoda ise gerçekten sayfaya uzun sürecek bir bilgi yükleniyor olabilir, bu durumda da aslında ziyaretçimize gerektiğinde bu yüklemeyi iptal etme şansı tanıyabiliriz. İşte tüm bu işlemleri istemci tarafında direk JavaScript kodları yazarak nasıl yapabileceğimizi makalemiz boyunca inceliyor olacağız.

Gelin ilk olarak kullanacağımız örneğimizin sayfa tasarımını, mark-up HTML kodunu oluşturalım.

            <asp:UpdatePanel ID="UpdatePanel1" runat="server">
                <ContentTemplate>
                    <asp:Label ID="Label1" runat="server"></asp:Label><br />
                    <asp:Button ID="Button1" runat="server" Text ="Yükle" OnClick="button1_Click" />
                    <asp:Button ID="Button2" style="visibility:hidden;"
                            runat="server" Text="İptal" />
                </ContentTemplate>
            </asp:UpdatePanel>

Sayfamıza yerleştirdiğimiz UpdatePanel içerisinde toplam iki adet düğme bulunuyor. Bu düğmelerden ilkini yükleme işlemini başlatmak için, ikincisini ise iptal etmek için kullanacağız. İptal etmek için kullanacağımız Button2 düğmesinin ilk başta görünmez olması için CSS özelliği olarak visibility değerini hidden şeklinde düzenliyoruz. İleriki kodlarımızda herhangi bir yükleme başladığında iptal düğmesini görünür hale getireceğiz. Düğmelerimizin yanı sıra sayfaya bir de Label1 adında etiket ekledik, bu etiket üzerine farklı mesajlar yazdırıyor olacağız.

Partial Class _Default
    Inherits System.Web.UI.Page
 
    Protected Sub button1_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        System.Threading.Thread.Sleep(3000)
        label1.Text = Date.Now.ToLongTimeString
    End Sub
End Class

Sayfamızın Code-Behind kısmında sadece yukarıdaki kodu kullanacağız. Button1, yani yükleme işlemini yapacak olan düğmemize tıklandığında ilk olarak 3 saniye süreyle mevcut Thread'i uykuya alıyoruz. Böylece örneğimizi denediğimizde AJAX yüklemesi üç saniye sürecek. Tabi siz bu kodu gerçek projelerinizde kullanmamalısınız. Bizim örneğimizde gerçek bir yükleme söz konusu olmadığı için kodumuzu simülasyon amacıyla düzenledik. Yükleme işleminin sonunda Label1 içerisine mevcut saat bilgisini yazdırıyoruz.

AJAX sunucu kontrolleri kullanılacak her sayfada bir ScriptManager bulunması gerektiğine dair kuralı hepimiz biliyoruz. Bunun nedeni sayfa içerisindeki tüm AJAX işlemlerinden ScriptManager'ın sorumlu olması ve tüm işlemlerin ScriptManager'ın kontrolünde ilerliyor olması. Bu durumda biz de işlemlerin iptali gibi konularda ScriptManager ile çalışmak durumundayız. Buradan itibaren yazacağımız tüm kodlar JavaScript kodları olacak, fakat her zamanki gibi kodlarımızı sayfanın Header (Baş) kısmına yazma şansımız yok. Yazacağımız tüm JavaScript kodları ScriptManager ile gelen JavaScript özelliklerini kullanacağı için sayfada da ScriptManager'dan sonra yer almaları gerekiyor. Kabaca yapımız aşağıdaki şekilde olacak.

<form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server" />
        <script type="text/javascript" language="javascript">
        //JavaScript kodlarımız buraya
        </script>
        <div>
            <asp:UpdatePanel ID="UpdatePanel1" runat="server">
                <ContentTemplate>
                    <asp:Label ID="Label1" runat="server"></asp:Label><br />
                    <asp:Button ID="Button1" runat="server" Text ="Yükle" OnClick="button1_Click" />
                    <asp:Button ID="Button2" style="visibility:hidden;"
                            runat="server" Text="İptal" />
                </ContentTemplate>
            </asp:UpdatePanel>
        </div>
    </form>

Gelelim yazacağımız JavaScript kodlarına ve anlamlarına. İlk olarak sayfamızdaki ScriptManager'a gelen her AJAX isteğini kontrol altına almak için bir handler eklememiz gerekiyor.

Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(DurumKontrol);

Yukarıdaki kod içerisinde sayfadaki ScriptManager yani PageRequestManager'a ait mevcut kopyayı (Instance) alıyoruz ve .add_initializeRequest metodu ile sayfada gerçekleşecek her bir AJAX talebinde çalıştırılmak üzere kendi hazırladığımız bir fonksiyonu atıyoruz. Fonksiyonumuzun adı DurumKontrol olacak ve aşağıdaki şekilde oluşturuyor olacağız.

        function DurumKontrol(sender, args)
        {
            var prm = Sys.WebForms.PageRequestManager.getInstance();
            if (args.get_postBackElement().id == 'Button2' & prm.get_isInAsyncPostBack())
                {
                    prm.abortPostBack();
                }
            else if (prm.get_isInAsyncPostBack() & args.get_postBackElement().id == 'Button1')
                {
                    args.set_cancel(true);
                    $get("Label1").innerHTML = "Yükleme devam ediyor...";
                }
            else if (!prm.get_isInAsyncPostBack() & args.get_postBackElement().id == 'Button1')
                {
                    $get("Label1").innerHTML = "Yükleniyor...";
                    $get("Button2").style.visibility = "visible";
                }
        }

Gelin şimdi bu fonksiyon içerisinde neler yaptığımıza tek tek bakalım. İlk olarak yine fonksiyon içerisinde kullanmak üzere prm adında bir değişken tanımlayarak sayfada kullanılan mevcut ScriptManager'a ait PageRequestManager objesinin bir kopyasını, vekilini alıyoruz.

            if (args.get_postBackElement().id == 'Button2' & prm.get_isInAsyncPostBack())
                {
                    prm.abortPostBack();
                }

Yukarıda inceleyebileceğimiz bir sonraki adımda kontrol ettiğimiz iki durum söz konusu. Bunlardan ilki; DurumKontrol fonksiyonumuzun çalışmasına neden olan kaynak AJAX talebinin sayfadaki hangi kontrolden geliyor olduğu. args.get_postBackElement().id komutu ile kaynak kontrolün ID bilgisini alabiliyoruz. Eğer kaynak element Button2 ise, yani İptal düğmesine basılmış ise ve hali hazırda süregelen bir AJAX asenkron veri aktarımı varsa hemen var olan aktarımı iptal etmemiz gerekiyor. prm.get_isInAsyncPostBack() komutu ile hali hazırda süregelen bir AJAX asenkron yüklemesi olup olmadığı öğrenebiliriz. Her iki durum da olumlu ise prm.abortPostBack(); komutu ile süregelen, yani aslında bir önceki yüklemeyi iptal ediyoruz.

            else if (prm.get_isInAsyncPostBack() & args.get_postBackElement().id == 'Button1')
                {
                    args.set_cancel(true);
                    $get("Label1").innerHTML = "Yükleme devam ediyor...";
                }

Bir sonraki adımda ek bir kontrol daha yapacağız. Eğer bir yükleme yapılıyorsa ve kullanıcı hala Yükle düğmesine tıklıyorsa, bu sefer de "Yükleme devam ediyor..." şeklinde bir uyarı göstererek yeni bir yükleme başlatmamamızda fayda var. Bu mekanizmayı kurmak için de ilk olarak süregelen bir yükleme var mı diye prm.get_isInAsyncPostBack() komutu ile kontrol ediyor ve eğer yeni elimize ulaşan yükleme talebini de Button1 oluşturmuş ise args.set_cancel(true) komutu ile yeni gelen talebi geçersiz kılıyor, geri çeviriyoruz. Son olarak sayfamızdaki Label1 içerisine uygun bir mesaj aktarıyoruz.

            else if (!prm.get_isInAsyncPostBack() & args.get_postBackElement().id == 'Button1')
                {
                    $get("Label1").innerHTML = "Yükleniyor...";
                    $get("Button2").style.visibility = "visible";
                }

Yapacağımız son kontrol aslında bir denetleme değil. Hatırlarsanız örneğimizin başında tasarımımızı yaparken İptal düğmesini sayfada görünmeyecek şekilde ayarlamıştık. Şimdi sıra geldi yeni bir yükleme başladığınıda hemen İptal düğmemizi görünür hale getirmeye. Eğer süregelen bir yükleme yoksa ve yeni bir yükleme işlemi Button1 aracılığı ile başlatılmış ise Label1 içerisine "Yükleniyor.." yazıyoruz ve Button2'nin visibility özelliğini visible olarak değiştiriyoruz.

Sayfamızın tam kodunu aşağıdaki inceleyebilirsiniz.

<%@ Page Language="VB" AutoEventWireup="true" CodeFile="Default.aspx.vb" Inherits="_Default" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server" />
        <script type="text/javascript" language="javascript">
        Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(DurumKontrol);
       
        function DurumKontrol(sender, args)
        {
            var prm = Sys.WebForms.PageRequestManager.getInstance();
            if (args.get_postBackElement().id == 'Button2' & prm.get_isInAsyncPostBack())
                {
                    prm.abortPostBack();
                }
            else if (prm.get_isInAsyncPostBack() & args.get_postBackElement().id == 'Button1')
                {
                    args.set_cancel(true);
                    $get("Label1").innerHTML = "Yükleme devam ediyor...";
                }
            else if (!prm.get_isInAsyncPostBack() & args.get_postBackElement().id == 'Button1')
                {
                    $get("Label1").innerHTML = "Yükleniyor...";
                    $get("Button2").style.visibility = "visible";
                }
        }
        </script>
        <div>
            <asp:UpdatePanel ID="UpdatePanel1" runat="server">
                <ContentTemplate>
                    <asp:Label ID="Label1" runat="server"></asp:Label><br />
                    <asp:Button ID="Button1" runat="server" Text ="Yükle" OnClick="button1_Click" />
                    <asp:Button ID="Button2" style="visibility:hidden;"
                            runat="server" Text="İptal" />
                </ContentTemplate>
            </asp:UpdatePanel>
        </div>
    </form>
</body>
</html>

Sonuç olarak yukarıdaki teknikler ile sayfa içerisindeki tüm AJAX veri transferi işlemlerine çok daha hakim bir yaklaşım ile çok daha kullanıcı dostu bir web platformu oluşturarak web sitelerimizin ziyaretçilerini biraz daha rahatlatabiliriz.

Hepinize kolay gelsin.

Sunday, August 26, 2007 7:03:36 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   AJAX | ASP.NET  | 
 Saturday, August 25, 2007

İnternet tarayıcılarındaki Geri ve İleri düğmelerinin ne kadar çok kişi tarafından kullanıldığını ilk AJAX web projelerinin kullanıcılarına teslim edilmesi ile anladım. Neredeyse herkes bu düğmelerin çalışmamasından şikayetçiyi (Haklılardı). AJAX altyapısı üzerine kurduğumuz web sitelerinde performansı arttırmak amacıyla olabildiğince işlemleri AJAX tekniği ile yapmaya çalışıyoruz. Bu durumun iki sakıncası var; birincisi web sitesi içerisindeki farklı konumlar için farklı adresler oluşmuyor. Kullanıcı web sitemizde onlarca düğmeye tıklayarak iç kısımlarda biryerlere ulaşmış olabiliyor fakat sayfa hiç yenilenmediği için ulaştığı noktanın bir adresi (URL) olmuyor. İkinci sakıncası ise makalemin ana nedeni olan ve bir önceki nedenden kaynaklanan, tarayıcılardaki Geri ve İleri düğmelerinin çalışmaması. Sayfanın adresi değişmediği için hiçbir internet tarayıcı sayfanın içerisindeki değişiklikleri adres geçmişine eklemiyor ve bu nedenle geriye veya ileriye gidiş de mümkün olmuyor.

Peki ne yapabiliriz? İlk aşamada sayfanın adresini değiştirmek için sayfa içi çapalar (linkler) kullanabiliriz. Bu tarz linkleri aşağıdaki şekilde tanımlayabilirsiniz. Normal adresin sonuna bir # işareti ile eklenirler ve adresein bu kısmının değişmesi için sayfanın yenilenmesi gerekmez.

http://www.biradres.com/birdosya.aspx?ID=2#Capa

Güzel bir taktik olduğu kesin fakat maalesef yukarıdaki şekliyle yaratılan adresler Internet Explorer içerisinde sayfa geçmişine eklenmeyebiliyor (IE sürümüne bağlı). Bizim tüm tarayıcılarla uyumlu olmamız şart. Bu durumda Internet Explorer için farklı bir teknik kullanmamız gerekecek. Sayfanın içerisine gizli bir IFRAME (Satır içi çerçeve) ekleyerek IFRAME içerisindeki dosyanın adresini değiştirebiliriz. Bu durum Internet Explorer'da adres geçmişine yeni bir sayfay eklemek için yeterli olacaktır. Tek yapmamız gereken sayfamızı yenileyen AJAX komutlarının başına IFRAME adresini de değiştiren bir kod eklemek. Peki Geri veya İleri düğmelerine basıldığını nasıl anlayacağız? IFRAME içerisinde kullanacağımız adres gerçek bir sayfaya yönlenecek ve eğer tarayıcıdaki düğmeler ile bu sayfa değişmişse bir üst seviyedeki ana sayfaya JavaScript ile bir parametre gönderecek. Böylece ana sayfa AJAX teknikleri ile kendini eski haline çevirecek.

Merak etmeyin, biz bunları tek tek yapmayacağız. Onun yerine bu konuda işimizi çok kolaylaştıracak bir sunucu kontrolü kullanacağız. Bahsettiğim kontrolün adı UpdateHistory. Aşağıdaki adresten kontrol paketini indirebilirsiniz. Paket içerisinde yer alan nStuff.UpdateControls.dll dosyasını Toolbox içerisine ekleyerek projenizdeki herhangi bir sayfaya sürükle&bırak tekniği ile kontrolü ekleyebilirsiniz.

http://www.nikhilk.net/Content/Samples/UpdateControls.zip

Kontrolü sayfaya eklediğinizde aşağıdaki şekilde gözükecektir. Kontrol üzerinde yapmamız gereken hiçbir ayar yok.

<nStuff:UpdateHistory ID="UpdateHistory1" runat="server">
</nStuff:UpdateHistory>

Sayfa içerisinde ayrıca deneme amaçlı olarak bir UpdatePanel bulunduralım. UpdatePanel içerisine bir düğme ve bir de Label yerleştirelim. Düğmeye her tıklandığında Label içerisindeki sayısal değeri alıp üzerine bir ekleyip geri döndürsün. Böylece sürekli içeriği değişen bir UpdatePanel sahibi olmuş oluruz.

<asp:UpdatePanel ID="UpdatePanel1" runat="server">
  <ContentTemplate>
    <asp:Label ID="etiket" runat="server">0</asp:Label>
    <asp:Button ID="dugme" runat="server" OnClick="dugme_Click" Text="TIKLA" />
  </ContentTemplate>
</asp:UpdatePanel>

Yukarıdaki kod içerisinde düğmeye tıklandığında etiket adındaki Label içerisindeki değeri alarak bir arttıracağımızdan bahsetmiştik. Arttırma işlemini yaparken aslında sayfanın içeriği değiştiği için gerekli kaydın tarayıcıya ait sayfa geçmişi listesine de eklenmesini istiyoruz. Böylece İleri ve Geri düğmeleri ile site içerisinde gezebileceğiz. Bunun için aşağıdaki kodu yazıyor olacağız.

    Protected Sub dugme_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        etiket.Text = CInt(etiket.Text) + 1
        UpdateHistory1.AddEntry(CInt(etiket.Text))
    End Sub

Yukarıdaki kodun ilk satırı bize yabancı değil. İkinci satıra baktığımızda ise UpdateHistory kontrolüne ait AddEntry metodunu kullandığımızı görüyoruz. Bu metoda vermiş olduğumuz tek parametre olan metin değeri sayfanın adresine, # işaretinden sonrasına ekleyecek. Tarayıcıda ileri veya geri düğmelerine basıldığında bizim sayfayı tekrar eski haline göre düzenlememiz için geriye yine bu veri döndürülecek. Veritabanına bağlı gerçek bir örnekte sayfada gösterilen veriyi bir veritabanı tablosundan Birincil Anahtar (Primary Key) değerini sorguyla göndererek aldığınızı düşünelim. Bu durumda AddEntry komutu ile adrese ekleyeceğimiz veri Primay Key'in ta kendisi olmalıdır. Böylece bir sonraki adımda göreceğimiz üzere sayfa kullanıcı tarafından ileri ve geri düğmeleri ile değiştirildiğinde adresin # işaretinden sonraki kısmına bakarak sayfaya kolaylıkla veri yerleştirebiliriz.

Son olarak gelelim kullanıcının ileri ve geri düğmelerini kullandığında sayfanın nasıl eski haline dönüştürüleceğine. Bu işlemi yapmak için UpdateHistory kontrolüne ait Navigate metodunu kullanacağız. Söz konusu metod bize hedef sayfanın EntryName değerini döndürüyor olacak.

Protected Sub UpdateHistory1_Navigate(ByVal sender As Object, ByVal e As nStuff.UpdateControls.HistoryEventArgs) Handles UpdateHistory1.Navigate
   If String.IsNullOrEmpty(e.EntryName) = False Then
      etiket.Text = e.EntryName
   Else
      etiket.Text = "0"
   End If
End Sub

Kodumuz içerisinde ilk olarak gelen parametrenin boş olup olmadığını kontrol ediyoruz. Eğer sayfa ilk defa açılıyorsa söz konusu parametre boş olacaktır. Bu durumda etiket adındaki Label içerisine 0 yazmamız yeterli. Eğer UpdateHistory tarafından bize döndürülen EntryName boş değilse kullanıcı bir şekilde Geri veya İleri düğmelerini kullanmış demektir. Kullanıcının hangi sayfaya gitmek istediğini EntryName değişkeni ile anlıyorum. Benim örneğimde bu veriyi direk etiket içerisine yazdırıyorum. Veritabanına bağlı gerçek bir örnekte siz geri dönen Primary Key verinize göre veritabanından gerekli içeriği çekerek sayfaya yerleştirebilirsiniz.

Son olarak projemin doğru çalışıp çalışmadığını kontrol etmek için UpdatePanel dışına da bir Label yerleştirerek sayfa açılışında mevcut saat bilgisini yazdırdım. Böylece sadece UpdatePanel içeriği mi yenileniyor yoksa tüm sayfa mı yenileniyor anlayabileceğim.

Son hali ile sayfanın HTML kodu aşağıdaki şekilde;

<%@ Page Language="VB" AutoEventWireup="true" CodeFile="Default.aspx.vb" Inherits="_Default" %>
<%@ Register Assembly="nStuff.UpdateControls" Namespace="nStuff.UpdateControls" TagPrefix="nStuff" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
        <ajaxToolkit:ToolkitScriptManager ID="ScriptManager1" runat="server" />
        <div>
            <nStuff:UpdateHistory ID="UpdateHistory1" runat="server">
            </nStuff:UpdateHistory>
            <asp:UpdatePanel ID="UpdatePanel1" runat="server">
                <ContentTemplate>
                    <asp:Label ID="etiket" runat="server">0</asp:Label>
                    <asp:Button ID="dugme" runat="server" OnClick="dugme_Click" Text="TIKLA" />
                </ContentTemplate>
            </asp:UpdatePanel>
            <asp:Label ID="Label1" runat="server" Text="Label"></asp:Label></div>
    </form>
</body>
</html>

Sayfamızın Code-Behind kısmı da aşağıdaki gibi sonlandı.

Partial Class _Default
    Inherits System.Web.UI.Page
 
    Protected Sub dugme_Click(ByVal sender As Object, ByVal e As System.EventArgs)
        etiket.Text = CInt(etiket.Text) + 1
        UpdateHistory1.AddEntry(CInt(etiket.Text))
    End Sub
 
    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Label1.Text = Date.Now.ToLongTimeString
    End Sub
 
    Protected Sub UpdateHistory1_Navigate(ByVal sender As Object, ByVal e As nStuff.UpdateControls.HistoryEventArgs) Handles UpdateHistory1.Navigate
        If String.IsNullOrEmpty(e.EntryName) = False Then
            etiket.Text = e.EntryName
        Else
            etiket.Text = "0"
        End If
    End Sub
End Class

Çözümümüz ile aslında sadece Geri ve İleri tuşları ile ilgili sorunu çözmedik. Ayrıca AJAX uygulamalarında her sayfanın ayrı adreslerinin olmaması sorununu da çözdük. Kopyala - Yapıştır tekniği ile adresler # işaretinden sonraki kısımları ile beraber taşındıklarında başka bir tarayıcıda denenmeleri durumda sayfa doğru konumda açılacaktır.

Hepinize kolay gelsin.

Saturday, August 25, 2007 12:33:13 AM (GTB Standard Time, UTC+02:00)  #    Comments [0]   AJAX | ASP.NET  | 
 Thursday, August 23, 2007

Uzun süredir görsel dersler hazırlamayı düşünüyordum. Esas sorun bu dersleri nasıl blog üzerinden paylaşacağımdı. Terzi kendi söküğünü dikemez konseptine uygun olarak bu konuda blogum için özel bir sayfa programlama şansım maalesef olmadı. Diğer seçenekler arasından ilki MSN SoapBox sitesini kullanmaktı, ikincisi ise nedirtv.com üzerinden görsel dersleri paylaşmaktı.

Blog sitesini de sürekli takip ettiğim Mehmet Nuri Çankaya'nın tavsiyesi ile nedirtv?com ekibi ile iletişime geçtim. nedirtv?com topluluk lideri Uğur Umutluoğlu'nun sıcak ve samimi yaklaşımı ile kararımı vermem pek de zor olmadı ve ilk iki görsel derim nedirtv?com üzerinde yayına girdi. Aşağıdaki adreslerden dersleri izleyebilirsiniz. nedirtv?com'a gönderdiğim videolardan blog üzerinden de haberdar olabileceksiniz, sürekli yazıyor olacağım.

AJAX Control Toolkit Animation Extender

AJAX Control Toolkit Yüklemesi ve Web Sitelerinde Kullanimi

Thursday, August 23, 2007 9:16:24 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   AJAX | ASP.NET | Görsel Ders  | 
 Wednesday, August 22, 2007

Herhangi bir web sitesi ile kullanıcı (istemci) arasındaki web trafiği normal şartlarda şifrelenmemiş (enkript edilmemiş) olarak aktarılır. Bu durum esasen ciddi bir güvenlik açığı olabilir. İstemci bilgisayar ile sunucu arasındaki ağ trafiğini yakalayabilen biri bu trafik üzerinden geçen veriyi ele geçirerek kötü amaçlarla kullanabilir. Bu açığı engellemek amacıyla özellikle E-Ticaret sitelerinde SSL uygulaması yapılır. https ile başlayan adreslerinden tanıyabileceğimiz bu web sitelerinde istemci ile sunucu arasındaki trafik enkript edilir. Böylece ağ trafiğinin yakalanması halinde bile herhangi bir şekilde söz konusu veri kullanılamayacaktır.

Peki SSL kullanılamayacak, ufak bütçeli projelerde en azından önemli verileri kullanıcıdan (istemci) alırken enkript etme şansımız yok mu? Varsayalım web sitemizin bir kullanıcı giriş sayfası var ve kullanıcımız birer TextBox kontrolüne kullanıcı adını ve şifresini yazıyor. Bir sniffer yazılımı kullanarak ağ trafiğini yakaladığımızda aşağıdaki şekilde şifreyi ele geçirebiliyoruz.

{"Sifre":"GizliSifrem"}

Oysa bu şifreyi istemci tarafında JavaScript kütüphaneleri ile anında MD5 algoritması ile enkript ederek sunucu tarafına gönderme şansımız var. Böyle bir durumda sniffer yazılımı kullandığımızda karşımıza çıkan sonuç aşağıdaki gibi.

{"Sifre":" afb5bcf186d39b00d94917df57b9c593 "}

Şimdi gelin bu işi nasıl yapacağımıza yakından bir göz atalım. İlk olarak Paul Johnston tarafından hazırlanmış olan MD5 JavaScript kütüphanesini aşağıdaki adresten bilgisayarımıza indirelim.

http://pajhome.org.uk/crypt/md5/

Örneğimizde bir ASP.NET AJAX projesi üzerinden çalışacağımız için bilgisayarımıza indirdiğimiz JavaScript dosyamızı web sayfamızdaki ScriptManager’a ScriptReference olarak tanımlayacağız. Böylece JavaScript dosyası sayfamıza linklenmiş olacak.

      <asp:ScriptManager EnablePageMethods="true" ID="ScriptManager1" runat="server">
        <Scripts>
          <asp:ScriptReference Path="md5.js" />
        </Scripts>
      </asp:ScriptManager>

Kütüphane içerisindeki hex_md5(); metodunu kullanıyor olacağız. Söz konusu metod String tipinde bir parametre alarak geriye şifrelenmiş Hex-String döndürüyor. Örneğimizde şifre kontrol işlemi için bir ASP.NET AJAX PageMethod kullanacağız. Sayfaya giriş için kullanacağımız Giriş düğmesi ve Şifre, Kullanıcı adı TextBox'ları bir UpdatePanel içerisinde yer alacak.

      <script language="javascript" type="text/javascript">
      function KontrolEt()
      {
        // Sayfada Sifre TextBox'ına girilmiş şifreyi alıyoruz.
        var Pass = $get("Sifre").value;
        // Şifreyi şifreliyoruz.
        var MD5 = hex_md5(Pass);
        // GirisKontrol PageMethod'unu çalıştırarak kullanıcıyı kontrol ediyoruz.
        PageMethods.GirisKontrolu(MD5, Oldu);
      }
      function Oldu(Sonuc)
      {
        // PageMethod tarafından döndürülen Evet/Hayır sonucunu mesaj kutusu ile gösteriyoruz.
        alert(Sonuc);
      }
      </script>
      <asp:UpdatePanel ID="UpdatePanel1" runat="server">
        <ContentTemplate>
         <asp:TextBox ID="Adi" runat="server"></asp:TextBox>
         <asp:TextBox ID="Sifre" runat="server"></asp:TextBox>
         <input id="Giris" type="button" value="Giris" onclick="KontrolEt();" />
        </ContentTemplate>
      </asp:UpdatePanel>

Sunucu tarafına baktığımızda ise elimizdeki gerçek şifreyi istemciden gelen enkript edilmiş şifre ile karşılaştırmak üzere enkript ediyor olacağız. Biz örneğimizde gerçek şifreyi veritabanından vs çekmeyeceğiz. Onun yerine ben GerçekŞifrem metnini şifre olarak kullanacağım. Siz projelerinizde bu metni kullandığınız veritabanlarına bağlayabilirsiniz. Aşağıda sunucu taraflı yazmış olduğumuz AJAX PageMethod kodu yer alıyor.

<System.Web.Services.WebMethod()> _
  Shared FunctionGirisKontrolu(ByVal Sifre As String) As Boolean
    'Şifremizi IOStream objesine çevirecek olan encoder objesini tanımlayalım.
    Dim encoder As New UTF8Encoding()
    'MD5 Servisine ulaşalım.
    Dim MD5 As New System.Security.Cryptography.MD5CryptoServiceProvider
    'Doğru şifreyi enkript edelim.
    Dim GercekSifre As Byte() = MD5.ComputeHash(encoder.GetBytes("GerçekŞifrem"))
    'Gerçek Şifremizi HEX'e çevirelim.
    Dim Dogru As New StringBuilder()
    For i As Integer = 0 To GercekSifre.Length - 1
      Dogru.Append(GercekSifre(i).ToString("x2"))
    Next i
    'Enkript edilmiş doğru şifre ile istemciden gelen enkript edilmiş şifreyi karlılaştıralım..
    If Dogru.ToString = Sifre Then
      Return True
    Else : Return False
    End If
  End Function

Hepsi bu kadar. Artık istemci tarafında girilen tüm şifreler enkript edildikten sonra sunucuya kontrol için gönderiliyor olacak. Böylece istemci ile sunucu arasındaki hiçbir yazılım veya donanım söz konusu şifreyi ağ trafiği üzerinden yakalasa bile tam olarak ne olduğunu anlayamayacaktır.

Hepinize kolay gelsin.

Wednesday, August 22, 2007 5:54:57 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   AJAX | ASP.NET | JavaScript  | 
 Tuesday, August 21, 2007

F5 tuşu ile Visual Studio içerisinde Debug başlattığınızda projeleriniz 1 dakikaya yakın bir bekleme süresi sonunda çalışır hale geliyorsa aşağıdaki adresten gerekli yamayı indirerek bilgisayarınıza kurabilirsiniz.

https://connect.microsoft.com/VisualStudio/Downloads/DownloadDetails.aspx?DownloadID=8262

Yavaşlamaya neden olan durum farklı klasörlerde aynı isimleri taşıyan code-behind dosyalarında aynı anda farklı kodların Debug için işaretlenmesinden kaynaklanıyor.

Tuesday, August 21, 2007 7:25:04 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   ASP.NET  | 
 Thursday, August 16, 2007

ASP.NET AJAX kitabımda detaylıca bahsettiğim AJAX Control Toolkit benim en çok ilgimi çeken kontrollerden biri olan Animation Extender maalesef dokümantasyon eksikliği yüzünden (kitabımı almayanlar tarafından :)) pek kullanılamıyor. Umarım bu makale bu boşluğu doldurur.

AJAX Control Toolkit Animation Extender kontrolü bize XML kodları yazarak DHTML animasyonları yaratma olanağı sağlıyor. Bu konsept bizim ileride Silverlight ile daha da aşina olacağımız XAML (XML kodu) ile animasyon yaratma konseptine çok yakın. Hızlı bir şekilde ilk örneğimize geçmeden önce bilgisayarınızda ASP.NET AJAX 1.0 ve AJAX Control Toolkit’in yüklü olduğundan emin olalım, eğer gerekli yüklemeler bilgisayarınızda eksikse aşağıdaki adreslerden indirerek hemen yükleyebilirsiniz.

http://www.microsoft.com/downloads/details.aspx?FamilyID=ca9d90fa-e8c9-42e3-aa19-08e2c027f5d6&displaylang=en
http://www.codeplex.com/AtlasControlToolkit/Release/ProjectReleases.aspx?ReleaseId=4923

Yükleme sonrası Visual Studio veya Visual Web Developer Express içerisinde ASP.NET web sitesi yaratmak istediğinizde seçenek olarak “AJAX Control Toolkit Web Site” seçeneği de karşınıza çıkacaktır.

AJAX Control Toolkit Web Site yaratırken Visual Studio ekranı...

Yeni bir “Ajax Control Toolkit Web Site” yarattıktan sonra karşımıza ilk çıkan default.aspx dosyasına Toolbar içerisinden bir AnimationExtender eklediğimizde sayfamızın tasarım bölümünde aşağıdaki HTML kodu ile karşılaşıyoruz

<%@ Page Language="VB" AutoEventWireup="true" CodeFile="Default.aspx.vb" Inherits="_Default" %>
 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
        <ajaxToolkit:ToolkitScriptManager ID="ScriptManager1" runat="server" />
        <div>
            <ajaxToolkit:AnimationExtender ID="AnimationExtender1" runat="server">
            </ajaxToolkit:AnimationExtender>
        </div>
    </form>
</body>
</html>

Sayfa içerisinde toplam iki adet kontrol bulunuyor. Bunlardan ilki olan ToolkitScriptManager sayfamızı ilk açtığımızda zaten içerisinde vardı. ASP.NET AJAX 1.0 ile daha önce projeler gerçekleştirmiş olanlar hatırlayacaktır; AJAX özellikleri kullanacağımız her sayfada ScriptManager adında bir kontrolün sayfanın en başında yer alması gerekiyordu. ScriptManager sayfamızdaki diğer AJAX kontrollerinin çalışabilmesi ve bizim istemci taraflı AJAX kodları yazarken Microsoft kütüphanesinden faydalanabilmemiz için gerekli olan tüm JavaScript kütüphanelerinin sayfaya eklenmesini sağlıyor. AJAX Control Toolkit söz konusu olduğunda Control Toolkit içerisindeki her kontrolün kendi ayrı JavaScript dosyalarının da sayfaya eklenmesi gerektiğini düşünürsek bazen bir sayfada kullanılan farklı AJAX kontrolleri nedeniyle 10-15 adet farklı JavaScript dosyalarının arkaplanda sayfaya eklenmiş olduğunu görebiliyorduk. Aslında tüm bu JavaScript kodlarının olabildiğince daha az sayıda dosyada derlenmesi ve toplu olarak sayfaya eklenmesi daha mantıklı olacaktır. İşte bu bahsettiğimiz optimizasyonu bizim yerimize ToolkitScriptManager yapacak. Bunun için de klasik ASP.NET AJAX ScriptManager yerine Control Toolkit kullanılan projelerde artık ToolkitScriptManager kullanıyor olacağız.

Sayfadaki diğer kontrol bizim biraz önce eklediğimiz AnimationExtender kontrolü. Gelin şimdi hızlı bir şekilde bu kontrolün aldığı özelliklere bakmak için aşağıdaki kodu inceleyelim.

<ajaxToolkit:AnimationExtender ID="AnimationExtender1" runat="server" TargetControlID="Button1">
    <Animations>
        <OnLoad> ... </OnLoad>
        <OnClick> ... </OnClick>
        <OnMouseOver> ... </OnMouseOver>
        <OnMouseOut> ... </OnMouseOut>
        <OnHoverOver> ... </OnHoverOver>
        <OnHoverOut> ... </OnHoverOut>
    </Animations>
</ajaxToolkit:AnimationExtender>

AnimationExtender kontrolümüz kendi içerisinde tanımlayacağımız tüm animasyonlara sayfadaki hangi elementin neden olacağını TargetControlID aracılığı ile bizden öğreniyor. Örneğin sayfada bir düğmeye tıklanacak ve sonrasında animasyon uygulanacaksa TargetControlID değerine söz konusu düğmenin ID bilgisini vermemiz gerekiyor. Tabi sadece tıklama (OnClick) gibi durumlarla sınırlandırılmış değiliz; yukarıdaki kod içerisinde de görebileceğiniz gibi OnLoad, OnMouseOver gibi çok sayıda durum (event) söz konusu. Her bir durumda uygulanmak üzerine farklı animasyonlar tanımlayabiliyoruz, fakat tüm bu durumlar TargetControlID özelliğine aktarılmış elementin durumlarına bağlı olacak.
Animasyonlarımızı XML kodları ile yazacağımızdan bahsetmiştim. Her bir farklı durum (onclick, onload… vs) için farklı animasyanları hazırladıktan sonra yukarıdaki örnekteki uygun duruma ait (onclick vs) XML taglarının arasına yazmamız gerekiyor. Hemen bir örnek ile ilerleyelim.

<ajaxToolkit:AnimationExtender ID="AnimationExtender1" runat="server" TargetControlID="Button1">
    <Animations>
        <OnHoverOver>
          <FadeOut Duration=".5" Fps="20" MinimumOpacity=".1" />
        </OnHoverOver>
        <OnHoverOut>
          <FadeIn Duration=".5" Fps="20" MinimumOpacity=".1" />
        </OnHoverOut>
    </Animations>
</ajaxToolkit:AnimationExtender>
 <asp:Button ID="Button1" runat="server" Text="Button" />

Sayfamıza ayrıca bir Button ekledik. Bu düğmeyi hedef kontrol yani TargetControlID olarak ayarladık. AnimasyonExtender içerisinde FadeIn ve FadeOut olarak iki farklı animasyonu hedef kontrolün iki farklı durumuna (OnHoverOver ve OnHoverOut) tanımladık. Böylece fare ile hedef kontrolün üzerine gelindiğinde OnHoverOver içerisinde tanımlanmış animasyon, fare ile hedef kontrolün üzerinden ayrıldığımızda ise OnHoverOut içerisindeki animasyon çalışacak. Animasyonların tanımlanması ile ilgili detayları birazdan inceleyeceğiz, şimdilik durum kontrolüne konsantre olarak yukarıdaki kodu tekrar incelemenizi tavsiye ediyorum.
Animasyon tanımlamalarımızı yaparken iki farklı seçeneğimiz var. Bunlardan ilki Sequence animasyon, diğeri ise Parallel. Sequence animasyon tagları içerisinde yer alan tüm animasyonlar tek tek, XML kodu içerisine yazılma sıraları ile gerçekleştirilir. Bir animasyon bitmeden diğerine geçilemez. Parallel animasyonlarda ise tüm animasyon aynı anda çalışır. Bir Sequence animasyon içerisinde bir Parallel animasyon yer alabileceği gibi tam tersi de olabilir. Gelin yine örnek üzerinden gidelim.
Örneğimizdeki sayfaya bir ASP.NET Calendar kontrolü ekleyeceğim. Sayfada yer alacak olan iki farklı düğmeden biri Calender kontrolünü %10 şeffaf yapacak, diğeri ise tekrar görünür hale getirecek. İki farklı hedef kontrolüm, yani iki farklı TargetControl olacağı için mecburen iki farklı AnimationExtender kullanacağım.

<ajaxToolkit:AnimationExtender ID="AnimationExtender1" runat="server" TargetControlID="Button1">
    <Animations>
        <OnClick>
          <Sequence AnimationTarget="Calendar1">
            <FadeOut Duration=".5" Fps="20" MinimumOpacity=".1" />
          </Sequence>
        </OnClick>
    </Animations>
</ajaxToolkit:AnimationExtender>
  <ajaxToolkit:AnimationExtender ID="AnimationExtender2" runat="server" TargetControlID="Button2">
    <Animations>
        <OnClick>
          <Sequence AnimationTarget="Calendar1">
            <FadeIn Duration=".5" Fps="20" MinimumOpacity=".1" />
          </Sequence>
        </OnClick>
    </Animations>
</ajaxToolkit:AnimationExtender>
<asp:Button ID="Button1" runat="server" OnClientClick="return false" Text="Görünmez Yap" />
<asp:Button ID="Button2" runat="server" OnClientClick="return false" Text="Görünür Yap" />
<asp:Calendar ID="Calendar1" runat="server"></asp:Calendar>

Yukarıdaki kod içerisinde bizim için yeni olabilecek iki şey var. Bunlardan ilki Sequence animasyonlar ve bu animasyonlara ait AnimationTarget özellikleri. Daha önceki örneğimizde animasyona neden olan kontrol, yani TargetControl ile animasyonun gerçekleştiği kontrol aynıydı. Oysa şu an biz bir düğmeye tıklayıp başka bir kontrole animasyon uygulamak istiyoruz. Bu nedenle animasyon serisine yani Sequence animasyonuna bir hedef belirtmemiz gerekiyor. Örneğimizde bu kontrol Calendar1 olacak çünkü animasyonu takvime uygulayacağım. Bir ikinci ilginç nokta da düğmelerimize verdiğimiz OnClientClick özellikleri. Sayfada normal ASP.NET düğmeleri kullandığımız için doğal olarak düğmelere her tıkladığımızda sayfa PostBack oluyor. Oysa bizim animasyonların çalışabilmesi için bunu engellememiz şart. Bu nedenle ClientClick özelliklerine “return falseJavaScript kodunu yazıyoruz. Böylece söz konusu düğmelere tıklandığında sayfa PostBack olmayacak, sadece animasyonlar çalışacak.
Şimdi bir de Parallel animasyon denemesi yapalım. Parallel animasyon tagları arasında iki farklı animasyon koyacağız. Animasyonlardan biri yine takvimin görünürlük değerini değiştirirken diğeri de aynı takvimin fon rengini beyazdan kırmızıya ve kırmızıdan beyaza çevirecek.

<ajaxToolkit:AnimationExtender ID="AnimationExtender1" runat="server" TargetControlID="Button1">
    <Animations>
        <OnClick>
          <Parallel AnimationTarget="Calendar1">
            <FadeOut Duration=".5" Fps="20" MinimumOpacity=".1" />
            <Color PropertyKey="backgroundColor" StartValue="#FFFFFF" EndValue="#ff0000" />
          </Parallel>
        </OnClick>
    </Animations>
</ajaxToolkit:AnimationExtender>
  <ajaxToolkit:AnimationExtender ID="AnimationExtender2" runat="server" TargetControlID="Button2">
    <Animations>
        <OnClick>
          <Parallel AnimationTarget="Calendar1">
            <FadeIn Duration=".5" Fps="20" MinimumOpacity=".1" />
            <Color PropertyKey="backgroundColor" StartValue="#ff0000" EndValue="#FFFFFF" />
          </Parallel>
        </OnClick>
    </Animations>
</ajaxToolkit:AnimationExtender>
<asp:Button ID="Button1" runat="server" OnClientClick="return false" Text="Görünmez Yap" />
<asp:Button ID="Button2" runat="server" OnClientClick="return false" Text="Görünür Yap" />
<asp:Calendar ID="Calendar1" runat="server"></asp:Calendar>

Animasyonlar içerisinde kullanabileceğiniz farklı animasyon taglarını sırayla inceleyelim.

<Resize Width="200" Height="300" Unit="px" />

Yukarıdaki animasyon ile hedef elementin yüksekliğinin ve genişliğinin verilen değerlere doğru bir animasyon ile büyütülmesini veya küçültülmesini sağlayabilirsiniz.

<EnableAction Enabled="false" />

Özellikle düğmelerin tıklanabilir olması veya olmaması arasında geçiş yapmak için yukarıdaki animasyon kodunu kullanabilirsiniz. Bir düğmenin rengini açık bir renkten koyu bir renge çevirirken aynı anda paralel bir animasyonda düğmeyi tıklanamaz hale de getirebilirsiniz.

<ScriptAction Script="alert('Selamlar!');" />

Sadece animasyon yapmakla kalmayıp animasyonlarınızın belirli noktalarında JavaScript kodları da çalıştırabilirsiniz. Bu özellik animasyonlar tamamlandıktan sonra kullanılırsa animasyonun bitmesi ile sayfada farklı işlemler yapabilmenizi sağlayabilir.

<Color AnimationTarget="MyContent"
 Duration="1"
 StartValue="#FF0000"
 EndValue="#666666"
 Property="style"
 PropertyKey="backgroundColor" />

Objelerin CSS özellikleri üzerinde direk oynamak için yukarıdaki kodu kullanabilirsiniz. Örneğin bu kod içerisinde hedef objenin fon rengi değiştiriliyor.

<StyleAction Attribute="display" Value="none" />

CSS özelliklerinin değişimi ile ilgili alternatif olarak StyleAction animasyonunu da kullanabilirsiniz.
Peki ya sayfadaki bir duruma, bir koşula göre farklı animasyonların çalışmasını istiyorsanız? Merak etmeyin, bunun için de bir çözüm var.

<Condition ConditionScript="$get('isaret').checked">
    <FadeOut AnimationTarget="up_container" minimumOpacity=".2" />
</Condition>

Yukarıdaki gibi condition tagı ile direk sayfadaki bir CheckBox’ın işaretli olup olmadığını kontrol edebileceğiniz gibi herhangi bir JavaScript komutu da çalıştırabilir veya sayfada tanımlı global bir JavaScript değişkenini kontrol edebilirsiniz. Şart sağlandığında Condition tagları arasındaki animasyon uygulanacaktır, aksi halde pas geçilecektir.

<FadeOut Duration=".5" Fps="20" MinimumOpacity=".1" />

FadeIn ve FadeOut animasyonlarını daha önce de kullandık. Detaylı olarak incelemek gerekirse söz konusu animasyonlar için animasyon süresini Duration özelliğine aktarmamız gerekiyor. Diğer yandan isterseniz animasyonun saniyede kaç kare gösterimi ile oluşturulacağına da Fps değeri ile karar verebilirsiniz. Son olarak şeffaflığa gidiş veya şeffaflıktan dönüş animasyonlarında MinimumOpacity ve MaximumOpacity ile minimum şeffaflık ve maksimum şeffaflık limitlerini de ayarlayabilirsiniz.

Tüm bu animasyonları isterseniz direk JavaScript ile de oluşturabilir ve kullanabilirsiniz.

var effects = new Array();
effects[0] = new AjaxControlToolkit.Animation.FadeInAnimation($get("Calendar1"), .3, 30, .3, 1, false);
AjaxControlToolkit.Animation.SequenceAnimation.play($get("Calendar1"), 0, 24, effects, 1); 

Yukarıdaki JavaScript kodumuz içerisinde ilk olarak animasyonlarımızın bir listesini saklayacak Array tipindeki değişkenimizi tanımlıyoruz. Benim değişkenimde sadece tek bir animasyon var, siz isterseniz birden çok animasyon ekleyebilirsiniz. Animasyonlarla ilgili parametreleri yine JavaScript ile aktarıyoruz. Son olarak da AjaxControlToolkit’e ait JavaScript sınıfları aracılığı ile hazırladığımız animasyon serisini bir SequenceAnimation olarak oynatmak için .play komutunu kullanıyoruz. Her bir animasyon için hedef HTML elementini ve gerekli animasyon parametrelerini vermemiz gerekiyor. XML-Script’e kıyasla biraz daha zor bir teknik olduğu için benim tavsiyem AnimationExtender animasyonlarını JavaScript ile birazdan inceleyeceğimiz şekilde kullanmanızdır.

<div id="gereksiz" runat="server" style="display:none;"></div>
<ajaxToolkit:AnimationExtender ID="AnimationExtender1" BehaviorID="Yoket" runat="server" TargetControlID="gereksiz">
    <Animations>
        <OnClick>
          <Parallel AnimationTarget="Calendar1">
            <FadeOut Duration=".5" Fps="20" MinimumOpacity=".1" />
            <Resize Width="400" Unit="px" />
          </Parallel>
        </OnClick>
    </Animations>
</ajaxToolkit:AnimationExtender>
  <ajaxToolkit:AnimationExtender ID="AnimationExtender2" BehaviorID="Goster" runat="server"  TargetControlID="gereksiz">
    <Animations>
        <OnClick>
          <Parallel AnimationTarget="Calendar1">
            <FadeIn Duration=".5" Fps="20" MinimumOpacity=".1" />
            <Resize Width="200" Unit="px" />
          </Parallel>
        </OnClick>
    </Animations>
</ajaxToolkit:AnimationExtender>
<input id="Button4" type="button" value="Yoket" onclick="var onclkBehavior = $find('Yoket').get_OnClickBehavior().get_animation();onclkBehavior.play();" />
<input id="Button1" type="button" value="Goster" onclick="var onclkBehavior = $find('Goster').get_OnClickBehavior().get_animation();onclkBehavior.play();" />
<input id="Button2" type="button" value="Gostermeye Ara Ver" onclick="var onclkBehavior = $find('Goster').get_OnClickBehavior().get_animation();onclkBehavior.pause();" />
<asp:Calendar ID="Calendar1" runat="server"></asp:Calendar>

Yukarıdaki örneğimizde AnimationExtender kontrolleri içerisinde tanımladığımız animasyonları JavaScript ile çalıştırıyoruz. Normal şartlarda AnimationExtender’lar başka .NET kontrollerine bağlanmak zorunda oldukları için yalancı bir .NET kontrolü yaratıp AnimationExtender kontrollerinin TargetControlID özelliklerini boş geçmememiz gerekiyor. Bunun için kodun en başında bir DIV yaratarak CSS özelliği ile görünmez yaptım. Söz konusu DIV’i her iki AnimationExtender’a da TargetControlID olarak verdim. Böylece bir sorun yaşamayacağız. AnimationExtender kontrollerini JavaScript ile yakalayabilmek için her iki kontrole de farklı BehaviorID isimleri atadım. Kontrollerin BehaviorID özelliklerindeki isimlerden animasyonları yakalayacağız. Gelelim bizim esas animasyonları oynatacak olan JavaScript kodlarımızın bulunduğu düğmelere. Formun en altında toplam dört adet HTML düğmesi bulunuyor. Bu düğmelerin OnClick özelliklerine ikişer satırlık JavaScript kodları koydum. Bu kodları tek tek incelemekte fayda var.

var onclkBehavior = $find('Yoket').get_OnClickBehavior().get_animation();
onclkBehavior.play();

İlk satırda sayfadaki animasyonumu yakalayacak olan komut bulunuyor. get_OnClickBehavior(). Komutu ile söz konusu animasyona ait OnClick durumuna atanmış animasyonu alabiliyoruz. Aldığımız animasyonu onclkBehavior adındaki değişkene aktardıktan sonra .play() komutu ile oynatıyoruz.

var onclkBehavior = $find('Goster').get_OnClickBehavior().get_animation();
onclkBehavior.pause();

.pause() komutu söz konusu animasyonu durduruyor. Fakat sonrasında tekrar play() komutu ile bu animasyonu devam ettirmek mümkün.

Umarım hepiniz için bu ilk yazgelistir.com makalem faydalı bir kaynak olmuştur. Silverlight ile web dünyasına giriş yapmadan önce bir süre için DHTML animasyon ihtiyaçlarınızı AnimationExtender kontrolü ile sağlayabileceğinizden eminim.

Herkese kolay gelsin…

Thursday, August 16, 2007 7:19:04 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   AJAX | ASP.NET | JavaScript  | 
 Tuesday, August 14, 2007

Web uygulamaları adı üzerinde birer "uygulama" yani "application" dır. IIS üzerinde her bir web uygulaması için bir "Application" tanımlanır ve ancak bu şekilde uygulamalar tek başlarına çalışabilirler. Bazı durumlarda bu uygulamaları resetlemek gerekebilir. Örneğin sitenin ziyaretçilerinin geride bıraktıkları Session değişkenlerinin TimeOut süreleri beklenmeden yokedilmesi için veya .NET Garbage Collector'ın bazı hatalar sonucu toplamalayı unuttuğu bellekteki objeleri yok etmek için hızlı bir restart en uygun çözümdür.

Bir web uygulamasına reset atmanın en bilinen yolu Web.Config dosyasında ufak bir değişiklik yapmak veya adını değiştirip tekrar geri almaktır. ASP.NET, söz konusu uygulama ile hayati ilişkisi olan dosyaları takip eder ve eğer herhangi bir değişiklik yapılmış ise uygulamayı yani application'ı baştan başlatır. Peki ya biz bunu programatik olarak yapmak istersek? Yani sayfamızda bir düğme bulunsun ve Web.Config vs ile uğraşmadan, FTP'den giriş yapmadan uygulamaya bu düğme aracılığı ile istediğimiz zaman restart atabilelim.

System.Web.HttpRuntime.UnloadAppDomain()

İşte sihirli metod yukarıda duruyor. Bu metodu klasik bir ASP.NET düğmesine koyduğunuzda uygulamanız düğmeye basıldığında restart atacaktır. Aslında komutun yaptığı şey var olan uygulamayı hafızadan kaldırmak. Peki nasıl tekrar yüklenecek? Unutmayın ki tüm ASP.NET web siteleri ve uygulamaları söz konusu web sitesinin herhangi bir sayfasına ilk gelen istekte hafızaya yüklenir. Yani uygulamamızın tekrar hafızaya yüklenmesi için tek yapmamız gereken herhangi bir sayfaya çağrı atmak. Aslında bu şartı da bir ASP.NET düğmesi kullanarak yerine getirmiş oluyoruz. Çünkü düğmeye tıklandığında uygulama hafızadan silinecek fakat sonrasında sayfa tekrar yüklenmek zorunda. Düğmeye bastıktan ve uygulama hafızadan silindikten sonra sayfa tekrar yüklenirken uygulamamız da baştan başlayarak hafızaya yüklenecek. Böylece uygulamamıza restart atmış oluyoruz.

Bu metodun tek güzel yanı işimizi programatik olarak yapabiliyor olmamız değil. Bu metod kullanılarak yapılan restart işlemleri Global.asax içerisinde yer alan application end gibi olayları da çalıştırıyor. Böylece tüm restart işlemlerini loglamanız mümkün. Kabaca değerlendirmek gerekirse en .NET dostu restart metodunu yazmış bulunuyoruz.

Tuesday, August 14, 2007 8:46:31 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   ASP.NET  | 
 Monday, August 13, 2007

Yeni Office 2007 ile beraber gelen yeni dosya formatlarına da alışmaya başladık. Bu yeni dosya formatlarından biri de Access 2007 ile beraber gelen accdb uzantılı veritabanı dosyaları. Yeni bir veritabanından bahsediyorsak tabi ki bu veritabanı için yeni bir de "Connection String"imizin olması gerekiyor. Eğer projelerinizde siz de benim gibi artık Access 2007 veritabanlarına geçiş yapmayı düşünüyorsanız aşağıdaki yeni "Connection String"imizi kullanabilirsiniz.

Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\veritabani1.accdb;Persist Security Info=False;

Ek olarak, eğer veritabanı bağlantınızda şifre kullanacaksanız, şifre bilgisini de aşağıdaki şekilde ekleyebilirsiniz.

Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\veritabani1.accdb;Jet OLEDB:Database Password=MyDbPassword;

Tüm bunları yapmış olmak aslında yeterli değil. Eğer Access 2007 veritabanını Office 2007 yüklü olmayan bir bilgisayarda kullanmak isterseniz veya bir web sitesinde Access 2007 veritabanı kullanacak olursanız dikkat etmeniz gereken bir nokta var. Yukarıda yazmış olduğumuz "Connection String" ile hedef bilgisayarda, yani web sitenizin çalışacağı sunucuda veya programınızın çalışacağı bilgisayarda Access 2007 veritabanına ulaşabilmeniz için 2007 Office System Driver: Data Connectivity Components'ın sistemde yüklü olması gerekiyor. Özellikle Access 2007 veritabanı kullanacağınız web siteleri için satın aldığınız barındırma hizmetleri ile ilgili sorun yaşamanız olası. Bu durumda "Hosting" şirketinizi arayarak durumdan haberdar etmeniz ve sunucuya gerekli araçları yüklemelerini sağlamanız gerekiyor. Aksi halde hiçbir şekilde Access 2007 veritabanınıza ulaşma şansınız olmayacaktır.

Access 2003 - 2007 upgrade sürecinde hepinize iyi şanslar ;)

Monday, August 13, 2007 8:24:05 PM (GTB Standard Time, UTC+02:00)  #    Comments [2]   ASP.NET | Access 2007  | 
 Friday, August 03, 2007

ASP.NET 3.5 sürümünün giderek yaklaştığı şu günlerde yavaş yavaş Beta Hosting hizmetleri de kendini göstermeye başladı. Crsytal Tech hosting şirketi ASP.NET 3.5 Beta Hosting'ini geçenlerde duyurdu. Özellikle ASP.NET 3.5 ile beraber LinQ çalışmaları yapanların gerçek bir sunucu üzerinde denemeler yapmalarını sağlayan hizmeti burdan duyurmak istedim. Tek sorun şu an için ASP.NET 3.5 sürümünü IIS 6.0 ve Windows Server 2003 üzerinde çalıştırıyor olmaları. Umarız yakın zamanda Server 2008 ve IIS7.0 tabanlı Beta Hosting'lerde karşımıza çıkar.

Beta hosting ile ilgili hizmet sayfasına buradan ulaşabilirsiniz.

Friday, August 03, 2007 9:27:33 AM (GTB Standard Time, UTC+02:00)  #    Comments [0]   AJAX | ASP.NET  | 
 Tuesday, July 24, 2007

ASP.NET AJAX kitabım raflarda yerini alalı iki ay oluyor. Yaz dönemine rast gelmesine rağmen ilginç bir satış grafiği oluşturan kitabımın bugün hepsiburada.com’da programlama kitapları arasında en çok satılanlar listesinde birinci sıraya yerleştiğini gördüm.

Önümüzdeki dönemde AJAX bilgisine sahip olmak programcılarda sıklıkla aranan bir özellik olacak. Bu çerçevede kitabımın birinci sıraya yerleşmesinin yanı sıra tüm programcılar arasında AJAX konusunun da ilgi sırasında en ön saflarda olması gerçekten sevindirici.

Tuesday, July 24, 2007 3:02:49 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   AJAX | ASP.NET | Genel  | 
 Monday, July 09, 2007

Biraz gecikmiş de olsam sizlere AJAX Control Toolkit’in yeni sürümünü duyurmak ve bazı yeniliklerden bahsetmek istiyorum. ASP.NET AJAX kitabımda detaylıca incelediğim AJAX Control Toolkit bağımsız programcılar tarafından ASP.NET AJAX ile kullanılabilir ASP.NET kontrollerinin toplandığı, ücretsiz ve açık kaynak kodlu bir kütüphane. Kütüphanenin 18 Haziran’da yayınlanmış en son sürümü olan 1.0.10606.0 paketini aşağıdaki adresten bilgisayarınıza indirebilirsiniz.

http://www.codeplex.com/AtlasControlToolkit/Release/ProjectReleases.aspx?ReleaseId=4923

Gelelim yeni sürümlerle beraber bizleri bekleyen yeniliklere. Benim ilk dikkatimi çeken kontrol kütüphanesi içerisindeki her kontrolün artık çok daha anlamlı ikonlara sahip olması oldu. Görsel açıdan güzel bir gelişme olmuş. Ayrıca bazı kontrollerle ilgili Visual Studio’nun Designer modunda bazı kolaylıklar sağlanmış. Bunlardan ilki Tab kontrollerinin tablarının designer arayüzünde de artık düzenlenebiliyor olması. Tab kontrolleri haricinde neredeyse tüm kontrollerle designer arayüzünde kullanılmak üzere işlevler eklenmiş. PageMethodları veri kaynağı olarak kullanabileceğimiz kontrollerin artık designer arayüzünde “Add Dynamic Populate Page Method” adında bir komutu var. Bu komuta direk tıklayarak uygun fonksiyonun doğru parametreler ile Code Behind sayfasına otomatik eklenmesi sağlanabiliyor. Bence bu işlev zaman kazandıran en önemli gelişmelerden biri olmuş.

En sevdiğim yeni özellik ise ToolkitScriptManager ile beraber geliyor. ASP.NET AJAX Extension kullanırken her sayfada bir ScriptManager olması şart. ScriptManager sayfaya ASP.NET AJAX kütüphanesi ile ilgili JavaScript dosyalarını, kodlarını eklerken AJAX Control Toolkit’in genel JavaScript kütüphanesi hariç sayfamızda kullandığımız her bir AJAX Control Toolkit kontrolü ile ilgili JavaScript kodları da ayrıca sayfaya linkleniyordu. Birden fazla AJAX Control Toolkit kontrolü kullandığımız bir sayfanın kullanıcıya giden kaynak kodunu incelediğimizde çok sayıda harici JavaScript kaynaklarının sayfaya linklendiğini görüyorduk. Bu durum artık ToolkitScriptManager ile değişiyor. Yeni sürümü ile beraber yarattığınız her AJAX Control Toolkit web sitesinde yaratılan her yeni web sayfasına ToolkitScriptManager otomatik olarak ekleniyor ve eski ScriptManager’ın tüm işlevlerini miras almak ile beraber sayfadaki farklı JavaScript kaynaklarını tek bir dosya halinde kullanıcıya gönderiyor. Sonuç olarak sayfanın yüklenmesi aşamasında 10, 15 adet harici JavaScript kaynağından yükleme yapmak yerine daha derli toplu yüklemeler yapılıyor. Toplam yükleme boyutu azalmıyor fakat yükleme sayısı azaldığı için performans artışı sağlanıyor.

Genel olarak yapılan yenilikler gerçekten başarılı, umarız kısa zamanda kütüphane yeni kontroller de eklenerek daha da genişler. Bir sonraki AJAX Control Toolkit sürümünde görüşmek üzere :)

Monday, July 09, 2007 1:34:36 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   AJAX | ASP.NET  | 
 Wednesday, June 06, 2007

Son 2.5 yıldır üzerinde yoğun olarak çalıştığım AJAX'ı konu alan kitabım toplam 1.5 yıllık bir çalışma sonucunda bugün elime ulaştı. ASP.NET platformunda her ölçekte AJAX uygulamaları geliştirmek isteyenlerin tüm sorularını cevaplayabilmek için sürekli genişlettiğim kitap sonunda 516 sayfa olarak raflarda yerini aldı. Sanırım hem Türkiye'de hem de uluslararası platformda sadece AJAX konulu en kapsamlı kitap olma özelliğine sahip.

AJAX'ın derinliklerine dalıp hiçbir ek araç, kütüphane kullanmadan sunucu programlama teknolojilerinden bağımsız olarak AJAX'ı inceledikten sonra kitap içerisinde ASP.NET 1.1 ve 2.0 ile birlikte kullanılabilecek olası tüm AJAX kütüphanelerini ve teknikleri ele aldım. Bunlar arasında tabi ki en önemlisi Microsoft'a ait ASP.NET AJAX Extensions ürünü. Ek olarak farklı tekniklerin artı ve eksi yönlerinin karşılaştırıldığı proje örnekleri, gerçek hayat projelerinde şu ana kadar karşılaştığım ve uzun çalışmalar ile çözümlerini bulduğum sorun-çözüm analizleri ve son olarak da AJAX sunucu kontrolleri yaratmanın yollarına kadar uzun bir serüven var kitap içerisinde. Tüm örnekleri ve canlı uygulamaları da bir CD içerisinde derledik.

Umarım şu an için en kapsamlı Türkçe kaynak olarak kitabım yararlı bir eser olur.

Online sipariş için aşağıdaki adresleri kullanabilirsiniz.

http://www.kitapyum.com/

http://www.hepsiburada.com/

Kitabın web sitesi için buraya tıklayabilirsiniz.

Wednesday, June 06, 2007 7:49:45 PM (GTB Standard Time, UTC+02:00)  #    Comments [4]   AJAX | ASP.NET  | 
 Tuesday, May 01, 2007

Microsoft ASP.NET Resmi Sitesi'nde son makalem yayınlanalı daha üç gün geçmişken bir makalem daha yayınlandı. Tekrar bir ekran görüntüsü alarak arşivdeki hatıralar arasındaki yerine yerleştiriyorum. İlk üç makaleyi tamamladığımıza göre bundan sonrakiler için tek tek blog sitemde mesaj atacağımı da sanmıyorum :) Tabi ne yapacağım belli de olmaz :)

Tuesday, May 01, 2007 3:00:33 PM (GTB Standard Time, UTC+02:00)  #    Comments [0]   Genel | ASP.NET  | 
Copyright © 2010 Daron Yöndem. Tüm hakları saklıdır.