Alessandro Del Sole's Blog

{ A programming space about Microsoft® .NET® }
posts - 1908, 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

Disabilita cookie ShinyStat

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 & VB 2010: utilizzare ComboBox nelle celle di DataGrid per mostrare dati di lookup

Per quante possibilità di data-binding complesse WPF possa offrire, è fuori di dubbio che in molti casi si ha la necessità di rappresentare dati in forma tabulare e quindi il controllo DataGrid diventa un ottimo compagno d'avventura. Quando si lavora in questi scenari si può avere la necessità di consentire (o limitare, se volete) all'utente di scegliere il valore di una cella attraverso una combobox. I valori di questa combo possono provenire da sorgenti dati diverse come una tabella di lookup, ossia una tabella che elenca dei valori, ciascuno dei quali è dello stesso tipo (es. stringa, data...) della colonna nella tabella "master" rappresentata nella DataGrid. Tramite data-binding, poi, bisogna far sì che l'elemento selezionato nella tabella di lookup (e quindi nella combo) vada automaticamente a riempire il campo corrispondente nella row della DataGrid.

Facciamo un esempio per capire. Ipotizziamo di essere i responsabili di consulenze in un'azienda. Ciascuna consulenza è rappresentata da una classe Consulenza, che è così definita (in modo ovviamente del tutto semplificato):

Public Class Consulenza
    Public Property DataConsulenza As Date
    Public Property NomeConsulente As String
    Public Property Autovettura As String
End Class

Poiché abbiamo necessità di lavorare in data-binding, possiamo utilizzare una ObservableCollection(Of Consulenza) definita in questo modo:

Public Class Consulenze
    Inherits System.Collections.ObjectModel.ObservableCollection(Of Consulenza)

    'Qui carichiamo qualche dato fittizio direttamente in fase di creazione dell'oggetto
    Public Sub New()
        Dim c1 As New Consulenza With {.DataConsulenza = Date.Today, .NomeConsulente = "Alessandro"}
        Me.Add(c1)
    End Sub
End Class

Come abbiamo visto la classe Consulenza espone una proprietà Autovettura, di tipo stringa, che rappresenta l'auto aziendale che il consulente utilizzerà per recarsi dal cliente. Nel nostro esempio vogliamo che tale proprietà contenga il numero di targa dell'automezzo. Vogliamo che all'interno della DataGrid l'utente sia in grado di selezionare l'automezzo da un elenco messo a disposizione tramite combo box. Per fare questo predisponiamo una classe Autovettura, che fornirà anche una descrizione del modello:

Public Class Autovettura
    Public Property Targa As String
    Public Property Modello As String
End Class

Ci vuole poi una collezione di automezzi, per ciascuno dei quali andranno indicati targa e modello. Creiamo ancora una ObservableCollection in questo modo:

Public Class Autovetture
    Inherits System.Collections.ObjectModel.ObservableCollection(Of Autovettura)

    'Carica dei dati fittizi (anche le targhe sono di pura fantasia :-))
    Public Sub New()
        Dim a1 As New Autovettura With {.Modello = "Fiat Punto", .Targa = "AA000BB"}
        Dim a2 As New Autovettura With {.Modello = "Alfa 147", .Targa = "AA001BC"}
        Dim a3 As New Autovettura With {.Modello = "Citroen A3", .Targa = "AA002BD"}

        Me.Add(a1)
        Me.Add(a2)
        Me.Add(a3)
    End Sub
End Class

Ora, passando allo XAML, aggiungiamo una nuova origine dati (Data|Add new data source) di tipo Object, quindi selezionando Consulenza dall'elenco degli oggetti disponibili nel namespace dell'applicazione. Fatto questo, dalla finestra Data Sources trasciniamo l'origine dati in modo che, tramite drag and drop, Visual Studio 2010 faccia un po' di lavoro per noi generando anche le colonne per la DataGrid. L'IDE genera una CollectionViewSource che punta alla collezione di oggetti Consulenza. Ne aggiungiamo un'altra, sempre all'interno delle Window.Resources, che punti a una collezione di oggetti Autovettura e che ci servirà nella combo box:

        <CollectionViewSource x:Key="AutovettureViewSource" />

Possiamo a questo punto già scrivere il code-behind in Visual Basic che carichi entrambe le sorgenti dati e le assegni alle due CollectionViewSource:

    Private mieConsulenze As Consulenze
    Private elencoAutomezzi As Autovetture

    Private Sub Window_Loaded(ByVal sender As System.ObjectByVal e As System.Windows.RoutedEventArgsHandles MyBase.Loaded

        Dim ConsulenzaViewSource As System.Windows.Data.CollectionViewSource = CType(Me.FindResource("ConsulenzaViewSource"), System.Windows.Data.CollectionViewSource)
        'Load data by setting the CollectionViewSource.Source property:
        Dim AutovettureViewSource As System.Windows.Data.CollectionViewSource = CType(Me.FindResource("AutovettureViewSource"), System.Windows.Data.CollectionViewSource)

        'ConsulenzaViewSource.Source = [generic data source]
        mieConsulenze = New Consulenze
        elencoAutomezzi = New Autovetture

        ConsulenzaViewSource.Source = mieConsulenze
        AutovettureViewSource.Source = elencoAutomezzi
    End Sub

In uno scenario reale le origini dati potrebbero essere costituite da EntitySet provenienti da un modello a oggetti di Entity Framework piuttosto che delle ObservableCollection utilizzate in questo (semplice) esempio. Tornando allo XAML, noterete che l'IDE genera anche una colonna chiamata AutovetturaColumn e che è di tipo testo. Ora noi vogliamo sostituire questa con una combobox. Possiamo ricorrere all'elemento DataGridComboBoxColumn che richiede l'impostazione delle seguenti proprietà:

  • ItemsSource, che punta alla collezione che espone l'elenco da visualizzare nella combo
  • DisplayMemberPath, che stabilisce il valore di quale proprietà debba essere visualizzato nella combo (onde evitare di visualizzare il nome dell'oggetto .NET)
  • SelectedValuePath, che stabilisce il valore di quale proprietà andare a recuperare dall'oggetto selezionato
  • SelectedValueBinding, che permette di indicare a quale proprietà di ciascun oggetto Consulenza andrà collegato il valore della proprietà selezionato nella combo.

Il tutto si traduce nel seguente XAML:

                
                <DataGridComboBoxColumn x:Name="AutovetturaColumn" Header="Autovettura" Width="SizeToHeader" DisplayMemberPath="Modello"
                                        SelectedValuePath="Targa" SelectedValueBinding="{Binding Path=Autovettura}"
                                        ItemsSource="{Binding Source={StaticResource AutovettureViewSource}}">

                </DataGridComboBoxColumn>

Notate come la combo sia alimentata (ItemsSource) attraverso la CollectionViewSource che fa riferimento all'elenco di autovetture. Per verificare in fase di debug che effettivamente il valore pescato tramite combo sia quello della targa della macchina e che sia correttamente assegnato all'istanza della Consulenza su cui stiamo lavorando, abbiamo diverse possibilità e la più semplice è quella di scrivere nella finestra di Output il valore della targa quando si completa l'editing della riga, gestendo l'evento RowEditEnding della DataGrid in questo modo:

    Private Sub ConsulenzaDataGrid_RowEditEnding(ByVal sender As Object,
                                                 ByVal e As System.Windows.Controls.DataGridRowEditEndingEventArgs) _
                                                 Handles ConsulenzaDataGrid.RowEditEnding
        Debug.WriteLine(CType(e.Row.Item, Consulenza).Autovettura)
    End Sub

Se avviamo l'applicazione potremo notare che effettivamente i valori richiesti sono mostrati nella combo tramite la proprietà specificata con DisplayMemberPath e che, in fase di commit della riga, è la targa della macchina ad essere regolarmente assegnata all'oggetto Consulenza:

E' chiaro che gli scenari possono essere profondamente diversi, ma la logica di lookup con la DataGridComboBoxColumn è sostanzialmente questa, indipendentemente da quale sia l'origine dati (collection, EntitySet, DataTable) che popola la combo.

Alessandro

Print | posted on giovedì 14 ottobre 2010 13:29 | Filed Under [ Visual Basic Windows Presentation Foundation Visual Studio 2010 ]

Powered by:
Powered By Subtext Powered By ASP.NET