Alessandro Del Sole's Blog

{ A programming space about Microsoft® .NET® }
posts - 1909, comments - 2047, trackbacks - 352

My Links

News

Your host

This is me! Questo spazio è dedicato a Microsoft® .NET®, di cui sono molto appassionato :-)

Cookie e Privacy

Microsoft MVP

My MVP Profile

Microsoft Certified Professional

Microsoft Specialist

Xamarin Certified Mobile Developer

Il mio libro su VB 2015!

Pre-ordina il mio libro su VB 2015 Pre-ordina il mio libro "Visual Basic 2015 Unleashed". Clicca sulla copertina per informazioni!

Il mio libro su WPF 4.5.1!

Clicca sulla copertina per informazioni! E' uscito il mio libro "Programmare con WPF 4.5.1". Clicca sulla copertina per informazioni!

These postings are provided 'AS IS' for entertainment purposes only with absolutely no warranty expressed or implied and confer no rights.
If you're not an Italian user, please visit my English blog

Le vostre visite

I'm a VB!

Guarda la mia intervista a Seattle

Follow me on Twitter!

Altri spazi

GitHub
I miei progetti open-source su GitHub

Article Categories

Archives

Post Categories

Image Galleries

Privacy Policy

WPF: il data-binding drag'n'drop in Visual Studio 2010 - quarta parte (Entity Framework)

In questo ultimo post della mini-serie dedicata al data-binding di tipo drag’n’drop per WPF, in Visual Studio 2010 Beta 1, continueremo il discorso inerente l’utilizzo di ADO.NET Entity Framework come fonte dati nell’ambito di un’applicazione Visual Basic che riprenderà quanto fatto nel terzo post nei confronti di un DataSet, quindi creeremo una finestra con visualizzazione di tipo Details in cui potremo scegliere un cliente da una ListBox, visualizzare i dettagli di un ordine sotto forma di dettaglio implementando pulsanti per la navigazione tra gli ordini.

 

Ciò premesso, dopo aver creato un nuovo progetto WPF con Visual Basic 2010 seguite tutti i passaggi visti nel secondo post di questa serie, fino al completamento della creazione dell’Entity Data Model. Accertiamoci, aprendo la finestra Data Sources, che questa contenga gli EntitySet generati da Entity Framework, come fonte dati quindi Customers e Orders. Dopodiché, dalla casella degli strumenti trasciniamo sulla finestra della nostra applicazione un controllo ListBox, riarragiando la disposizione della Grid principale in questo modo:

 

    <Grid>

        <Grid.ColumnDefinitions>

            <ColumnDefinition Width="200" />

            <ColumnDefinition/>

        </Grid.ColumnDefinitions>

        <Grid.RowDefinitions>

            <RowDefinition/>

            <RowDefinition Height="40"/>

        </Grid.RowDefinitions>

       

        <ListBox Grid.Column="0" Grid.Row="0"/>

    </Grid>

 

Ora, dalla finestra Data Sources, selezioniamo l’elemento CompanyName dell’oggetto Customers e trasciniamolo sulla ListBox come in figura:

Nell’ambito di tale operazione, Visual Studio ha generato un po’ di codice XAML. In primo luogo, ha generato una nuova risorsa che punta all’elenco dei clienti, ciascuno dei quali è rappresentato da un oggetto Customer e per i quali viene creata una lista:

 

    <Window.Resources>

        <CollectionViewSource x:Key="CustomersViewSource__NorthwindEntities" d:DesignSource="{d:DesignInstance my:Customer, CreateList=True}" />

    </Window.Resources>

 

Contestualmente Visual Studio assegna la proprietà DataContext della Grid principale, di modo che i controlli che da essa dipendono (in modo particolare la ListBox) siano alimentati proprio da tale origine:

 

    <Grid DataContext="{StaticResource CustomersViewSource__NorthwindEntities}">

 

Infine la ListBox, modificata in questo modo:

 

        <ListBox Grid.Column="0" Grid.Row="0"

                 DisplayMemberPath="CompanyName" IsSynchronizedWithCurrentItem="True"

                 ItemsSource="{Binding}" SelectedIndex="0" />

 

Si evince che il controllo è popolato in binding, tramite la proprietà ItemsSource. L’origine per il data-binding è costituita, come detto, dalla proprietà DataContext della Grid. Il passaggio successivo è quello di predisporre il data-binding per gli ordini. Torniamo alla finestra Data Sources e selezioniamo l’oggetto Orders nidificato in Customers, accertandoci che sia selezionata la visualizzazione di tipo Details come in figura:

 

Trasciniamo quindi Orders sulla parte destra della nostra finestra; al rilascio, dovremmo ottenere un risultato simile al seguente:

Come possiamo vedere, quindi, l’IDE ha generato per noi una serie di controlli, essenzialmente una serie di Label e TextBox (con eccezione delle date, rappresentate da DatePicker) racchiusa in una ulteriore Grid rappresentata dal seguente codice XAML:

 

        <Grid DataContext="{StaticResource CustomersOrdersViewSource}" Grid.Column="1" Name="Grid1">

            <Grid.ColumnDefinitions>

                <ColumnDefinition Width="Auto" />

                <ColumnDefinition Width="Auto" />

            </Grid.ColumnDefinitions>

            <Grid.RowDefinitions>

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

                <RowDefinition Height="Auto" />

            </Grid.RowDefinitions>

            <Label Content="Order ID:" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />

            <TextBox Grid.Column="1" Grid.Row="0" Height="23" HorizontalAlignment="Left" Margin="3" Name="OrderIDTextBox" Text="{Binding Path=OrderID}" VerticalAlignment="Center" Width="120" />

            <Label Content="Employee ID:" Grid.Column="0" Grid.Row="1" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />

            <TextBox Grid.Column="1" Grid.Row="1" Height="23" HorizontalAlignment="Left" Margin="3" Name="EmployeeIDTextBox" Text="{Binding Path=EmployeeID}" VerticalAlignment="Center" Width="120" />

            <Label Content="Order Date:" Grid.Column="0" Grid.Row="2" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />

            <DatePicker Grid.Column="1" Grid.Row="2" HorizontalAlignment="Left" Margin="3" Name="OrderDateDatePicker" SelectedDate="{Binding Path=OrderDate}" VerticalAlignment="Center" />

            <Label Content="Required Date:" Grid.Column="0" Grid.Row="3" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />

            <DatePicker Grid.Column="1" Grid.Row="3" HorizontalAlignment="Left" Margin="3" Name="RequiredDateDatePicker" SelectedDate="{Binding Path=RequiredDate}" VerticalAlignment="Center" />

            <Label Content="Shipped Date:" Grid.Column="0" Grid.Row="4" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />

            <DatePicker Grid.Column="1" Grid.Row="4" HorizontalAlignment="Left" Margin="3" Name="ShippedDateDatePicker" SelectedDate="{Binding Path=ShippedDate}" VerticalAlignment="Center" />

            <Label Content="Ship Via:" Grid.Column="0" Grid.Row="5" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />

            <TextBox Grid.Column="1" Grid.Row="5" Height="23" HorizontalAlignment="Left" Margin="3" Name="ShipViaTextBox" Text="{Binding Path=ShipVia}" VerticalAlignment="Center" Width="120" />

            <Label Content="Freight:" Grid.Column="0" Grid.Row="6" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />

            <TextBox Grid.Column="1" Grid.Row="6" Height="23" HorizontalAlignment="Left" Margin="3" Name="FreightTextBox" Text="{Binding Path=Freight}" VerticalAlignment="Center" Width="120" />

            <Label Content="Ship Name:" Grid.Column="0" Grid.Row="7" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />

            <TextBox Grid.Column="1" Grid.Row="7" Height="23" HorizontalAlignment="Left" Margin="3" Name="ShipNameTextBox" Text="{Binding Path=ShipName}" VerticalAlignment="Center" Width="120" />

            <Label Content="Ship Address:" Grid.Column="0" Grid.Row="8" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />

            <TextBox Grid.Column="1" Grid.Row="8" Height="23" HorizontalAlignment="Left" Margin="3" Name="ShipAddressTextBox" Text="{Binding Path=ShipAddress}" VerticalAlignment="Center" Width="120" />

            <Label Content="Ship City:" Grid.Column="0" Grid.Row="9" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />

            <TextBox Grid.Column="1" Grid.Row="9" Height="23" HorizontalAlignment="Left" Margin="3" Name="ShipCityTextBox" Text="{Binding Path=ShipCity}" VerticalAlignment="Center" Width="120" />

            <Label Content="Ship Region:" Grid.Column="0" Grid.Row="10" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />

            <TextBox Grid.Column="1" Grid.Row="10" Height="23" HorizontalAlignment="Left" Margin="3" Name="ShipRegionTextBox" Text="{Binding Path=ShipRegion}" VerticalAlignment="Center" Width="120" />

            <Label Content="Ship Postal Code:" Grid.Column="0" Grid.Row="11" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />

            <TextBox Grid.Column="1" Grid.Row="11" Height="23" HorizontalAlignment="Left" Margin="3" Name="ShipPostalCodeTextBox" Text="{Binding Path=ShipPostalCode}" VerticalAlignment="Center" Width="120" />

            <Label Content="Ship Country:" Grid.Column="0" Grid.Row="12" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />

            <TextBox Grid.Column="1" Grid.Row="12" Height="23" HorizontalAlignment="Left" Margin="3" Name="ShipCountryTextBox" Text="{Binding Path=ShipCountry}" VerticalAlignment="Center" Width="120" />

        </Grid>

 

Sebbene lungo, in realtà il codice è molto semplice. I controlli, quindi TextBox e DatePicker per le date (ed è importante notare come Visual Studio abbia automaticamente assegnato il controllo più appropriato in base al tipo di dato), sono popolati tramite Binding. La collezione di oggetti che costituisce l’origine dati, e che va a costituire il valore della proprietà DataContext per questa nuova Grid, è rappresentata da una nuova CollectionViewSource, chiamata CustomersOrdersViewSource e definita anch’essa nelle risorse:

 

        <CollectionViewSource x:Key="CustomersOrdersViewSource" Source="{Binding Path=Orders, Source={StaticResource CustomersViewSource__NorthwindEntities}}" />

    </Window.Resources>

 

Completiamo poi la nostra interfaccia aggiungendo alcuni pulsanti per lo spostamento tra gli ordini e per il salvataggio delle modifiche, scrivendo il seguente codice prima del tag di chiusura della Grid principale:

 

        <StackPanel Orientation="Horizontal" Grid.Column="1" Grid.Row="1">

            <Button Margin="5" Content="&lt;Previous" Name="PrevButton" Width="100" Height="30"/>

            <Button Margin="5" Content="Next>" Name="NextButton" Width="100" Height="30"/>

            <Button Margin="5" Content="Save changes" Name="SaveButton" Width="100" Height="30"/>

        </StackPanel>

 

Passiamo ora al codice Visual Basic che l’IDE ha generato per noi. Nel file di code-behind troveremo, innanzitutto, un metodo chiamato GetCustomersQuery che esegue la query sull’elenco di clienti, restituendola sotto forma di ObjectQuery(Of T):

 

    Private Function GetCustomersQuery(ByVal NorthwindEntities As ItemsBrowsingWithEntityFramework.NorthwindEntities) As

                     System.Data.Objects.ObjectQuery(Of ItemsBrowsingWithEntityFramework.Customer)

 

        Dim CustomersQuery As System.Data.Objects.ObjectQuery(Of ItemsBrowsingWithEntityFramework.Customer) =

                              NorthwindEntities.Customers

        'Update the query to include Orders data in Customers. You can modify this code as needed.

        CustomersQuery = CustomersQuery.Include("Orders")

        'Do not modify the following code.

        Return CustomersQuery

    End Function

 

Chiaramente potremmo modificare la query a seconda delle eventuali nostre esigenze, qui stiamo sostanzialmente ottenendo l’elenco di tutti i clienti. L’eager-loading (metodo Include) ottiene anche gli ordini associati a ciascun cliente.

Il rimanente codice si occupa di caricare e collegare i dati al caricamento della Window:

 

    Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded

 

        Dim NorthwindEntities As ItemsBrowsingWithEntityFramework.NorthwindEntities = New

                                 ItemsBrowsingWithEntityFramework.NorthwindEntities()

        'Load data into Customers. You can modify this code as needed.

        Dim CustomersViewSource__NorthwindEntities As System.Windows.Data.CollectionViewSource = CType(Me.FindResource

            ("CustomersViewSource__NorthwindEntities"), System.Windows.Data.CollectionViewSource)

        Dim CustomersQuery As System.Data.Objects.ObjectQuery(Of ItemsBrowsingWithEntityFramework.Customer) = Me.GetCustomersQuery

            (NorthwindEntities)

        CustomersViewSource__NorthwindEntities.Source = CustomersQuery.Execute(System.Data.Objects.MergeOption.AppendOnly)

        CustomersViewSource__NorthwindEntities.View.MoveCurrentToFirst()

    End Sub

 

Si ottiene l’istanza della CollectionViewSource associata ai clienti che riceve il risultato della query precedentemente analizzata. L’ultima riga di codice sposta il focus sul primo elemento della collezione ottenuta.

Si tratta di una generazione di codice abbastanza essenziale, il più delle volte non idonea a soddisfare esigenze reali. Tipicamente anche le modifiche che andremo a fare ora non saranno probabilmente sufficienti in uno scenario reale, se non altro ci consentiranno di migliorare il codice di default e soprattutto di capire dove e come intervenire per migliorare il tutto. Poiché vogliamo essere in grado di implementare nostre azioni nei confronti dell’origine dati, dobbiamo spostare a livello di classe la dichiarazione di istanza dell’ObjectContext e quella della CollectionViewSource afferente i Customers, oltre ad aggiungere una nuova dichiarazione:

 

Class Window1

 

    Private NorthwindEntities As ItemsBrowsingWithEntityFramework.NorthwindEntities

    Private CustomersViewSource__NorthwindEntities As System.Windows.Data.CollectionViewSource

    Private CustomersOrdersViewSource As CollectionViewSource

 

Dichiariamo, così, anche la CollectionViewSource associata agli ordini. Successivamente modifichiamo il gestore Window_Loaded come segue:

 

    Private Sub Window_Loaded(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles MyBase.Loaded

 

        NorthwindEntities = New ItemsBrowsingWithEntityFramework.NorthwindEntities()

 

        'Load data into Customers. You can modify this code as needed.

        CustomersViewSource__NorthwindEntities = CType(Me.FindResource("CustomersViewSource__NorthwindEntities"),

        System.Windows.Data.CollectionViewSource)

        'Otteniamo anche l’istanza dell’altra CollectionViewSource.

        CustomersOrdersViewSource = CType(Me.FindResource("CustomersOrdersViewSource"), System.Windows.Data.CollectionViewSource)

 

        Dim CustomersQuery As System.Data.Objects.ObjectQuery(Of ItemsBrowsingWithEntityFramework.Customer) = Me.GetCustomersQuery

           (NorthwindEntities)

        CustomersViewSource__NorthwindEntities.Source = CustomersQuery.Execute(System.Data.Objects.MergeOption.AppendOnly)

        CustomersViewSource__NorthwindEntities.View.MoveCurrentToFirst()

    End Sub

 

Come già precisato, tecniche migliori di programmazione devono prevedere la separazione dello strato dell’interfaccia da quelli delle logiche e dell’accesso ai dati, per cui questo esempio così com’è va bene a livello introduttivo ma va sicuramente rivisto.

 

Associamo poi un gestore per il Click del pulsante Save Changes:

 

    Private Sub SaveButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles SaveButton.Click

        Try

            Me.NorthwindEntities.SaveChanges()

        Catch ex As Exception

 

        End Try

    End Sub

 

Quindi, sfruttando le View che abbiamo introdotto nel precedente post, possiamo gestire gli altri due pulsanti grazie ai quali possiamo spostarci agevolmente tra gli ordini:

 

    Private Sub NextButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles NextButton.Click

        If Me.CustomersOrdersViewSource.View.CurrentPosition < CType(Me.CustomersOrdersViewSource.View, CollectionView).Count - 1 Then

            Me.CustomersOrdersViewSource.View.MoveCurrentToNext()

        End If

 

    End Sub

 

    Private Sub PrevButton_Click(ByVal sender As System.Object, ByVal e As System.Windows.RoutedEventArgs) Handles PrevButton.Click

        If Me.CustomersOrdersViewSource.View.CurrentPosition > 0 Then

            Me.CustomersOrdersViewSource.View.MoveCurrentToPrevious()

        End If

    End Sub

 

Se ora proviamo ad avviare l’applicazione otterremo un risultato simile al seguente:

Vi basterà utilizzare i vari controlli e, se volete, fare delle modifiche ai dati per accertarvi che tutto effettivamente funziona. Non trovo superfluo ricordare ancora che la conclusione di questa mini-serie introduttiva non precluderà l’approfondimento di tecniche inerenti il data-binding in WPF 4.0 e che gli esempi proposti sono suscettibili di ulteriori modifiche a livello di architettura per una migliore separazione tra i vari strati.

 

Il codice sorgente relativo a tutti e quattro gli esempi sin qui illustrati è disponibile in area Download di VB T&T, a questo indirizzo.

 

Alessandro

Print | posted on lunedì 15 giugno 2009 01:57 | Filed Under [ Visual Basic Windows Presentation Foundation Visual Studio 2010 ]

Powered by:
Powered By Subtext Powered By ASP.NET