LINQ-TO-XML per creare codice XAML con VB 2010 di renato.marzaro, 07 giugno 2010
LINQ-TO-XML per creare codice XAML con VB 2010 a cura di Renato Marzaro
LINQ-to-Xml permette di creare e leggere qualunque tipo di file a struttura Xml. Da questo concetto non sono esclusi i file .Xaml, che caratterizzano il lato “design” delle applicazioni basate su Windows Presentation Foundation.
In questo articolo vedremo come creare e leggere file .Xaml contenenti definizioni di interfaccia utente WPF, proprio utilizzando LINQ-to-Xml.
Lo scopo del progetto è questo: in WPF è possibile creare i cosiddetti FlowDocument, documenti dal contenuto dinamico e riadattabile al variare delle impostazioni dell’interfaccia. I FlowDocument sono oggetti WPF e, come tali, definibili via XAML. Il loro contenuto può essere visualizzato, poi, in controlli FlowDocumentReader. Poiché i FlowDocument sono definibili via XAML e poiché è possibile caricare a run-time file di codice XAML (utilizzando la classe System.Windows.Markup.XamlReader) ed utilizzarne i controlli in essi definiti da codice managed, vedremo come creare un’applicazione WPF in grado di ricevere da parte dell’utente elementi come titolo, sottotitolo e testo di un ipotetico documento FlowDocument, che verrà salvato come codice XAML tramite LINQ-to-Xml (sfruttando gli Xml Literals di Visual Basic 2010) e poi ricaricato e mostrato in un FlowDocumentReader mediante la classe XamlReader.
La finalità di questo articolo è quella di illustrare un’altra interessante possibilità di utilizzo di LINQ-to-Xml e come integrarlo con WPF, quindi vi rimando alla documentazione MSDN per approfondimenti sul controllo FlowDocumentReader e i FlowDocument.
Create una nuova applicazione WPF (Windows). La finestra sarà suddivisa in tre aree:
la prima, una riga contenente i pulsanti per salvare e leggere i FlowDocument;
la seconda, contenente una serie di Label e TextBox per l’inserimento delle parti principali dell’ipotetico documento, che verrà poi esportato come XAML;
la terza, contenente un controllo FlowDocumentReader che utilizzeremo per visualizzare i FlowDocument esportati;
Iniziamo a definire l’interfaccia, suddividendo il contenitore Grid, proposto per default, in due parti. Questo il primo XAML:
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="40"/>
</Grid.RowDefinitions>
Il codice successivo implementa due pulsanti, uno per l’esportazione e uno per il caricamento dei dati:
<StackPanel Grid.Row="1" Orientation="Horizontal"
HorizontalAlignment="Center">
<Button Margin="10,5,10,5" Name="ExportAsXaml">Salva XAML</Button>
<Button Margin="10,5,10,5" Name="LoadFromXaml">Carica XAML</Button>
</StackPanel>
Il contenitore StackPanel permette di contenere più controlli sulla stessa “linea”, in orizzontale (come nel nostro caso) o in verticale.
Definiamo, ora, un ulteriore StackPanel che conterrà due controlli: un FlowDocumentReader e un Border. Il Border, in particolare, conterrà poi, a sua volta, Label e TextBox. Il FlowDocumentReader:
<StackPanel Grid.Row="0" Orientation="Horizontal">
<FlowDocumentReader Name="MyFlowReader" />
e il Border:
<Border>
<StackPanel Orientation="Vertical" Width="485">
<Label Margin="5,5,5,5">Titolo:</Label>
<TextBox Margin="5,5,5,5" Name="TitleTextBox"></TextBox>
<Label Margin="5,5,5,5">Sottotitolo:</Label>
<TextBox Margin="5,5,5,5" Name="SubTitleTextBox"></TextBox>
<Label Margin="5,5,5,5">Testo documento:</Label>
<TextBox Margin="5,5,5,5" AcceptsReturn="True" AcceptsTab="True"
Name="DocTextBox" Height="340" TextWrapping="Wrap"/ >
</StackPanel>
</Border>
</StackPanel>
A seguito di queste operazioni, il designer si presenta come in figura:
Fate doppio clic sul pulsante Salva XAML, di modo che l’IDE generi automaticamente il relativo gestore di evento. Prima di passare a scrivere codice, facciamo una considerazione. I FlowDocument sono oggetti al cui interno sono nidificati oggetti Paragraph. Ciascun Paragraph rappresenta una porzione di testo che può essere formattata a proprio piacimento, con tanto di figure e collegamenti ipertestuali, gradienti e quant'altro sia possibile sfruttando le nidificazioni di UIElement di WPF. Un piccolo esempio di FlowDocument minimale è il seguente:
<FlowDocument>
<Paragraph FontSize="24" FontWeight="Bold">
Esempio di paragrafo in un FlowDocument
</Paragraph>
</FlowDocument>
Ora, noi abbiamo implementato tre TextBox. Quindi, nel FlowDocument che andremo a creare con LINQ, avremo tre elementi Paragraph. La formattazione di ciascun Paragraph la stabiliremo, per praticità e rapidità illustrativa, direttamente nel codice, anche se potrebbe essere consigliabile permetterne la selezione all’utente. Invece il contenuto di ciascun Paragraph lo scriveremo nel file .Xaml sfruttando le espressioni incorporate (embedded expression) di LINQ-to-Xml. Riporto il codice del gestore dell’evento Click, al termine del quale faremo ulteriori commenti:
Private Sub ExportAsXaml_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles ExportAsXaml.Click
Dim document As XElement = <FlowDocument
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Name="flowDoc" TextAlignment="Justify"
IsOptimalParagraphEnabled="True"
IsHyphenationEnabled="True"
IsColumnWidthFlexible="True"
ColumnWidth="250"
ColumnGap="20">
<Paragraph FontSize="20"
FontWeight="Bold">
<Paragraph.Foreground>
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">
<GradientStop Offset="0" Color="Orange"/>
<GradientStop Offset="0.5" Color="Red"/>
<GradientStop Offset="0.7" Color="Yellow"/>
<GradientStop Offset="0.8" Color="Red"/>
<GradientStop Offset="1" Color="Orange"/>
</LinearGradientBrush>
</Paragraph.Foreground>
<%= TitleTextBox.Text %>
</Paragraph>
<Paragraph FontSize="14"
FontWeight="Bold"
Foreground="Yellow">
<Italic><%= SubTitleTextBox.Text %></Italic>
</Paragraph>
<Paragraph FontSize="12" Foreground="Red">
<%= DocTextBox.Text %>
</Paragraph>
</FlowDocument>
'Conversione in stringa per evitare aggiunta Testa Xml (<?xml version="1.0"?>)
Dim doc As String = document.ToString
My.Computer.FileSystem.WriteAllText(AppDomain.CurrentDomain.BaseDirectory + "MyFlowDocument.xaml", doc, False)
End Sub
Un file .Xaml non deve necessariamente contenere, come elemento di primo livello, un oggetto Window o un oggetto Page, ma può contenere un qualunque controllo, purché venga specificato il namespace Xml relativo allo schema Microsoft per XAML. Di interessante segnalo la dichiarazione dell’oggetto FlowDocument con assegnazione di alcune proprietà di layout e, nel primo elemento Paragraph, l’utilizzo di un gradiente per il colore di primo piano del testo. In sostanza, con LINQ-to-Xml possiamo scrivere file di codice XAML complessi in modo analogo a come vengono generati dall’IDE. Come poi potete osservare, l’utilizzo delle espressioni incorporate all’interno degli elementi Paragraph permette di attingere al contenuto delle caselle di testo desiderate per specificare il valore degli elementi stessi
I tag XML sono stati assegnati a un oggetto XElement. Il contenuto di questo oggetto è stato convertito in stringa, prima di essere salvato su file, poiché se usassimo il metodo Save della classe stessa il compilatore aggiungerebbe automaticamente al file la dichiarazione XML, cosa non necessaria in file .Xaml. Per tale motivo abbiamo poi salvato il file utilizzando il metodo WriteAllText della classe My.Computer.FileSystem. Notate come il file venga salvato nella directory ove risiede l’eseguibile, sfruttando la directory corrente dell’AppDomain. Ovviamente, per un test come questo va bene specificare in anticipo il nome del file da scrivere, ma sicuramente è preferibile richiedere all’utente di effettuare la sua scelta.
Inserite, poi, le due seguenti direttive in testa al file di codice:
Imports System.Windows.Markup
Imports System.IO, System.Xml
Il passaggio successivo è quello di implementare il gestore dell’evento Click del pulsante per la lettura del documento. Il codice, commentato al suo interno, è il seguente:
Private Sub LoadFromXaml_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles LoadFromXaml.Click
'Istanzia nuovo stream che punta al file .Xaml da leggere
Dim xamlStream As New StreamReader(AppDomain.CurrentDomain.BaseDirectory + "MyFD_Test.xaml")
' XmlReader visto che i file XAML hanno struttura Xml
Dim reader As XmlReader = XmlReader.Create(xamlStream)
'XamlReader esegue parsing file Xml
Dim myDocument As FlowDocument = CType(XamlReader.Load(reader), FlowDocument)
'Assegna a FlowDocumentReader contenuto file .Xaml
MyFlowReader.Document = myDocument
End Sub
Intendo dare risalto alle ultime due righe di codice. Nella prima, si dichiara un oggetto FlowDocument che riceve il contenuto del file .Xaml, caricato tramite il metodo Load della classe XamlReader e convertito in FlowDocument. Il controllo FlowDocumentReader predisposto nell’interfaccia riceve, poi, il nuovo oggetto.
Non ci resta che avviare l’applicazione. La figura seguente mostra un esempio di inserimento di testo, mostrato dopo essere stato salvato come file di codice XAML tramite LINQ-to-Xml e ricaricato tramite la classe XamlReader:
Riassumendo nell’articolo abbiamo visto :
esistenza e utilizzo dei documenti FlowDocument in WPF;
caricamento a run-time di file di codice XAML tramite Visual Basic;
generazione di file di codice XAML tramite LINQ-to-Xml.
