Makale Özeti

ASP.NET AJAX Extension 1.0 ile beraber gelen UpdatePanel kontrolüne ait sunucu taraflı Update metodu ile aynı işlemi yapan istemci taraflı bir JavaScript metodu yaratacağımız bu makalede tüm bu işlemleri bizim için yapabilecek bir de UpdatePanel JavaScript Extender kontrolü oluşturacağız.

Makale

ASP.NET AJAX Extension 1.0 ile beraber gelen UpdatePanel sunucu kontrolü biz yazılım geliştiricilerin hayatını ciddi şekilde kolaylaştırdı. Fakat maalesef UpdatePanel'in çok büyük bir eksiği. UpdatePanel'in UpdateMode özelliği Conditinal olarak düzenlendiğinde sunucu tarafında UpdatePanel1.Update gibi bir kod ile herhangi bir UpdatePanel nesnesinin içeriğinin asenkron olarak yenilenmesini sağlayabilirken bu işlemin istemci tarafından tetiklenebilmesini sağlayacak hazır bir çözüm yok.

Makalemiz boyunca yukarıda bahsi geçen sorunu çözmek için nasıl bir teknik kullanabileceğimize ve işimizi kolaylaştırmak için bu teknikleri bizim için otomatik olarak kullanabilecek bir Control Toolkit Extender kontrolünü nasıl programlayabileceğimize değineceğiz.

UpdatePanel JavaScript Extender

Sorunumuzun çözüm tekniklerine girmeden önce gelin makalemiz boyunca hazırlayacağımız UpdatePanel JavaScript Extender kontrolünün kullanım şekline bakalım.

      <JS:UpdatePanelJavaScriptExtender TargetControlID="UpdatePanel1"
                                        ClientCommand="Guncelle"
                                        ID="UpdatePanelJavaScriptExtender1"
                                        runat="server">
      </JS:UpdatePanelJavaScriptExtender>

Yukarıdaki kodumuz içerisinde UpdatePanelJavaScriptExtender için tanımladığımız iki özellik yer alıyor. Bunlardan ilki TargetControlID özelliği. Sayfamızda JavaScript komutları ile Update etmek istediğimiz UpdatePanel'in ID bilgisini Extender kontrolümüzün TargetControlID özelliğine aktarmak durumundayız. İkinci aşamada ise karşımıza ClientCommand özelliği çıkıyor. JavaScript ile istemci tarafında UpdatePanel'i Update ederken kullanmak isteyeceğimiz JavaScript fonksiyonunun adını buraya parametre olarak vermemiz gerekiyor. Bizim örneğimizde UpdatePanel1'i yenilemek için sayfada Guncelle JavaScript fonksiyonunu kullanacağız. Kullanacağımız örneğe ait tam sayfa HTML kodu aşağıdaki şekilde;

<%@ Page Language="VB" AutoEventWireup="true" CodeFile="Default.aspx.vb" Inherits="_Default" %>
<%@ Register Assembly="UpdatePanelJavaScript" Namespace="UpdatePanelJavaScript.UpdatePanelJavaScript"
  TagPrefix="JS" %>
<!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:Label ID="Label1" runat="server" Text="Label"></asp:Label>
        </ContentTemplate>
      </asp:UpdatePanel>
      <JS:UpdatePanelJavaScriptExtender TargetControlID="UpdatePanel1"
                                        ClientCommand="Guncelle"
                                        ID="UpdatePanelJavaScriptExtender1"
                                        runat="server">
      </JS:UpdatePanelJavaScriptExtender>
      <input id="Button1" type="button" value="button" onclick="Guncelle(1);" />
    </div>
  </form>
</body>
</html>

Gördüğünüz gibi sayfamızda, içerisinde bir Label bulunan bir UpdatePanel ve bir adet HTML Button bulunuyor. HTML buttonun OnClick özelliğine Guncelle JavaScript fonksiyonumuz yerleştirilmiş. Burada dikkat etmemiz gereken bir diğer nokta da aslında Guncelle fonksiyonuna bir de parametre vermiş olmamış. UpdatePanelJavaScriptExtender kontrolümüz sadece UpdatePanel'i yenilemek ile kalmayacak sunucu tarafına parametre de aktarabiliyor olacak. Şimdi de sunucu tarafında yazdığımız koda bakalım.

Partial Class _Default
    Inherits System.Web.UI.Page
 
    Protected Sub UpdatePanelJavaScriptExtender1_Update _
                            (ByVal Sender As Object, _
                            ByVal E As System.EventArgs, _
                            ByVal parameter As String) _
                            Handles UpdatePanelJavaScriptExtender1.Update
        Label1.Text = parameter
    End Sub
End Class

UpdatePanelJavaScriptExtender kontrolümüzün Update adında bir durumu (event) var. Bu durum biz istemci tarafında Guncelle fonksiyonumuzu çalıştırdığımızda gerçekleşiyor olacak. Update durumunun getirdiği parametrelere bakarsak arada parametre adında bir String değer bulunduğunu görebiliriz. Bu değer bizim istemci tarafında Guncelle fonksiyonuna verdiğimiz 1 değeri olacak. Kodumuz içerisinde gelen bu değeri Label1 içerisine yazıyoruz. Siz projelerinizde farklı işlemler yapabilir veya farklı dinamik parametreler, hatta JSON olarak serialize ederek her tür veriyi, objeyi gönderebilirsiniz.

Peki Nasıl?

Nasıl oluyor da normal şartlarda UpdatePanel'in JavaScript ile Update edilme özelliği yokken bizim UpdatePanelJavaScript Extender kontrolümüz bunu yapıyor? Bir düşünelim; biz UpdatePanel içerisine bir TextBox koysak ve AutoPostBack özelliğini True yapsak bu kontrol içerisine birşey yazıldığında UpdatePanel'in yenilenmesini ve TextBox içerisinde yazının da sunucu tarafına gönderilmesini sağlar mı? Kesinlikle. Bu durumda gidelim UpdatePanel içerisine ekranda görünmeyen, gizli bir TextBox ekleyelim. İstediğimiz zaman TextBox içine JavaScript ile bir metin yazıp (parametremizi) sonra da TextBox'ın içeriği değiştirildiğinde çalışan onchanged durumundaki JavaScript kodunu çalıştıralım. Sonra da gidip sunucu tarafında TextBox'ın içindeki veriyi alıp işlemlerimizi yapalım. İşte UpdatePanelJavaScript Extender kontrolümüzün yaptığı da aslında bu. Ama bunların hepsini bizden gizli olarak, bizi hiç uğraştırmadan yapıyor.

İş Başına

Kontrolümüzü kullanmak güzeldi, kolaydı ama bizim bu kontrolün nasıl hazırlandığını ve hazırlanma aşamasında karşılaşılabilecek olası sorunları da incelememiz şart. Yeni bir ASP.NET AJAX Control Project açarak kodlarımızı yazmaya başlayabiliriz. İlk olarak gelin kontrolümüz için yazdığımız JavaScript kodumuza yani kontrol projemizdeki JavaScript dosyasının içeriğine bakalım.

/**
 * @author Daron Yöndem
 * @web http://daron.yondem.com
 */
Type.registerNamespace('UpdatePanelJavaScript');
UpdatePanelJavaScript.UpdatePanelJavaScriptBehavior = function(element) {
    UpdatePanelJavaScript.UpdatePanelJavaScriptBehavior.initializeBase(this, [element]);
    //ClientCommand özelliğimiz için iç bir değişken tanımladık.
    this._ClientCommandValue = null;
}
UpdatePanelJavaScript.UpdatePanelJavaScriptBehavior.prototype = {
    initialize : function() {
        UpdatePanelJavaScript.UpdatePanelJavaScriptBehavior.callBaseMethod(this, 'initialize');
    },
    dispose : function() {
        UpdatePanelJavaScript.UpdatePanelJavaScriptBehavior.callBaseMethod(this, 'dispose');
    },
    //ClientCommand özelliği için Set ve Get JavaScript metodlarını tanımladık.
     get_ClientCommand : function() {
        return this._ClientCommandValue;
    },
    set_ClientCommand : function(value) {
        this._ClientCommandValue = value;
    }
}
UpdatePanelJavaScript.UpdatePanelJavaScriptBehavior.registerClass('UpdatePanelJavaScript.UpdatePanelJavaScriptBehavior', AjaxControlToolkit.BehaviorBase);
//JavaScript fonksiyonu çalıştırıldığında burası çalışacak.
//Buradaki Update JavaScript metodu parametre olarak
//parametremizi ve gizli TextBox'ın ID sini alıyor.
UpdatePanelJavaScript.Update = function(HiddenBoxID, parameter) {
  //Gizli TextBox kontrolünü buluyoruz.
       var HiddenBox =$get(HiddenBoxID);
  //Parametre yoksa rastgele bir sayı döndürelim.
       if (typeof(parameter)=="undefined")
       {
             parameter = "RANDOMPARAM" + Math.random();
       };
       //Eğer bir önceki aktarılan parametre ile
       //şimdiki aynı ise parametremizin sonuna rastgele bir sayı ekliyoruz.
       if (HiddenBox.value == parameter)
       {
             parameter = parameter + "RANDOMPARAM" + Math.random();
       };
       //Parametremizi TextBox'ın içine koyuyoruz.
       HiddenBox.value = parameter;
       //TextBox'ın onchange özelliğinde JavaScript kodunu alıyoruz ve temizliyoruz.
       var MyCommand = String(HiddenBox.onchange).replace('function anonymous()\n{\n','');
       MyCommand = MyCommand.replace('\n}','');
       MyCommand = MyCommand.replace('function onchange(event) {\njavascript:\n','');
       //onchange durumundaki JavaScript kodunu çalıştırıyoruz.
       eval(MyCommand);
};

Olabildiğince satır arası yorumlar ile kodumuzda neler yaptığımıza anlatmaya çalıştım. Özellikle birkaç önemli noktaya değinmekte fayda var. JavaScript kodlarımız içerisinde UpdatePanelJavaScript Extender kontrolümüz için Update adında bir fonksiyon tanımlıyoruz. Bu fonksiyon içerisinde direk elimizdeki TextBox kontrolüne ulaşamıyoruz. Eğer sayfamızda birden çok UpdatePanel JavaScript Extender kontrolü varsa yukarıdaki JavaScript kodu sayfaya sadece bir defa ekleniyor. Yani yukarıdaki kodun değişkenlere bağımlı olması şart. Bu durumda biz de TextBox kontrolümüzün adını Update JavaScript fonksiyonumuza parametre olarak vermeye karar verdik. Söz konusu Update fonksiyonunu kullanacak başka bir JavaScript fonksiyonu oluşturmayı ve TextBox kontrolümüzü UpdatePanel'in içerisine eklemeyi sunucu tarafındaki kodumuzla bir sonraki aşamada yapıyor olacağız.

JavaScript kodumuz ayrıca kendisine verilen parametrenin bir önceki parametre ile yani TextBox kontrolünün içeriği ile aynı olup olmadığını da kontrol ediyor. Eğer aynı ise sonuna rastgele bir sayı ekliyor, bu rastgele sayıyı da sunucu tarafında siliyor olacağız. Neden böyle birşey yapmaya ihtiyaç duyduğumuza gelince; maalesef TextBox'ın içeriği değişmez ise onchange durumunu çalıştıramıyoruz. O nedenle aynı parametre gönderildiğinde de bir Update sağlayabilmek için sonuna rastgele bir sayı ekleyerek değiştirmiş gibi davranmamız gerekiyor. Son olarak kodumuzla gizli TextBox'ımızın onchange JavaScript kodunu alarak ve temizleyerek kendimiz çalıştırıyoruz.

Şimdi geçelim sunucu tarafındaki Extender kodumuza.

Imports System
Imports System.ComponentModel
Imports System.Web.UI
Imports System.Web.UI.WebControls
Imports AjaxControlToolkit
 
#Region "Assembly Resource Attribute"
<Assembly: System.Web.UI.WebResource("UpdatePanelJavaScript.UpdatePanelJavaScriptBehavior.js", "text/javascript")>
#End Region
 
Namespace UpdatePanelJavaScript
 
    <Description("Creates JavaScript interface simulating UpdatePanel.Update()")> _
    <Designer(GetType(UpdatePanelJavaScriptDesigner))> _
    <ClientScriptResource("UpdatePanelJavaScript.UpdatePanelJavaScriptBehavior", "UpdatePanelJavaScript.UpdatePanelJavaScriptBehavior.js")> _
    <TargetControlType(GetType(UpdatePanel))> _
    Public Class UpdatePanelJavaScriptExtender
        Inherits ExtenderControlBase
 
        'Durumları ile beraber UpdatePanel içerisine ekleyeceğimiz
        'TextBox'ı yaratıyoruz.
        WithEvents MyTextBox As New System.Web.UI.WebControls.TextBox
        'Extender kontrolümüze ait Update durumunu tanımlıyoruz.
        Public Event Update(ByVal Sender As Object, ByVal E As EventArgs, ByVal parameter As String)
 
        'ClientCommand özelliğine ait Get ve Set metodları.
        <ExtenderControlProperty()> _
        <DefaultValue("Update")> _
        Public Property ClientCommand() As String
            Get
                Return GetPropertyValue("ClientCommand", "")
            End Get
            Set(ByVal value As String)
                SetPropertyValue("ClientCommand", value)
            End Set
        End Property
 
        'Extender Render edildiğinde ilk çalıştırılan kodlar.
        Private Sub UpdatePanelJavaScriptExtender_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Init
            'TextBox AutoPostBack olsun.
            MyTextBox.AutoPostBack = True
            'TextBox sayfada görünmez olsun
            MyTextBox.Style.Add("visibility", "hidden")
            MyTextBox.Style.Add("display", "none")
            'Hedef UpdatePanel'i bulalım.
            Dim TargetPanel As System.Web.UI.UpdatePanel = Me.TargetControl
            'TextBox'ı ekleyelim.
            TargetPanel.ContentTemplateContainer.Controls.Add(MyTextBox)
            'OnClientCommand özelliğine verilen JavaScript fonksiyonunu yaratalım.
            Dim script As New System.Web.UI.HtmlControls.HtmlGenericControl("script")
            script.Attributes.Add("type", "text/javascript")
            script.Attributes.Add("language", "javascript")
            Dim builder As New System.Text.StringBuilder
            builder.Append("function ")
            builder.Append(Me.ClientCommand)
            builder.AppendLine("(parameter) {")
            builder.Append("UpdatePanelJavaScript.Update('")
            builder.Append(MyTextBox.ClientID)
            builder.AppendLine("', parameter);")
            builder.AppendLine("};")
            script.InnerHtml = builder.ToString
            'JavaScript fonksiyonumuzu Extender'a ekleyelim.
            Me.Controls.Add(script)
        End Sub
 
        'UpdatePanel içerisine eklediğimiz TextBox'a ait TextChanged durumunu kontrol ediyoruz..

        Sub Control_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyTextBox.TextChanged
            Dim SenderControl As System.Web.UI.WebControls.TextBox = sender
         'RANDOMPARAM'ın varlığını kontrol ederek RaiseEvent ile kendi Update durumumuzu çalıştırıyoruz.
            If SenderControl.Text.IndexOf("RANDOMPARAM") <> -1 Then
                If SenderControl.Text.IndexOf("RANDOMPARAM") = 0 Then
                    RaiseEvent Update(Me, e, "")
                Else
                    RaiseEvent Update(Me, e, SenderControl.Text.Substring(0, SenderControl.Text.IndexOf("RANDOMPARAM")))
                End If
            Else
                RaiseEvent Update(Me, e, SenderControl.Text)
            End If
        End Sub
    End Class
 
End Namespace

Yukarıdaki kod içerisinde önemli noktalardan birincisi UpdatePanel içerisine ekleyeceğimiz TextBox kontrolümüzü WithEvents ile yaratıyor olmak. Böylece söz konusu TextBox'a ait TextChanged durumunu Extender içerisinde kontrol edebileceğiz ve kendi Extender'ımızın Update durumunu çalıştırabileceğiz. İkincisi ise kendi Update durumumuzu (event) tanımlıyor olmamız.

Extender ilk olarak web sayfasına eklendiğinde ve sayfa ilk olarak istemcide çalıştırıldığında Extender'a ait Inıt durumu çalışacaktır. Biz Inıt durumunda kendi TextBox'ımızı CSS özellikleri ile görünmez yaparak AutoPostBack özelliğini de ayarladıktan sonra hedef UpdatePanel içerisine ekliyoruz. Sonraki adımda da Extender kontrolüne verilen ClientCommand özelliğine göre Extender JavaScript kodlarımızdaki Update metodunu kullanacak sayfa içi JavaScript kodunu yaratmalıyız. Bunun için de bir StringBuilder ve HTMLGenericControl kullandık. Son olarak JavaScript kodumuzu Extender'a ekledik.

Gelelim TextBox'a ait TextChanged durumuna. TextBox içerisine JavaScript ile yerleştirilen metni kontrol etmemiz gerekiyor. Eğer RANDOMPARAM var ise silmemiz gerek. Bu işlemleri de tamamladıktan sonra RaiseEvent ile kendi Update durumumuzu çalıştırıyoruz. Böylece Extender'ı kullananlar Update durumu üzerinde direk parametreyi alabilecekler.

Altenatif Teknikler

Yukarıdaki kontrolü kullanmak veya kontrolün işleyiş mantığına uymanın haricinde farklı teknikler de söz konusu. Örneğin aşağıdaki kodu kullanarak herhangi bir HTML objesinden direk başka bir .NET objesinin PostBack JavaScript kodunu alarak çalıştırabilirsiniz.

<input id="Button1" type="button" value="button" onclick="<%= ClientScript.GetPostBackEventReference(new PostBackOptions(TextBox1, "")) %>" />

Tabi yukarıdaki kod içerisinde TextBox'ın içeriğine bir parametre koyma şansınız olmayacaktır. Farklı bir şekilde düzeneyerek böyle bir özellik eklemek de mümkün. Extender kontrolümüzün güzel yanı bize tüm bu işlevleri kolayca kullanabileceğimiz istediğimiz isimde JavaScript fonksiyonları ve sunucu taraflı durum kontrolü sağlaması.

Hepinize kolay gelsin.

Daron Yöndem
MCPD, MCITP, MCTS, MCSD, MCAD
MCDBA, MCP, ACP, ICSD, IEL'03
http://daron.yondem.com

UpdatePanel JavaScript Extender kontrolüne ait kaynak kodunu buradan indirebilirsiniz.