mercoledì 30 novembre 2011
#

Per rendere multilingua l’interfaccia del nostro plug-in dobbiamo aggiungere al progetto Silverlight dei File di risorse contenenti le traduzioni di tutti i testi che vogliamo visualizzare.
Per localizzazione non si intende la traduzione automatica di tutti i messaggi dell’applicazione, bensì semplicemente la traduzione di tutti i testi visibili nei controlli. La traduzione dei messaggi di validazione la vedremo in un prossimo post. 
Creiamo un nuova applicazione Silverlight:

Creiamo il nostro controllo:

<Grid x:Name="LayoutRoot" Background="White">
<sdk:Label Margin="25,24,0,0" Name="Label1" Content="Label1" HorizontalContentAlignment="Center" HorizontalAlignment="Left" Width="55" Height="23" VerticalAlignment="Top" d:LayoutOverrides="VerticalAlignment" />
<TextBox Height="23" HorizontalAlignment="Left" Margin="84,20,0,0" Name="TextBox1" VerticalAlignment="Top" Width="120" HorizontalContentAlignment="Center" />
<Button Content="Button" Height="23" HorizontalAlignment="Left" Margin="84,49,0,0" Name="Button1" VerticalAlignment="Top" Width="120" HorizontalContentAlignment="Center" />
</Grid>
Andiamo ad impostare le culture (CultureInfo) che il nostro plug-in potrà identificare. Per farlo dobbiamo modificare il file di progetto. Scarichiamo il progetto e andiamo a modificarne il file .vbproj:


Identifichiamo il tag <SupportedCultures>
<SupportedCultures>it</SupportedCultures>
ed aggiungiamoci le lingue desiderate:
<SupportedCultures>it;en;en-US;fr;</SupportedCultures>
In questo caso, oltre che all’italiano, abbiamo aggiunto l’inglese (en), l’inglese americano (en-US), ed il francese (fr).
Salviamo il file e ricarichiamo il progetto:

Aggiungiamo un file di risorse in cui andremo ad inserire tutte le descrizioni che vogliamo visualizzare nelle varie lingue. In Esplora Soluzioni clicchiamo col tasto destro sul nome del progetto (o in una cartella specifica), selezioniamo Aggiungi=>Nuovo elemento… ed aggiungiamo un File di risorse:

Apriamo il file di risorse cliccandoci sopra due volte:

Inseriamo due nuovi valori stringa che si riferiscono rispettivamente al testo dell’etichetta ‘Label1’ ed al testo del pulsante ‘Button1’.
Stiamo impostando i valori per la lingua di default, ossia l’italiano, quindi i valori delle stringhe dovranno essere in italiano.
Impostare come Public i marcatori d’accesso di ogni risorsa.
A questo punto è necessario crearsi una classe che ci permetta di accedere alle risorse tramite il DataBinding dei controlli.

Public Class myLocalization
' Wrapper per le risorse.
Public ReadOnly Property Risorse_MainPage() As My.Resources.MainPage
Get
Return New My.Resources.MainPage
End Get
End Property
End Class
NB: se nel file delle risorse non avessimo impostato i marcatori su Public, non avremmo potuto accedervi dalla nostra classe.
Referenziamo la nostra classe all’interno del nostro plug-in importando il relativo namespace (nell’esempio lo contrassegniamo col suffisso ’my’):
<UserControl x:Class="myLocalization.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
xmlns:my="clr-namespace:myLocalization"
Inseriamo la nostra classe nelle risorse statiche (StaticResource) dell’UserControl Silverlight:
<UserControl.Resources>
<my:myLocalization x:Key="myWrapperLocalization"/> <!--Imposta nelle risorse la classe per la localizzazione.-->
</UserControl.Resources>
A questo punto la nostra classe (che è esposta col nome ‘myWrapperLocalization’) è accessibile da tutti i controlli contenuti all’interno dell’UserControl.
Impostiamo il binding dei controlli interessati:
- Label1 => Content
- Button1 => Content

Origine (Source):

Percorso (Path):

Questo è il risultato in XAML:
<Button Content="{Binding Path=Risorse_MainPage.Button1, Source={StaticResource myWrapperLocalization}}"
Una volta impostati il binding è possibile notare che nel design i controlli hanno già recuperato i valori della lingua di default:

Adesso creiamo le risorse per tutte le altre lingue che ci interessano. Copiare/Incollare il file di risorsa (.resx) che abbiamo utilizzato per la lingua di default (MainPage.resx) e traduciamo ogni singolo valore:

Rinominiamo la nostra risorsa appena tradotta in ‘MainPage.en.resex’.

In questo modo abbiamo creato un file di risorse per la lingua inglese.
Modifichiamo i valori delle risorse all’interno di questo file stando attenti a NON MODIFICARE le relative chiavi identificative (Nome):
Il motore delle risorse segue un ordine ben preciso nel leggere i file .resex riferiti alle culture: per primo legge il file contenente la lingua specifica, ossia quello denominato ‘nomefile.[cultura-lingua].resex’ => MainPage.en-US.resex (in questo caso l’inglese americano), se eventualmente non dovesse trovare questo file, allora legge quello riferito alla cultura generica (detta neutrale) ‘nomefile.[cultura].resex’ => MainPage.en.resex . Nel caso poi non dovesse trovare neppure il file con la cultura generica, il motore andrà a leggere il file con la lingua di default MainPage.resex .
Questa lettura gerarchica ci permette di scrivere la maggior parte delle voci multilingue nei file .resex relativi alle lingue generiche. Solo nei casi particolari si dovranno specificare le stesse voci nei file delle lingue specifiche.
Crearsi tutti i file desiderati ricordandosi di rinominare correttamente ognuno dei file .resx:

Di default Silverlight legge la lingua del sistema operativo, quindi per testare il progetto dobbiamo apportare alcune modifiche.
Possiamo testare la nostra applicazione in due modi:
- forzare da codice la lingua del plug-in;
- impostare la lingua del plug-in tramite la pagina web che lo ospita.
Il modo più semplice è quello di forzare la lingua del plug-in nel suo evento Application_Startup:
Private Sub Application_Startup(ByVal o As Object, ByVal e As StartupEventArgs) Handles Me.Startup
' Forza la lingua.
System.Threading.Thread.CurrentThread.CurrentUICulture = New Globalization.CultureInfo("fr")
Me.RootVisual = New MainPage()
End Sub
Il nostro controllo in francese:

Per impostare la lingua dalla pagina web che ospita il controllo vi rimando al post ASP.NET: passare dinamicamente un valore (o un insieme di valori) a Silverlight.
In questo modo sarà possibile testare la nostra applicazione Silverlight cambiando le impostazioni della lingua direttamente dal nostro browser:



lunedì 28 novembre 2011
#
A differenza del post precedente (ASP.NET: passare un valore statico a Silverlight), per passare dinamicamente un valore a Silverlight da una pagina .aspx, bisogna: contrassegnare il plug-in Silverlight come oggetto server, aggiungerci il parametro InitParams, contrassegnare anch’esso come oggetto server, popolare il parametro direttamente dalla pagina .aspx.
Assegnare un identificativo (id) all’oggetto Silverlight e contrassegnare il plug-in come oggetto server:
<body>
<form id="form1" runat="server" style="height:100%">
<div id="silverlightControlHost" runat="server">
<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
<param name="source" value="ClientBin/SilverlightApplication1.xap"/>
<param name="onError" value="onSilverlightError" />
<param name="background" value="white" />
<param name="minRuntimeVersion" value="4.0.50826.0" />
<param name="autoUpgrade" value="true" />
<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.50826.0" style="text-decoration:none">
<img src="http://go.microsoft.com/fwlink/?LinkId=161376" alt="Scarica Microsoft Silverlight" style="border-style:none"/>
</a>
</object><iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px"></iframe></div>
</form>
</body>
Aggiungere il parametro InitParams, contrassegnarlo come oggetto server, impostargli un identificativo e aggiungergli un attributo ‘value’ vuoto:
<body>
<form id="form1" runat="server" style="height:100%">
<div id="silverlightControlHost" runat="server">
<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
<param name="source" value="ClientBin/SilverlightApplication1.xap"/>
<param name="onError" value="onSilverlightError" />
<param name="background" value="white" />
<param name="minRuntimeVersion" value="4.0.50826.0" />
<param name="autoUpgrade" value="true" />
<param name="initParams" id="myInitParams" runat="server" value=""/>
<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.50826.0" style="text-decoration:none">
<img src="http://go.microsoft.com/fwlink/?LinkId=161376" alt="Scarica Microsoft Silverlight" style="border-style:none"/>
</a>
</object><iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px"></iframe></div>
</form>
</body>
In un qualunque evento della pagina .aspx che precede l’inizializzazione dell’oggetto Silverlight, aggiungere la coppia chiave/valore dei dati desiderati all’attributo ‘value’:
Public Class SilverlightApplication1TestPage
Inherits System.Web.UI.Page
Private Sub Me_Load(sender As Object, e As System.EventArgs) Handles Me.Load
If Not IsPostBack Then
' Recupera le preferenze linguistiche del browser.
Dim preferenzaCultura As String() = HttpContext.Current.Request.UserLanguages
' Passa il valore all'oggetto Silverlight.
Me.myInitParams.Attributes.Add("value", "UICulture=" & preferenzaCultura(0))
End If
End Sub
End Class
Aprire il file di code-behind ‘App.xaml.vb(cs)’ e nell’Application_Startup leggere la collection InitParams:
Private Sub Application_Startup(ByVal o As Object, ByVal e As StartupEventArgs) Handles Me.Startup
' Legge i parametri iniziali del pug-in.
If e.InitParams.Count > 0 Then
Dim cultureName As String = e.InitParams("UICulture").ToString()
' Imposta la lingua del browser.
If String.IsNullOrEmpty(cultureName) = False Then
System.Threading.Thread.CurrentThread.CurrentUICulture = New Globalization.CultureInfo(cultureName)
End If
' Aggiunge il valore alle risorse.
App.Current.Resources.Add("UICulture", System.Threading.Thread.CurrentThread.CurrentUICulture.DisplayName)
End If
Me.RootVisual = New MainPage()
End Sub
In questo esempio, dalla pagina .aspx è stata passata la preferenza della lingua del browser e Silverlight l’ha impostata come propria lingua dell’applicazione.

E’ possibile passare dinamicamente più di un valore all’oggetto Silverlight:
Me.myInitParams.Attributes.Add("value", "chiave1=Valore1,chiave2=Valore2,Chiave3=Valore3")
Per un esempio completo sul passaggio di più valori leggere il post ASP.NET: passare un valore statico a Silverlight.
Per passare a Silverlight il valore di una pagina .aspx bisogna aggiungere allo stesso plug-in il parametro <InitParams>.
Nella pagina del server identificare l’inizializzazione dell’oggetto Silverlight ed aggiungerci il seguente tag:
<param name="initParams" value="chiave1=valore1"/>
dove l’attributo ‘value’ è il nostro valore personalizzato contrassegnato dalla chiave univoca ‘chiave1’.
<body>
<form id="form1" runat="server" style="height:100%">
<div id="silverlightControlHost">
<object data="data:application/x-silverlight-2," type="application/x-silverlight-2" width="100%" height="100%">
<param name="source" value="ClientBin/SilverlightApplication1.xap"/>
<param name="onError" value="onSilverlightError" />
<param name="background" value="white" />
<param name="minRuntimeVersion" value="4.0.50826.0" />
<param name="autoUpgrade" value="true" />
<param name="initParams" value="chiave1=valore1"/>
<a href="http://go.microsoft.com/fwlink/?LinkID=149156&v=4.0.50826.0" style="text-decoration:none">
<img src="http://go.microsoft.com/fwlink/?LinkId=161376" alt="Scarica Microsoft Silverlight" style="border-style:none"/>
</a>
</object><iframe id="_sl_historyFrame" style="visibility:hidden;height:0px;width:0px;border:0px"></iframe></div>
</form>
</body>
A questo punto è possibile leggere il parametro InitParams direttamente dall’applicazione Silverlight.
Aprire il file di code-behind ‘App.xaml.vb(cs)’ e nell’Application_Startup recuperare e memorizzare nelle risorse dell’applicazione il contenuto della collection InitParams:
Private Sub Application_Startup(ByVal o As Object, ByVal e As StartupEventArgs) Handles Me.Startup
' Legge i parametri iniziali del pug-in.
Me.Resources.Add("Nuovo nome risorsa", e.InitParams("chiave1"))
Me.RootVisual = New MainPage()
End Sub
La collection InitParams è leggibile solamente in questa routine dell’applicazione Silverlight.
A questo punto è possibile recuperare il valore passato dalla pagina .aspx recuperandolo dalle risorse dell’applicazione:
Partial Public Class MainPage
Inherits UserControl
Public Sub New()
InitializeComponent()
Me.Label1.Content = App.Current.Resources("Nuovo nome risorsa")
End Sub
End Class

E’ possibile inserire più di un valore nell’inizializzazione dell’oggetto Silverlight:
<param name="initParams" value="chiave1=valore1,chiave2=valore2,chiave3=valore3"/>
Per leggere tutti i valori impostati si dovrà ciclare l’intera collections InitParams:
Private Sub Application_Startup(ByVal o As Object, ByVal e As StartupEventArgs) Handles Me.Startup
' Legge i parametri iniziali del pug-in.
For Each p In e.InitParams
Me.Resources.Add(p.Key, p.Value)
Next
Me.RootVisual = New MainPage()
End Sub
Successivamente si potrà ciclare l’intera collection delle risorse dell’applicazione, oppure si potrà richiamare direttamente un singolo valore tramite la sua chiave identificativa:
Partial Public Class MainPage
Inherits UserControl
Public Sub New()
InitializeComponent()
For Each r In App.Current.Resources
Me.Label1.Content &= String.Format("{0}={1}{2}", r.key, r.value, Environment.NewLine)
Next
End Sub
End Class

Abbiamo visto come passare dei valori statici all’oggetto Silverlight, ossia scrivendoli manualmente del markup della pagina .aspx in fase di progettazione della stessa. Per passare invece dei valori dinamicamente leggere il post ASP.NET: passare dinamicamente un valore (o un insieme di valori) a Silverlight.
venerdì 25 novembre 2011
#
In attesa di sapere se esisterà mai una versione 6.0 di Silverlight, facciamoci due “risate”……
lunedì 14 novembre 2011
#
Per iterare tutti i gli elementi (UIElement) all'interno di una pagina di Silverlight, è possibile utilizzare
la classe VisualTreeHelper il cui metodo GetChild va richiamato in modo ricorsivo:

Private Function RecuperaFigli(ByVal d As DependencyObject) _
As IEnumerable(Of DependencyObject)
' Itera gli elementi in modo ricorsivo.
Dim elementi As New List(Of DependencyObject)
elementi.Add(d)
For i As Integer = 0 To VisualTreeHelper.GetChildrenCount(d) - 1
elementi.AddRange(RecuperaFigli(VisualTreeHelper.GetChild(d, i)))
Next
' Ritorna il valore.
Return elementi
End Function
Private Sub MainPage_Loaded(sender As Object, e As System.Windows.RoutedEventArgs) _
Handles Me.Loaded
' Recupera tutti i controlli della pagina.
Dim elencoControlli = Me.RecuperaFigli(Me)
' Visualizza l'elenco in una messagebox.
Dim msg As String = String.Empty
For Each u As UIElement In elencoControlli
msg &= String.Format("{0}{1}", u.GetType.FullName, Environment.NewLine)
Next
MessageBox.Show(msg)
End Sub
lunedì 11 aprile 2011
#
Memorizzo qui un piccolo snippet per popolare manualmente una DataGridView (bound e/o unbound) in cui sono già state create le DataGridViewColumns.
Dim dgvRow As New DataGridViewRow
dgvRow.CreateCells(Me.dgvAllegati)
With dgvRow
.Cells(0).Value = "valore colonna 0"
.Cells(1).Value = "valore colonna 1"
.Cells(2).Value = "valore colonna 2"
.Cells(3).Value = "valore colonna 3"
End With
Me.dgvAllegati.Rows.Add(dgvRow)
Questo codice è comodo se non vogliamo andare a lavorare direttamente sulla sorgente dati.
martedì 9 marzo 2010
#
Lavorando (“smanettando”) coi Web Services si potrebbe incorrere nella seguente eccezione di Visual Studio:
“Impossibile caricare la sezione di configurazione endpoint per il contratto 'mio Web Service Soap'. Trovata più di una configurazione per il contratto. Indicare la sezione di configurazione endpoint preferita per nome.”
L’errore non sta nella configurazione del server, bensì nella configurazione dell’applicazione che deve collegarsi al Web Service.
Succede che nel file di configuazione dell’applicazione talvolta vengano generati in automatico due riferimenti uguali allo stesso Web Service.
Per correggere l’errore basta aprire il file di configurazione dell’applicazione selezionandolo da Esplora Soluzioni di Visual Studio:
Scorrerlo ed identificare il tag <endpoint> in cui viene configurata la chiamata al Web Service.
A questo punto è possibile notare la doppia dichiarazione:
Basterà quindi eliminare la seconda (terza, o quarta… :-P) dichiarazione per far funzionare correttamente la nostra applicazione.
HTH
PS: Perchè in italiano c’è pochissima documentazione a riguardo??…
Sfruttando gli ultimi 2 post: Macro per utilizzare lo scanner da Word 2007 e Esportare un documento Office 2007 in PDF, ho leggermente automatizzato il procedimento aziendale di “scansione, inserimento immagni, esportazione in PDF”.
Tutta la procedura viene svolta da una semplice macro:
Sub Crea_PDF()
'
' Crea_PDF Macro
' Riduce i margini del foglio.
With Selection.PageSetup
.LineNumbering.Active = False
.Orientation = wdOrientPortrait
.TopMargin = CentimetersToPoints(1.27)
.BottomMargin = CentimetersToPoints(1.27)
.LeftMargin = CentimetersToPoints(1.27)
.RightMargin = CentimetersToPoints(1.27)
.Gutter = CentimetersToPoints(0)
.HeaderDistance = CentimetersToPoints(1.25)
.FooterDistance = CentimetersToPoints(1.25)
.PageWidth = CentimetersToPoints(21)
.PageHeight = CentimetersToPoints(29.7)
.FirstPageTray = wdPrinterDefaultBin
.OtherPagesTray = wdPrinterDefaultBin
.SectionStart = wdSectionNewPage
.OddAndEvenPagesHeaderFooter = False
.DifferentFirstPageHeaderFooter = False
.VerticalAlignment = wdAlignVerticalTop
.SuppressEndnotes = False
.MirrorMargins = False
.TwoPagesOnOne = False
.BookFoldPrinting = False
.BookFoldRevPrinting = False
.BookFoldPrintingSheets = 1
.GutterPos = wdGutterPosLeft
End With
' Scannerizza le immagini.
On Error Resume Next
WordBasic.InsertImagerScan
' Esporta in PDF.
ActiveDocument.ExportAsFixedFormat OutputFileName:= _
"C:\Documents and Settings\Andrea\Desktop\Doc1.pdf", ExportFormat:= _
wdExportFormatPDF, OpenAfterExport:=True, OptimizeFor:= _
wdExportOptimizeForPrint, Range:=wdExportAllDocument, From:=1, To:=1, _
Item:=wdExportDocumentContent, IncludeDocProps:=True, KeepIRM:=True, _
CreateBookmarks:=wdExportCreateNoBookmarks, DocStructureTags:=True, _
BitmapMissingFonts:=True, UseISO19005_1:=False
' Messaggio di notifica.
MsgBox "Creazione del file PDF completata.", vbInformation, "Crea PDF"
' Chiude Word chiedendo conferma.
Application.Quit
End Sub
NB: In questo caso il file PDF viene generato nel “C:\Documents and Settings\Andrea\Desktop”, sempre col solito nome (Doc1.pdf). Questa è una soluzione rapida per chi, come me, crea dei semplici file temporanei. E’ invece possibile personalizzare i nomi dei file inserendo ad esempio una inputbox prima dell’esportazione.
Per esportare un documento Office 2007 (Word, Excel) in PDF, basta installare questo componente aggiuntivo scaricabile direttamente dal sito Microsoft:
http://www.microsoft.com/downloads/details.aspx?familyid=4d951911-3e7e-4ae6-b059-a2e79ed87041&displaylang=it
Una volta installato il componente basterà poi cliccare su: Pulsante Office=>Salva con nome=>PDX o XPS.
That's all folks!
Per scrivere una macro che utilizza lo scanner da Word 2007 basta una sola riga di codice:
WordBasic.InsertImagerScan
In questo modo, appena effettuata la scansione, l’immagine verrà inserita automaticamente all’interno del documento.