November 2007 - Posts
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
Mi è capitato di dover aprire da Office 2007 (Word ed Excel) dei file creati con OpenOffice e salvati in formato ODF (OpenDocument File), rispettivamente con estensione .odt (OpenDocument Text) e .ods (OpenDocument Spreadsheet).
Per chi dovesse avere questo stesso problema, o magari quello di aprire file .odp (OpenDocument Presentation), o più in generale di ineroperabilità tra i formati ODF e Microsoft OpenXML, può tornare utile il progetto di OpenXML/ODF Translator Add-ins for Office (grazie a Roberto per la segnalazione).
Io ho scaricato, installato e provato solo gli ODF Add-in per Word 2007 e per Excel 2007, che sembrano funzionare egregiamente.
Una volta installati gli add-in, sia in Word 2007 che in Excel 2007 compaiono due nuove voci del menù, per aprire e salvare file ODF, come nella figura seguente.

View blog reactions