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 - seconda parte (Entity Framework)

Nel post precedente abbiamo introdotto una nuova caratteristica di Microsoft Visual Studio 2010, ossia il data-binding di tipo drag’n’drop per applicazioni WPF. Abbiamo visto come utilizzare questo tipo di tecnica nei confronti di un DataSet e di un database di Microsoft Access al fine di ottenere una rappresentazione di dati in forma tabulare.

 

In questo post faremo la stessa cosa, ma questa volta lavorando nei confronti di un Entity Data Model generato su ADO.NET Entity Framework, nei confronti di un database di SQL Server.

 

Dopo aver quindi aperto la Beta 1 di Visual Studio 2010, creiamo un nuovo progetto Visual Basic per applicazione WPF con le modalità illustrate la volta scorsa. Poiché nell’ambito delle applicazioni Visual Basic che usano Entity Framework la Beta 1 di Dev10 è abbastanza fragile e va in crash facilmente, vi consiglio di salvare e compilare il progetto subito e di ripetere queste operazioni abbastanza frequentemente. Si tratta di problemi di Beta, che sicuramente verranno risolti in futuro.

 

Fatto questo, andiamo ad aggiungere un nuovo Entity Data Model al progetto, utilizzando il comando Project|Add new item e selezionando l’apposito template dalla finestra Add new item:

Il primo passaggio della procedura guidata ci richiede di specificare se il modello deve essere generato da zero o a partire da un database esistente. Selezioniamo quest’ultima possibilità:

e andiamo avanti. Nella schermata successiva dobbiamo specificare la connessione dati, nel mio caso eseguita verso il database Northwind. Possiamo anche lasciare invariate le altre impostazioni:

Passando alla schermata successiva vanno indicate le tabelle del database che intendiamo mappare sotto forma di entità. Nel nostro caso selezioneremo ancora le tabelle Customers e Orders, classico scenario master-details. Una novità interessante è la possibilità di rendere al singolare i nomi delle entità attraverso un’apposita casella di controllo; Visual Studio si occuperà di rendere al plurale i nomi degli EntitySet:

Dopo alcuni secondi il modello a oggetti è disponibile nel designer per Entity Framework di Visual Studio, come possiamo vedere nella seguente figura:

 

A questo punto è opportuno salvare il progetto e poi compilarlo. Come detto, l’IDE potrebbe andare in crash a questo punto. Se avete salvato il progetto, potete chiudere Visual Studio e riaprirlo per tornare al progetto senza problemi. Andiamo ora ad aprire la finestra Data Sources, tramite l’apposito comando del menu Data. Il risultato che otterremo è il seguente:

La considerazione che ora viene spontaneo fare è che finalmente tale finestra, in Visual Studio 2010, offre il supporto anche ad ADO.NET Entity Framework, cosa che fino a Visual Studio 2008 SP 1 non avviene.

Ripetiamo gli stessi identici passaggi visti la volta scorsa, quindi trasciniamo la proprietà CompanyName dell’insieme Customers sulla finestra WPF e osserveremo come Visual Studio abbia generato il seguente XAML, che poi è uguale a quello dell’esempio del precedente post:

 

    <Window.Resources>

        <CollectionViewSource x:Key="CustomersViewSource__NorthwindEntities" d:DesignSource="{d:DesignInstance my:Customer,

                              CreateList=True}" />

    </Window.Resources>

    <Grid>

        <Grid DataContext="{StaticResource CustomersViewSource__NorthwindEntities}" Height="31.96" Margin="0,0,48,230" Name="Grid1"

              Width="230.136666666667">

            <Grid.ColumnDefinitions>

                <ColumnDefinition Width="Auto" />

                <ColumnDefinition Width="Auto" />

            </Grid.ColumnDefinitions>

            <Grid.RowDefinitions>

                <RowDefinition Height="Auto" />

            </Grid.RowDefinitions>

            <Label Content="Company Name:" Grid.Column="0" Grid.Row="0" HorizontalAlignment="Left" Margin="3"

                   VerticalAlignment="Center" />

            <ComboBox DisplayMemberPath="CompanyName" Grid.Column="1" Grid.Row="0" Height="23" HorizontalAlignment="Left"

                      IsSynchronizedWithCurrentItem="True"

                      ItemsSource="{Binding}" Margin="3" Name="CompanyNameComboBox" SelectedIndex="0" VerticalAlignment="Center"

                      Width="120">

                <ComboBox.ItemsPanel>

                    <ItemsPanelTemplate>

                        <VirtualizingStackPanel />

                    </ItemsPanelTemplate>

                </ComboBox.ItemsPanel>

            </ComboBox>

        </Grid>

    </Grid>

 

Quindi, anche in questo caso, evidenziamo la generazione di una CollectionViewSource che si occupa di collegare l’origine dati con l’interfaccia e che poi va a costituire la sorgente per la proprietà ItemsSource della ComboBox (questo avviene “a cascata” grazie all’assegnazione della proprietà DataContext del contenitore Grid). La Combo viene popolata in binding e visualizza, tramite DisplayMemberPath, il valore della proprietà CompanyName della collezione associata, ossia l’elenco di Customer.

 

Dalla finestra Data Sources trasciniamo poi sulla finestra anche l’oggetto Orders, nidificato in Customers, al fine di generare una DataGrid popolata automaticamente tramite data-binding. Il trascinamento farà si che Visual Studio generi il seguente XAML per la DataGrid:

 

        <DataGrid AutoGenerateColumns="False" EnableRowVirtualization="True" Height="200" HorizontalAlignment="Left"

                  IsSynchronizedWithCurrentItem="True" ItemsSource="{Binding}"

                  Margin="60,106,0,0" Name="OrdersDataGrid" RowDetailsVisibilityMode="VisibleWhenSelected" SelectedIndex="0"

                  VerticalAlignment="Top" Width="400">

            <DataGrid.Columns>

                <DataGridTextColumn Binding="{Binding Path=OrderID}" Header="Order ID" Width="SizeToHeader" />

                <DataGridTextColumn Binding="{Binding Path=EmployeeID}" Header="Employee ID" Width="SizeToHeader" />

                <DataGridTemplateColumn Header="Order Date" Width="SizeToHeader">

                    <DataGridTemplateColumn.CellTemplate>

                        <DataTemplate>

                            <DatePicker SelectedDate="{Binding Path=OrderDate}" />

                        </DataTemplate>

                    </DataGridTemplateColumn.CellTemplate>

                </DataGridTemplateColumn>

                <DataGridTemplateColumn Header="Required Date" Width="SizeToHeader">

                    <DataGridTemplateColumn.CellTemplate>

                        <DataTemplate>

                            <DatePicker SelectedDate="{Binding Path=RequiredDate}" />

                        </DataTemplate>

                    </DataGridTemplateColumn.CellTemplate>

                </DataGridTemplateColumn>

                <DataGridTemplateColumn Header="Shipped Date" Width="SizeToHeader">

                    <DataGridTemplateColumn.CellTemplate>

                        <DataTemplate>

                            <DatePicker SelectedDate="{Binding Path=ShippedDate}" />

                        </DataTemplate>

                    </DataGridTemplateColumn.CellTemplate>

                </DataGridTemplateColumn>

                <DataGridTextColumn Binding="{Binding Path=ShipVia}" Header="Ship Via" Width="SizeToHeader" />

                <DataGridTextColumn Binding="{Binding Path=Freight}" Header="Freight" Width="SizeToHeader" />

                <DataGridTextColumn Binding="{Binding Path=ShipName}" Header="Ship Name" Width="SizeToHeader" />

                <DataGridTextColumn Binding="{Binding Path=ShipAddress}" Header="Ship Address" Width="SizeToHeader" />

                <DataGridTextColumn Binding="{Binding Path=ShipCity}" Header="Ship City" Width="SizeToHeader" />

                <DataGridTextColumn Binding="{Binding Path=ShipRegion}" Header="Ship Region" Width="SizeToHeader" />

                <DataGridTextColumn Binding="{Binding Path=ShipPostalCode}" Header="Ship Postal Code" Width="SizeToHeader" />

                <DataGridTextColumn Binding="{Binding Path=ShipCountry}" Header="Ship Country" Width="SizeToHeader" />

            </DataGrid.Columns>

        </DataGrid>

 

Anche in questo caso la DataGrid è popolata tramite binding della proprietà ItemsSource. La generazione delle colonne non è automatica e ciò consente a Visual Studio di utilizzare i controlli più appropriati a seconda dei tipi di dato, come nel caso del DateTime per il quale viene utilizzato un DatePicker all’interno di colonne personalizzate (DataGridTemplateColumn) mentre per tutti gli altri campi viene utilizzato il tipo DataGridTextColumn per la visualizzazione generica in formato testo.

 

E’ stata anche generata la seguente riga nelle risorse, per l’utilizzo di una CollectionViewSource che ci permette di lavorare sugli ordini:

 

        <CollectionViewSource x:Key="CustomersOrdersViewSource" Source="{Binding Path=Orders, Source={StaticResource

                              CustomersViewSource__NorthwindEntities}}" />

 

Se andiamo ora a vedere cosa è successo nel file di code-behind, possiamo innanzitutto osservare la generazione di un metodo che esegue la query sull’elenco dei clienti che popola la Combo:

 

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

                                             System.Data.Objects.ObjectQuery(Of

                                             TabularDataWithEntityFramework.Customer)

 

        Dim CustomersQuery As System.Data.Objects.ObjectQuery(Of TabularDataWithEntityFramework.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

 

Una query molto semplice, che ottiene, in forma di ObjectQuery(Of Customer), l’elenco dei clienti con i relativi ordini collegati (metodo Include). L’altro codice generato è quello relativo al caricamento della finestra, che finalizza il databinding:

 

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

 

        Dim NorthwindEntities As TabularDataWithEntityFramework.NorthwindEntities = New TabularDataWithEntityFramework.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 TabularDataWithEntityFramework.Customer) = Me.GetCustomersQuery(NorthwindEntities)

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

        CustomersViewSource__NorthwindEntities.View.MoveCurrentToFirst()

    End Sub

 

In questo gestore di evento si ottiene l’istanza dell’ObjectContext di Entity Framework (NorthwindEntities), quindi si ottiene l’istanza della CollectionViewSource inerente i clienti. Poiché la query formulata nel metodo precedente include anche gli ordini, la DataGrid trae beneficio da questo per popolarsi.

 

Anche in questo caso, però, ci sono delle modifiche da fare. Innanzitutto torniamo allo XAML per posizionare in modo migliore i controlli e per aggiungere un pulsante che ci consenta di salvare le modifiche ai dati. Suddividiamo la Grid principale in tre righe:

 

    <Grid DataContext="{StaticResource CustomersOrdersViewSource}">

        <Grid.RowDefinitions>

            <RowDefinition Height="30" />

            <RowDefinition />

            <RowDefinition Height="40" />

        </Grid.RowDefinitions>

 

Posizioniamo quindi i controlli per la scelta del cliente nella prima riga:

 

        <Grid DataContext="{StaticResource CustomersViewSource__NorthwindEntities}"  Name="Grid1" Grid.Row="0">

 

Quindi spostiamo la DataGrid nella seconda riga:

 

        <DataGrid AutoGenerateColumns="False" EnableRowVirtualization="True" IsSynchronizedWithCurrentItem="True"

                  ItemsSource="{Binding}" Margin="0,34,0,0" Name="OrdersDataGrid"

                  RowDetailsVisibilityMode="VisibleWhenSelected" SelectedIndex="0" Grid.Row="1">

 

Infine, aggiungiamo, nella terza riga, un pulsante per salvare le modifiche:

 

        <Button Grid.Row="2" Width="100" Height="30" Name="SaveButton" Content="Save changes" />

 

Il tutto risulterà ora come in figura:

 

Facciamo doppio clic sul pulsante per generare un gestore di evento Click, che scriveremo come segue:

 

    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

            MessageBox.Show(ex.ToString)

        End Try

    End Sub

 

Il problema che ora si pone è che NorthwindEntities non è raggiungibile. Infatti, dal gestore Window_Loaded dobbiamo spostarlo a livello di classe. Quindi avremo la seguente dichiarazione:

 

    'TabularDataWithEntityFramework è il nome del mio progetto, con conseguente

    'namespace di primo livello

    Dim NorthwindEntities As TabularDataWithEntityFramework.NorthwindEntities

 

Poi, all’interno di Window_Loaded, modifichiamo la riga interessata in questo modo:

 

        NorthwindEntities = New TabularDataWithEntityFramework.NorthwindEntities()

 

Ora possiamo avviare l’applicazione. Otterremo un risultato simile alla seguente figura, dalla quale si evince la possibilità di utilizzare specifici controlli per la selezione/visualizzazione di date:

Come già detto precedentemente, la DataGrid consente il data-binding di tipo Two Ways, quindi possiamo modificare i dati all’interno del controllo con la certezza che, all’invocazione del metodo SaveChanges, verranno persistiti nel database sottostante. Inoltre, la DataGrid aggiunge sempre per default una riga vuota alla fine, che è possibile utilizzare per aggiungere nuovi record.

 

Possiamo così concludere questo secondo post inerente questa specifica novità per WPF in Visual Studio 2010. Nei prossimi due inizieremo a vedere qualcosa di diverso, con visualizzazioni di tipo detail invece che tabulari e l’utilizzo delle View per spostarci tra record.

 

Alessandro

Print | posted on domenica 7 giugno 2009 03:09 | Filed Under [ Visual Basic Windows Presentation Foundation Visual Studio 2010 ]

Powered by:
Powered By Subtext Powered By ASP.NET