C#: uno strano comportamento con l'override

Per una buona comprensione di questo post è necessario conoscere l'ereditarietà a livello elementare e cosa sono i params.

Ieri mi sono trovato all'improvviso in un loop infinito. Per quanto potesse essere chiaro cosa stava succedendo, non mi è stato subito chiaro il PERCHE'.

Oggi ho scritto un po' di codice in modo da mettere la situazione su un "banco di prova".

La necessità era quella di ampliare una classe di log in modo da incorporare una funzionalità inn stile string.format.

   1:          public class clsLogger
   2:          {
   3:              public void scrivi(string Msg)
   4:              {
   5:                  Console.WriteLine("1)" + Msg);
   6:              }
   7:              public void scrivi(string Msg, params object[] arg)
   8:              {
   9:                  scrivi("2)" + string.Format(Msg, arg));
  10:              }
  11:          }
  12:   
  13:   
  14:          static void Main(string[] args)
  15:          {
  16:              Console.WriteLine("********* Test Overload ***********");
  17:   
  18:              clsLogger Test = new clsLogger();
  19:              Test.scrivi("Scrivi");
  20:              Test.scrivi("Scrivi, parametro: {0}", 123456);
  21:   
  22:              Console.ReadKey();
  23:          }

In questo modo il compilatore è perfettamente in grado di distinguere i due metodi: la chiamata scrivi della riga 19 si riferisce al metodo scrivi della riga 3 mentre la chiamata della riga 20 alla funzione scrivi della riga 7.

Complichiamoci un poco la situazione: la nostra classe di log deve estenderne una di base in cui è definito il metodo scrivi semplice.

   1:          public class clsLoggerBase
   2:          {
   3:              public virtual void scrivi(string Msg)
   4:              {
   5:                  Console.WriteLine("1)" + Msg);
   6:              }
   7:          }
   8:   
   9:          public class clsLoggerInherits : clsLoggerBase
  10:          {
  11:              public override void scrivi(string Msg)
  12:              {
  13:                  base.scrivi(Msg);
  14:              }
  15:              public void scrivi(string Msg, params object[] arg)
  16:              {
  17:                  scrivi("2)" + string.Format(Msg, arg));
  18:              }
  19:          }
  20:   
  21:          static void Main(string[] args)
  22:          {
  23:              Console.WriteLine("********* Test Overload ***********");
  24:   
  25:              clsLoggerInherits Test = new clsLoggerInherits();
  26:              Test.scrivi("Scrivi");
  27:              Test.scrivi("Scrivi, parametro: {0}", 123456);
  28:   
  29:              Console.ReadKey();
  30:          }

In questo caso il compilatore referenzia sempre il metodo della riga 15. Questo avviene sia nelle due chiamate alle righe 26 e 27 della funzione Main, sia nella chiamate interna di riga 17 (che diventa ricorsiva generando un'eccezione di stackoverflow).

Anche non sovrascrivendo la funzione scrivi in clsLoggerInherits otteniamo la stessa situazione.

Ajax.NET Javascript: Validazione dei parametri

Esiste una funzione nascosta nell'ajax.net che si occupa della validazione dei parametri: Function._validateParams. Inizialmente questa funzione esisteva solo in debug, mentre in release veniva usata la versione compressa: per noi programmatori era inutilizzabile. Tuttavia nella versione rilasciata del framework hanno cambiato idea lasciando la definizione.

Dato che il nome di questa funzione inizia con "_" dovremmo considerarla privata o interna alla classe Function: ma dato che trovo questa funzione molto utile ho deciso di ignorare bellamente la questione e di farne uso.

In sostanza cosa consente di fare? Di validare facilmente i parametri di una funzione. Attraverso un array di oggetti e la lista dei parametri verifica il tipo, il numero e altre informazioni. Se c'è una differenza fra quanto richiediamo e quello che viene passato lancia un'eccezione.

   1:  function onlyString(str)
   2:  {
   3:      //Parameter validation
   4:      var e = Function._validateParams(arguments,
   5:                                       [{name: "str", type: String, elementMayBeNull: false,optional:false}]);
   6:      if (e) {throw e;} //On Parameter error throw an exception
   7:   
   8:     //Main code
   9:      alert(str);
  10:  }

Come possiamo facilmente capire onlyString è una funzione fondamentale per la nostra webApplication. Ci sono due parametri da passare: arguments  e l'Array di configurazione. Questo contiene oggetti che spiegano alla funzione come deve controllare ogni parametro. E' importante inserire un oggetto di configurazione per ogni parametro altrimenti avremo un'eccezione che ci avverte che il numero di parametri è sbagliato. Per comodità possiamo creare l'Array delle configurazioni inline tramite JSON.

Vogliamo essere sicuri che il parametro str sia una stringa, non sia null e che venga passato:

   1:  [{name: "str", type: String, elementMayBeNull: false,optional:false}]

Ma cosa possiamo settare nel singolo oggetto di configurazione? Ci viene in aiuto Jay Kimble con un suo post di Aprile '07 su CodeBetter:

  • type - (Type) Indica di che tipo deve essere il parametro (Alcuni esempi: Array, Function, String, Sys.Component, ma soprattuto le nostre classi)
  • mayBeNull -(boolean) Se il parametro accetta null come valore
  • integer - (boolean) indica che il parametro deve essere un intero
  • domElement - (boolean) Il parametro deve essere un elemento del DOM (div, table, span, ...)
  • optional - (boolean) Indica se il parametro è opzionale e quindi può essere omesso.
  • elementType - (Type) - se type è 'Array': Indica il tipo di dato accettato nell'array.
  • elementInteger - (boolean) se type è 'Array' si assicura che sia un Array di interi
  • elementDomElement - (boolean) se type è 'Array' si assicura che sia un Array di elementi del dom
  • elementMayBeNull -(boolean) se type è 'Array' valuta se gli elementi dell'Array possono essere null.

Qui trovate due link che possono aiutarvi ad approfondire:

 

Passare dati fra WebForm

Nota

Sul forum di asp.net ho trovato un riferimento ad un post si Haissam. Mi è sembrato molto ben fatto e visto che questo problema ritorna con un'increadibile frequenza nei forum ho deciso di postare qui una traduzione dell'articolo sperando che possa aiutare i programmatori italiani:

 

In questo post, vi mostrerò diversi modi di passare informazioni fra webform. Si possono adottare diverse tecniche, dipende tutto da cosa vi serve!

Sappiamo tutti che l'Http non gestisce lo stato, quindi le informazioni dovranno essere salvate da qualche parte per poterle poi recuperare.

  1. Query String
  2. Cookies
  3. Variabili di Sessione
  4. Cross Page Postback
  5. Form Submit
  6. Server.Trasnfer o Server.Execute


Parleremo in dettaglio di ognuna specificando quali tipi possono lavorare.

1) QueryString:

Usando le variabili in query string puoi passare varibili fra webform. Supponiamo che tu debba passare il contenuto di TextBox1.Text da WebForm1 a WebForm2 alla pressione di un pulsante:

   1:  protected void Button1_Click(object sender, EventArgs e)
   2:  {
   3:      Response.Redirect("WebForm2.aspx?id=" + TextBox1.Text);
   4:  }

Per leggere il valore di "id" da WebForm2:

   1:  string queryStringID = Request.QueryString["id"];

2) Cookies:

Come probabilmente già sai, i cookies sono piccoli file di testo salvati sul client. I Cookie possono contenere massimo 4kb circa. Da quando i cookie si trovano sul client ad ogni richiesta del client al tuo sito, il browser cerca i cookie e li spedisce nell'oggetto Request.

Es: Per Salvare il valore di TextBox1.Text in un cookie:

   1:  protected void Button1_Click(object sender, EventArgs e)
   2:  {
   3:      HttpCookie cookie = new HttpCookie("UserName");
   4:      cookie.Value = TextBox1.Text;
   5:      cookie.Expires = DateTime.Now.AddDays(1);
   6:      Response.Cookies.Add(cookie);
   7:      Response.Redirect("WebForm2.aspx");
   8:  }

Adesso nel Page_Load di WebForm2, possiamo leggere il valore:

   1:  if(Request.Cookies["UserName"] != null)
   2:  {
   3:      Response.Write(Request.Cookies["UserName"].Value);
   4:  }

3) Variabili di Sessione:

Per Default le variabili di sessione sono salvate nella memoria del webserver. Le Sessioni sono uniche per ogni utente.

Per salvare una variabile di sessione:

   1:  protected void Button1_Click(object sender, EventArgs e)
   2:  {
   3:      Session["UserName"] = TextBox1.Text;
   4:      Response.Redirect("WebForm2.aspx");
   5:  }

Per leggere il valore da WebForm2:

   1:  Response.Write(Session["UserName"]);

4) Cross Page Postback (ASP.NET 2.0):

Il cross page postback è stato introdotto con l'ASP.NET 2.0. Ho già scritto un post in merito.

Ex: Setta la proprietà PostBackUrl di Button1 indirizzandolo a WebForm2.aspx.

Inserisci questo codice in WebForm2 per leggere il valore di TextBox1:

   1:  TextBox txtFirstPage = (TextBox)(PreviousPage.FindControl("TextBox1"));
   2:  Response.Write(String.Format("You wrote {0} in Page1",txtFirstPage.Text));

5) Submit Form:

Possimao eseguire il submit di un form in WebForm1.aspx a webform2.aspx. In questo caso possiamo recuperare tutti gli elementi contenuti nel form di webform1 da webform2. Quello che faremo in questo esempio è di aggiungere un altro form (non server side) che conterrà due controlli html. Il primo sarà un submit ed il secondo sarà un campo hidden che conterrà il valore del controllo server TextBox1 per poter essere inviato a webform2.

   1:  <form id="Form1" method="post" runat="server">
   2:      <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
   3:  </form>
   4:  <form name="SubmittedForm" action="WebForm2.aspx" method="post">
   5:      <input id="Submit1" type="submit" value="submit" onclick="CopyTextToHiddenField()" />
   6:      <input name="Hidden1" type="hidden" />
   7:  </form>

Ovviamente ti sarai accorto che abbiamo gestito l'evento onclick del controllo submit che chiamerà una funzione javascript per copiare il valore di TextBox1 nel campo hidden (Hidden1) per poter essere inviato a WebForm2. Avrai anche notato l'attributo action del secondo form in cui specifichiamo in quale pagina dobbiamo essere reindirizzati.

Aggiungiamo questo javascript nella <head> di WebForm1

   1:  <script language="javascript">
   2:      function CopyTextToHiddenField()
   3:      {
   4:          var textbox1Value = document.getElementById("<%=TextBox1.ClientID%>").value;
   5:          document.forms[1].document.getElementById("Hidden1").value = textbox1Value;
   6:      }
   7:  </script>

Adesso possiamo recuperare il valore di Hidden1 da WebForm2 con questo codice:

   1:  Response.Write(Request.Form["Hidden1"]);   

6) Server.Transfer e Server.Execute

Queste due funzioni accettano 2 parametri. Il primo è il nome della webform a cui si vuol fare il redirect, il secondo è un parametro booleiano che specifica se il form debba essere preservato.

   1:  protected void Button1_Click1(object sender, EventArgs e)
   2:  {
   3:      Server.Transfer("WebForm2.aspx", true);
   4:  }

Adesso nel page_load di  WebForm2 possiamo usare questo codice per leggere il valore di TextBox1

   1:  Response.Write(Request.Form["TextBox1"]);

il codice per Server.Execute è identico.


Spero che questo post possa essere d'aiuto e ringrazio Haissam per avermi autorizzato alla pubblicazione della traduzione.

Per eventuali domande/suggerimenti/altro contattatemi pure.

Link al post originale di Haissam 

Technorati Tag: ,,

Custom Event in Custom Control

Introduzione

In una logica di programmazione basata su i controlli è normale avere la necessità di creare eventi per comunicare con la pagina o più in generale con l'oggetto contenitore. In questo articolo vedremo come fare passo dopo passo.

Nonostante la tecnica per la gestione degli eventi nei controlli server e nei controlli client (ascx) sia identica, la solution allegata contiene esempi di realizzazione con entrambi i tipi di controllo.

Cosa vogliamo realizzare?

Un semplice controllo dotato di un textbox e un pulsante. Alla pressione del pulsante il controllo deve generare un evento in cui notifica il valore contenuto nella textbox. La Pagina contenitore intercetta l'evento del controllo e scrive il testo notificato in una label.

Lista della spesa: Di cosa abbiamo bisogno?

1) un Delegate: un delegate è un modo per rappresentare la signtaure di un metodo: Quanti e quali parametri deve avere e qual'è il tipo restituito dalla funzione? Nel nostro caso rappresenta "come deve essere fatta" la funzione di risposta all'evento http://msdn.microsoft.com/it-it/library/900fyy8e(en-us).aspx

2) un Evento: Semplicemente abbiamo bisogno di definirlo come attributo e specificare il delagate a cui fa riferimento

Parte 1 - Prepariamo il nostro evento:

Iniziamo con il dichiarare nel nostro controllo il delegate e l'evento:

        /// <summary>
        /// Event Delegate: How answering function must be?
        /// </summary>
        /// <param name="text"></param>
        public delegate void dlgSubmitText(string text);
 
        /// <summary>
        /// Evant object refearing to dlgSubmitText
        /// </summary>
        public event dlgSubmitText SubmitText;

 

In pratica abbiamo detto che nel nostro controllo esiste un evento la cui funzione di risposta ha un parametro di tipo stringa e non restisce valori.

Parte 2 - Invochiamo l'evento

Dobbiamo intercettare la pressione del pulsante interno al controllo in modo da poter generare l'evento.

        void Btn_Click(object sender, EventArgs e)
        {
            //Retrive TextBox object
            TextBox Txt = (this.FindControl("Txt") as TextBox);
            
            //Invoke Event with txt.text as parameter
            //!!! It's important to check if SubmitText is managed by (SubmitText != null) before to invoke event
            if (SubmitText != null){SubmitText.Invoke(Txt.Text);}
        }

E' molto importante ricordarsi l'if(SubmitText != null) perchè se l'evento non fosse gestito dal contenitore avremmo un'eccezione.

Parte 3 - Il controllo è pronto proviamolo:

Creiamo una pagina aspx per gestire il contollo ed il suo evento. Nel nostro caso quando intercettiamo l'evento scriviamo il testo che ci arriva come parametro in una label.

ASPX:

<%@ Page Language="C#" AutoEventWireup="true"  CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Namespace="wmb" TagPrefix="wmb" %>
<%@ Register Src="~/MyAscxControl.ascx" TagPrefix="wmb" TagName="MyAscxControl" %>
<!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>Untitled Page</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        <wmb:MyControl id="MyControl" runat="Server" OnSubmitText="MyControl_SubmitText"></wmb:MyControl><br />
        <wmb:MyAscxControl ID="AscxControl" runat="server" OnSubmitText="AscxControl_SubmitText" /><br />
        <br />
        <asp:Label ID="lblResult" runat="server"></asp:Label>
    </div>
    </form>
</body>
</html>

CS:

public partial class _Default : System.Web.UI.Page 
{
    /// <summary>
    /// Custom event Managment (Server Control)
    /// </summary>
    /// <param name="text"></param>
    public void MyControl_SubmitText(string text)
    {
        lblResult.Text = "Server Event Text:" + text;
    }
 
    /// <summary>
    /// Custom event Managment (Ascx Control)
    /// </summary>
    /// <param name="text"></param>
    public void AscxControl_SubmitText(string text)
    {
        lblResult.Text = "ASCX Event Text:" + text;
    }
}

Conclusioni e Sorgente:

Come avete potuto vedere il procedimento è molto semplice, flessibile ed offre grandi possibilità.

Per ogni suggerimento su come migliorare quest'articolo, domande o altro scrivetemi pure.

 

 

TextBox come visualizzatori di informazioni

Spesso utiliziamo i textbox come semplici visualizzatori di informazioni inserite dall'utente in qualche altro modo. Pensa ad un calendario in cui selezionaniamo una data oppure ad una popup di input. Non volendo che l'utente possa inserire manualmente informazioni nella textbox settiamo l'attributo ReadOnly="True" oppure Enable="false", ma se vogliamo leggere questo valore lato server abbiamo una brutta sorpresa: il valore non è stato modificato.

Creaiamo un esempio abbastanza semplice:

Default.aspx

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Untitled Page</title>
    <script type="text/javascript">
        function InsertNewValue()
        {
            var answer=prompt("Inserisci il nuovo valore", "Nuovo Valore");
            document.getElementById("txtNormale").value = answer;
            document.getElementById("txtReadOnly").value = answer;
            document.getElementById("txtReadOnlyAddAttribute").value = answer;
        }
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
        Textbox Normale:<asp:TextBox ID="txtNormale" runat="server" Text="Valore originale"></asp:TextBox><br />
        TextBox con ReadOnly:<asp:TextBox ID="txtReadOnly" runat="server" Text="Valore originale" Readonly="true"></asp:TextBox><br />
        TextBox con Add Attribute ReadOnly<asp:TextBox ID="txtReadOnlyAddAttribute" runat="server" Text="Valore originale"></asp:TextBox><br />
        <input value="Modifica" type="button" onclick="InsertNewValue();" /><br />
        <asp:Button ID="btnPostBack" runat="server" Text="DoPostBack" /><br />
        <asp:Label ID="lblResult" Text="" runat="server"></asp:Label>
    </div>
    </form>
</body>
</html>

Default.aspx.cs

   1:  public partial class _Default : System.Web.UI.Page 
   2:  {
   3:      protected override void OnInit(EventArgs e)
   4:      {
   5:          base.OnInit(e);
   6:          btnPostBack.Click += new EventHandler(btnPostBack_Click);//Agganciare l'evento del pulsante
   7:          //txtReadOnlyAddAttribute.Attributes.Add("disabled", "disabled");
   8:          txtReadOnlyAddAttribute.Attributes.Add("readonly", "readonly");
   9:      }
  10:   
  11:      void btnPostBack_Click(object sender, EventArgs e)
  12:      {
  13:          string lblTest = @"Risultati inseriti:<br/>
  14:              txtNormale = '{0}'<br/> 
  15:              txtReadOnly = '{1}'<br/>
  16:              txtReadOnlyAddAttribute = '{2}'<br/>
  17:          ";
  18:          lblResult.Text = string.Format(lblTest, txtNormale.Text, txtReadOnly.Text, txtReadOnlyAddAttribute.Text);
  19:      }
  20:  }

Abbiamo 3 TextBox che vengono modificate assieme dalla funzione Javascript InsertNewValue con un semplice prompt alla pressione di "Modifica". In più abbiamo un pulsante che esegue il postback e scrive i 3 valori modificati in una label. La prima textbox serve per vedere il comportamento standard, la secoda il comportamento con readonly attivo ed infine la soluzione al problema.

Modificando il valore  ed eseguendo l postback otteniamo questo risultato:

Risultati inserito:
txtNormale = 'Nuovo Valore'
txtReadOnly = 'Valore originale'
txtReadOnlyAddAttribute = 'Nuovo Valore'

Spiegazione:

Perchè otteniamo questa differenza? Perchè l'uso dell'Attributes.Add(string, string) inserisce l'attributo così com'è nel tag html, ma non influenza in nessun modo il controllo server. L'attributo Readonly invece cambia il comportamento del controllo che ignora le modifiche perchè è "readonly".

Nota su Disable:

L'attributo "disable" non funziona come readonly: Il valore infatti non subisce modifiche. Per fare una prova commentate la riga 8 e decommentate la riga 7.

Scarica il Codice dell'Esempio:

 

Technorati Tag:

Le dimensioni sono importanti!

Spesso ho bisogno di sapere qual'è la superficie del browser: non la dimensione della finiestra ma l'area effettivamente utilizzabile (esclusi menu barre di scorrimento e altro).

Ho trovato un ottimo articolo di Stephen Chapman in cui parla proprio di questo e chiude con un bel codice da riutilizzare! Godetevelo :

Ah dimenticavo, sempre per gestire le dimensioni provate questo calibro da monitor, io mi trovo benissimo (se avete suggerimenti su altri strumenti di questo tipo dite pure).

[OT] - Connessione ADSL

Non ci posso credere, dopo anni di tentativi infruttosi con Telecom, Libero e molti altri. Dopo decine di ore spese ad aspettare inutilmente ai callcenter. Ho l'ADSL a casa. Per tutto questo ringrazio NGI (giuro non mi danno commissioni) e Elio Tondo il mio caro expresidente che me l'ha suggerita!
Ed ora a noi due internet!

(un Wamba gongolante, moolto gongolante)

Classi abstract e interfacce

Oggi sul forum di ASP.NET ho risposto ad un post che mi è sembrato interessante, ed ho deciso di riproporlo come post.

"Ho una classe di base abstract B1 e un inrefaccia I1. Creo un'altra classe abstract B2 che eredita da B1 ed implementa I1. Infine ho due classi D1 e D2 che ereditano da B2. Dichiarando un metodo in I1 e volendolo implementare solo in D1 e D2, come posso fare? "

Continua e soluzione

Triplet

Ho scoperto di recente la classe triplet .

Non sono un architetto, ma mi ha lasciato molto perplesso. Che senso ha? Perchè non crearsi una classe personalizzata? Mi piacerebbe sapere le vostre impressioni.

Image button - Lo sfondo

Utilizzando gli imagebutton mi sono scontrato spesso con la trasparenza dell'immagine.
L'imagebutton usa il bianco come colore di sfondo e l'effetto grafico non è certo entusiasmante. Per correggere questo difetto basta utilizzare l'attributo BackColor settato a "Transparent" e il pulsante rispetterà le trasparenze dell'immagine.
«April»
SunMonTueWedThuFriSat
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456