Chiamare un page-method da jQuery

by Andrea Dottor 17 July 2012 22:20

Con ASP.NET AJAX sappiamo bene come interrogare un page-method, ed in rete troviamo parecchia documentazione. Il tutto si basa sul controllo ScriptManager, dove abilitando il flag EnablePageMethods viene automaticamente creato un proxy javascript che ci permette di chiamare il page-method in modo molto semplice e veloce.

A questa pagina troviamo un esempio di come chiamare un page-method utilizzando ASP.NET AJAX: Exposing Web Services to Client Script

E' possibile però evitare l'utilizzo/dipendenza da ASP.NET AJAX, ed evitare quindi di avere nella pagina un controllo ScriptManager. Per fare questo ci viene in aiuto jQuery.

Come utilizzare jQuery per chiamare il page-method?

La cosa è molto semplice e si basa sull'uso del metodo Ajax di jQuery, e sul come comporre l'url da invocare.

Ecco qui un esempio di codice per invocare il page-method Save presente all'interno della pagina Product.aspx. Il metodo Save ha un parametro objToSave di tipo string (che riceverà un prodotto in formato JSON).

var product = new Object();
product.Description = "Descrizione";
product.Code = "ABC123";
product.Data = "20120712";
product.CategoryID = 12;

var dataToSave = new Object();
dataToSave.objToSave = JSON.stringify(product);

$.ajax({
  type: "POST",
  url: "Product.aspx/Save",
  data: JSON.stringify(dataToSave),
  contentType: "application/json; charset=utf-8",
  dataType: "json",
  success: function (msg) {
    alert('Risultato del salvataggio: ' + msg.d);
  },
  error: function (err) {
    alert('Errore: ' + err.statusText);
  }
});

Come potete bene vedere l'url da chiamare da jQuery è composto dal nome della pagina seguito dal nome del page-method ("Product.aspx/Save").
Questo metodo permette di avere anche oggetti in ritorno da metodo, che verranno gestiti dal codice inserito all'interno del metodo success.

Utilizzando questo codice non si hanno dipendenze dalle librerie ASP.NET AJAX, e aggiungo che il codice JavaScript necessario è inferiore a quello del proxy che verrebbe generato dal controllo ScriptManager.

E quindi, per l'ennesima volta, jQuery batte ASP.NET AJAX Winking smile

Tags: ,

ASP .NET | ASP.NET AJAX | jQuery

Codice e slide della sessione ASP.NET e lo sviluppo Client Side

by Andrea Dottor 20 March 2012 14:30

Ecco pronti al download il codice e le slide della mia sessione "Il mondo è sempre più client. ASP.NET e lo sviluppo Client Side" che ho tenuto il 9 marzo 2012.

IScriptControl, Knockout e le ASP.NET Web API sono stati i principali protagonisti :-)

Abstract: Le applicazioni che sviluppiamo, e che il mercato richiede sempre più, stanno modificando il modo in cui le applicazione vengono scritte. La logica di interazione con l'utente stà diventando sempre più client side, questo grazie anche all'espansione di dispositivi mobile che fruiscono le nostre applicazioni. JQuery ed il javascript in generale hanno un ruolo sempre maggiore, e permetteno in molti casidi poter far fare la differenza a ciò che sviluppiamo.

Come per il codice dello scorso meeting, per quanto riguarda il database utilizzato nella demo è necessario scaricare il database AdventureWorks che trovate al seguente link http://msftdbprodsamples.codeplex.com/e modificare la connectionstring del progetto web.

Tags: , , ,

ASP .NET | ASP.NET AJAX | XeDotNet | Microsoft

ASP.NET Ajax Control Toolkit e DynamicPopulateExtenderControlBase

by Andrea 14 September 2009 06:51

Stavo introducendo il controllo HoverMenuExtender all'interno di uno UserControl e nell'intellisense mi compaiono alcune proprietà che mi risultano "anomale" per il controllo che stavo per utilizzare. Anomale nel senso che non mi aspettavo proprio di trovare tali proprietà in questo controllo.
HoverMenuExtender 
Le proprietà in questione sono quelle che hanno il suffisso Dynamic: DynamicContextKey, DynamicControlID, DynamicServiceMethod e DynamicServicePath.

Visto che (dal nome) potevano fare al caso mio, guardo la documentazione online, e scopro che non esiste documentazione a riguardo. Allora, come spesso accade, scarico i sorgenti degli ajax control toolkit e do un'occhiata al codice per capire dove fossere utilizzate e a cosa servissero. Ed è qui che sono rimasto sorpreso.

I controlli HoverMenuExtender, DropDownExtender, ModalPopupExtender e PopupControlExtender derivano dalla classe base DynamicPopulateExtenderControlBase, che contiene per l'appunto queste proprietà:

  • DynamicServicePath: url del web service che conterrà il metodo da chiamare. (lasciare vuoto se si intende chiamare un page method)
  • DynamicServiceMethod: metodo del web service da chiamare.
  • DynamicContextKey: permette di definire un parametro opzionale per parametrizzare il metodo chiamato. (parametro opzionale)
  • DynamicControlID: id del controllo che verrà utilizzato come container per iniettare il codice HTML restituito dal metodo.

Utilizzando queste proprietà è possibile utilizzare un metodo (DynamicServiceMethod) di un web service (DynamicServicePath) per farsi ritornare una porzione di condice HTML, che verrà poi iniettato in un preciso controllo (DynamicControlID).

Per provare se il tutto funziona correttamente, la via più semplice è quella di provare il tutto.
Creo un metodo in un web service che restituisca una porzione di codice HTML:

   1: [WebMethod]
   2: [ScriptMethod]
   3: public string GetHoverMenuContent(string contextKey)
   4: {
   5:     return String.Format("<span style='{0}'>Hello World!</span>", contextKey);
   6: }

e nella pagina vado a inserire il controllo, con le proprietà correttamente valorizzate:

   1: <ajax:HoverMenuExtender 
   2:     ID="hoverMenuExtender"
   3:     runat="server"
   4:     TargetControlID="linkButton" 
   5:     PopupControlID="Panel1" 
   6:     PopupPosition="Right" 
   7:     DynamicControlID="Panel2"
   8:     DynamicContextKey='font-weight:bold' 
   9:     DynamicServiceMethod="GetHoverMenuContent" 
  10:     DynamicServicePath="~/ajax.asmx" />
  11:  
  12: <asp:Panel ID="Panel1" runat="server" style="display:none;">
  13:   <asp:Label ID="Panel2" runat="server" />
  14: </asp:Panel> 

Il risultato che ho ottenuto è stato esattamente quello voluto, e quindi che al passaggio del mouse sopra al linkButton (o al controllo definito nella proprietà TargetControlID), è stato chiamato il metodo del web service, passando nel parametro contextKey il valore specificato nella prorietà DynamicContextKey (in questo caso il valore è fisso a font-weight:bold), e una volta ritornato il codice HTML, questo è stato inserito come innerHTML del controllo Panel2, e il Panel1 reso visibile.

In poche parole, tramite queste proprietà si ha la possibilità di popolare i controlli con maggiore dinamicità, e delegando la creazione di parte del contenuto HTML ad un metodo esterno. Questo in molti casi risulta veramente utile, permettondo con poche righe di codice di ottenere il comportamento/effetto voluto, e dimostrando per l'ennesima volta la flessibilità dei controlli contenuti negli AJAX Control Toolkit.

Tags: ,

ASP.NET AJAX | ASP .NET | .NET

Visualizzare e nascondere un PopupControlExtender da una funzione server side

by Andrea 20 July 2009 08:55

Qualcuno ha posto la domanda, se fosse possibile visualizzare o nascondere un popup creato con il PopupControlExtender direttamente da una funzione server side.

A differenza del ModalPopupExtender, PopupControlExtender il  non dispone di queste funzionalità server side, ma con poche righe di codice si può aggirare la problematica.

Per fare questo dobbiamo valorizzare la proprietà BehaviorID dell'extender:

   1: <ajax:PopupControlExtender 
   2:   ID="popup"
   3:   runat="server" 
   4:   BehaviorID="popupExt" ... />

utilizzando i metodi client showPopup e hidePopup, possiamo registrare uno script che si occupi di far visualizzare o nascondere il popup. Lo script utilizzerà la funzione $find per cercare l'extender all'interno del DOM della pagina, e successivamente verrano chiamate le funzioni showPopup oppure hidePopup esposte dallo script dell'extender.

Grazie al metodo RegisterStartupScript esposto dallo ScriptManager possiamo registrare lo script che immadiatamente verrà eseguito alla fine del postback.

   1: // visualizzare il popup
   2: ScriptManager.RegisterStartupScript(this, this.GetType(), "showPopup", "$find('popupExt').showPopup();", true);
   3:  
   4: // nascondere il popup
   5: ScriptManager.RegisterStartupScript(this, this.GetType(), "hidePopup", "$find('popupExt').hidePopup();", true);

 

Tags: ,

.NET | ASP .NET | ASP.NET AJAX

Seadragon Ajax

by andrea 13 March 2009 01:56

Leggendo il post di Mike Ormond sono venuto a conoscenza che Microsoft Live Labs  ha sfornato un nuovo progetto che ha dell'incredibile, dal nome Seadragon Ajax.

logo_color_seadragon_90 Questo progetto è una "replica" di Deep-Zoom, la funzionalità realizzata per silverlight che vi permette di gestire una foto grande quanto un campo da calcio, ed eseguire lo zoom fino al minimo particolare. Ma a differenza di Deep-Zoom questa è una funzionalità AJAX, che si appoggia completamente a JavaScript.

Ecco il link per vedere il progetto: http://livelabs.com/seadragon-ajax/

Tags: ,

ASP .NET | ASP.NET AJAX

FileUpload caricato in un postback asincrono

by andrea 13 March 2009 01:54

Se avete provato a caricare un controllo FileUpload durante un PostBack asincrono, oppure settate la visibilità da false a true, avrete notato che il controllo non funziona correttamente e quindi non vi permette di caricare il file (anche se con PostBackTrigger impostato correttamente).

Questo problema avviene perchè il controllo non è in grado di aggiornare correttamente il tag form della pagina.

Per risolvere il problema si deve modificare il tag form aggiungendo l'enctype:

   1: enctype="multipart/form-data"

è anche possibile aggiungerlo da codice utilizzando questo codice nel page_load:

   1: Page.Form.Attributes.Add("enctype", "multipart/form-data"); 

 

Tags:

ASP.NET AJAX | ASP .NET | .NET

Aggiornare un UpdatePanel da un differente UserControl

by andrea 11 March 2009 06:54

Quando si utilizza un UpdatePanel consiglio sempre di impostare la proprietà UpdateMode a Conditional e ChildrenAsTriggers a False, per avere il pieno controllo su ciò che accade, e far trafficare il minimo html possibile.

Ci sono condizioni però in qui questo non sembra possibile.
Uno di questi casi è quando il postback asincrono dovrebbe essere scatenato da un controllo contenuto all'interno di un'altro UserControl. La soluzione più semplice sarebbe stata quella di settare UpdateMode ad Always, ma questo causerebbe l'aggiornamento durante un qualsiasi postback asincrono.
Ma anche a questo caso esiste una soluzione, e la soluzione sono gli eventi.

UserControl 1
Controllo che contiene il pulsante che deve scatenare il PostBack asincrono.

Questo UserControl esporrà un evento Command, che servirà come trigger per indicare che è stato scatenato un evento in un controllo in esso contenuto (in questo caso un LinkButton).
Andiamo a gestire l'evento OnCommand del LInkButton, e al suo interno faremo rilanciare l'evento utilizzando il metodo RaiseBubbleEvent.

Gli UserControl possono contenere controlli che scatenano eventi. Il metdo RaiseBubbleEvent permette di rilanciare all'esterno questi eventi, permettendo di riassegnare il sender.

Si ha quindi la condizione che l'evento OnCommand scatenato dal LinkButton verrà visto come evento OnCommand (contenente le stesse informazioni) ma generato dallo UserControl.

   1: public event CommandEventHandler Command;
   2:  
   3:  
   4: protected void Button_Command(object sender, CommandEventArgs e)
   5: {
   6:     CommandEventHandler handler = this.Command;
   7:  
   8:     if (handler != null)
   9:         handler(this, e);
  10:  
  11:     base.RaiseBubbleEvent(this, e);
  12: }

UserControl 2
Controllo che contiene l'UpdatePanel da aggiornare con il PostBack asincrono.

Il controllo contiene un UpdatePanel che deve essere aggiornato dal PostBack asincrono scatenato dal controllo contenuto nel primo UserControl.

Quello che si può fare è esporre la collection del Triggers dell'UpdatePanel, in modo da poterli impostare da chi utilizza il controllo.

Creiamo una proprietà Triggers (di tipo UpdatePanelTriggerCollection) nello UserControl e gli facciamo ritornare la collezione di trigger dell'UpdatePanel.
Per poterla vedere nel design, aggiungiamo gli attributi DesignerSerializationVisibility e PersistenceMode.

DesignerSerializationVisibility permette di specificare la visibilità della proprietà a design-time.

PersistenceMode specifica come la proprietà viene inserita nello UserControl, se tramite attributo o come proprietà interna.
Esempio di Attribute è la proprietà ID="…", o runat="sever" , che vendono specificate come attributi del tag.
Esempio di InnerProperty sono i SelectedParameters, o i Triggers, che devono essere specificati in un elemento contenuto nel tag del controllo, e non come attributi.

   1: [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
   2: [PersistenceMode(PersistenceMode.InnerProperty)]
   3: public UpdatePanelTriggerCollection Triggers
   4: {
   5:     get { return updPanel.Triggers; }
   6: }

La pagina

Inseriamo nella pagina i due UserControl. Come sarà possibile vedere, nel controllo DisplayData (UserControl 2) sarà visibile una proprietà Triggers che ci da la possibilità di inserire un trigger associato al controllo Filters (e che si aggancerà all'evento Command che noi abbiamo esposto).
In questo modo, al click del pulsante contenuto nel UserControl di ricerca, avremo l'aggiornamento asincrono dell'UpdatePanel contenuto nel UserControl DisplayData.

   1: <dtr:Filters ID="filters" runat="server" />
   2: <dtr:DisplayData ID="displayData" runat="server">
   3:   <Triggers>
   4:       <asp:AsyncPostBackTrigger ControlID="filters" EventName="Command" />
   5:   </Triggers>
   6: </dtr:DisplayData>

Ecco quindi che grazie alla possibilità di esporre (e gestire) gli eventi, abbiamo potuto mantenere l'UpdatePanel con UpdateMode a Conditional, permettendo quindi di non far scatenare postback asincroni del tutto inutili.

Tags: , , ,

ASP.NET AJAX | ASP .NET | .NET

MultiHandleSliderExtender in un Templated Control

by Andrea 05 March 2009 05:18

Se provate a utilizzare un MultiHandleSliderExtender all'interno di una GridView, o di un Repeater o di un altro Templated Control, vi capiterà con una bella eccezione javascript (null is null or not an object) in quanto non l'extender non riesce a trovare i controlli impostati come MultiHandleSliderTarget.
Dando un'occhiata al file javascript di questo extender si può notare che il controllo esegue la funzione $get (recupera un elemento all'interno della pagina) usando come parametro l'id inserito, ma che nel caso di inserimento in un Templated Control, questo sarà differente dall'effettivo id utilizzato nella pagina (ClientID), sollevando quindi l'eccezione.

Per risolvere questo è sufficiente gestire l'evento RowDataBound (nel caso della GridView) e sostituire in ogni MultiHandleSliderTarget l'id dei controlli con i rispetti ClientID.

Ecco il codice necessario:

   1: protected void GridView1_RowDataBound(object sender, GridViewRowEventArgs e)
   2: {
   3:     if (e.Row.RowType == DataControlRowType.DataRow)
   4:     {
   5:         MultiHandleSliderExtender slider = e.Row.FindControl("multiHandleSliderExtenderTwo") as MultiHandleSliderExtender;
   6:         foreach (MultiHandleSliderTarget target in slider.MultiHandleSliderTargets)
   7:             target.ControlID = e.Row.FindControl(target.ControlID).ClientID; 
   8:     }
   9: }

Tags: , , , ,

.NET | ASP .NET | ASP.NET AJAX

AutoComplete Extender - alcuni workaround

by Andrea 04 April 2008 14:04

BoxRicercaStò utilizzando il controllo AutoComplete Extender, contenuto negli Ajax Control Toolkit, e mi sono trovato ad affrontare diversi problemi dovuti a bug del controllo ed altri dovuti a problemi con IE6.
Voglio però sottolineare che grazie alla flessibilità di questi controlli, sono riuscito facilmente a trovare alcuni workaround senza dover scrivere montagne di codice.

Nella mia applicazione mi trovo ad avere un controllo TextBox a cui è collegato l'AutoComplete Extender (per agevolare l'inserimento del testo), e posizionati subito sotto sono presenti alcune DropDownList.

Iniziamo dal primo problema:
con IE6, all'apertura del pannello dell'AutoComplete Extender, le DropDownList rimangono in primo piano, "bucando" il pannello.
La soluzione che ho trovato è stata quella di intervenire nelle animations, inserendo alcuni ActionScript che nascondono le DropDownList quando il pannello si apre, e le rivisualizzano non appena il pannello si stà chiudendo. In questo modo, l'AutoComplete Extender sarà visualizzato senza problemi grafici.

Altro problema:
alla chiusura del pannello non riuscivo più a modificare il valore delle DropDownList perchè il pannello viene nascosto impostando lo stile visibility, che lo fa quindi mantere in primo piano, bloccando l'accesso ai controlli posti sotto.
La soluzione più veloce è stata quella di intervenire anche qui nelle ActionScript, recuperandomi l'oggetto del pannello (che ha l'ID composto da BehaviorID__completionListElem) e settandogli la proprietà display a none. Facendo così, il pannello non risulta più essere in primo piano, e si ha l'accesso ai controlli in precedenza bloccati.

Ecco qui il codice per risolvere questi due problemi:

<ajaxToolkit:AutoCompleteExtender 
    runat="server" 
    BehaviorID="AutoCompleteEx" 
    ID="autoComplete1"
    TargetControlID="CittaCod" 
    ServiceMethod="GetCityList" 
    ServicePath="~/Services.asmx"
    MinimumPrefixLength="2" 
    CompletionInterval="1000" 
    FirstRowSelected="true" 
    EnableCaching="true"
    CompletionSetCount="20" 
    CompletionListCssClass="autocomplete_completionListElement"
    CompletionListItemCssClass="autocomplete_listItem" 
    CompletionListHighlightedItemCssClass="autocomplete_highlightedListItem">
    <Animations>
    <OnShow>
        <Sequence>
            <OpacityAction Opacity="0" />
            <HideAction Visible="true" />
            <ScriptAction Script="
                var behavior = $find('AutoCompleteEx');
                if (!behavior._height) {
                    var target = behavior.get_completionList();
                    behavior._height = target.offsetHeight - 2;
                    target.style.height = '0px';
                }" />
            <Parallel Duration=".4">
                <FadeIn />
                <Length PropertyKey="height" 
                        StartValue="0" 
                        EndValueScript="$find('AutoCompleteEx')._height" />
            </Parallel>                                
            <ScriptAction Script="                 
                var ddlPrezzoMin = $get(ddlPrezzoMinID);
                var ddlPrezzoMax = $get(ddlPrezzoMaxID);
                var ddlMqMin = $get(ddlMqMinID);
                var ddlMqMax = $get(ddlMqMaxID);
                
                ddlPrezzoMin.style.visibility = 'hidden';
                ddlPrezzoMax.style.visibility = 'hidden';
                ddlMqMin.style.visibility = 'hidden';
                ddlMqMax.style.visibility = 'hidden'; 
                " />
        </Sequence>
    </OnShow>
    <OnHide>
        <Sequence>
            <ScriptAction Script="    
                var ddlPrezzoMin = $get(ddlPrezzoMinID);
                var ddlPrezzoMax = $get(ddlPrezzoMaxID);
                var ddlMqMin = $get(ddlMqMinID);
                var ddlMqMax = $get(ddlMqMaxID);
                
                ddlPrezzoMin.style.visibility = 'visible';
                ddlPrezzoMax.style.visibility = 'visible';
                ddlMqMin.style.visibility = 'visible';
                ddlMqMax.style.visibility = 'visible';
                
                var autoCompleteElement = $get('AutoCompleteEx_completionListElem');
                autoCompleteElement.style.display = 'none';                                    
                " />
            <Parallel Duration=".4">
                <FadeOut />
                <Length 
                    PropertyKey="height" 
                    StartValueScript="$find('AutoCompleteEx')._height" 
                    EndValue="0" />
            </Parallel>
        </Sequence>
    </OnHide>
    </Animations>
</ajaxToolkit:AutoCompleteExtender>

 

Tags: , , , ,

ASP.NET AJAX

AJAX Control Toolkit Tabs, nascondere un TabPanel

by Andrea 06 July 2007 11:07

Da un post fatto nei newsgroup sono venuto a conoscenza di uno strano comportamento che affligge i TabPanel del controllo Tabs degli AJAX Control Toolkit e ho trovato un semplice workaround.
CropperCapture[5]

Infatti, se dovete nascondere un tab, viene spontaneo usare la proprietà Visible del TabPanel da nascondere ( e direi che la cosa, a mio avviso, sarebbe una cosa ovvia) ma non è così.
Impostando a false la proprietà Visible del primo TabPanel (per esempio), il tab viene correttamente nascosto, ma rimane visibile il testo contenuto nel HeaderText.

TabPippo.Visible = false;
CropperCapture[6]

Per risolvere questo inconveniente, al posto di utilizzare la proprietà Visible usate la proprietà Enabled, che farà nascondere completamente il tab in questione.

TabPippo.Enabled = false;
CropperCapture[7]

Il perchè di questi comportamenti non me lo spiego... Quello che mi sarei aspettato invece è che la proprietà Visible, se impostata a False, nascondesse completamente il tab, mentre l'Enabled a True che mi disabilitasse il click in modo da non poter abilitare quel TabPanel (e il tab dovrebbe rimanere visibile), ma forse sarebbero stati dei comportamenti troppo normali.

...ma l'importante è conoscerli ed eventualmente trovare dei workaround come questo (in tempi brevi).... ;)

Tags: , , ,

ASP.NET AJAX