DynamicData e ColorPicker FieldTemplates

by Andrea 16 September 2009 12:17

Con i Dynamic Data la velocità di realizzare l'applicazione aumenta notevolmente (ovvio che dipende sempre dal tipo di applicazione che si deve realizzare) e con la creazione di FieldTemplates di ha la possibilità di migliorare l'iterazione tra applicazione e utente.

Una delle necessità che ho avuto è stata quella di dover far inserire il valore in esadecimale di un colore (es: #FFFFFF), e consapevole del fatto che un utente non potesse conoscere i valori a memoria, mi sono adoperato per realizzare un opportuno FieldTemplate.
L'ultima versione degli AJAX Control Toolkit mi è venuta in aiuto grazie al controllo ColorPicker che è stato recentemente incluso, ed ecco qui il risultato ottenuto:

ColorFieldTemplate

Il codice del FieldTemplate è il seguente:

   1: public partial class Color_EditField : FieldTemplateUserControl
   2: {
   3:     protected override void ExtractValues(IOrderedDictionary dictionary)
   4:     {
   5:         dictionary[Column.Name] = ConvertEditedValue(txtColor.Text);
   6:     }
   7:  
   8:     public override Control DataControl
   9:     {
  10:         get { return txtColor; }
  11:     }
  12: }
   1: <%@ Control Language="C#" CodeBehind="Color_Edit.ascx.cs" Inherits="DynamicData.FieldTemplates.Color_EditField" AutoEventWireup="True" %>
   2:  
   3: <asp:ImageButton 
   4:     runat="Server" 
   5:     ID="btnOpenColorPicker" 
   6:     style="float:left;margin:0 3px" 
   7:     ValidationGroup="none" 
   8:     ImageUrl="~/images/cp_button.png" 
   9:     AlternateText="Click to show color picker" />
  10: <asp:Panel 
  11:     runat="server"
  12:     ID="pnlColor" 
  13:     style="width:18px;height:18px;border:1px solid #000;margin:0 3px;float:left" />
  14: <atk:ColorPickerExtender 
  15:     runat="server"
  16:     ID="colorPicker"     
  17:     TargetControlID="txtColor" 
  18:     PopupButtonID="btnOpenColorPicker" 
  19:     SampleControlID="pnlColor" 
  20:     PopupPosition="Absolute" />
  21: <asp:TextBox ID="txtColor" 
  22:     runat="server" 
  23:     Text='<%# FieldValueEditString %>' 
  24:     style="visibility:hidden;" />

Nella mia applicazione non avevo la necessita di visualizzare il codice del colore (che compare nella TextBox), ma nel caso a qualcuno interessasse, sarà sufficiente togliere lo stile "visibility:hidden;" dalla TextBox txtColor.

Tags: ,

ASP.NET AJAX | ASP .NET | .NET

ASP.NET Ajax Control Toolkit e DynamicPopulateExtenderControlBase

by Andrea 14 September 2009 15: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 17: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 10: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 10: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 15: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 14: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

Controllo Calendar e Z-Index

by andrea 20 November 2008 18:15

A volte le cose banali sono quelle che fanno perdere maggior tempo. Chissa che quindi questo post faccia risparmiare qualche minuto a qualcuno di voi. ;)

Oggi mi sono trovato alle prese con il controllo Calendar degli AJAX Control Toolkit, che si posizionava al di sotto del div contenente le google maps.
  
Controlando con la Developer Toolbar avevo verificato l'impostazione dello z-index, che era impostato correttamente a 1000. L'unico dubbio che avevo era a riguardo della proprietà, che mi veniva chiamata zIndex (senza il trattino), ma ho pensato che fosse comunque valido. Calendar_index_0Calendar_index_2
Dopo vari test, mi sono detto di provare a settare correttamente lo z-index impostando io lo stile, ed effettivvamente il problema si è risolto. :D

.ajax__calendar_container
{
    z-index: 1000;
}

Calendar_index_1

Tags:

ASP.NET AJAX | ASP .NET

AutoComplete Extender - alcuni workaround

by Andrea 04 April 2008 23: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

ASP.NET 2.0 AJAX - Usare ASP.NET AJAX in siti preesistenti

by Andrea 20 November 2007 08:11

Vi segnalo che giovedì 22 novembre alle ore 14.30 terrò un webcast su come inserire e utilizzare ASP.NET AJAX in siti preesistenti.

Se non vi siete ancora registrati il link è il seguente: REGISTRAZIONE

Ho scelto di tenerlo come Livello 200 perché frequentando i newsgroup ho notato che parecchie delle domande su ASP.NET AJAX riguardavano per l'appunto questo argomento, e spero quindi di poter essere d'aiuto a parecchi.

Vi aspetto numerosi. smile_wink

Technorati Tags: ,

Tags:

ASP.NET AJAX