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 - terza parte (DataSet)

Riprendiamo il discorso iniziato nei post precedenti, inerente le nuove tecniche di data-binding drag’n’drop disponibili in Visual Studio 2010 già nell’attuale Beta 1, dal punto di vista di applicazioni Visual Basic. In precedenza abbiamo visto come creare applicazioni per la rappresentazione di dati in forma tabulare, sfruttando il nuovo controllo DataGrid sia nei confronti di oggetti DataSet che nei confronti di Entity Data Model & Entity Framework.

 

In questo terzo post faremo qualcosa di diverso e sfrutteremo il drag’n’drop per creare una rappresentazione di tipo master-detail più interessante lavorando, come nel primo post della serie, nei confronti di un DataSet generato a partire da un database di Microsoft Access. Nel prossimo post, invece, ripeteremo il tutto nei confronti di un Entity Data Model generato a partire da un database di SQL Server.

 

Ripetiamo quindi tutti i passaggi illustrati nel primo post fino alla generazione del DataSet compresa; in sostanza, nella finestra origini dati dobbiamo vedere disponibili gli oggetti Customers e Orders. Compiliamo poi il progetto per far sì che i riferimenti agli oggetti vengano aggiornati. A questo punto, suddividiamo la griglia principale in due colonne e due righe aggiungendo, inoltre, un controllo ListBox che conterrà l’elenco dei clienti, 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>

 

Apriamo ora la finestra Data Sources, espandiamo Customers e selezioniamo la voce CompanyName. Trasciniamo tale voce sulla ListBox e rilasciamo:

Visual Studio genera quindi il seguente XAML (l’unica modifica che ho fatto riguarda il posizionamento in righe/colonne):

 

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

                 IsSynchronizedWithCurrentItem="True"

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

 

Quindi la ListBox viene popolata in binding, come ormai abbiamo imparato. Per far sì che questo accada, Visual Studio ha generato due nuove risorse: il riferimento al DataSet e una CollectionViewSource che punta ai Customers esposti dal DataSet stesso:

 

    <Window.Resources>

        <my:NorthwindDataSet x:Key="NorthwindDataSet" />

        <CollectionViewSource x:Key="CustomersViewSource" Source="{Binding Path=Customers, Source={StaticResource NorthwindDataSet}}" />

    </Window.Resources>

    <Grid DataContext="{StaticResource CustomersViewSource}">

 

L’assegnazione della proprietà DataContext nella Grid finalizza il tutto. Ora torniamo alla finestra Data Sources e selezioniamo la voce Orders nidificata in Customers, assicurandoci di selezionare la visualizzazione di tipo Details come in figura:

 

 

Selezioniamo tale voce, trasciniamola sulla colonna libera della Window e rilasciamo. Dopo alcuni secondi otterremo il seguente risultato:

che si traduce, a livello di XAML, in alcune aggiunte. Innanzitutto una CollectionViewSource che punta agli ordini relazionati ai clienti:

 

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

 

quindi la generazione di una nuova Grid, la cui proprietà DataContext è assegnata proprio con la nuova CollectionViewSource, che definisce un elenco delle voci per ciascun ordine second lo stile Detail:

 

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

            <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" />

                <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="Customer 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="CustomerIDTextBox" Text="{Binding Path=CustomerID}"

 VerticalAlignment="Center" Width="120" />

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

            <TextBox Grid.Column="1" Grid.Row="2" 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="3" HorizontalAlignment="Left" Margin="3" VerticalAlignment="Center" />

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

 VerticalAlignment="Center" />

            <Label Content="Required 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="RequiredDateDatePicker" SelectedDate="{Binding Path=RequiredDate}"

 VerticalAlignment="Center" />

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

            <DatePicker Grid.Column="1" Grid.Row="5" HorizontalAlignment="Left" Margin="3" Name="ShippedDateDatePicker" SelectedDate="{Binding Path=ShippedDate}"

 VerticalAlignment="Center" />

            <Label Content="Ship Via:" 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="ShipViaTextBox" Text="{Binding Path=ShipVia}"

VerticalAlignment="Center" Width="120" />

            <Label Content="Freight:" 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="FreightTextBox" Text="{Binding Path=Freight}"

VerticalAlignment="Center" Width="120" />

            <Label Content="Ship Name:" 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="ShipNameTextBox" Text="{Binding Path=ShipName}"

VerticalAlignment="Center" Width="120" />

            <Label Content="Ship Address:" 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="ShipAddressTextBox" Text="{Binding Path=ShipAddress}"

VerticalAlignment="Center" Width="120" />

            <Label Content="Ship City:" 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="ShipCityTextBox" Text="{Binding Path=ShipCity}"

VerticalAlignment="Center" Width="120" />

            <Label Content="Ship Region:" 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="ShipRegionTextBox" Text="{Binding Path=ShipRegion}"

VerticalAlignment="Center" Width="120" />

            <Label Content="Ship Postal Code:" 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="ShipPostalCodeTextBox" Text="{Binding Path=ShipPostalCode}"

VerticalAlignment="Center" Width="120" />

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

            <TextBox Grid.Column="1" Grid.Row="13" Height="23" HorizontalAlignment="Left" Margin="3" Name="ShipCountryTextBox" Text="{Binding Path=ShipCountry}"

VerticalAlignment="Center" Width="120" />

        </Grid>

 

Come si può osservare ciascun elemento dell’ordine è rappresentato da una coppia Label/TextBox oppure Label/DatePicker, in quanto Visual Studio è in grado di associare il controllo più appropriato a seconda del tipo di dato. I vari controlli sono popolati in binding secondo le modalità ormai già note (Binding Path=).

 

Passando al codice Visual Basic, quello che segue è ciò che Visual Studio 2010 genera per default:

 

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

 

        Dim NorthwindDataSet As ItemsBrowsingDemoWithDataSet.NorthwindDataSet = CType(Me.FindResource("NorthwindDataSet"),

                                ItemsBrowsingDemoWithDataSet.NorthwindDataSet)

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

        Dim NorthwindDataSetCustomersTableAdapter As ItemsBrowsingDemoWithDataSet.NorthwindDataSetTableAdapters.CustomersTableAdapter =

            New ItemsBrowsingDemoWithDataSet.NorthwindDataSetTableAdapters.CustomersTableAdapter()

        NorthwindDataSetCustomersTableAdapter.Fill(NorthwindDataSet.Customers)

        Dim CustomersViewSource As System.Windows.Data.CollectionViewSource = CType(Me.FindResource("CustomersViewSource"),

            System.Windows.Data.CollectionViewSource)

        CustomersViewSource.View.MoveCurrentToFirst()

        'Load data into the table Orders. You can modify this code as needed.

        Dim NorthwindDataSetOrdersTableAdapter As ItemsBrowsingDemoWithDataSet.NorthwindDataSetTableAdapters.OrdersTableAdapter = New

            ItemsBrowsingDemoWithDataSet.NorthwindDataSetTableAdapters.OrdersTableAdapter()

        NorthwindDataSetOrdersTableAdapter.Fill(NorthwindDataSet.Orders)

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

            System.Windows.Data.CollectionViewSource)

        CustomersOrdersViewSource.View.MoveCurrentToFirst()

    End Sub

 

In parole povere, si ottiene l’istanza del DataSet e delle CollectionViewSource, inoltre vengono popolati i TableAdapter relativi alle tabelle Customers e Orders. Il data-binding vero e proprio è posto in essere dallo XAML.

Per quanto utile, questo codice non ci consente di spostarci tra gli ordini, ossia tra gli oggetti che costituiscono le collezioni CollectionViewSource quindi dobbiamo farlo manualmente. In primo luogo, quindi, muoviamo a livello di classe le dichiarazioni di DataSet, CollectionViewSource e TableAdapter per gli Orders:

 

    'Muovo a livello di classe le dichiarazioni

    Private NorthwindDataSet As ItemsBrowsingDemoWithDataSet.NorthwindDataSet

    Private CustomersViewSource As System.Windows.Data.CollectionViewSource

    Private CustomersOrdersViewSource As System.Windows.Data.CollectionViewSource

    Private NorthwindDataSetOrdersTableAdapter As ItemsBrowsingDemoWithDataSet.NorthwindDataSetTableAdapters.OrdersTableAdapter

 

Nel codice sottostante, poi, sostituiamo la dichiarazione di istanza degli oggetti sopra citati con una semplice assegnazione:

 

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

 

        NorthwindDataSet = CType(Me.FindResource("NorthwindDataSet"), ItemsBrowsingDemoWithDataSet.NorthwindDataSet)

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

        Dim NorthwindDataSetCustomersTableAdapter As ItemsBrowsingDemoWithDataSet.NorthwindDataSetTableAdapters.CustomersTableAdapter =

            New ItemsBrowsingDemoWithDataSet.NorthwindDataSetTableAdapters.CustomersTableAdapter()

        NorthwindDataSetCustomersTableAdapter.Fill(NorthwindDataSet.Customers)

 

        'Modifico la dichiarazione della CollectionViewSource

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

        CustomersViewSource.View.MoveCurrentToFirst()

 

        'Modifico la dichiarazione del TableAdapter.

        NorthwindDataSetOrdersTableAdapter = New ItemsBrowsingDemoWithDataSet.NorthwindDataSetTableAdapters.OrdersTableAdapter()

        NorthwindDataSetOrdersTableAdapter.Fill(NorthwindDataSet.Orders)

 

        'Modifico la dichiarazione della CollectionViewSource

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

        CustomersOrdersViewSource.View.MoveCurrentToFirst()

    End Sub

 

Torniamo poi per un momento allo XAML e definiamo tre nuovi pulsanti, due per andare avanti e indietro, uno per salvare le modifiche ai dati:

 

        <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>

 

Possiamo sfruttare proprio le View delle CollectionViewSource per spostarci tra gli elementi. I due seguenti gestori di evento fanno proprio questo:

 

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

        If Me.CustomersOrdersViewSource.View.CurrentPosition < CType(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

 

Mentre il seguente codice si occupa di salvare le modifiche.

 

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

        Try

            Me.NorthwindDataSetOrdersTableAdapter.Update(Me.NorthwindDataSet.Orders)

            MessageBox.Show("Done!")

        Catch ex As Exception

            MessageBox.Show("An error occurred while attempting to save changes")

        End Try

    End Sub

 

Ora possiamo finalmente avviare e testare la nostra applicazione, che si presenterà come in figura:

Ovviamente il codice è suscettibile di notevoli miglioramenti, ad esempio utilizzando tecniche per separare i comandi dall’interfaccia (come nel Model-View-ViewModel) ma può essere un buon modo di iniziare a prendere confidenza con le View le quali espongono anche metodi per l’aggiunta e la rimozione di oggetti. A tal proposito si potrebbe prevedere di implementare una finestra per l’aggiunta di un ordine, direttamente in binding, per poi sfruttare i metodi stessi delle View.

 

Il codice sorgente a corredo del presente post sarà rilasciato in concomitanza col prossimo, a completamento della serie di articoli.

 

Alessandro

Print | posted on martedì 9 giugno 2009 03:04 | Filed Under [ Visual Basic Windows Presentation Foundation Visual Studio 2010 ]

Powered by:
Powered By Subtext Powered By ASP.NET