"bir yazılımcının not defteri.."

11 Eylül 2010 Cumartesi

Sys.WebForms.PageRequestManager

Sayfamıza bir UpdatePanel eklediğimizde otomatikman bir alt render motoru eklemiş oluruz. Yani o updatepane içeriği sayfanın geri kalanından bağımsız olarak sunucuya gidip gelmeye başlıyordu.  Bunun ingilizcedeki karşılığı “partial-page rendering”.  Başka bir deyişle aynı sayfa içinde çalışan, ama birbirinden bağımsız olarak sunucuya gidip gelen sayfa bölümleri de diyebiliriz. Ve sayfamıza ne kadar UpdatePanel eklersek o kadar çok birbirinden bağımsız render motoru  (partial-page rendering) eklemiş oluruz.

Peki sayfa içindeki render motorlarını kim organize ediyor?  Bunu yapan  Ms Ajax Libary içinde bulunan ve ScriptManager in bir parçası olan PageRequestManager sınıfıdır. 


 PageRequestManager ile neler yapabilriz?

* Partial-page rendering için özel hata mesajları yönetimi oluşturabiliriz. Yani hata mesajlarını daha detaylı olarak denetleyip kendi belirlediğimiz javascript fonksyonlarına bağlayabiliriz (error message handling).

* Çoklu asenkron post-back i istemci tarafında derinlemesine kontrol edebiliriz. Veri gidiş gelişi başladı, bitti, yükleniyor gibi durumları ve olayları kontrol edebilir ve oluşan tüm Dom Event lerini kendi belirlediğimiz özel javascript fonksyonlarına bağlayabiliriz.
* Sayfa durumunda sürekli haberdar olabiliriz; yani handi updatepanelin postback i başlattığı, hangi elementin olayı başlattığı gibi..
* Kullanıcıya kalmadan, kendimiz tüm parametrelerini istediğimiz gibi ayarlayarak bir asenkron postback i tetikleyebiliriz.



PageRequestManager sınıfı ScriptManager kütüphanesinin bir parçası olduğu için öncelikle sayfamıza bir adet ScriptManager eklemeliyiz. Ve de ScriptManager in EnablePartialRendering özelliği mutlaka true değerinde ayarlanmış olmalı. Bildiğim kadarı ile Ms Ajax ın yeni versiyonlarında bu zaten default olarak true olarak geliyor ve extradan belirtmemize gerek yok ama yinede kulağımıza küpe olsun diyelim.

Bilmemiz gereken şeylerden biri de sayfamızda sadece bir tane PageRequestManager nesnesi olur. Yapılan tüm işlemler bu tek nesne üzerinden devam eden bir olaylar döngüsüdür.  Aslına bakarsanız hiçbir zaman PageRequestManager  sınıfından biz nesne üretemiyoruz.  Bu nesnenin üretilişi ve sonlandırılışı ScriptManager in tekelinde. Sayfada herhangi bir partial-page rendering durumu başladığında script manager in kendisi PageRequestManager tipinde bir nesne üretiyor. Biz sadece client tarafında yaratılmış olan halihazırdaki bu nesneyi çağırarak kullanıyoruz. Kendimiz yeni bir tane yaratamıyoruz.

Dilerseniz öncelikle PageReuestManager in olaylarını (events) inceleyelim; ki bu olaylara istediğimiz javascript kodu bağlayabiliriz (handle edebiliriz). Bu olaylar ile ilgili olarak anlamamız önemli bir nokta: bu olayların her bir asenkron postback için tekrar tekrar oluştuğudur. Sayfamızda bir den fazla updatepanel olabilir. Ama PageRequestManager sınıfına ait tek bir nesne sayfanın yaşam döngüsü içerisinde sürekli bu olaylara maruz kalır. Ve biz hangi controlün olayı tetiklediğini veya hangi updatePanel in postback durumuna geçriğini bilmek isteyebiliriz. Bunu da javascript fonksyonlarımıza atadığımız argümanlar ile elde edeceğiz.

Bu konuyu örnekler ile ele alacağız. Şimdi PageRequestManager in postback döngüsü içinde tetiklenen olaylarına bir gözatalım:


initializeRequest
 
Postback isteğinin hemen öncesinde tetiklenen olaydır. Gerekli objeler client tarafında yaratılır. Olay bilgisi InitializeRequestEventArgs objesi ile olayın fonksyonuna aktarılır. Böylece fonksyonun formal parametreleri ile asenkron postback i tetikleyen nesneyi elde edebiliriz.

beginRequest
initalizeRequest ten sonra çalışır. İstek  sunucuya gönderilmeden hemen önce tetiklenen olaydır. Objeler bir önceki olayda oluşturulmuştur (initalize edilmiştir). Olay bilgisi fonksyone BeginRequestEventArgs objesi ile aktarılır. Bu obje de yine postback e sebep olan sayfa elementini elde eder. (initalizeRequest te olduğu gibi.)

pageLoading

Son postback talebi için sunucu istemciye cevap gönderdiği anda ortaya çıkar. Yine client tarafında tetiklenir ve sunucunun cevabı henüz yeni gelmiştir. Ve henüz sayfamızda herhangi bir güncelleme yapılmamıştır. Bu olayın bilgisi fonksyonumuza PageLoadingEventArgs objesi ile aktarılır. Daha sonrada sayfamızda istediğimiz güncellemeleri yapabiliriz.

pageLoaded

Son postback in ardından sayfamızın ilgili alanları güncellendikten sonra(!) bu olay tetiklenir. Olay bilgisi PageLoadedEventArgs objesi ile fonksyona aktarılır. Bu hangi panellerin yaratıldığı yada güncellendiği bilgisidir. Senkron postback lerde  paneller sadece yaratılabilir. Ama asenkron postback lerde hem yaratılır hemde update edilebilir.

endRequest

Tüm postback istemi tamamlandığında tetiklenen olaydır. Olay bilgisi fonksyona EndRequestEventArgs objesi ile gönderilir. Bu obje varsa meydana gelen hataların bilgisini aktarır. Ayrıca response objesini de yaratır. Bu olayı işlemin bitiminin ardından kulanıcıya bildirim göstermek ya da hataları yönetmek için kullanabilriiz.



Bu olaylar ile ilgili bazı önmli noktaların altını çizmekt fayda var :

1- Sayfa ne kadar alt postback barındırırsa barındırsın bu olaylar her zaman son tetiklenen asenkton postback için geçerlidir.

2- Bir asenkron postback istemi iptal edilebilir (birazdan göreceğiz), ve iptal edilen postback in initalizeRequest olayı çalışır; durdurulma zamanına göre diğer olayları  çalışmayabilir.

3- Bir sayfa çağrıldığında yada browser penceresinden refresh edildiğinde PageRequestManager olaylarından sadece pageLoaded çalışır. Diğerleri çalışmaz. Eğer sayfada Sys.Application in add_load gibi handle edilmiş fonksyonları var ise onlar da pageLoaded dan sonra çalışır.

4- Bir asenkron postback oluştuğunda ise yukarıda listelenmiş olan olaylardan handle edilmiş olanları, belitrilen sıra ile çalışır (initalizeRequest,  beginRequest, pageLoading, pageLoaded, endRequest), handle edilmemiş olaylar bir fonksyona yönlendirmediğimiz için sayfayı etkilemez.

5- Eğer sayfa başında çalışması için  Sys.Application.add_load ile handle ettiğimiz bir javascript kodu var ise,  bir asenkron postback durumunda olay sırası initalizeRequest,  beginRequest, pageLoading, pageLoaded,  Sys.Application.add_load(handler_fonksyonu) , endRequest  şeklinde olur.


Sayfanın o anki postback ine ait olan PageRequestManager sınıfını elde elmek için :
Sys.WebForms.PageRequestManager sınıf adı çağrılır.
Bu sınıfın getInstance() yöntemi o anki postbackin sayfada oluşturulan nesesini bize verir.
şöyleki :
Sys.WebForms.PageRequestManager.getInstance();


Bu yazım şekli ile o anki porstback esnasında yürürlükte olan PageRequestManager nesnesini yakalıyoruz. Bu nesneyi ister bir değişkene atar ve o değişken adı üzerinden devam edebiliriz, veyahutta istersek bu yazım şekli üzerinden devam edebiliriz. 
Şöyle ki : 
var prm = Sys.WebForms.PageRequestManager.getInstance(); 

Daha da uzun ve açık haliyle şöyle de yapabilirdik :
var pMngr = Sys.WebForms.PageRequestManager(); var prm = pMngr.getInstance(); 

bu şekilde yazarak tüm yordamlara erişmek için değişken adının yanına nokta koyarak devam edebilir ve kodu kısaltabiliriz. Yukarıda basettiğimiz olayları handle etmek için yani bizim belirlediğimiz fonsyonlara bağlamak için olay adının başına "add_" ibaresi getirilerek komut yazılır şöyle ki :

Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(InitializeRequestHandler);
Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(BeginRequestHandler);
Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(PageLoadingHandler);
Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(PageLoadedHandler);
Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);


aynı şekilde bu yazım şeklini şöylede yapabilirdik :
var prm = Sys.WebForms.PageRequestManager.getInstance();


prm.add_initializeRequest(InitializeRequestHandler);
prm.add_beginRequest(BeginRequestHandler);
prm.add_pageLoading(PageLoadingHandler);
prm.add_pageLoaded(PageLoadedHandler);
prm.add_endRequest(EndRequestHandler); 



Bu olayları bağladığımız Javascript fonksyonlarına çağıran objenin adresinin ve bir de parametre bilgisi olmak üzere 2 formal parametre yollanır. Dolayısı ile bu iki formal parametreyi tanımladığımız fonksyonlarda tanımlıyoruz. Şöyleki :


function BeginRequestHandler(sender, args)  {                     
            var elem = args.get_postBackElement();                     
            alert(elem.value);                
}

postback e neden olan elemantin bilgisini javascript fonrksyonumuzda args formal parametresi ile yakaladık. Sonrada get_postBackElement() metodu ile bu elemenin adına ulaştık.


Hemen basit bir örnek ile pekiştirelim, ve neyi nereye yazacağımızı da netleştirelim,


<html>
<head runat="server"> 

    <script language=javascript>
        function ApplicationLoadHandler(sender, args) {
            alert("Application Load Handler");              
        }


        function initRequest(sender, args) {
            alert("Init Request Handler");            
        }


        function beginRequest(sender, args) {
            alert("Begin Request Handler");
        }


        function pageLoading(sender, args) {
            alert("Page Loading Handler");
        }


        function pageLoaded(sender, args) {
            alert("Page Loaded Handler");
        }


        function endRequest(sender, args) {
            alert("End Request Handler");
        }

    </script>

</head>

<body>
    <form id="form1" runat="server">
   
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>

    <script language=javascript>
        Sys.Application.add_load(ApplicationLoadHandler);         Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(initRequest);
        Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(beginRequest);
        Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(pageLoading);
        Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(pageLoaded);
        Sys.WebForms.PageRequestManager.getInstance().add_endRequest(endRequest);
    </script>    

    <asp:UpdatePanel ID="UpdatePanel1" runat="server">
        <ContentTemplate>
            <asp:Button ID="Button1" runat="server" Text="Button" />
        </ContentTemplate>
    </asp:UpdatePanel>

    </form>
</body>
</html>




Burada form tagından hemen sonra bir scriptmanager onunda hemen ardından tam 5 adet olayı handle ettik. Bunlardan 4 ü PageRequestManager in olayları. Yani bir asenkron postback durumunda çalışmak üzere handle edilmiş olaylar. Bir tane de  “Sys.Application.add_load(ApplicationLoadHandler);”  cümleciği ile sayfanın hemen açılışına çalışması için handle edilmiş bir olay var. Dilerseniz bu örneği bilgisayarınızda çalıştırıp alert mesajlarını takip ederek olayların çalışma sırasını takip edelim. Benim gözlemlerim o ki : 

Sayfa ilk yüklenirken (herhangi bir postbackden gelmeden önce), yahutta sayfa yenilenirken (refresh olurken) henüz herhangi bir postback söz konusu olmamasına rağmen 
PageRequestManager
in pageLoaded olayının çalıştığı. Ardından da Sys.Application.add_load()  ile handle edilmiş olayının çalıştığıdır. Kaldıki asenkron olmayan normal postbacklerde de sayfa tümüyle sunucuya gidip geldiği için bu olaylar da tıpkı ilk yükleme gibi davranacaktır; yani yine önce PageRequestManager in pageLoaded olayının ve ardından da Sys.Application.add_load()  olayı çalışacaktır.

Bir asenkron postback durumunda öncelikle PageRequestManager  in olaylarının çalışmaya başladığı ve ise sırası ile : initializeRequest,  beginRequest,  pageLoading,  pageLoaded, olaylarının tetiklendiği,, ve bunlardan sonra Sys.Application nesnesinin uygulama başında çalışması için handle edilmiş olayı varsa onun tetiklendiği, ve tüm bunlar bittiğinde de yine  PageRequestManager  in endRequest olayının tetiklendiğidir.

Bu olayları yöneterek uygulamamıza daha şimdiden pek çok hoş görsel özellikler ekleyebiliriz. Veri beklenirken animasyonları oynatmak, gelince belli panelleri gizleyip göstermek gibi. Veri beklenirken sayfada olacak değişiklikleri beginRequest olayına, işlem bittiğinde tekrar sayfada tekrar yapmak istediğimiz düzenlemeleri ise tahmin ettiğiniz gibi endRequest olayına handle ettiğimiz fonksyonlara yazarız. Bunlarla ilgili gayet basit ve de şık bir örneğe  http://msdn.microsoft.com/en-us/library/bb397432.aspx  linkinden ulaşabilirsiniz. Ben de zaten bilgilerimi çoğunlukla msdn den aldığıma göre örnek kodları orjinal kaynağından vermek daha doğru.



Şimdide dilerseniz, olaylardan sonra biraz da PageRequestManager in özelliklerine ve metotlardına biraz gözatalım,

get_isInAsyncPostBack()
En sık kullanılanlardan biri. Bu metod bool tipinde (true/false) değer döndürür ve adından da anlaşılacağı üzere postback asenkron olup olmadığını kontrol ediyor. Normal (asenkron olmayan postbacklerde her zaman false değer döndüren bu fonksyon, asenkron postback durumunda ise PageRequestManager in sadece pageLoading ve pageLoaded olaylarında true değer döndürür.

var prm = Sys.WebForms.PageRequestManager.getInstance();
var  donus= prm.get_isInAsyncPostBack() ;

beginAsyncPostBack(updatePanelsToUpdate, eventTarget, eventArgument, causesValidation, validationGroup); Kendimiz bir asenkron postback başlatmak isteyebiliriz. Bunu detaylı olarak başka bir makalede inceleyeceğiz.


dispose()
PageRequestManager nesnesi ve ona bağlı tüm üyelerin client tarafındaki javascript kaynaklarını serbest bırakır. Bu metod normalde Javascript teki window.unload() olayı esnasında otomatik olarak çalıştırılır. Eğer daha önceden nesnenin kullanım esnasında çağırır isek hata meydana gelebilir. Normal şartlarda kullanmamıza gerek yoktur :)

abortPostBack() Adından da anlaşılacağı üzere istediğimiz bir anda postback i iptal etmek için kullanılabilir. bu isteğin daha çok kullanıcıdan gelmesi pratikte daha uygundur. Mesela kullanıcıların isteğin sonucunu beklemeden iptal edebilmesini mümkün kılmak isteyebiliriz;  ama duruma göre biz de postback i iptal etmek isteyebiliriz. Bu konu yine detaylı olarak başka bir makalede incelenecek.



Handle ettiğimiz bu fonkstyonların argümanları üzerinde de bazı optimizasyonlar yapabiliyorduk.
Dilerseniz bunların da birkaçını inceleyelim,


function BeginRequestHandler(sender, args)  {      
 
//asenkron postback i tetikleyen elementin adını elde etmek için                                 
var p1 =  args.get_postBackElement();
 
//uygulamanın o isteği bir süre reddetmesi için         
var p2 = 
args.set_cancel(true);
 //varsa oluşan hata mesajını elde etmek için            
var p3 = 
args.get_error(); 
//hangi panelin yada panellerin update olduğunun bilgisini elde etmek için 
          
var p4 =
args.get_panelsUpdated(); 
}