Gianni Giaccaglini

Tricks & mini applics on WPF
posts - 46, comments - 0, trackbacks - 0

Binding ADO tramite classe ad hoc

 

Binding ADO tramite classe ad hoc

Il binding su dataset ADO, tema ovviamente centrale nel mondo WPF, può essere implementato in vari modi. Il più semplice sfrutta il controllo DataGrid (grossomodo equivalente al DataGridView dei Form). Il caso qui discusso è tratto da WPF in action, un manuale dell’Editore Manning

(www.manning.com) che chi scrive ha già raccomandato in questo blog. Il metodo adottato si basa su una classe specifica che definisce un semplice dataset formato da due tabelle, con proprietà che corrispondono ai rispettivi campi unitamente a metodi opportuni per crearle e gestirle, dopo di che istruzioni binding associate a controlli della finestra WPF, tipicamente delle TextBox, provvedono automaticamente ad attingere i valori dei campi desiderati.

Preparazione del progetto

La nostra mini applicazione ha lo scopo di gestire un insieme di indicatori (bookmars) relativi a postazioni web di vario genere. La classe in esame, denominata Biblioteca, possiede come tabella principale una Bookmarks per l’appunto, dotata di campi ovvero colonne (Column nel gergo ADO) Id, Titolo, Uri, Genere e UltimaMod (data dell’ultima modifica). Una seconda tabella reca invece i campi Nome e Quant (si noterà poi che, di fatto, non tutti i campi, pensati per rendere la classe più aperta, sono utilizzati nella mia soluzione).

Dal sito seguente si può scaricare un archivio Bibliotecario.zip comprendente l'omonimo eseguibile (fruibile solo su PC ove è installato VisualStudio 2010) più una figura .jpg che mostra il risultato da conseguire:

http://www.giannigiaccaglini.it/download/Bibliotecario.zip

Da cui si può notare che il cuore della finestra primaria sta nel riquadro in alto a sinistra. Come vedremo si tratta di una ListWiew dotata di colonne intestate (Nome e URL), ciascuna comprendente i campi ricavati tramite binding a proprietà delle classi Biblioteca fin d’ora ben intuibili (relative in particolare alla tabella Bookmars). Selezionando e cliccando su un segnalibro si ottiene la comparsa del Genere (Editore, Sito o quant’altro) in alto a destra, mentre l’indirizzo URL viene replicato più sotto. Evidente lo scopo dei pulsanti di comando in basso.

La prima operazione da compiere, fin dalla creazione del progetto, è l’assegnazione di un nome. Scegliamo, con qualche enfasi, Bibliotecario.

Allo scopo di rendere accessibile la classe Biblioteca, descritta in dettaglio fra poco unitamente al dataset Biblio che ne costituisce la base, occorrono due modifiche di tipo local al file Application.xml, che riporto per intero, per massima comodità:

 

</

Imports

Imports System.IO

Imports System.ComponentModel

#Region

Biblio.Tables(

I commenti che sto per esporre hanno natura descrittiva, essendo la conoscenza di ADO un presupposto (ma questa soluzione, volendo, funziona pure come ricetta). Cominciamo con i metodi Load e Save che ben si vedano sopra questo paragrafo. Come anticipato, il primo legge lo schema BiblioFileName, definito nell’archivio bookmarks.library, mentre Save accetta le modifiche apportate assegnando i dati default e, inoltre, trascrive il predetto schema. Per visualizzarlo è sufficiente aprire bookmarks.library, file automaticamente creato e collocato accanto all’eseguibile finale Bibliotecario.exe. Si può visualizzarlo con un editor di testo (Notepad incluso), comunque lo riporto qui sotto per comodità dei pigri:

<xs:element name="Bibliotecario" msdata:IsDataSet="true" msdata:UseCurrentLocale="true"><Titolo>O’ Reilly</Titolo></Bookmarks><Genere>Editore</Genere></Bookmarks><Genere>Sito</Genere></Bookmarks><Quant>0</Quant>

#Region

End

Public

Bookmarks.Rows.Add(

Biblio.AcceptChanges()

NotifyPropertyChanged(

L’istruzione conclusive, evidenziata in grassetto, secondo gli Autori del sullodato manuale Manning è quella che assicura il binding tra i campi delle tabelle di Biblio e i componenti di MainWindow.xaml. Francamente però il meccanismo di tale "magia" (proprio così la indicano i nostri autori) non mi è parso troppo chiaro. Così ho osato eliminare non solo l’istruzione NotifyPropertyChanged("Bookmarks") ma l’intero meccanismo di questa interfaccia, vale a dire l’intera Region "INotifyPropertyChanged" e l’istruzione iniziale Implements INotifyPropertyChanged. Risultato: l’applicativo continua a funzionare!

Nota

A questo punto affido il resto dei metodi e proprietà della classe Biblioteca all’esegesi autogestita di quanti possiedono perlomeno i rudimenti di ADO.

. Ovvero sembra che la soluzione se la cavi con le sole istruzioni ADO. Se qualche esperto è in grado di chiarire questo mistero, si faccia avanti.

<

<</

</

Class

DimBiblio.Save()MessageBoxEndDimEnd

L’interfaccia INotifyPropertyChanged , come dice il suo nome, interviene con un evento scatenantesi quando una certa proprietà viene aggiornata. Essendo meno nota della più popolare IComparable ne riprendo quasi pari pari l’esempio della Guida:

' Direttive Imports

... Omissis ...

Public

 

ValNome = valueNotifyPropertyChanged(' aggiornata la proprietà passata come argomento (propertyName) PublicLa conoscenza delle interfacce è un prerequisito, pertanto spero che bastino ii commenti incorporati, tuttavia insisto nel sottolineare l’assoluta necessità di inserire Implements INotifyPropertyChanged.PropertyChanged anche accanto al metodo NotifyPropertyChanged

<

<

 

</

Private

 

 

 

End

Private

 

 

 

W1.Close()

 

End

 

sono state rigettate e accettate solo dopo un primo clic su F5, inoltre nel riquadro Progettazione della Mainwindow perdurano in alto messaggi di errore che indicano l'impossibilità di trovare Biblioteca! cui si accompagna l'impossibilità di usare la Casella strumenti per apportare modifiche (cosa che respa possibile nel sottostante riquadro XAML).

Ho motivo di temere che a queste difficoltà andranno incontro molti altri soggetti di media competenza. Di qui l'appello ai massimi esperti:

per favore chiarite al popolo le possibili cause, indicando con chiari DETTAGLI i passi da compiere per evitarle!

xmlns:local="clr-namespace:Bibliotecario"

 

<Application.Resources>

 

<local:Biblioteca x:Key="Biblio" />

 

</Application.Resources>
Sub

Nessun rilievo tranne sottolineare l’importanza, in luogo del normale Show, del metodo ShowDialog che "mette in primo piano" la Window1, restituendo alla chiusura i dati immessi dall’utente.

Problemi possibili...

Anzi probabili, che chi scrive confessa di aver risolto sì (come dimostra il mini applicativo Bibliotecario.exe) ma fortunosamente. Il punto è che le istruzioni

Exit Sub

 

End If

Biblio.AddBookmark(Txt1, Txt2, Txt3)

' MessageBox.Show("Tutte le caselle di testo andavano riempite!")
Sub Aggiungi(ByVal sender As Object, ByVal e As RoutedEventArgs)Dim Biblio As Biblioteca = CType(FindResource("Biblio"), Biblioteca)Dim W1 As New Window1

W1.ShowDialog()

 

 

 

 

Dim Txt1 = W1.TextBox1.TextDim Txt2 = W1.TextBox2.TextDim Txt3 = W1.TextBox3.TextIf Txt1 = "" Or Txt2 = "" Or txt3 = "" Then
Sub

Dopo di che la nuova Sub Aggiungi si presenta così:

MessageBox.Show("Tutte le caselle di testo vanno rienmpite!")Exit Sub

 

End If

 

Me.Close()
Sub btnOK_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles btnOK.ClickIf TextBox1.Text = "" Or TextBox2.Text = "" Or TextBox3.Text = "" Then
Grid>Window>

Essa fornisce tre TextBox affiancate a sinistra da Label contenenti "Nome:", "Indirizzo URL" e "Genere" indicanti a chiare lettere il significato delle caselle di testo, più un pulsante "OK", associato a questa routine cliccante, che obbliga l’utente a completare tutte e tre le caselle::

<RowDefinition />

 

<RowDefinition />

 

<RowDefinition />

 

<RowDefinition />

 

</Grid.RowDefinitions>

 

<Label Content="Nome:" Height="30" HorizontalAlignment="Left" Margin="16,29,0,0" Name="Label1" VerticalAlignment="Top" Width="81" />

 

<TextBox Height="36" HorizontalAlignment="Right" Margin="0,28,4,0" Name="TextBox1" VerticalAlignment="Top" Width="248" />

 

<Label Grid.Row="1" Content= "Indirizzo URL:" Height="30" HorizontalAlignment="Left" Margin="16,29,0,0" Name="Label2" VerticalAlignment="Top" Width="81" />

 

<TextBox Grid.Row="1" Height="36" HorizontalAlignment="Right" Margin="0,28,4,0" Name="TextBox2" VerticalAlignment="Top" Width="248" />

 

<Label Grid.Row="2" Content="Genere:" Height="30" HorizontalAlignment="Left" Margin="16,29,0,0" Name="Label3" VerticalAlignment="Top" Width="81" />

 

<TextBox Grid.Row="2" Height="36" HorizontalAlignment="Right" Margin="0,28,4,0" Name="TextBox3" VerticalAlignment="Top" Width="248" />

 

<Button Grid.Row="3" Name="btnOK" Content="OK" Margin="111,21,142,12" HorizontalAlignment="Center" Width="132" />

 

</

Grid.RowDefinitions>
Window x:Class="Window1"

 

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

 

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

 

Title="Immetti dati" Height="300" Width="407">

 

<Grid>

 

Sub NotifyPropertyChanged(ByVal propertyName As String)_Implements INotifyPropertyChanged.PropertyChanged ' Da NON dimenticare!

 

 

End

RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))End Sub Class

Nota.

Un perfezionamento del metodo Aggiungi

Per lo scopo didattico primario ho previsto l’aggiunta di un record fisso (relativo a Wikipedia). Ma alla fine ho escogitato una semplice miglioria, basata sull’utilizzo di una seconda Window1:

 

 

Class PersonaImplements INotifyPropertyChanged

 

Private ValNome As String

 

Public Property Nome() As String

 

Get

 

 

Return ValNomeEnd Get

 

 

Set(ByVal value As String)

' Quando la proprietà cambia attiva la routine NotifyPropertyChanged

 

 

"Nome")End Set

 

End Property

 

' Dichiara l’evento PropertyChanged

 

 

 

Public Event PropertyChanged As PropertyChangedEventHandler _Implements INotifyPropertyChanged.PropertyChanged' NotifyPropertyChanged lancia l’evento PropertyChanged allorché viene

 

Imports

System.ComponentModel

Imports

 

 

System.Windows.Data

' Classe che implementa l’interfaccia INotifyPropertyChanged

Sub Class

Richiamo su INotifyPropertyChanged

Biblio As Biblioteca = CType(FindResource("Biblio"), Biblioteca)"Wikipedia", "http://www.wikipedia.it", "Sito")
Try

 

End Sub

 

 

Biblio.AddBookmark(

 

End

 

Private Sub Aggiungi(ByVal sender As Object, ByVal e As RoutedEventArgs)
.Show("Si deve prima selezionare un record!")
Biblio As Biblioteca = CType(FindResource("Biblio"), Biblioteca)End Sub

 

 

 

Private Sub Cancella(ByVal sender As Object, ByVal e As RoutedEventArgs)Dim row As DataRowView = CType(bookmarks.SelectedItem, DataRowView)Try

row.Delete()

 

Catch ex As Exception

 

 

MainWindow

 

Close()

 

Private Sub Chiudi(ByVal sender As Object, ByVal e As RoutedEventArgs)End Sub

 

 

 

 

Private Sub Salva(ByVal sender As Object, ByVal e As RoutedEventArgs)
StackPanel>

 

<Grid DockPanel.Dock="Bottom" DataContext="{Binding ElementName=bookmarks, Path=SelectedItem}">

 

<Grid.ColumnDefinitions>

 

<ColumnDefinition Width="1*" />

 

<ColumnDefinition Width="1*" />

 

</Grid.ColumnDefinitions>

 

<Grid.RowDefinitions>

 

<RowDefinition Height="1*" />

 

<RowDefinition Height="1*" />

 

</Grid.RowDefinitions>

 

<DockPanel Grid.Column="0" Grid.Row="0">

 

<Label MinWidth="50" DockPanel.Dock="Left" Content="Titolo:" />

 

<TextBox Text="{Binding Path=Titolo}" />

 

</DockPanel>

 

<DockPanel Grid.Column="1" Grid.Row="0">

 

<Label MinWidth="50" DockPanel.Dock="Left" Content="Genere:" />

 

<TextBox Margin="0,0,10,0" Text="{Binding Path=Genere}" />

 

</DockPanel>

 

<DockPanel Grid.Column="0" Grid.Row="1" Grid.ColumnSpan="2">

 

<Label MinWidth="50" Content="URL:"/>

 

<TextBox Margin="0,0,10,0" Text="{Binding Path=Uri}"/>

 

</DockPanel>

 

 

<ListView Name="bookmarks"

ItemsSource

"{

=Binding Source={StaticResource Biblio}, Path=Bookmarks}">

 

<ListView.View>

 

<GridView>

 

<GridViewColumn Header="Nome"

DisplayMemberBinding

="{Binding Path=Titolo}"/>

 

<GridViewColumn Header="URL"

DisplayMemberBinding

="{Binding Path=Uri}"/>

 

</GridView>

 

</ListView.View>

 

</ListView>

 

</Grid>

 

</DockPanel>Window>

I commenti? Questo intervento si rivolge a gente di medio calibro, che come minimo possiede un’infarinatura sulle tecniche binding, pertanto li affido interamente all’esegesi autogestita, favorita, spero, da un certa eloquenza del codice XAML.

Codice MainWindow.xaml.VB

È relativo agli eventi click dei vari pulsanti e a questo punto non resta che riportarlo senza ulteriori commenti, salvo osservare che per estrema brevità il metodo Aggiungi prevede l’aggiunta di un sito costante:

Biblio.AddBookmark(

"Wikipedia", "http://www.wikipedia.it", "Sito")

Lasciamo a chi fosse interessato la facile modifica ottenuta aggiungendo tre opportune caselle di testo.

Button MinWidth="60" Content="Salva" Click="Salva" />

 

<Button MinWidth="60" Content="Cancella" Margin="30,0,0,0"

Click

="Cancella" />

 

<Button MinWidth="60" Content="Aggiungi" Click="Aggiungi" />

 

Window x:Class="MainWindow"

 

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

 

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

 

Title="MainWindow" Height="350" Width="525">

 

<DockPanel LastChildFill="True">

 

<StackPanel Orientation="Horizontal" DockPanel.Dock="Bottom"

 

FlowDirection="RightToLeft">

 

<Button MinWidth="60" Content="Chiudi" Click="Chiudi" />

 

Codice XAML della finestra WPF

Lo riporto interamente qui di seguito.

Sub AddBookmark(ByVal Nome As String, ByVal url As String, ByVal Genere As String)New Object() {System.Math.Max(System.Threading.Interlocked.Increment(bookmarkIdent), bookmarkIdent - 1), Nome, url, Genere, DateTime.Now})"Bookmarks")

End

Sub
Sub Region

Tale metodo agisce quando la proprietà-argomento propertyName subisce modifiche. Nel nostro caso ciò avviene in occasione dell’aggiunta di un bookmarck:

"INotifyPropertyChanged"

 

 

 

 

 

#End

Public Event PropertyChanged As PropertyChangedEventHandler _Implements INotifyPropertyChanged.PropertyChangedPrivate Sub NotifyPropertyChanged(ByVal propertyName As [String])RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))

<?xml version="1.0" standalone="yes"?>

<Bibliotecario>

<xs:schema id="Bibliotecario" xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">

 

<xs:complexType>

<xs:choice minOccurs="0" maxOccurs="unbounded">

<xs:element name="Bookmarks">

<xs:complexType>

<xs:sequence>

<xs:element name="Id" type="xs:int" minOccurs="0" />

<xs:element name="Titolo" type="xs:string" minOccurs="0" />

<xs:element name="Uri" type="xs:string" minOccurs="0" />

<xs:element name="Genere" type="xs:string" minOccurs="0" />

<xs:element name="UltimaMod" type="xs:dateTime" minOccurs="0" />

</xs:sequence>

</xs:complexType>

</xs:element>

<xs:element name="Ident">

<xs:complexType>

<xs:sequence>

<xs:element name="Nome" type="xs:string" minOccurs="0" />

<xs:element name="Quant" type="xs:int" minOccurs="0" />

</xs:sequence>

</xs:complexType>

</xs:element>

</xs:choice>

</xs:complexType>

</xs:element>

</xs:schema>

<Bookmarks>

<Id>1</Id>

 

<Uri>http://www.oreilly.com/</Uri>

<Genere>Editore</Genere>

<UltimaMod>2011-09-01T11:16:17.9401388+02:00</UltimaMod>

 

<Bookmarks>

<Id>2</Id>

<Titolo>Manning</Titolo>

<Uri>http://www.Manning.com/</Uri>

 

<UltimaMod>2011-09-01T11:16:17.9401388+02:00</UltimaMod>

 

<Bookmarks>

<Id>3</Id>

<Titolo>WPF Tips &amp; tricks</Titolo>

<Uri>http://www.wpfitalia.it/</Uri>

 

<UltimaMod>2011-09-01T11:16:17.9401388+02:00</UltimaMod>

 

<Ident>

<Nome>Bookmarks</Nome>

 

</Ident>

</Bibliotecario>

L’eloquenza dello schema non pensa meriti altri commenti.

L’interfaccia INotifyPropertyChanged: ma serve davvero?

Proseguiamo nel nostro giro esplorativo, riportando di nuovo la regione relativa all’evento scatenato dall’interfaccia INotityPropertyChanged (non a tutti ben nota, più avanti riporto quel che ne dice la Guida):

"Operazioni sui dati"

 

 

AddBookmark(

AddBookmark(

AddBookmark(

Biblio.AcceptChanges()

 

Private Sub CreaBookmarksDefault()"Ident").Rows.Add(New Object() {"Bookmarks", bookmarkIdent})"O’ Reilly", "http://www.oreilly.com/", "Editore")"Manning", "http://www.Manning.com/", "Editore")"WPF Tips & tricks", "http://www.wpfitalia.it/", "Sito")End Sub

 

Biblio.ReadXml(BiblioFilename,

 

Public Sub Load()XmlReadMode.ReadSchema)End Sub

 

Biblio.AcceptChanges()

Biblio.WriteXml(BiblioFilename,

 

#End

Public Sub Save()XmlWriteMode.WriteSchema)End Sub Region

#Region

"INotifyPropertyChanged"

 

 

 

 

 

#End

End

Public Event PropertyChanged As PropertyChangedEventHandler _Implements INotifyPropertyChanged.PropertyChangedPrivate Sub NotifyPropertyChanged(ByVal propertyName As String)RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyName))End Sub Region Class
System.Data

Public

Class Biblioteca

 

Implements INotifyPropertyChanged

 

Public Biblio As DataSet

 

 

Private bookmarkIdent As Integer = 0Private BiblioFilename As String = "bookmarks.library"

 

CreaFonteDati()

 

Public Sub New()If Not File.Exists(BiblioFilename) Then

CreaBookmarksDefault()

Save()

 

Else

Load()

 

End If

 

End Sub

 

Biblio =

 

 

.Columns.Add(

.Columns.Add(

.Columns.Add(

.Columns.Add(

.Columns.Add(

 

Private Sub CreaFonteDati()New DataSet("Bibliotecario")Dim Bookmarks As New DataTable("Bookmarks")With Bookmarks"Id", GetType(Int32))"Titolo", GetType(String))"Uri", GetType(String))"Genere", GetType(String))"UltimaMod", GetType(DateTime))End With

 

 

.Columns.Add(

.Columns.Add(

 

Dim Ident As New DataTable("Ident")With Ident"Nome", GetType(String))"Quant", GetType(Int32))End With

Biblio.Tables.Add(Bookmarks)

Biblio.Tables.Add(Ident)

 

End Sub

 

Public ReadOnly Property Bookmarks() As DataTable

 

Get

 

 

Return Biblio.Tables("Bookmarks")End Get

 

End Property

 

Bookmarks.Rows.Add(

Biblio.AcceptChanges()

NotifyPropertyChanged(

 

Public Sub AddBookmark(ByVal Nome As String, ByVal url As String, ByVal Genere As String)New Object() {System.Math.Max(System.Threading.Interlocked.Increment(bookmarkIdent), bookmarkIdent - 1), Nome, url, Genere, DateTime.Now})"Bookmarks")End Sub
<Application x:Class="Application"

 

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

 

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

 

xmlns:local="clr-namespace:Bibliotecario"

 

StartupUri="MainWindow.xaml">

 

<Application.Resources>

 

<local:Biblioteca x:Key="Biblio" />

 

</Application.Resources>Application>

Delle due direttive locali evidenziate in grassetto, la cui sintassi do per nota, la prima aggiunge il namespace dell’intero progetto (Bibliotecario) la seconda punta alla classe Biblioteca con il dataset Biblio che funge da chiave (x:Biblio).

La classe Biblioteca e il suo dataset Bibliotecario

Senza troppo indugiare riportiamo subito l’intero codice, commentandone rapidamente le prime righe. Le direttive Imports "arruolano" le librerie per accesso a dati ADO, per l’Input/output e quella per il ComponentModel, indispensabile per gestire l’Interfaccia INotifyPropertyChanged, che viene subito fissata con la specifica istruzione Implements. Seguono le variabili Biblio (di tipo pubblico, dovendo questo dataset essere visibile all’esterno della classe), bookmarkIdent (identificatore del bookmark) e BiblioFileName, una stringa "bookmarks.library", che è il nome dell’archivio di tipo XML che descrive le caratteristiche strutturali dell’intero Bibliotecario (e non, si badi bene, della sola Biblioteca). Si osservi poi, in questa disamina iniziale, la routine d’avvio New. Essa si preoccupa anzitutto di creare il dataset Biblio, quindi controlla l’esistenza o meno dell’archivio BiblioFilename provvedendo nel primo caso a creare dei bookmark default (O’Reylly, Manning e WPF Tips, v. figura precedente) e a salvare l’archivio bookmarks.library e le modifiche a Biblio (metodo Save, v. più avanti) o altrimenti a caricare (metodo Load, v. più avanti) l’archivio bookmarks.library.

Print | posted on giovedì 8 settembre 2011 15:10 |

Feedback

No comments posted yet.

Post Comment

Title  
Name  
Email
Url
Comment   
Please add 8 and 7 and type the answer here:

Powered by:
Powered By Subtext Powered By ASP.NET