...mi ritrovai al solito incasinato.
Mi ricordo che ai tempi di Dante voleva dire 35 anni (come si fa l'emoticon per le corna?
).
Oggi ci sono arrivato, ma il regalo più bello l'ho già avuto 9 giorni fa, con la nascita della piccola Rosa (che aggiunta a Teresa ha aumentato considerevolmente il livello di entropia, già da prima della nascita...)
Be' non mi resta che pubblicare le foto delle mie bellezze, per post "tecnici" (o pseudo-tali) ci vorrà un po' di tempo ancora...


P.S.: ho appreso oggi di essere nato lo stesso giorno di DotNetSide, anche se di qualche anno prima...
View blog reactions
... ma stavolta le Poste Italiane non c'entrano.
Contrariamente alle mie aspettative, infatti, ci ha messo solo 12 giorni, anzi 6 giorni lavorativi, peccato che sia stato spedito quando il Natale era già passato.
Visti i precedenti, voglio sperare che il 2008 sia l'anno della svolta per le poste ...
View blog reactions
Utilizzando i Web Application Project, mi è capitato un altro problema, stavolta con i profili di ASP.NET. A differenza di un WebSite, con i Web Application Project, infatti, non è possibile accedere da codice ad oggetti Profile.
Ciò è dovuto al fatto che gli oggetti Profile sono tipi generati runtime da ASP.NET, mentre nei Web Application Project i file sono compilati prima e non possono accedere a questi tipi.
Pertanto per accedere all'ASP.NET Profile è necessario scrivere una classe che faccia da proxy. Da questo indirizzo su codeplex è possibile scaricare un add-in per Visual Studio 2005 che genera una classe proxy strongly typed, la classe WebProfile, mediante la quale è possibile accedere all'ASP.NET Profile.
Per scaricare l'add-in:
ASP.NET WebProfile Generator
View blog reactions
Con l'utilizzo di AJAX, magari anche solo attraverso gli UpdatePanel, può capitare di voler distinguere tra un PostBack asincrono ed uno sincrono. Per farlo è sufficiente riferirirsi alla proprietà IsInAsyncPostBack dello ScriptManager.
Ad esempio, supponiamo di avere la seguente pagina Default.aspx:
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="Default.aspx.vb"
Inherits="EsPB._Default" %>
<!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>Prova PostBack</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<div>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<p>
<asp:Label ID="Label1" runat="server" Text="PostBack:" />
</p>
<p>
<asp:Button ID="Button1" runat="server" Text="Asincrono" />
</p>
</ContentTemplate>
</asp:UpdatePanel>
<asp:Button ID="Button2" runat="server" Text="Sincrono" />
</div>
</form>
</body>
</html>
Per distinguere tra i due tipi di postback si può scrivere nel file Default.aspx.vb:
Partial Public Class _Default
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load
If Me.IsPostBack Then
If ScriptManager1.IsInAsyncPostBack Then
Label1.Text &= " Asincrono"
Else
Label1.Text &= " Sincrono"
End If
End If
End Sub
End Class
In questo modo premendo Button1, posto all'interno dell'UpdatePanel, si genera un postback asincrono e quindi si aggiunge la scritta "Asincrono" alla label nella pagina. Invece premendo Button2, che genera un normale postback (viene ricaricata l'intera pagina), si aggiunge la scritta "Sincrono".
Quando si fa uso di MasterPage, bisogna far riferimento allo ScriptManager della pagina Master. Ad esempio
MasterPage.Master
<%@ Master Language="VB" AutoEventWireup="false"
CodeBehind="MasterPage.master.vb" Inherits="EsPB.MasterPage" %>
<!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>Prova PostBack con MasterPage</title>
</head>
<body>
<form id="form1" runat="server">
<asp:ScriptManager ID="ScriptManager1" runat="server">
</asp:ScriptManager>
<div>
<asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
</asp:ContentPlaceHolder>
</div>
</form>
</body>
</html>
DefaultContent.aspx
<%@ Page Language="vb" AutoEventWireup="false"
MasterPageFile="~/MasterPage.Master" Codebehind="DefaultContent.aspx.vb"
Inherits="EsPB.DefaultContent" Title="Prova PostBack con MasterPage" %>
<asp:Content ID="Content1" ContentPlaceHolderID="ContentPlaceHolder1"
runat="server">
<asp:ScriptManagerProxy ID="ScriptManagerProxy1" runat="server">
</asp:ScriptManagerProxy>
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<p>
<asp:Label ID="Label1" runat="server" Text="PostBack:" />
</p>
<p>
<asp:Button ID="Button1" runat="server" Text="Asincrono" />
</p>
</ContentTemplate>
</asp:UpdatePanel>
<asp:Button ID="Button2" runat="server" Text="Sincrono" />
</asp:Content>
DefaultContent.aspx.vb
Public Partial Class DefaultContent
Inherits System.Web.UI.Page
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load
If Me.IsPostBack Then
Dim sm As ScriptManager = Me.Master.FindControl("ScriptManager1")
If sm.IsInAsyncPostBack Then
Label1.Text &= " Asincrono"
Else
Label1.Text &= " Sincrono"
End If
End If
End Sub
End Class
View blog reactions
Tempo fa avevo il problema di far lavorare Expression Web nella root del web server di sviluppo del framework, invece che sotto una directory virtuale. Avevo risolto lo stesso problema per Visual Studio 2005, ma questa volta però non sono stato capace di trovare nulla (né in rete né tra le opzioni del programma).
Allora mi è venuto in mente di aver letto questo post (per l'autore: hai visto che a qualcuno è servito?
), in cui si spiega come sia possibile avviare manualmente il web server di sviluppo, ossia il file WebDev.Webserver.exe che si trova nella directory %WINDIR%\Microsoft.NET\Framework\v2.0.50727
Le istruzioni per l'uso sono visualizzabili digitando da linea di comando:
%WINDIR%\Microsoft.NET\Framework\v2.0.50727\WebDev.Webserver.exe /?
che produce in output:
ASP.NET Development Server Usage:
WebDev.WebServer /port:<port number> /path:<physical path> [/vpath:<virtual path>]
port number:
[Optional] An unused port number between 1 and 65535.
The default is 80 (usable if you do not also have IIS listening on the same port).
physical path:
A valid directory name where the Web application is rooted.
virtual path:
[Optional] The virtual path or application root in the form of '/<app name>'.
The default is simply '/'.
Example:
WebDev.WebServer /port:8080 /path:"c:\inetpub\wwwroot\MyApp" /vpath:"/MyApp"
You can then access the Web application using a URL of the form:
http://localhost:8080/MyApp
Per risolvere il mio problema è bastato quindi lanciare il comando:
WebDev.WebServer /path:"c:\miosito"
(poiché avevo anche bisogno di lavorare sulla porta 80, oltre che nella root)
Sempre sul post di Paolo Ongari ci sono, invece, le istruzioni per un file batch completo.
View blog reactions
Da un po' di tempo sto provando ad utilizzare i Web Application Project di Visual Studio 2005 per la realizzazione di siti web. Ci sono, a mio avviso, diversi vantaggi in questo approccio, ad esempio la possibilità di gestire diversi file di configurazione. Però con l'uso sto scoprendo anche diverse "scomodità", almeno finché non si impara come fare.
Un problema che mi ha portato via un bel po' di tempo è stato la scomparsa dell'App_Code. Nei progetti Web Application non esiste la directory App_Code, ossia quella directory speciale, introdotta con ASP.NET 2.0 in cui basta mettere il codice sorgente delle nostre classi per trovarsele condivise per tutta l'applicazione. In pratica è come referenziare un assembly, solo che qui l'assembly è generato a runtime.
Poiché avevo la necessità di condividere delle classi tra le pagine di una mia applicazione web, ho testardamente provato ad aggiungere al progetto una cartella denominata App_Code. Ovviamente non compariva tra le voci di "Add ASP.NET Folder", ma una volta aggiunta (come cartella qualsiasi, dandole il nome "App_Code"), è stata visualizzata con l'icona tipica della cartella ASP.NET.
Ho quindi aggiunto le mie classi in questa cartella, ma anche dopo aver compilato, queste non erano condivise, l'intellisense di Visual Studio non le vedeva e forzandone l'uso nelle pagine, il compilatore dava errore.
Dopo un bel po' di ricerche su Internet, e di prove seguendo consigli sbagliati trovati in rete del tipo "metti le classi in App_GlobalResource" (stavolta stranamente non ho trovato nessuno che abbia dato la colpa a Vista
), finalmente sono arrivato alla banalissima soluzione:
è sufficiente mettere le classi che andrebbero nella cartella App_Code in una normale cartella[1] ed assicurarsi che ciascuno dei file delle classi (.vb o .cs) abbia impostato nelle proprietà, alla voce "Build Action", il valore "Compile" e non "Content", che sembra quello predefinito.
Magari l'avessi saputo prima...
[1] dovrebbe andare bene anche la cartella App_Code, che in un Web Application Project ha lo stesso valore di una cartella qualsiasi, però io non rischierei (anche se con piccole prove sembrava funzionare normalmente)
View blog reactions
In questo post di Alessandro Gallo (segnalato da Scott Guthrie insieme a tanta altra roba interessante) si trova una descrizione dettagliata di come è fatto il layout e come sia possibile personalizzare lo SliderExtender attraverso i CSS.
Per dettagli:
SliderExtender layout and custom appearance
View blog reactions
A quanto pare non esiste un modo per ottenere automaticamente il valore di un GUID dopo aver effettuato un INSERT con SQL Server (cosa possibile, invece, con una normale colonna identity attraverso SELECT @@IDENTITY in coda alla INSERT).
Bisogna perciò adottare altre tecniche, come ad esempio le tre suggerite in questo articolo. In breve si potrebbe:
- creare una Stored Procedure per l'INSERT che generi il valore per il GUID (invece che generarlo automaticamente assegnando la funzione NEWID() alla colonna), ne memorizzi il valore e lo resitutuisca dopo aver effettuato l'INSERT;
- aggiungere un trigger associato alla INSERT della tabella per cui ci interessa il GUID che restituisca il valore del GUID generato all'inserimento, magari fatto inmodo da utilizzare @@GUID (analogo a @@IDENTITY)
- non far generare automaticamente il GUID da SQL Server, ma generarlo sul client prima dell'INSERT e memorizzarne il valore.
Per tutti i dettagli:
How to retrieve the newly inserted GUID value?
Technorati Tags:
GUID,
SQL,
SQL Server View blog reactions
Buon 2008 a tutti.
View blog reactions
In un sito web a cui sto lavorando ho inserito in una pagina il controllo Tabs dell'AJAX Control Toolkit. In questa pagina avevo l'esigenza di permettere al visitatore di cambiare il tab visibile anche mediante un link nella pagina stessa.
All'inizio ho provato semplicemente con un LinkButton inserito in un UpdatePanel, modificando dal lato server la proprietà ActiveTabIndex del TabContainer. Questa soluzione, semplicissima da implementare (anche se probabilmente genera parecchio codice superfluo), creava problemi con il rendering di una mappa visualizzata in uno dei tab (problema di cui ho scritto in un precedente post).
Allora ho provato ad utilizzare JavaScript, ma un primo abbozzo che sembrava funzionare senza problemi, in realtà si è dimostrato compatibile solo con IE (ho provato IE7, non so su IE6) e non con Firefox.
Dopo qualche inutile tentativo per rendere il mio approccio compatibile anche con Firefox, in questo post ho trovato la spiegazione del metodo giusto per affrontare il problema.
Il trucco è nel riferirsi al Behavior corrispondente al controllo (come sempre per i controlli dell'AJAX Control Toolkit), per il quale si trovano esposti i metodi e le proprietà "pubblici"[1], accessibili lato client. Per accedere al Behavior, è sufficiente riferirsi in JavaScript alla proprietà control del TabContainer.
Riassumo qui come fare.
Dato un controllo Tabs, costituito da un TabContainer col nome MyTabs e dai TabPanel interni ad esso, ad esempio:
<ajaxToolKit:TabContainer ID="MyTabs" runat="server">
<ajaxToolKit:TabPanel runat="server" ID="tab1">
<HeaderTemplate> Uno </HeaderTemplate>
<ContentTemplate>
Primo pannello
</ContentTemplate>
</ajaxToolKit:TabPanel>
<ajaxToolKit:TabPanel runat="server" ID="tab2">
<HeaderTemplate> Due </HeaderTemplate>
<ContentTemplate>
Secondo pannello
</ContentTemplate>
</ajaxToolKit:TabPanel>
<ajaxToolKit:TabPanel runat="server" ID="tab3">
<HeaderTemplate> Tre </HeaderTemplate>
<ContentTemplate>
Terzo pannello
</ContentTemplate>
</ajaxToolKit:TabPanel>
</ajaxToolKit:TabContainer>
Basterà inserire nella pagina il seguente codice BLOCKED SCRIPT
function changeTab( tabIndex ){
var tabBehavior = $get('<%=MyTabs.ClientID%>').control;
tabBehavior.set_activeTabIndex(tabIndex);
}
A questo punto un semplice collegamento ipertestuale, inserito in un punto qualsiasi della pagina (e quindi anche in uno dei TabPanel) può essere sufficiente a richiamare la funzione.
Ad esempio:
<a href="BLOCKED SCRIPTchangeTab(1);">Vai al secondo tab</a>
View blog reactions
[1] Trattandosi di JavaScript, non ha molto senso parlare di metodi, proprietà, pubblico e privato, anche se sia nella libreria AJAX di Microsoft che in quelle dell'AJAX Control Toolkit sono state adottate convenzioni per i nomi che rendono la programmazione in JavaScript simile alla programmazione ad oggetti. Per approfondimenti sul tema si può partire da
questo post di
Andrea Boschin.
La soluzione proposta nel post precedente presenta almeno un paio di inconvenienti significativi:
- deve essere "aggiustata" per poter funzionare su diversi tipi di browser (probabilmente annegando tra gli if)
- non funziona con AJAX, poiché anche inserendo il controllo TreeView in un UpdatePanel, il click su una CheckBox genera ovviamente un PostBack sincrono
Una soluzione migliore e, forse, anche più semplice, l'ho trovata su questo thread tra i forum di ASP.NET.
Si inserisce nella pagina un controllo Button, reso invisibile attraverso lo stile, e al'evento OnClick del TreeView si simula una Click del controllo Button. A questo punto il PostBack è garantito. Se, inoltre, il TreeView ed il Button sono entrambi in un UpdatePanel, sarà generato un PostBack asincrono.
In questo modo, in più, si ottiene anche una maggiore compatibilità crossbrowser, lasciando questo fastidioso compito al rendering di ASP.NET ed alla libreria AJAX di Microsoft.
Riepilogando, nella pagina si inserisce:
<asp:ScriptManager ID="ScriptManager1" runat="server" />
<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:TreeView ID="TV1" runat="server" ShowCheckBoxes="Leaf">
<Nodes>
<asp:TreeNode Text="Nodo 1" Value="1">
<asp:TreeNode Text="Nodo 1.1" Value="1.1" />
<asp:TreeNode Text="Nodo 1.2" Value="1.2" />
<asp:TreeNode Text="Nodo 1.3" Value="1.3" />
</asp:TreeNode>
<asp:TreeNode Text="Nodo 2" Value="2">
<asp:TreeNode Text="Nodo 2.1" Value="2.1" />
<asp:TreeNode Text="Nodo 2.2" Value="2.2" />
</asp:TreeNode>
<asp:TreeNode Text="Nodo 3" Value="3">
<asp:TreeNode Text="Nodo 3.1" Value="3.1" />
<asp:TreeNode Text="Nodo 3.2" Value="3.2" />
<asp:TreeNode Text="Nodo 3.3" Value="3.3" />
<asp:TreeNode Text="Nodo 3.4" Value="3.4" />
</asp:TreeNode>
</Nodes>
</asp:TreeView>
<asp:Button Id="Btn" runat="server" />
</ContentTemplate>
</asp:UpdatePanel>
Mentre il codice corrispondente, con riferimento all'esempio del post precedente (si intercetta l'evento CheckChanged per imporre solo una CheckBox spuntata) diventa:
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load
If Me.IsPostBack = False Then
TV1.Attributes.Add("onclick", _
String.Format("$get('{0}').click();", Btn.ClientID))
Btn.Attributes.Add("style", "visibility: hidden")
End If
End Sub
Protected Sub TV1_TreeNodeCheckChanged(ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.TreeNodeEventArgs) _
Handles TV1.TreeNodeCheckChanged
If e.Node.Checked Then
For Each n As TreeNode In TV1.Nodes
Me.UncheckOtherNodes(n, e.Node)
Next
End If
End Sub
Private Sub UncheckOtherNodes(ByVal n As TreeNode, _
ByVal nc As TreeNode)
If n.ChildNodes.Count > 0 Then
For Each cn As TreeNode In n.ChildNodes
Me.UncheckOtherNodes(cn, nc)
Next
Else
If n IsNot nc Then
n.Checked = False
End If
End If
End Sub
cambiando, quindi, solo la parte relativa al Page.Load
View blog reactions
Il controllo web di ASP.NET TreeView consente, attraverso la proprietà ShowCheckBoxes, di mostrare accanto ad ogni nodo una CheckBox.
Precisamente, è possibile assegnare a ShowCheckBoxes i valori All, Leaf, None, Parent, Root a seconda del tipo di nodi per i quali si vogliono visualizzare le CheckBox.
Ad esempio con il seguente codice:
<asp:TreeView ID="TV1" runat="server" ShowCheckBoxes="Leaf">
<Nodes>
<asp:TreeNode Text="Nodo 1" Value="1">
<asp:TreeNode Text="Nodo 1.1" Value="1.1" />
<asp:TreeNode Text="Nodo 1.2" Value="1.2" />
<asp:TreeNode Text="Nodo 1.3" Value="1.3" />
</asp:TreeNode>
<asp:TreeNode Text="Nodo 2" Value="2">
<asp:TreeNode Text="Nodo 2.1" Value="2.1" />
<asp:TreeNode Text="Nodo 2.2" Value="2.2" />
</asp:TreeNode>
<asp:TreeNode Text="Nodo 3" Value="3">
<asp:TreeNode Text="Nodo 3.1" Value="3.1" />
<asp:TreeNode Text="Nodo 3.2" Value="3.2" />
<asp:TreeNode Text="Nodo 3.3" Value="3.3" />
<asp:TreeNode Text="Nodo 3.4" Value="3.4" />
</asp:TreeNode>
</Nodes>
</asp:TreeView>
si avrebbe il risultato visualizzato nell'immagine a lato.
Il problema è che non c'è verso di impostare un PostBack automatico per l'evento click su una CheckBox.
Per esempio, supponiamo di voler imporre che nel TreeView indicato possa essere selezionata una sola CheckBox. Il codice potrebbe essere il seguente:
Protected Sub TV1_TreeNodeCheckChanged(ByVal sender As Object, _
ByVal e As System.Web.UI.WebControls.TreeNodeEventArgs) _
Handles TV1.TreeNodeCheckChanged
If e.Node.Checked Then
For Each n As TreeNode In TV1.Nodes
Me.UncheckOtherNodes(n, e.Node)
Next
End If
End Sub
Private Sub UncheckOtherNodes(ByVal n As TreeNode, _
ByVal nc As TreeNode)
If n.ChildNodes.Count > 0 Then
For Each cn As TreeNode In n.ChildNodes
Me.UncheckOtherNodes(cn, nc)
Next
Else
If n IsNot nc Then
n.Checked = False
End If
End If
End Sub
Per intercettare l'evento, però, è necessario un altro controllo che generi il PostBack, ad esempio un altro Button nella stessa pagina.
Cercando in rete ho trovato qui una soluzione al problema, che riporto. In pratica si aggiunge un po' di codice JavaScript nella pagina:
function postbackOnCheck() {
var o = window.event.srcElement;
if (o.tagName == 'INPUT' && o.type == 'checkbox' &&
o.name != null && o.name.indexOf('CheckBox') > -1) {
__doPostBack("","");
}
}
Si imposta, quindi, l'attributo OnClick al controllo TreeView per richiamare questa funzione:
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load
If Me.IsPostBack = False Then
TV1.Attributes.Add("OnClick", "postbackOnCheck()")
End If
End Sub
ed il gioco è fatto, ogni volta che si seleziona o deseleziona una CheckBox viene generato un PostBack e quindi la funzione di gestione dell'evento.
Questa soluzione, però, soffre di alcuni difetti (compatibilità cross browser, utilizzo con AJAX) che saranno oggetto di un prossimo post.
View blog reactions
Sapendo che:
- LIFO sta per Last In First Out, un metodo per gestire le code in cui il primo arrivato è l'ultimo ad uscire
- FIFO sta per Fisrt In First Out, per cui il primo arrivato è il primo ad uscire
- sono abbonato ad una rivista mensile e le poste me ne hanno consegnato il numero di settembre qualche giorno dopo averne ricevuto il numero di ottobre (con notevole ritardo pure questo)
quale metodo usano alle poste per smaltire le consegne arretrate?
Technorati Tags:
poste,
lifo,
fifo
View blog reactions
... ossia l'ambitissimo Dilbert Board Game 
Chissà se mi arriverà per Natale...
View blog reactions
Qualche giorno fa ho scaricato Uptime Gadget, un semplice gadget per la sidebar di Vista che permette di visualizzare il tempo trascorso dall'ultimo riavvio.
Ho provato ad installarlo, ma senza alcun risultato: non avevo alcun messaggio e il gadget non era stato aggiunto a quelli già presenti.
Cercando un po' in rete, ho letto che questo problema capita spesso quando si prova ad installare un gadget in lingua inglese su Vista in italiano. Più in generale, può capitare quando le lingue del gadget e di Vista non coincidono[1].
Grazie ad una dritta suggerita qui ho scoperto come ovviare all'inconveniente:
- Scompattare il file di installazione del gadget, che termina appunto con l'estensione .gadget in una cartella con lo stesso nome, compresa l'estensione (nel mio caso si trattava di Uptime.gadget). Per farlo è sufficiente aprire il file utilizzando Winrar.
- Rinominare la sottocartella en-US, che si trova nella cartella creata, in it-IT
- Copiare la cartella Uptime.Gadget in C:\Program Files\Windows Sidebar\Gadgets, fornendo le necessarie autorizzazioni quando richieste dallo UAC
A questo punto si può tranquillamente aggiungere il gadget alla sidebar, poiché comparirà tra quelli disponibili. 
Nell'immagine il gadget in funzione.
(Giacché c'ero l'ho tradotto, cambiando le stringhe nel file Uptime.Gadget\it-IT\core\core.framework.vbs)
[1] Forse è per questo che sul sito ufficiale i gadget per la sidebar di Vista mi appaiono di default filtrati per lingua = italiano
View blog reactions
More Posts
Next page »